方便起见,本节以上一节中生成的以Nor启动的u-boot作为修改目标,在以后的修改中,不管是SPL启动的u-boot还是Nor启动的u-boot修改的内容和方法都是一样的。如果你对NandFlash的操作不是很熟悉,请先移步到本博客博文《NandFlash操作详解》,熟悉了NandFlash的操作后移植u-boot的NandFlash部分就会很轻松了。
启动u-boot后提示“NAND:? 0 MiB”,所以使用grep “NAND:” *-nR 搜索这个提示信息,最后可以定位到common/board_r.c中的initr_nand函数,在initr_nand函数中有对NandFlash进行初始化的函数nand_init(),下面追踪进这个初始化函数中分析代码。
board_init_r
???????? initr_nand
?????????????????? nand_init(drivers/mtd/nand/nand.c)
??????????????????????????? nand_init_chip
???????????????????????????????????? board_nand_init(drivers/mtd/nand/s3c2410_nand.c)
追踪到板级支持函数board_nand_init时发现这个函数位于drivers/mtd/nand/s3c2410_nand.c,所以需要复制s3c2410_nand.c文件为s3c2440_nand.c:
root@ubuntu:/home/uboot/u-boot-2015.07-rc3# cp drivers/mtd/nand/s3c2410_nand.c drivers/mtd/nand/s3c2440_nand.c ? ??? ? ? ? ? ? ? ???? ? ? ? ? ? ? ??? ?
|
?
修改Makefile支持s3c2440_nand.c:
root@ubuntu:/home/uboot/u-boot-2015.07-rc3# vim drivers/mtd/nand/Makefile
drivers/mtd/nand/Makefile中:
61 obj-$(CONFIG_NAND_NOMADIK) += nomadik.o
?62 obj-$(CONFIG_NAND_S3C2410) += s3c2410_nand.o? ? ? ? ?
?63 obj-$(CONFIG_NAND_S3C2440) += s3c2440_nand.o
?64 obj-$(CONFIG_NAND_SPEAR) += spr_nand.o
|
?
可以看到s3c2410_nand.o是否编译取决于CONFIG_NAND_S3C2410,所以需要在smdk2440中把NandFlash相关的宏修改过来:
smdk2440.h中:
177 #ifdef CONFIG_CMD_NAND? ? ? ??
178 #if 0
179 #define CONFIG_NAND_S3C2410
180 #define CONFIG_SYS_S3C2410_NAND_HWECC
181 #else
182 #define CONFIG_NAND_S3C2440
183 #define CONFIG_SYS_S3C2440_NAND_HWECC
184 #endif
185
186 #define CONFIG_SYS_MAX_NAND_DEVICE????? 1
187 #define CONFIG_SYS_NAND_BASE??????????? 0x4E000000
|
在smdk2440_nand.c中使用替换功能吧全部的2410替换成2440.make编译测试是否通过。
make
编译通过。
C语言是自顶向下执行的,结合执行顺序追踪initr_nand函数可以整理出如下执行结构:
board_init_r
???????? initr_nand
?????????????????? nand_init(drivers/mtd/nand/nand.c)
??????????????????????????? nand_init_chip//设置nand_chip结构体,提供底层的操作函数
???????????????????????????????????? board_nand_init(drivers/mtd/nand/s3c2440_nand.c)
?????????????????????????????????????????????? nand->select_chip = NULL;
?????????????????????????????????????????????? nand->cmd_ctrl = s3c24x0_hwcontrol;
?????????????????????????????????????????????? nand->dev_ready = s3c24x0_dev_ready;
???????????????????????????????????? nand_scan(drivers/mtd/nand/nand_base.c)
?????????????????????????????????????????????? nand_scan_ident
??????????????????????????????????????????????????????? nand_set_defaults//设置nand_chip结构体的默认值
???????????????????????????????????????????????????????????????? chip->cmdfunc = nand_command
???????????????????????????????????????????????????????????????? chip->waitfunc = nand_wait
???????????????????????????????????????????????????????????????? chip->select_chip = nand_select_chip
???????????????????????????????????????????????????????????????? chip->read_byte = busw ? nand_read_byte16 : nand_read_byte;
??????????????????????????????????????????????????????? nand_get_flash_type
???????????????????????????????????????????????????????????????? chip->select_chip(mtd,0);//片选
???????????????????????????????????????????????????????????????? chip->cmdfunc(mtd,NAND_CMD_RESET,-1,-1);
???????????????????????????????????????????????????????????????? chip->cmdfunc(mtd,NAND_CMD_READID,0x00,-1);
???????????????????????????????????????????????????????????????? *maf_id = chip->read_byte(mtd);
???????????????????????????????????????????????????????????????? *dev_id = chip->read_byte(mtd);?? ? ? ? ? ? ? ??
|
在nand_scan_ident函数中会调用nand_get_flash_type函数,阅读代码和注释可以知道nand_get_flash_type的功能是获取厂商ID和设备ID,打开NandFlash手册找到获取ID的时序:

