前面已经让板子能够支持NORFlash了,还没有支持NANDFlash。
一. 找到之前第一步注释掉的NAND相关的宏定义
//#define CONFIG_S3C2410 /* specifically a SAMSUNG S3C2410 SoC */
#ifdef CONFIG_S3C2410
#define CONFIG_NAND_S3C2410
#define CONFIG_SYS_S3C2410_NAND_HWECC
#define CONFIG_SYS_MAX_NAND_DEVICE 1
#define CONFIG_SYS_NAND_BASE 0x4E000000
#endif
为了程序的兼容性,不直接把宏定义取消注释。在宏定义边上再宏定义一个
//#define CONFIG_S3C2410 /* specifically a SAMSUNG S3C2410 SoC */
#define CONFIG_S3C2440 /* specifically a SAMSUNG S3C2410 SoC */
然后将NAND的相关宏定义格式改为
#ifdef CONFIG_S3C2410
#define CONFIG_NAND_S3C2410
#define CONFIG_SYS_S3C2410_NAND_HWECC
#else
#define CONFIG_NAND_S3C2440
#define CONFIG_SYS_S3C2440_NAND_HWECC
#endif
#define CONFIG_SYS_MAX_NAND_DEVICE 1
#define CONFIG_SYS_NAND_BASE 0x4E000000
#endif
这样这个uboot以后对2410还是有很好的支持性的。
编译,解决错误
二. 报错:s3c2410_nand.c的第72行出现错误。(一般出现一堆错误的时候,解决第一个错误,解决完再重新编译, 再解决第一个错误,直到没有错误。因为很多错误是有连带性的,一个错误解决可能会连带解决一些错误)
找到代码段,是调用了一个结构体,结构体的定义是这样的
struct s3c2410_nand *nand = s3c2410_get_base_nand();
定位到这个结构体的初始化
#ifdef CONFIG_S3C2410
/* NAND FLASH (see S3C2410 manual chapter 6) */
struct s3c2410_nand {
u32nfconf;
u32nfcmd;
u32nfaddr;
u32nfdata;
u32nfstat;
u32nfecc;
};
#endif
#ifdef CONFIG_S3C2440
/* NAND FLASH (see S3C2440 manual chapter 6) */
struct s3c2440_nand {
u32 nfconf;
u32 nfcont;
u32 nfcmd;
u32 nfaddr;
u32 nfdata;
u32 nfeccd0;
u32 nfeccd1;
u32 nfeccd;
u32 nfstat;
u32 nfstat0;
u32 nfstat1;
};
#endif
这里可以明白,我们使用的宏是2440,所以2410这个结构体并没有,在代码中把2410改为2440。
我们干脆就重新做一个s3c2440_nand.c
文件,找到目录drivers\mtd\nand
里面的s3c2410_nand.c
,复制一份改为s3c2440_nand.c
,要将其加入uboot,所以找到该文件目录里的Makefile,找到里面有一条语句
COBJS-$(CONFIG_NAND_S3C2410) += s3c2410_nand.o
说明原先的2410也是条件编译进入uboot。在include\configs
目录内找到smdk2440.h。
#ifdef CONFIG_CMD_NAND
#ifdef CONFIG_S3C2410
#define CONFIG_NAND_S3C2410
#define CONFIG_SYS_S3C2410_NAND_HWECC
#else
#define CONFIG_NAND_S3C2440
#define CONFIG_SYS_S3C2440_NAND_HWECC
#endif
#define CONFIG_SYS_MAX_NAND_DEVICE 1
#define CONFIG_SYS_NAND_BASE 0x4E000000
#endif
宏定义在这里,这段代码段就是我们刚才修改过的,也就是说s3c2410_nand.c
本身就已经不参与编译了,我们再边上加上一句
COBJS-$(CONFIG_NAND_S3C2410) += s3c2410_nand.o
COBJS-$(CONFIG_NAND_S3C2440) += s3c2440_nand.o
如果定义了CONFIG_NAND_S3C2440
,那么就编译s3c2440_nand.c
这样就可以了。
进入s3c2440_nand.c
目录。将刚才发现错误的地方修改如下
struct s3c2440_nand *nand = s3c2440_get_base_nand();
三.查看nand相关函数是否支持2440
在board_init_r
中找到
nand_init(); /* go init the NAND */
进入函数
void nand_init(void)
{
#ifdef CONFIG_SYS_NAND_SELF_INIT
board_nand_init();
#else
int i;
for (i = 0; i < CONFIG_SYS_MAX_NAND_DEVICE; i++)
nand_init_chip(i);
#endif
printf("%lu MiB\n", total_nand_size / 1024);
#ifdef CONFIG_SYS_NAND_SELECT_DEVICE
/*
* Select the chip in the board/cpu specific driver
*/
board_nand_select_device(nand_info[nand_curr_device].priv, nand_curr_device);
#endif
}
查看得知CONFIG_SYS_NAND_SELF_INIT
这个宏定义是否定义由另一个宏定义决定
#if defined(CONFIG_NAND_FSL_ELBC)
#define CONFIG_SYS_NAND_SELF_INIT
#endif
而CONFIG_NAND_FSL_ELBC
没有被定义,也就是说程序是执行的是#else
下面的内容,进入nand_init_chip(i);
函数。
#ifndef CONFIG_SYS_NAND_SELF_INIT
static void nand_init_chip(int i)
{
struct mtd_info *mtd = &nand_info[i];
struct nand_chip *nand = &nand_chip[i];
ulong base_addr = base_address[i];
int maxchips = CONFIG_SYS_NAND_MAX_CHIPS;
if (maxchips < 1)
maxchips = 1;
mtd->priv = nand;
nand->IO_ADDR_R = nand->IO_ADDR_W = (void __iomem *)base_addr;
if (board_nand_init(nand))
return;
if (nand_scan(mtd, maxchips))
return;
nand_register(i);
}
#endif
进入board_nand_init
看看
int board_nand_init(struct nand_chip *nand)
{
u_int32_t cfg;
u_int8_t tacls, twrph0, twrph1;
struct s3c24x0_clock_power *clk_power = s3c24x0_get_base_clock_power();
struct s3c2440_nand *nand_reg = s3c2440_get_base_nand();
debug("board_nand_init()\n");
writel(readl(&clk_power->clkcon) | (1 << 4), &clk_power->clkcon);
/* initialize hardware */
#if defined(CONFIG_S3C24XX_CUSTOM_NAND_TIMING)
tacls = CONFIG_S3C24XX_TACLS;
twrph0 = CONFIG_S3C24XX_TWRPH0;
twrph1 = CONFIG_S3C24XX_TWRPH1;
#else
tacls = 4;
twrph0 = 8;
twrph1 = 8;
#endif
cfg = S3C2410_NFCONF_EN;
cfg |= S3C2410_NFCONF_TACLS(tacls - 1);
cfg |= S3C2410_NFCONF_TWRPH0(twrph0 - 1);
cfg |= S3C2410_NFCONF_TWRPH1(twrph1 - 1);
writel(cfg, &nand_reg->nfconf);
/* initialize nand_chip data structure */
nand->IO_ADDR_R = (void *)&nand_reg->nfdata;
nand->IO_ADDR_W = (void *)&nand_reg->nfdata;
nand->select_chip = s3c2440_nand_select;
/* read_buf and write_buf are default */
/* read_byte and write_byte are default */
#ifdef CONFIG_NAND_SPL
nand->read_buf = nand_read_buf;
#endif
/* hwcontrol always must be implemented */
nand->cmd_ctrl = s3c2440_hwcontrol;
nand->dev_ready = s3c2440_dev_ready;
#ifdef CONFIG_S3C2410_NAND_HWECC
nand->ecc.hwctl = s3c2410_nand_enable_hwecc;
nand->ecc.calculate = s3c2410_nand_calculate_ecc;
nand->ecc.correct = s3c2410_nand_correct_data;
nand->ecc.mode = NAND_ECC_HW;
nand->ecc.size = CONFIG_SYS_NAND_ECCSIZE;
nand->ecc.bytes = CONFIG_SYS_NAND_ECCBYTES;
#else
nand->ecc.mode = NAND_ECC_SOFT;
#endif
#ifdef CONFIG_S3C2410_NAND_BBT
nand->options = NAND_USE_FLASH_BBT;
#else
nand->options = 0;
#endif
debug("end of nand_init\n");
return 0;
}
下面这段代码应该是设置NAND控制器的时间参数的。我当时写的时候三个时间参数设置是
tacls = 0;
twrph0 = 1;
twrph1 = 0;
但是无所谓,只要它的时间参数比我的久就行了,久是肯定可以直接用的,比我的时间短那就得看手册支不支持怎么短的时间了。
tacls = 4;
twrph0 = 8;
twrph1 = 8;
#endif
cfg = S3C2410_NFCONF_EN;
cfg |= S3C2410_NFCONF_TACLS(tacls - 1);
cfg |= S3C2410_NFCONF_TWRPH0(twrph0 - 1);
cfg |= S3C2410_NFCONF_TWRPH1(twrph1 - 1);
writel(cfg, &nand_reg->nfconf);
查看手册比对寄存器设置的时候这些代码是否是对的,我就不想管这些了,直接粗暴一点,反正之前这块设置的代码还在,我就直接改成了
#else
tacls = 4;
twrph0 = 8;
twrph1 = 8;
#endif
#if 0
cfg = S3C2410_NFCONF_EN;
cfg |= S3C2410_NFCONF_TACLS(tacls - 1);
cfg |= S3C2410_NFCONF_TWRPH0(twrph0 - 1);
cfg |= S3C2410_NFCONF_TWRPH1(twrph1 - 1);
#else
/* 设置时序 */
cfg = ((twrph1 - 1)<<12)|((twrph0 - 1)<<8)|((tacls - 1)<<4);
#endif
writel(cfg, &nand_reg->nfconf);
设置完时序以后 初始化ECC, 禁止片选还没有做,我也自己给他在后面加上
/* 使能NAND Flash控制器, 初始化ECC, 禁止片选 */
writel( (1<<4)|(1<<1)|(1<<0), &nand_reg->nfcont);
函数剩下的部分是结构体参数的赋值,应该是NAND的底层操作函数在这里上报。
nand->select_chip = NULL;
/* read_buf and write_buf are default */
/* read_byte and write_byte are default */
#ifdef CONFIG_NAND_SPL
nand->read_buf = nand_read_buf;
#endif
/* hwcontrol always must be implemented */
nand->cmd_ctrl = s3c2440_hwcontrol;
nand->dev_ready = s3c2440_dev_ready;
#ifdef CONFIG_S3C2410_NAND_HWECC
nand->ecc.hwctl = s3c2410_nand_enable_hwecc;
nand->ecc.calculate = s3c2410_nand_calculate_ecc;
nand->ecc.correct = s3c2410_nand_correct_data;
nand->ecc.mode = NAND_ECC_HW;
nand->ecc.size = CONFIG_SYS_NAND_ECCSIZE;
nand->ecc.bytes = CONFIG_SYS_NAND_ECCBYTES;
#else
nand->ecc.mode = NAND_ECC_SOFT;
#endif
#ifdef CONFIG_S3C2410_NAND_BBT
nand->options = NAND_USE_FLASH_BBT;
#else
nand->options = 0;
#endif
我们要看现在uboot的nand能不能支持2440了,就一个个把这些函数看下去,看每一个函数里面的代码是否和2440匹配。
a.select_chip
函数设置的是null
,就先不管,CONFIG_NAND_SPL
没有宏定义,所以read_buf
也先不管,先看nand->cmd_ctrl = s3c2410_hwcontrol;
这个函数
先把名字都从2410改成2440。
static void s3c2440_hwcontrol(struct mtd_info *mtd, int cmd, unsigned int ctrl)
{
struct nand_chip *chip = mtd->priv;
struct s3c2440_nand *nand = s3c2440_get_base_nand();
debug("hwcontrol(): 0x%02x 0x%02x\n", cmd, ctrl);
if (ctrl & NAND_CTRL_CHANGE) {
ulong IO_ADDR_W = (ulong)nand;
if (!(ctrl & NAND_CLE))
IO_ADDR_W |= S3C2410_ADDR_NCLE;
if (!(ctrl & NAND_ALE))
IO_ADDR_W |= S3C2410_ADDR_NALE;
chip->IO_ADDR_W = (void *)IO_ADDR_W;
if (ctrl & NAND_NCE)
writel(readl(&nand->nfconf) & ~S3C2410_NFCONF_nFCE,
&nand->nfconf);
else
writel(readl(&nand->nfconf) | S3C2410_NFCONF_nFCE,
&nand->nfconf);
}
if (cmd != NAND_CMD_NONE)
writeb(cmd, chip->IO_ADDR_W);
}
这个函数应该是负责写命令或者写数据,直接将其修改为我们自己的
static void s3c2440_hwcontrol(struct mtd_info *mtd, int dat, unsigned int ctrl)
{
struct s3c2440_nand *nand = s3c2440_get_base_nand();
if (ctrl & NAND_CLE)
{
/* 发命令 */
writeb(dat, &nand->nfcmd);
}
else if (ctrl & NAND_ALE)
{
/* 发数据 */
writeb(dat, &nand->nfaddr);
}
}
接下来就是比对每一个操作函数里面的操作寄存器的方式和我们的是否一样,同时把所有的2440修改为2410。
之前代码里看到过select_chip 函数是空,那么怎么选中片子呢?
nand->select_chip = null;
一层层函数下去找到nand_set_defaults函数,看名字是设置默认值
void board_init_r(gd_t *id, ulong dest_addr)
nand_init(); /* go init the NAND */
nand_init_chip(i);
nand_scan(mtd, maxchips)
nand_scan_ident(mtd, maxchips, NULL);
nand_set_defaults(chip, busw);
这个函数的作用是,查看之前上报用的结构体,如果某项为空,就使用程序自带的默认函数。
/*
* Set default functions
*/
static void nand_set_defaults(struct nand_chip *chip, int busw)
{
/* check for proper chip_delay setup, set 20us if not */
if (!chip->chip_delay)
chip->chip_delay = 20;
/* check, if a user supplied command function given */
if (chip->cmdfunc == NULL)
chip->cmdfunc = nand_command;
/* check, if a user supplied wait function given */
if (chip->waitfunc == NULL)
chip->waitfunc = nand_wait;
if (!chip->select_chip)
chip->select_chip = nand_select_chip;
if (!chip->read_byte)
chip->read_byte = busw ? nand_read_byte16 : nand_read_byte;
if (!chip->read_word)
chip->read_word = nand_read_word;
if (!chip->block_bad)
chip->block_bad = nand_block_bad;
if (!chip->block_markbad)
chip->block_markbad = nand_default_block_markbad;
if (!chip->write_buf)
chip->write_buf = busw ? nand_write_buf16 : nand_write_buf;
if (!chip->read_buf)
chip->read_buf = busw ? nand_read_buf16 : nand_read_buf;
if (!chip->verify_buf)
chip->verify_buf = busw ? nand_verify_buf16 : nand_verify_buf;
if (!chip->scan_bbt)
chip->scan_bbt = nand_default_bbt;
if (!chip->controller)
chip->controller = &chip->hwcontrol;
}
找到它默认的选择片子的函数
/**
* nand_select_chip - [DEFAULT] control CE line
* @mtd: MTD device structure
* @chipnr: chipnumber to select, -1 for deselect
*
* Default select function for 1 chip devices.
*/
static void nand_select_chip(struct mtd_info *mtd, int chipnr)
{
struct nand_chip *chip = mtd->priv;
switch (chipnr) {
case -1:
chip->cmd_ctrl(mtd, NAND_CMD_NONE, 0 | NAND_CTRL_CHANGE);
break;
case 0:
break;
default:
BUG();
}
}
-1表示取消选中,0表示选中,这里只写了取消选中,没写选中,显然这个函数我们是用不了的。自己在s3c2440_nand.c
建一个函数
static void s3c2440_nand_select(struct mtd_info *mtd, int chipnr)
{
struct s3c2440_nand *nand = s3c2440_get_base_nand();
switch (chipnr) {
case -1: /* 取消选中 */
nand->nfcont |= (1 << 1);
break;
case 0:
nand->nfcont &= ~(1 << 1);
break;
default:
BUG();
}
}
在结构体上报中修改一下
nand->select_chip = s3c2440_nand_select;
编译,烧写,运行。
没有错误且已经能识别NAND了,因为烧写在NAND里,0地址对应的是片内内存,无法去访问NOR,所以NOR识别出来应该是0k。但是NOR启动的话,两个都能识别。
U-Boot 2012.04.01 (Aug 12 2016 - 16:02:01)
CPUID: 32440001
FCLK: 400 MHz
HCLK: 100 MHz
PCLK: 50 MHz
DRAM: 64 MiB
WARNING: Caches not enabled
Flash: 0 KB
NAND: 256 MiB
*** Warning - bad CRC, using default environment
In: serial
Out: serial
Err: serial
Net: CS8900-0
SMDK2410 #
后记:
boot有四五百K,我使用的是oflash,烧写速度很慢,提速的方式可以使用先烧写下的好用的uboot,我使用了韦东山的,两百多K,然后再使用好用的uboot去驱动dnw下载。
现在我们写的boot已经能使用串口了,因此可以使用串口下载,速度也还可以。
输入help查看指令,有loady下载方式
loady - load binary file over serial line (ymodem mode)
我使用的Xshell没找到loady模式,(load有好几种模式,loadx,loady,loadz,大多数都不支持loady)。百度下载了一个XP的超级终端,提供loady。终端中串口连接上,输入命令。
loady 0x30000000
后面的是烧写地址。板子就在等待文件,终端中选择boot路径,选择loady模式,点确定就能下载。发送文件这个步骤手速要快,如果失败了,就是时间间隔太久了,可以复制文件路径,进去直接粘贴点确定,要快一点。
烧写进去以后是在板子的内存里,输入
nand erase 0 80000 //擦除nand, 从0地址一直到80000
nand write 0 80000 //将内存中的boot写入nand,从0地址到80000
reset //重启boot