Linux開發十六
nand驅動
象棋小子 1048272975
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 0
mknod /dev/mtdblock1 c 31 1
mknod /dev/mtdblock2 c 31 2
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內核無法讀取數據。
讀取Kernel分區512字節的數據,bootloader固化Linux內核時,採用了與Linux內核nand驅動層一致的方式,可以正確地讀取數據。
4. 附錄
https://pan.baidu.com/s/1slczwhJ
bootloader源碼以及使用說明
https://pan.baidu.com/s/1eRDJtNs
Qt5.8官網源碼
https://pan.baidu.com/s/1nuGmSqt
本系列例程的根文件系統
https://pan.baidu.com/s/1i5btLGT
opev3.2.0官網源碼
https://pan.baidu.com/s/1pLpuHw3
yaffs官網源碼
https://pan.baidu.com/s/1bpkZynt
busybox-1.26.2官網源碼
https://pan.baidu.com/s/1i4EtjfR
tslib官網源碼
https://pan.baidu.com/s/1i5MGRhb
mplayer-1.3.0官網源碼
https://pan.baidu.com/s/1sl0fXlr
基於S3C2416修改的linux-4.10.10源碼