通过时序图可以知道读取ID的操作步骤应该是:
1、? 片选
2、? 发送命令0x90h
3、? 发送列地址00h
4、? 读厂商ID
5、? 读设备ID
6、? 读取3rd、4th、5th Cycle
7、? 取消片选
对照注释和代码后:
?
1、? 片选?????????????????????????????????????????????????????????????????? select_chip(mtd,0);
1.1?? 复位???????????????????????????????????????????????????????????????? chip->cmdfunc(mtd,-1);
2、? 发送命令0x90h???????????????????????????????????????????? chip->cmdfunc(mtd,-1);
3、? 发送列地址00h???????????????????????????????????????????? chip->cmdfunc(mtd,-1);
4、? 读厂商ID???????????????????????????????????????????????????????? *maf_id= chip->read_byte(mtd);
5、? 读设备ID???????????????????????????????????????????????????????? *dev_id= chip->read_byte(mtd);
6、? 读取3rd、4th、5th Cycle
7、? 取消片选
?
片选函数select_chip在nand_set_defaults函数中被设置为nand_select_chip,追踪到nand_select_chip函数为(drivers/mtd/nand/nand_base.c):
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();
}
}
|
可以看到在case 0的情况是什么也没做,而分析代码可以知道case 0是选中的情况,正确的选中是需要操作NFCONT寄存器的[1]位的,而这里什么也没做。这个片选函数位于drivers/mtd/nand/nand_base.c中,而这个文件不是s3c2440独有的文件,所以不建议在这哥文件中修改,我们仿造这个片选函数在s3c2440_nand.c中新建一个函数s3c2440_nand_select():
drivers/mtd/nand/s3c2440_nand.c中:
112 static void s3c2440_nand_select(struct mtd_info *mtd,int chipnr)
113 {
114???????? struct s3c24x0_nand *nand = s3c24x0_get_base_nand();? ? ? ? ? ?
115
116???????? switch (chipnr) {
117???????? case -1:
118???????????????? nand->nfcont |= (1<<1);
119???????????????? break;
120? ???????case 0:
121???????????????? nand->nfcont &= ~(1<<1);
122???????????????? break;
123
124???????? default:
125???????????????? BUG();
126???????? }
127 }
128
129
130 int board_nand_init(struct nand_chip *nand)
131 {
132???????? u_int32_t cfg;
?
|
新增s3c2440_nand_select函数后在board_nand_init中赋值使s3c2440_nand_select有效:
drivers/mtd/nand/s3c2440_nand.c中:? ? ? ? ? ? ? ? ? ? ? ? ? ?
159???????? nand->IO_ADDR_R = (void *)&nand_reg->nfdata;
160???????? nand->IO_ADDR_W = (void *)&nand_reg->nfdata;
161
162???????? nand->select_chip = s3c2440_nand_select;
163
164???????? /* read_buf and write_buf are default */
165???????? /* read_byte and write_byte are default */
|
可以看出,chip->cmdfunc(mtd,-1)函数即负责发送命令90h,也负责发送地址00h。而chip->cmdfunc函数在nand_set_defaults函数中被赋值为nand_command函数。nand_command函数追踪为:(位于drivers/mtd/nand/nand_base.c中)
nand_command(struct mtd_info *mtd,unsigned int command,int column,int page_addr)?? ? ? ? ? ? ? ? ? ?
chip->cmd_ctrl(mtd,readcmd,ctrl);//发送命令
chip->cmd_ctrl(mtd,command,column,ctrl);//发送列号
chip->cmd_ctrl(mtd,page_addr,ctrl)//;发送行号
chip->cmd_ctrl(mtd,NAND_CMD_STATUS,NAND_CTRL_CLE | NAND_CTRL_CHANGE);
?
|
可以在代码中看出nand_command的工作都是调用chip->cmd_ctrl来完成的,而chip->cmd_ctrl函数在board_nand_init函数中被赋值为s3c24x0_hwcontrol,也就是说s3c24x0_hwcontrol函数负责发送命令和地址,发送地址还是发送命令由chip->cmd_ctrl传入的ctrl参数决定!
???? 现在知道了s3c24x0_hwcontrol的作用,原来的s3c24x0_hwcontrol函数代码过于复杂,修改s3c24x0_hwcontrol函数代码为:
drivers/mtd/nand/s3c2440_nand.c文件中:? ?
41 static void s3c24x0_hwcontrol(struct mtd_info *mtd,int dat,unsigned int ctrl)
?42 {
?43???????? struct s3c24x0_nand *nand = s3c24x0_get_base_nand();
?44
?45???????? if (ctrl & NAND_CLE)
?46???????? {
?47???????????????? writeb(dat,&nand->nfcmd);
?48???????? }
?49
?50???????? else if (ctrl & NAND_ALE)
?51???????? {
?52?????????????? ??writeb(dat,&nand->nfaddr);
?53???????? }
?54 }
?55
?56 static int s3c24x0_dev_ready(struct mtd_info *mtd)
|
?
是否还记得NandFlash裸机操作时是需要先做NandFlash的初始化的,而u-boot的NandFlash初始化位于board_nand_init函数中,如下:
drivers/mtd/nand/s3c2440_nand.c文件中:? ?
136 #endif
137
138
139???????? cfg = S3C2440_NFCONF_EN;
140???????? cfg |= S3C2440_NFCONF_TACLS(tacls - 1);
141???????? cfg |= S3C2440_NFCONF_TWRPH0(twrph0 - 1);
142???????? cfg |= S3C2440_NFCONF_TWRPH1(twrph1 - 1);
143???????? writel(cfg,&nand_reg->nfconf);
144
145???????? /* initialize nand_chip data structure */
146???????? nand->IO_ADDR_R = (void *)&nand_reg->nfdata;
147???????? nand->IO_ADDR_W = (void *)&nand_reg->nfdata;
148
149???????? nand->select_chip = s3c2440_nand_select;
?
|
追踪S3C2440_NFCONF_EN等宏可以看到:
#define S3C2440_NFCONF_EN????????? (1<<15)
#define S3C2440_NFCONF_TACLS(x)??? ((x)<<8)
#define S3C2440_NFCONF_TWRPH0(x)?? ((x)<<4)
#define S3C2440_NFCONF_TWRPH1(x)?? ((x)<<0)
而s3c2440手册中对NFCONT的定义如下:

所以这些宏都是错误的,并且board_nand_init函数中没有配置NFCONT寄存器,反正是做初始化,就把原来的部分代码注释,使用自己写的初始化代码:
修改drivers/mtd/nand/s3c2440_nand.c中的board_nand_init函数:?? ? ? ? ? ? ? ??
116 int board_nand_init(struct nand_chip *nand)
117 {
118???????? u_int32_t cfg;
119???????? u_int8_t tacls,twrph0,twrph1;
120???????? struct s3c24x0_clock_power *clk_power = s3c24x0_get_base_clock_power();
121???????? struct s3c24x0_nand *nand_reg = s3c24x0_get_base_nand();
122
123???????? debug("board_nand_init()n");
124
125???????? writel(readl(&clk_power->clkcon) | (1 << 4),&clk_power->clkcon);
126
127???????? /* initialize hardware */
128 #if defined(CONFIG_S3C24XX_CUSTOM_NAND_TIMING)
129???????? tacls? = CONFIG_S3C24XX_TACLS;
130???????? twrph0 = CONFIG_S3C24XX_TWRPH0;
131???????? twrph1 =? CONFIG_S3C24XX_TWRPH1;
132 #else
133???????? tacls = 4;
134???????? twrph0 = 8;
135???????? twrph1 = 8;
136 #endif
137
138 /*
139???????? cfg = S3C2440_NFCONF_EN;
140???????? cfg |= S3C2440_NFCONF_TACLS(tacls - 1);
141???????? cfg |= S3C2440_NFCONF_TWRPH0(twrph0 - 1);
142???????? cfg |= S3C2440_NFCONF_TWRPH1(twrph1 - 1);
143 */
144
145???????? cfg = ((tacls - 1)<<12) | ((twrph0 - 1)<<8) | ((twrph1 - 1)<<4);
146???????? writel(cfg,&nand_reg->nfconf);
147
148???????? writel((1<<4)|(1<<1)|(1<<0),&nand_reg->nfcont);
149
150???????? /* initialize nand_chip data structure */
151???????? nand->IO_ADDR_R = (void *)&nand_reg->nfdata;
152???????? nand->IO_ADDR_W = (void *)&nand_reg->nfdata;
153
154???????? nand->select_chip = s3c2440_nand_select;
|
?
???????? 经过上面的修改,u-boot已经正常支持NandFlash了,测试思路:往内存中写入特定值,再使用nand write命令把内存中的内容写入到NandFlash,使用nand dump命令或nand read查看NandFlash中的值。
SMDK2410 # md.b 32000000 20? //查看内存0x32000000中连续0x20字节的内容
32000000: f7 8c 33 cc 33 ec 3b 45 f3 cc 72 cc 33 dc b3 dc??? ..3.3.;E..r.3...
32000010: 73 c4 33 cc 37 cd 33 fc 31 fc 37 c4 27 cc 33 cc??? s.3.7.3.1.7.'.3.
SMDK2410 # mw.b 32000000 0x11 10? //向0x32000000中重复写入16个0x11
SMDK2410 # md.b 32000000 20???? //再次查看0x32000000中的内容,确保成功写入
32000000: 11 11 11 11 11 11 11 11 11 11 11 11 11 11 11 11??? ................
32000010: 73 c4 33 cc 37 cd 33 fc 31 fc 37 c4 27 cc 33 cc??? s.3.7.3.1.7.'.3.
SMDK2410 # nand write 32000000 20000 10?? //内存32000000处连续16字节内容写入//NandFlash的0x20000处
?
NAND write: device 0 offset 0x20000,size 0x10
?16 bytes written: OK
SMDK2410 # md.b 33000000 20??????????? //查看0x33000000中连续0x20字节的内容
33000000: ed 37 8c 33 cc 37 cc b7 de 3b ec 33 cc b3 4c 33??? .7.3.7...;.3..L3
33000010: ec 3f dc 3b ec 3b dc 33 c8 3b cc b3 cc 33 ce 33??? .?.;.;.3.;...3.3
SMDK2410 # nand read 33000000 20000 10? //把NandFlash中0x20000处连续16字节的
//内容读入内存0x33000000
?
NAND read: device 0 offset 0x20000,size 0x10
?16 bytes read: OK
SMDK2410 # md.b 33000000 20?????????? //查看从NandFlash中读到的内容?? ? ? ? ? ? ? ??
33000000: 11 11 11 11 11 11 11 11 11 11 11 11 11 11 11 11??? ................
33000010: ec 3f dc 3b ec 3b dc 33 c8 3b cc b3 cc 33 ce 33??? .?.;.;.3.;...3.3
SMDK2410 # nand dump 20000
Page 00020000 dump:
??????? 11 11 11 11 11 11 11 11? 11 11 11 11 11 11 11 11
??????? ff ff ff ff ff ff ff ff? ff ff ff ff ff ff ff ff
|
经过上述测试,确定以及肯定NandFlash移植成功!
?
下一节,DM9000支持