很多天没有看嵌入式的东西了,今天来看一下,继续之前移植uboot到jz2440开发板。今天我们来实现Uboot支持NAND FLASH。
在之前的文章里(点击连接查看之前的记录),我们为了编译通过把NAND FLASH 给屏蔽掉了,现在把它加回来。
将:include/configs/smdk2440.h: 中的#define CONFIG_CMD_NAND取消注释,重新编译,看一下编译的结果错误是什么?错误如下:
,遇到错误要按照从第一个错误开始解决的办法,因为有时候第一个错误解决了,后面的错误可能就没有了。我们这里第一个错误是:
s3c2410_nand.c:72: error: dereferencing pointer to incomplete type
显示大概的意思是不完整的类型,我们去s3c2410_nand.c中72行去寻找,是如下代码:
if (ctrl & NAND_NCE)
72行: writel(readl(&nand->nfconf) & ~S3C2410_NFCONF_nFCE,
&nand->nfconf);
调用了一个nand结构体:
struct s3c2410_nand *nand = s3c2410_get_base_nand();
结构体如下:
#ifdef CONFIG_S3C2410
/* NAND FLASH (see S3C2410 manual chapter 6) */
struct s3c2410_nand {
u32 nfconf;
u32 nfcmd;
u32 nfaddr;
u32 nfdata;
u32 nfstat;
u32 nfecc;
};
#endif
我们应该是没有定义CONFIG_S3C2410,因为之前是我们在smdk2440.h中注释掉了2410的定义,换成了2440:
//#define CONFIG_S3C2410 /* specifically a SAMSUNG S3C2410 SoC */
#define CONFIG_S3C2440 /* specifically a SAMSUNG S3C2440 SoC */
那我们只能修改代码支持2440的部分了(想要更加了解代码如何修改,需要了解nand flash的操作,详细可以去看韦东山视频第二期的关于nand flash的操作)。
操作步骤:
1.把drivers\mtd\nand\s3c2410_nand.c复制为s3c2440_nand.c
该目录下的Makefile里面关于2410的配置为:
COBJS-$(CONFIG_NAND_S3C2410) += s3c2410_nand.o
去smdk2440.h中搜索CONFIG_NAND_S3C2410,发现有如下两行代码:
#define CONFIG_NAND_S3C2410
#define CONFIG_SYS_S3C2410_NAND_HWECC
将这两行代码修改为:
#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
修改上面的Makefile:
添加一行:
COBJS-$(CONFIG_NAND_S3C2440) += s3c2440_nand.o
这代表如果配置文件里面定义了CONFIG_NAND_S3C2440,则执行这一行的编译。
2.首先我们去看一下板级初始化相关函数board.c里面关于nand初始化的函数,在board.c中board_init_r函数中有:
void board_init_r(gd_t *id, ulong dest_addr)
{
......
......
nand_init(); /* go init the NAND */
......
......
}
跳转到如下代码(nand.c中):
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
}
先跳转到nand_init_chip这里执行:
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 = 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 = s3c2410_hwcontrol;
nand->dev_ready = s3c2410_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;
}
将以上的这句话:
struct s3c2410_nand *nand_reg = s3c2410_get_base_nand();
改为:
struct s3c2440_nand *nand_reg = s3c2440_get_base_nand();
然后往下看代码,看到设置时序的时候,与2440手册对比发现,发现时序设置不对,它是适用于2410的,并不适用于2440,将以下代码:
cfg = S3C2410_NFCONF_EN;
cfg |= S3C2410_NFCONF_TACLS(tacls - 1);
cfg |= S3C2410_NFCONF_TWRPH0(twrph0 - 1);
cfg |= S3C2410_NFCONF_TWRPH1(twrph1 - 1);
修改为:
/* 设置时序 lyy*/ cfg = ((tacls-1)<<12)|((twrph0-1)<<8)|((twrph1-1)<<4);
然后我们先来做一件事,把这个程序中的关于ECC的代码全部删掉,这一部分太复杂,但是对我们的程序又没有影响太大。删掉下面的代码:
#ifdef CONFIG_S3C2410_NAND_HWECC
void s3c2410_nand_enable_hwecc(struct mtd_info *mtd, int mode)
{
struct s3c2410_nand *nand = s3c2410_get_base_nand();
debug("s3c2410_nand_enable_hwecc(%p, %d)\n", mtd, mode);
writel(readl(&nand->nfconf) | S3C2410_NFCONF_INITECC, &nand->nfconf);
}
static int s3c2410_nand_calculate_ecc(struct mtd_info *mtd, const u_char *dat,
u_char *ecc_code)
{
struct s3c2410_nand *nand = s3c2410_get_base_nand();
ecc_code[0] = readb(&nand->nfecc);
ecc_code[1] = readb(&nand->nfecc + 1);
ecc_code[2] = readb(&nand->nfecc + 2);
debug("s3c2410_nand_calculate_hwecc(%p,): 0x%02x 0x%02x 0x%02x\n",
mtd , ecc_code[0], ecc_code[1], ecc_code[2]);
return 0;
}
static int s3c2410_nand_correct_data(struct mtd_info *mtd, u_char *dat,
u_char *read_ecc, u_char *calc_ecc)
{
if (read_ecc[0] == calc_ecc[0] &&
read_ecc[1] == calc_ecc[1] &&
read_ecc[2] == calc_ecc[2])
return 0;
printf("s3c2410_nand_correct_data: not implemented\n");
return -1;
}
#endif
然后我们用source insight里面的替代功能,将s3c2410_hwcontrol,s3c2410_dev_ready,s3c2440_nand,s3c2440_get_base_nand,这几个函数全部替换成…2440….
再来分析:s3c2440_hwcontrol函数,它最后面有这样几行代码:
if (ctrl & NAND_NCE) /* 使能选中 */
writel(readl(&nand->nfconf) & ~S3C2410_NFCONF_nFCE,
&nand->nfconf);
else /* 取消选中 */
writel(readl(&nand->nfconf) | S3C2410_NFCONF_nFCE,
&nand->nfconf);
这个是控制寄存器的片选的配置,这是2410的配置,我们是2440的配置,需要查看2440手册查看相关寄存器,我们的2440需要配置的是NFCONT寄存器。查看寄存器配置后,将上面的代码修改为:
if (ctrl & NAND_NCE) /* 使能选中 */
writel(readl(&nand->nfcont) & ~(1<<1),
&nand->nfcont);
else /* 取消选中 */
writel(readl(&nand->nfcont) | (1<<1),
&nand->nfcont);
突然想到上面有一个地方还行需要设置一下nfcont寄存器,在s3c2440_nand.c中的board_nand_init函数中加入一段话(下面两行):
/* 设置时序 lyy*/
cfg = ((tacls-1)<<12)|((twrph0-1)<<8)|((twrph1-1)<<4);
writel(cfg, &nand_reg->nfconf);
/* 使能NAND Flash控制器, 初始化ECC, 禁止片选 */ /*加入的*/
writel((1<<4)|(1<<1)|(1<<0),&nand_reg->nfcont); /*加入的*/
通过分析,发现还需要在s3c2440_nand.c中的board_nand_init函数里加上nand->select_chip = s3c2440_nand_select;这个函数,将以下代码修改为:nand->select_chip = s3c2440_nand_select;
nand->select_chip = NULL;
修改为:
nand->select_chip = s3c2440_nand_select;
编写s3c2440_nand_select函数,放在board_nand_init上面。
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();
}
}
通过分析知道,需要修改s3c2440_hwcontrol函数重新全部修改为下面得函数:
static void s3c2440_hwcontrol(struct mtd_info *mtd, int dat, unsigned int ctrl)
{
struct nand_chip *chip = mtd->priv;
struct s3c2440_nand *nand = s3c2440_get_base_nand();
if (ctrl & NAND_CLE)
{
/* 发命令 */
writeb(dat, &nand->nfcmd);
}
if (ctrl & NAND_ALE)
{
/* 发地址 */
writeb(dat, &nand->nfaddr);
}
}
上面的分析过于复杂,对于我来说还是有一定难度,我也无法用文字描述清楚,下面给出函数调用的大致流程,等以后水平高了,再回头来看年轻的时候做过的记录。
分析过程:
nand_init
nand_init_chip
board_nand_init
设置nand_chip结构体, 提供底层的操作函数
nand_scan
nand_scan_ident
nand_set_defaults
chip->select_chip = nand_select_chip;
chip->cmdfunc = nand_command;
chip->read_byte = busw ? nand_read_byte16 : nand_read_byte;
nand_get_flash_type
chip->select_chip
chip->cmdfunc(mtd, NAND_CMD_RESET, -1, -1);
nand_command() // 即可以用来发命令,也可以用来发列地址(页内地址)、行地址(哪一页)
chip->cmd_ctrl
s3c2440_hwcontrol
chip->cmdfunc(mtd, NAND_CMD_READID, 0x00, -1);
*maf_id = chip->read_byte(mtd);
*dev_id = chip->read_byte(mtd);
修改的文件一共涉及三个文件:s3c2440_nand.c,smdk2440.h,Makefile,这三个文件。
重新编译烧写到开发板(烧写步骤会单独写一篇文章记录):
很奇怪,又出现了这个错误:
Flash: ERROR: too many flash sectors
记得在之前修改代码支持NOR FLASH中也出现过一样的错误(点击查看之前的文章)。我们将CONFIG_SYS_MAX_FLASH_SECT这个值改大一些试一下,改为128(我记得之前改过,怎么又变回去了?奇怪!!!)
然后重新烧写启动:
从中可以看出已经识别出了nand:256MiB.那么说明我们的uboot已经支持nand flash。
下面从NAND 启动看一下,首先我们从NOR直接把uboot拷贝到NAND。输入以下命令:
nand erase 0 80000
nand write 0 0 80000
然后测试烧写到NNAD FLASH中的uboot是否正常
nand read 30000000 0 80000
cmp.b 0 30000000 80000 (比较拷贝的代码是否全部一样)
然后切换到NAND重启。显示:
这样的话,就说明我们的nand启动也是正常的!!!!
写了很久,真的很累也很难,但是我相信付出一定有回报的~
想获得各种学习资源以及交流学习的加我:
qq:1126137994
微信:liu1126137994
可以共同交流关于嵌入式,操作系统,C++语言,C语言,数据结构等技术问题!