在上一篇關於“支持NandFlash讀寫”的文章中,我們很好地完成了u-boot對NandFlash的讀寫,但這個讀寫進行的是軟件ECC,即用軟件編程的方法實現ECC。我們知道S3C2440的NandFlash控制器是支持硬件ECC的,因此在這裡我們就來講解如何實現硬件ECC。
NandFlash的每一頁分為main區和spare區,S3C2440的NandFlash控制器支持這兩個區的硬件ECC,但為了兼容u-boot-2011.06,我們只實現main區的硬件ECC。
為了實現硬件ECC,首先需要在include/configs/zhaocj2440.h文件內定義宏CONFIG_S3C2440_NAND_HWECC,這樣在drivers/mtd/nand/s3c2440_nand.c文件內就定義了硬件ECC所需要的三個函數:s3c2440_nand_enable_hwecc函數、s3c2440_nand_calculate_ecc函數和s3c2440_nand_correct_data函數,而且在board_nand_init函數內,又把這三個函數分別賦給了相對應的結構體的三個成員,這樣在進行NandFlash讀寫時,就會調用這三個函數,從而實現了硬件ECC。s3c2440_nand_enable_hwecc函數負責使能硬件ECC,s3c2440_nand_calculate_ecc函數負責計算ECC(當然這種計算是由硬件來完成的),s3c2440_nand_correct_data函數負責進行ECC的校驗(同樣地,這種校驗也是由硬件自動完成的)。
相關閱讀:
U-Boot源代碼下載地址 http://www.linuxidc.com/Linux/2011-07/38897.htm
U-Boot-2011.06啟動流程分析 http://www.linuxidc.com/Linux/2011-07/39310.htm
u-boot-2011.06在基於s3c2440開發板的移植之編譯配置 http://www.linuxidc.com/Linux/2011-10/45455.htm
u-boot-2011.06在基於s3c2440開發板的移植之NorFlash啟動 http://www.linuxidc.com/Linux/2011-10/45456.htm
u-boot-2011.06在基於S3C2440開發板的移植之解決raise: Signal # 8 caught http://www.linuxidc.com/Linux/2011-10/454554.htm
u-boot-2011.06在基於s3c2440開發板的移植之支持NandFlash讀寫 http://www.linuxidc.com/Linux/2011-10/45457.htm
u-boot-2011.06在基於s3c2440開發板的移植之硬件ECC http://www.linuxidc.com/Linux/2011-10/454558.htm
u-boot-2011.06在基於s3c2440開發板的移植之DM9000 http://www.linuxidc.com/Linux/2012-09/70507.htm
u-boot-2011.06在基於s3c2440開發板的移植之nandflash啟動 http://www.linuxidc.com/Linux/2012-09/70508.htm
u-boot-2011.06在基於s3c2440開發板的移植之支持YAFFS2 http://www.linuxidc.com/Linux/2012-09/70509.htm
u-boot-2011.06在基於s3c2440開發板的移植之引導內核與加載根文件系統 http://www.linuxidc.com/Linux/2012-09/70510.htm
u-boot-2011.06在基於s3c2440開發板的移植之結束篇 http://www.linuxidc.com/Linux/2012-09/70511.htm
為了理解u-boot是如何進行硬件ECC的,我們先來簡要地分析一下相關的函數。NandFlash是以頁為最小單位進行讀寫操作的,支持硬件ECC的讀操作最終是由nand_read_page_hwecc函數(在drivers/mtd/nand目錄下)來完成的,支持硬件ECC的寫操作最終是由nand_write_page_hwecc函數(在drivers/mtd/nand目錄下)來完成的。nand_read_page_hwecc函數的流程為先讀取main區數據,同時通過調用s3c2440_nand_calculate_ecc函數來得到硬件ECC;再讀取spare區數據;然後提取出儲存在spare區內的main區ECC;最後通過調用s3c2440_nand_correct_data函數來對剛剛讀取的main區數據進行校驗。nand_write_page_hwecc函數的流程比較簡單,它先寫入main區數據,同時通過調用s3c2440_nand_calculate_ecc函數來得到硬件ECC;然後就是把硬件ECC寫入到spare區內。
無論是nand_write_page_hwecc函數,還是nand_write_page_hwecc函數,內部都有一個這樣的for循環體:
for(i = 0; eccsteps; eccsteps--, i += eccbytes, p += eccsize) {
…… ……
}
其中三個主要變量的定義為:
eccsize= chip->ecc.size;
eccbytes= chip->ecc.bytes;
eccsteps= chip->ecc.steps;
下面我們就來介紹一下這個循環的作用:不同的CPU的NandFlash控制器一次所能完成的硬件ECC的字節數是不一樣的,例如有些CPU一次只能完成512字節的硬件ECC,但如果開發板上的NandFlash每頁有2048個字節,那該怎麼辦呢?這時就要用到一個循環體,通過循環多次來得到一頁的硬件ECC。例如上面這種情況,就要循環4次(2048÷512=4),才能得到這個頁內數據完整的硬件ECC。另外每一次硬件ECC,不同的CPU所生成的ECC字節數也是不同的,有的是3個字節,有的是4個字節。
那麼,上面那三個變量的含義就分別為:
ecc.size:每一次硬件ECC所檢驗的字節個數
ecc.bytes:每一次硬件ECC所生成的字節個數
ecc.steps:每一頁需要進行硬件ECC的次數
對於S3C2440來說,一次硬件ECC可以檢驗2048個字節,並且生成4個字節的ECC,因此ecc.size應該為2048,ecc.bytes應該為4。而ecc.steps是通過計算得到的,即系統上電後能夠獲知NandFlash的每頁的大小,用這個值除以ecc.size就等於ecc.steps。所以對於這三個參數,只需事先定義好前兩個參數即可。而這兩個參數是在drivers/mtd/nand/s3c2440_nand.c文件中的board_nand_init函數內被定義賦值的,即:
nand->ecc.size = 2048;
nand->ecc.bytes = 4;
u-boot-2011.06對S3C2440的NandFlash控制器的寄存器定義得不完整,而且有錯誤,因此我們還需要對此進行修改。刪除arch/arm/include/asm/arch-s3c24x0/s3c24x0.h文件內的第167行至第178行內容,添加進下面的內容:
struct s3c2440_nand {
u32 nfconf;
u32 nfcont;
u32 nfcmd;
u32 nfaddr;
u32 nfdata;
u32 nfmeccd0;
u32 nfmeccd1;
u32 nfseccd;
u32 nfstat;
u32 nfestat0;
u32 nfestat1;
u32 nfmecc0;
u32 nfmecc1;
u32 nfsecc;
u32 nfsblk;
u32 nfeblk;
};
最後,我們對s3c2440_nand_enable_hwecc函數、s3c2440_nand_calculate_ecc函數和s3c2440_nand_correct_data函數進行修改。
void s3c2440_nand_enable_hwecc(structmtd_info *mtd, int mode)
{
struct s3c2440_nand *nand = s3c2440_get_base_nand();
debugX(1,"s3c2440_nand_enable_hwecc(%p,%d)\n", mtd, mode);
writel(readl(&nand->nfcont)| S3C2440_NFCONT_INITECC& ~S3C2440_NFCONT_MECCL,&nand->nfcont);
}
該函數的任務就是初始化ECC(即復位ECC),並解鎖main區ECC。
static int s3c2440_nand_calculate_ecc(struct mtd_info *mtd, constu_char *dat,
u_char *ecc_code)
{
struct s3c2440_nand *nand = s3c2440_get_base_nand();
u32 mecc0;
writel(readl(&nand->nfcont)| S3C2440_NFCONT_MECCL,&nand->nfcont);
mecc0= readl(&nand->nfmecc0);
ecc_code[0]= mecc0 & 0xff;
ecc_code[1] = (mecc0 >> 8) &0xff;
ecc_code[2] = (mecc0 >> 16) &0xff;
ecc_code[3] =(mecc0 >> 24) & 0xff;
debugX(1,"s3c2440_nand_calculate_hwecc(%p,):0x%02x 0x%02x 0x%02x 0x%02x\n",
mtd , ecc_code[0], ecc_code[1], ecc_code[2], ecc_code[3]);
return 0;
}