nand flash具有大容量、改写速度快、接口简单等优点,适用于大量数据的存储,为固态大容量存储提供了廉价有效的解决方案。Linux内核已经支持s3c2416的nand控制器,可以支持各种容量的nand flash。

1. nand设备

nand设备包含了名字、独有的资源等等一些驱动程序的硬件或自定义信息。通过platform_add_devices(platform_device_register)函数将定义的平台设备注册到内核中,用于匹配设备驱动。

内核在drivers\mtd\nand\ s3c2410.c目录中实现了s3c2416 nand驱动, nand设备平台代码如下。

  1. static struct mtd_partitionhome_default_nand_part[] = {
  2. [0]= {
  3. .name = "Bootloader",
  4. .offset = 0,
  5. .size = 0x100000,
  6. .mask_flags = MTD_CAP_NANDFLASH,
  7. },
  8. [1]= {
  9. .name = "Logo",
  10. .offset = 0x100000,
  11. .size = 0x200000,
  12. .mask_flags = MTD_CAP_NANDFLASH,
  13. },
  14. [2]= {
  15. .name = "Kernel",
  16. .offset = 0x300000,
  17. .size = 0x400000,
  18. .mask_flags = MTD_CAP_NANDFLASH,
  19. },
  20. [3]= {
  21. .name = "Rootfs",
  22. .offset = 0x700000,
  23. .size = MTDPART_SIZ_FULL,
  24. },
  25. };
  26. static struct s3c2410_nand_sethome_nand_sets[] = {
  27. [0]= {
  28. .name = "NAND",
  29. .nr_chips = 1,
  30. .nr_partitions = ARRAY_SIZE(home_default_nand_part),
  31. .partitions = home_default_nand_part,
  32. },
  33. };
  34. static struct s3c2410_platform_nandhome_nand_info = {
  35. .tacls = 10,
  36. .twrph0 = 20,
  37. .twrph1 = 10,
  38. .ecc_mode = NAND_ECC_HW,
  39. .nr_sets = ARRAY_SIZE(home_nand_sets),
  40. .sets = home_nand_sets,
  41. };
  42. staticstruct resource s3c_nand_resource[] = {
  43. [0] = DEFINE_RES_MEM(S3C_PA_NAND, SZ_1M),
  44. };
  45. structplatform_device s3c_device_nand = {
  46. .name ="s3c2410-nand",
  47. .id =-1,
  48. .num_resources = ARRAY_SIZE(s3c_nand_resource),
  49. .resource =s3c_nand_resource,
  50. };

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。 16_Nand驱动 - 图1

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内核,剩余空间作为第四个分区根文件系统区域。 16_Nand驱动 - 图2

cat /proc/partitions可以知道有四个mtd设备,分别对应nand flash的四个分区。在/dev目录中创建这四个mtd设备文件。

  1. mknod /dev/mtdblock0 c 31 0
  2. mknod /dev/mtdblock1 c 31 1
  3. mknod /dev/mtdblock2 c 31 2
  4. mknod /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内核无法读取数据。

16_Nand驱动 - 图3

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

16_Nand驱动 - 图4