u-boot-2016.03 在mini2440上移植之nandflash 硬件ecc

MINI2440 開發板使用的是8bit,256M blocksize= 128k,pagesize =2k的nandflash。編程

寬帶爲8bit ,由S3C2440 datasheet 可知硬件生產的ecc爲4byte。函數

S3C2440 硬件ecc 操做流程:spa

1.先讀出NFMECC0寄存器中由硬件生產的ecc data。debug

2.將讀出的ecc數據,進行處理以後,分別寫入NFMECCD0和NFMECCD1寄存器,code

 硬件自動檢測,檢測結果可從NFESTAT0 寄存器讀取。ip

3. 讀取NFESTAT0寄存器,進行錯誤類別判斷。開發

4.若是是1bit error 進行數據糾正,數據錯誤>2bit沒法糾正。get

在上一篇關於「支持NandFlash讀寫」的文章中,咱們很好地完成了u-boot對NandFlash的讀寫,但這個讀寫進行的是軟件ECC,即用軟件編程的方法實現ECC。咱們知道S3C24x0的NandFlash控制器是支持硬件ECC的,所以在這裏咱們就來說解如何實現硬件ECC。flash

 

NandFlash的每一頁分爲main區和spare區,S3C24x0的NandFlash控制器支持這兩個區的硬件ECC,但只實現main區的硬件ECC。it

 

爲了實現硬件ECC,首先須要在include/configs/smdk2410.h文件內定義宏CONFIG_S3C2410_NAND_HWECC,這樣在drivers/mtd/nand/s3c2440_nand.c文件內就定義了硬件ECC所須要的三個函數:s3c24x0_nand_enable_hwecc函數、s3c24x0_nand_calculate_ecc函數和s3c24x0_nand_correct_data函數,並且在board_nand_init函數內,又把這三個函數分別賦給了相對應的結構體的三個成員,這樣在進行NandFlash讀寫時,就會調用這三個函數,從而實現了硬件ECC。s3c24x0_nand_enable_hwecc函數負責使能硬件ECC,s3c24x0_nand_calculate_ecc函數負責計算ECC(固然這種計算是由硬件來完成的),s3c24x0_nand_correct_data函數負責進行ECC的校驗(一樣地,這種校驗也是由硬件自動完成的)。

 

爲了理解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區數據,同時經過調用s3c24x0_nand_calculate_ecc函數來獲得硬件ECC;再讀取spare區數據;而後提取出儲存在spare區內的main區ECC;最後經過調用s3c24x0_nand_correct_data函數來對剛剛讀取的main區數據進行校驗。nand_write_page_hwecc函數的流程比較簡單,它先寫入main區數據,同時經過調用s3c24x0_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。

include/configs/smdk2410.h文件內定義

#define CONFIG_SYS_NAND_ECCSIZE  2048

#define CONFIG_SYS_NAND_ECCBYTES  4

如今準備開始移植,打開drivers/mtd/nand/s3c2410_nand.c 文件,修改爲以下函數

void s3c24x0_nand_enable_hwecc(struct mtd_info *mtd, int mode)
{
        struct s3c24x0_nand *nand = s3c24x0_get_base_nand();
        debug("s3c24x0_nand_enable_hwecc(%p, %d)\n", mtd, mode);
        writel(readl(&nand->nfcont) | S3C2440_NFCONT_INITECC, &nand->nfcont);
}

static int s3c24x0_nand_calculate_ecc(struct mtd_info *mtd, const u_char *dat,
                                      u_char *ecc_code)
{
        struct s3c24x0_nand *nand = s3c24x0_get_base_nand();
        unsigned int 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;
        debug("s3c24x0_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;
}

static int s3c24x0_nand_correct_data(struct mtd_info *mtd, u_char *dat,
                                     u_char *read_ecc, u_char *calc_ecc)
{
        struct s3c24x0_nand *nand = s3c24x0_get_base_nand();
        u32 meccdata0,meccdata1,estat0,err_byte_addr;
        int ret=-1;
        u8 repaired;
        meccdata0 = (read_ecc[1] << 16) | read_ecc[0];
        meccdata1 = (read_ecc[3] << 16) | read_ecc[2];
        writel(meccdata0,&nand->nfeccd0);
        writel(meccdata1,&nand->nfeccd1);
        /*read ecc status*/
        estat0 = readl(&nand->nfstat0);
        switch (estat0 &0x3){
        case 0: /*no error*/
                ret =0;
                break;
        case 1:
                /*
                 *1bit error(correctable)
                 *(nfestat0 >>7) & 0x7ff    error byte number
                 *(nfestat0 >>4) & 0x7      error bit number
                */
                err_byte_addr = (estat0 >>7 ) & 0x7ff;
                repaired = dat[err_byte_addr]^(1<<((estat0 >>4) & 0x7));
                printf("S3C NAND: 1 bit error detected at byte%ld. "
                     "Correcting from 0x%02x to0x%02x...OK\n",
                     err_byte_addr, dat[err_byte_addr],repaired);
              dat[err_byte_addr]= repaired;
              ret= 1;
              break;
        case  2: /* Multiple error */
        case  3: /* ECC area error */

       printf("S3C NAND: ECC uncorrectable errordetected. "

                     "Not correctable.\n");
              ret= -1;
              break;

        }
        return ret;}

相關文章
相關標籤/搜索