nand flash具有大容量、改写速度快、接口简单等优点,适用于大量数据的存储,为固态大容量存储提供了廉价有效的解决方案。Linux内核已经支持s3c2416的nand控制器,可以支持各种容量的nand flash。
1. nand设备
nand设备包含了名字、独有的资源等等一些驱动程序的硬件或自定义信息。通过platform_add_devices(platform_device_register)函数将定义的平台设备注册到内核中,用于匹配设备驱动。
内核在drivers\mtd\nand\ s3c2410.c目录中实现了s3c2416 nand驱动, nand设备平台代码如下。
static struct mtd_partitionhome_default_nand_part[] = {[0]= {.name = "Bootloader",.offset = 0,.size = 0x100000,.mask_flags = MTD_CAP_NANDFLASH,},[1]= {.name = "Logo",.offset = 0x100000,.size = 0x200000,.mask_flags = MTD_CAP_NANDFLASH,},[2]= {.name = "Kernel",.offset = 0x300000,.size = 0x400000,.mask_flags = MTD_CAP_NANDFLASH,},[3]= {.name = "Rootfs",.offset = 0x700000,.size = MTDPART_SIZ_FULL,},};static struct s3c2410_nand_sethome_nand_sets[] = {[0]= {.name = "NAND",.nr_chips = 1,.nr_partitions = ARRAY_SIZE(home_default_nand_part),.partitions = home_default_nand_part,},};static struct s3c2410_platform_nandhome_nand_info = {.tacls = 10,.twrph0 = 20,.twrph1 = 10,.ecc_mode = NAND_ECC_HW,.nr_sets = ARRAY_SIZE(home_nand_sets),.sets = home_nand_sets,};staticstruct resource s3c_nand_resource[] = {[0] = DEFINE_RES_MEM(S3C_PA_NAND, SZ_1M),};structplatform_device s3c_device_nand = {.name ="s3c2410-nand",.id =-1,.num_resources = ARRAY_SIZE(s3c_nand_resource),.resource =s3c_nand_resource,};
nand ecc校验方式在home_nand_info结构体中设置,设置为NAND_ECC_HW。在板级初始化函数home2416_machine_init()中加入nand平台数据s3c_nand_set_platdata(&home_nand_info),在static struct platform_device *home2416_devices[]板级平台设备列表中加入&s3c_device_nand,使nand设备能够注册到内核中。
2. 内核配置
Linux配置支持nand设备驱动,选中Device Drivers->Memory Technology Device(MTD) support->NAND Device Support->NAND Flash support for Samsung S3CSoCs。

nand ecc校验方式在nand platform data中设置,由于nand flash会出现位翻转的问题,一般需要对nand flash进行ecc校验和纠错。Linux内核支持无ecc、软件ecc、硬件ecc这三种方式,内核无ecc情况下,应该保证数据的其他地方有ecc校验(如yaffs允许使用自身的ecc校验),不然数据可能是不可靠的。一般情况下是在内核做ecc校验,对于支持硬件方式产生ecc的nand控制器,驱动层应优先让内核采用硬件ecc,软件ecc会耗费更多的cpu资源,也远达不到硬件ecc的速度性能。
3. nand测试
cat /proc/mtd可以知道分区信息。我们在nand平台设备中设置了四个分区,第一个分区用来存放bootloader,第二个分区用来存放Logo,第三个分区用来存放Linux内核,剩余空间作为第四个分区根文件系统区域。

cat /proc/partitions可以知道有四个mtd设备,分别对应nand flash的四个分区。在/dev目录中创建这四个mtd设备文件。
mknod /dev/mtdblock0 c 31 0mknod /dev/mtdblock1 c 31 1mknod /dev/mtdblock2 c 31 2mknod /dev/mtdblock3 c 31 3
bootloader是上电启动后最先运行的一段代码,它的固化以及启动是与芯片平台息息相关的,需要参考相应芯片平台手册。例如对于s3c2416 nand启动,要求bootloader必须放在nand flash的0地址处,其中的8k SteppingStone必须采用8位ecc校验。
对于Linux内核,根文件系统,数据(如Logo)是由bootloader来固化的,其中Linux内核是由bootloader加载的,Linux内核固化的方式,如nand flash固化地址、固化采用的ecc校验等并无强制要求,只需bootloader采用与固化时相同的方式去加载即可。
根文件系统是由Linux内核启动去挂载的。因此,Linux内核在挂载根文件系统时,就采用内核的ecc校验方式去读取nand flash。这就要求bootloader在固化根文件系统时,固化的方式(如ecc layout、ecc校验纠错方式)必须与内核nand驱动层采用的方式完全一致,不然内核将无法读取根文件系统中的数据并挂载。
我们在Linux内核配置中选择了硬件ecc,驱动层每256 byte产生3 byte的硬件ecc,ecc layout方式可以查看Linux内核源码。笔者在bootloader中实现了跟Linux内核一致的方式,可以固化并启动Linux系统,具体实现可以参考笔者的bootloader源码。
读取Bootloader分区的512字节数据,bootloader有相应的ecc校验方式,与Linux的ecc校验方式不一致,Linux内核无法读取数据。

读取Kernel分区512字节的数据,bootloader固化Linux内核时,采用了与Linux内核nand驱动层一致的方式,可以正确地读取数据。

