上一篇帖子分析了nand flash,这篇帖子开始移植u-boot对nand flash的读写支持,进入正题。
一.修改mini2440.h
之前再初步移植的时候注释掉了nand的相关部分,这里再打开,在mini2440.h的第96行:
#define CONFIG_CMD_NAND
既然打开了nand的配置,就需要在nand configuration里面做配置,在mini2440.h里面只有s3c2410的,这里需要进行添加,在第187行,红色字体为添加进去的部分:
/*
*NAND configuration
*/
#ifdef CONFIG_CMD_NAND
#ifdef CONFIG_S3C2440
#define CONFIG_NAND_S3C2440
#defineCONFIG_SYS_S3C2440_NAND_HWECC
#else
#define CONFIG_NAND_S3C2410
#define CONFIG_SYS_S3C2410_NAND_HWECC
#endif
#define CONFIG_SYS_MAX_NAND_DEVICE 1
#define CONFIG_SYS_NAND_BASE 0x4E000000
#endif
二.拷贝s3c2410_nand.c为s3c2440_nand.c
由于支持了2410的nand的读写,所以只需要以其为蓝本,将其拷贝再做修改就可以了。这里进入目录:/drivers/mtd/nand/下,执行
cp s3c2410_nand.c s3c2440_nand.c.
然后使用vim打开s3c2440_nand.c,这里面配置都是2410的,我们执行命令:
:%s/2410/2440
这样会将该文件下的所有2410替换成2440,关于vim字符替换的命令格式可以自行网上查阅。
三.修改/drivers/mtd/nand/下Makefile
第55行:
obj-$(CONFIG_NAND_S3C2410) += s3c2410_nand.o
是说如果定义了CONFIG_NAND_S3C2410那么久将s3c2410_nand编译进去,这里我们在下面一行添加2440的:
obj-$(CONFIG_NAND_S3C2440) +=s3c2440_nand.o
四.修改drivers/mtd/nand/s3c2440_nand.c
因为我们只是把2410的蓝本拷贝过来,现在要根据2440和具体的nand型号做修改,这里首先修改的是时序,第19行:
#define S3C2440_NFCONF_TACLS(x) ((x)<<8)
#define S3C2440_NFCONF_TWRPH0(x) ((x)<<4)
#define S3C2440_NFCONF_TWRPH1(x) ((x)<<0)
这里的三个参数TACLS,TWRPH0,TWRPH1是在读写nand的时候需要配置的三个时序参数,这里可以对照s3c2440和nand的datasheet,对照网上别人的分析总结如下:
TACLS:CLE/ALE有效到nWE有效之间的持续时间。
TWRPH0:CLE/ALE的持续时间。
TWRPH1:表示CLE/ALE的维持时间(hold time)。
同时看到s3c2440的datasheet的NFCONF寄存器有关位如下:
上图中给出了三个值的取值范围,对应寄存器的位,12,8,4以及持续时间的计算方法,HCLK一般是100MHZ,所以我们需要进行修改:
#define S3C2440_NFCONF_TACLS(x) ((x)<<12)
#define S3C2440_NFCONF_TWRPH0(x) ((x)<<8)
#define S3C2440_NFCONF_TWRPH1(x) ((x)<<4)
下面第123行:
#ifdefined(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
这里我们没有定义CONFIG_S3C24XX_CUSTOM_NAND_TIMING,所以会执行下面的参数,这三个参数等于多少,网上有很多,我没有一一去试过,这里暂且用一组0.1,1,修改如下:
#ifdefined(CONFIG_S3C24XX_CUSTOM_NAND_TIMING)
tacls = CONFIG_S3C24XX_TACLS;
twrph0 = CONFIG_S3C24XX_TWRPH0;
twrph1 = CONFIG_S3C24XX_TWRPH1;
#else
tacls= 0;
twrph0= 1;
twrph1= 0;
#endif
在下面第133行:
cfg = S3C2440_NFCONF_EN;
cfg |= S3C2440_NFCONF_TACLS(tacls - 1);
cfg |= S3C2440_NFCONF_TWRPH0(twrph0 - 1);
cfg |= S3C2440_NFCONF_TWRPH1(twrph1 - 1);
writel(cfg, &nand_reg->nfconf);
我们将这部分修改如下:
//cfg =S3C2440_NFCONF_EN;
cfg |= S3C2440_NFCONF_TACLS(tacls);
cfg |= S3C2440_NFCONF_TWRPH0(twrph0);
cfg |= S3C2440_NFCONF_TWRPH1(twrph1);
writel(cfg, &nand_reg->nfconf);
writel((1<<4)|(1<<1)|(1<<0),&nand_reg->nfcont);
这部分cfg这一段后面的第一个writel主要是将刚才设置的tacls, twrph0, twrph1赋给了NFCONF,这里&nand_reg指的是代表nand flash寄存器的结构体, writel使用方法复习下就是往内存映射的I/O空间上写数据:
void writel (unsigned char data , unsigned short addr )。
下面一个writel这里的(1<<4)|(1<<1)|(1<<0)意思是往nandflash的NFCONT寄存器的第0,1,4位分别置一,这里查看datasheet:
也就是初始化了ECC,禁止片选,以及使能了NandFlash控制器。
接着,修改s3c2440_hwcontrol函数,第48行:
if (ctrl & NAND_CTRL_CHANGE) {
ulong IO_ADDR_W = (ulong)nand;
if (!(ctrl & NAND_CLE))
IO_ADDR_W |=S3C2440_ADDR_NCLE;
if (!(ctrl & NAND_ALE))
IO_ADDR_W |=S3C2440_ADDR_NALE;
/*判断是命令还是地址,从而决定IO_ADDR_W的指向。*/
if(cmd==NAND_CMD_NONE)
{
IO_ADDR_W=&nand->nfdata;
}
chip->IO_ADDR_W = (void*)IO_ADDR_W;
if (ctrl & NAND_NCE)
writel(readl(&nand->nfcont)& ~(1<<1),
&nand->nfcont);
else
writel(readl(&nand->nfcont)| (1<<1),
&nand->nfcont); //控制片选
}
修改部分已经用红色字体标注出来了,以上修改还是因为2410和2440的寄存器不同,并且他们的偏移也不一样。
if(cmd==NAND_CMD_NONE)
{
IO_ADDR_W=&nand->nfdata;
}
这部分代码主要是针对后面我们使用nand write写入数据失败的问题,具体是由于我们上一次是写的命令寄存器,但是下一次直接写数据,但是我们的IO_ADDR_W 还是指向的命令寄存器,所以这里如果判断我们写入的不是命令,就直接指向我们的数据寄存器NFDATA,这样我们就可以成功的使用nandwrite命令了。s3c2440_hwcontrol这个函数主要是命令控制函数,下面还是针对偏移不同做出的修改,在第23行:
#define S3C2440_ADDR_NALE 4
#define S3C2440_ADDR_NCLE 8
修改成如下:
#define S3C2440_ADDR_NALE 8
#define S3C2440_ADDR_NCLE 12
#define S3C2440_NFCONF_nFCE (1<<1)
同时注释掉第18行://#define S3C2440_NFCONF_nFCE (1<<11)
到这里我们的s3c2440_nand.c文件修改结束。
五.修改drivers/mtd/nand/nand_base.c
在第207行,在nand_select_chip函数里面,修改第207行,如下:
case0:
chip->cmd_ctrl(mtd,NAND_CMD_NONE, NAND_CTRL_CLE| NAND_CTRL_CHANGE);
break;
六.编译测试
make distclen
makemini2440_config
make
对生成的u-boot.bin,chmod 777 u-boot.bin
拷贝到tftp目录下cp u-boot.bin /tftpboot
从nor flash启动开发板,执行:
tftp 0x30008000 u-boot.bin
go 0x30008000
出现以下信息:
U-Boot 2014.04 (Sep 02 2015 - 08:47:58)
CPUID: 32440001
FCLK: 400 MHz
HCLK: 100 MHz
PCLK: 50 MHz
DRAM: 64 MiB
WARNING: Caches not enabled
Flash: 2 MiB
NAND: 256 MiB
*** Warning - bad CRC, using defaultenvironment
In: serial
Out: serial
Err: serial
Net: dm9000
MINI2440 Deep_l_zh #
可以看到红色字体说明已经识别到了我们的nand,下面对读写进行测试:
MINI2440 Deep_l_zh # nand write0x30008000 0 0xff
NAND write: device 0 offset 0x0, size 0xff
255 bytes written: OK
这暂时说明读是没有问题的,接下来往里面写:
MINI2440 Deep_l_zh # nand read 0x320000000 0xff
NAND read: device 0 offset 0x0, size 0xff
NAND read from offset 0 failed -74
0bytes read: ERROR
出现错误了。。。
七.nand read错误解决办法
网上查找资料说是根据校验码-74,是ecc校验的问题,具体修改方法比较复杂,这里我修改了一下时序参数。
tacls = 3;
twrph0 = 3;
twrph1 = 2;
然后在读写之前进行nand erase操作,这个很重要,可以读出如下信息:
MINI2440 Deep_l_zh # nand read 0x300080000 0xff
NAND read: device 0 offset 0x0, size 0xff
255bytes read: OK
可以看出来是读出来了,接着进行读写数据对比测试:
MINI2440 Deep_l_zh # nand write 0x30008000 0 0xff
NAND write: device 0 offset 0x0, size 0xff
255 bytes written: OK
MINI2440 Deep_l_zh # nand read 0x32000000 0 0xff
NAND read: device 0 offset 0x0, size 0xff
255 bytes read: OK
MINI2440 Deep_l_zh # cmp.b 0x30008000 0x32000000 0xff
Total of 255 byte(s) were thesame
完全一样,说明我们读写都成功了。至此,nand flash读写移植结束,如有不正确的地方,还请指出,大家共同进步。