方便起见,本节以上一节中生成的以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,NAND_CMD_RESET, -1, -1);
2、 发送命令0x90h chip->cmdfunc(mtd,NAND_CMD_READID, 0x00, -1);
3、 发送列地址00h chip->cmdfunc(mtd,NAND_CMD_READID, 0x00, -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, NAND_CMD_READID, 0x00, -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, ctrl);//发送命令
chip->cmd_ctrl(mtd, 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支持