u-boot移植3:支持 nandflash 的硬件 ECC

转载地址:https://blog.csdn.net/qqliyunpeng/article/details/51333603

u-boot 版本:2016.03

用的交叉编译工具:arm-none-linux-gnueabi-


操作的文件:drivers/mtd/nand/s3c2440_nand.c

1. 程序分析:


要知道的几点:

  • 从s3c2440_nand.c 中知道,要想开启 硬件的ECC ,需要定义一个 CONFIG_S3C2440_NAND_HWECC 的宏,可以放在板子的 .h 文件中 include/configs/lip2440.h 
  • 对板子的 nandflash 的硬件ECC 校验的函数实际的操作的文件是 drivers/mtd/nand/nand_base.c
  • nandflash 的每一页分为 main 区和 spare 区,并同时支持这两个区的硬件 ecc
  • nandFlash是以页为最小单位进行读写操作的

1.1 当定义了 CONFIG_S3C2440_NAND_HWECC 后,在s3c2440_nand.c 中就定义了3个函数:

  • 函数 s3c24x0_nand_enable_hwecc ,作用是 复位ecc,解锁main去ecc
  • 函数 s3c24x0_nand_calculate_ecc ,作用是 计算ecc
  • 函数 s3c24x0_nand_correct_data ,作用是 进行校准

1.2 在 board_nand_init 函数中关联上 nand_chip 结构体中相应的函数指针:

 
  1. #ifdef CONFIG_S3C2440_NAND_HWECC

  2. nand->ecc.hwctl = s3c24x0_nand_enable_hwecc;

  3. nand->ecc.calculate = s3c24x0_nand_calculate_ecc;

  4. nand->ecc.correct = s3c24x0_nand_correct_data;

  5. nand->ecc.mode = NAND_ECC_HW;

  6. nand->ecc.size = CONFIG_SYS_NAND_ECCSIZE;

  7. nand->ecc.bytes = CONFIG_SYS_NAND_ECCBYTES;

  8. nand->ecc.strength = 1;

  9. #else

  10. nand->ecc.mode = NAND_ECC_SOFT;

  11. #endif

1.3 在 drivers/mtd/nand/nand_base.c 中的函数中进行使用:

比如 nand_read_page_hwecc 函数中的:

 
  1. for (i = 0; eccsteps; eccsteps--, i += eccbytes, p += eccsize) {

  2. chip->ecc.hwctl(mtd, NAND_ECC_READ);

  3. chip->read_buf(mtd, p, eccsize);

  4. chip->ecc.calculate(mtd, p, &ecc_calc[i]);

  5. }

支持硬件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区内。

---引用自 赵春江的专栏 博客

在这些函数中关键的 for 循环中所用的参数是我们需要修改的:

for(i = 0; eccsteps; eccsteps--, i += eccbytes, p += eccsize) {

……  …… 

}

for循环的作用是适应不同 cpu 的nandflash 控制器一次所能完成的硬件ECC的字节数不同

举例:

        如果:

  • CPU一次能完成512字节的硬件ECC
  • 开发板上的NandFlash每页有2048个字节

需要分4次来计算ecc,还要注意的是每次ecc产生的结果字节数也有所不同,有的3字节,有的4字节。

eccsize= chip->ecc.size;

eccbytes= chip->ecc.bytes; 

eccsteps= chip->ecc.steps;

我们需要查看的是前两个参数,后边的一个参数是程序中自己计算的

我们的s3c2440:

nand->ecc.size = 2048;

nand->ecc.bytes = 4;

2. 程序修改:

2.1 修改配置文件:include/configs/lip2440.h 

    

--- a/include/configs/lip2440.h

+++ b/include/configs/lip2440.h

@@ -147,7 +147,7 @@

 #define CONFIG_SYS_FLASH_BANKS_LIST     { CONFIG_SYS_FLASH_BASE }

 #define CONFIG_SYS_MAX_FLASH_SECT      (35)

-#define CONFIG_ENV_ADDR                        (CONFIG_SYS_FLASH_BASE + 0x070000)

+#define CONFIG_ENV_ADDR                        (CONFIG_SYS_FLASH_BASE + 0x100000)

 #define CONFIG_ENV_IS_IN_FLASH

 #define CONFIG_ENV_SIZE                        0x10000

 /* allow to overwrite serial and ethaddr */

@@ -168,8 +168,11 @@

 #ifdef CONFIG_CMD_NAND

 #define CONFIG_NAND_S3C2440

 #define CONFIG_SYS_S3C2440_NAND_HWECC

+#define CONFIG_S3C2440_NAND_HWECC

 #define CONFIG_SYS_MAX_NAND_DEVICE     1

 #define CONFIG_SYS_NAND_BASE           0x4E000000

+#define CONFIG_SYS_NAND_ECCSIZE 2048

+#define CONFIG_SYS_NAND_ECCBYTES 4

 #endif

2.2 更改 s3c2440 的寄存器的:arch/arm/include/asm/arch-s3c24x0/s3c24x0.h

@@ -145,16 +145,16 @@ struct s3c24x0_nand {

        u32     nfaddr;

        u32     nfdata;

 #ifndef CONFIG_S3C2410

-       u32     nfeccd0;

-       u32     nfeccd1;

-       u32     nfeccd;

+       u32     nfmeccd0;

+       u32     nfmeccd1;

+       u32     nfseccd;

 #endif

        u32     nfstat;

 #ifdef CONFIG_S3C2410

        u32     nfecc;

 #else

-       u32     nfstat0;

-       u32     nfstat1;

+       u32     nfestat0;

+       u32     nfestat1;

        u32     nfmecc0;

        u32     nfmecc1;

        u32     nfsecc;

2.3 修改关键的3函数:drivers/mtd/nand/s3c2440_nand.c

@@ -82,32 +82,75 @@ 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->nfconf) | S3C2440_NFCONF_INITECC, &nand->nfconf);

+       writel((readl(&nand->nfcont) | S3C2440_NFCONT_INITECC) & ~S3C2440_NFCONT_MECCL, &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();

-       ecc_code[0] = readb(&nand->nfecc);

-       ecc_code[1] = readb(&nand->nfecc + 1);

-       ecc_code[2] = readb(&nand->nfecc + 2);

-       debug("s3c24x0_nand_calculate_hwecc(%p,): 0x%02x 0x%02x 0x%02x\n",

-             mtd , ecc_code[0], ecc_code[1], ecc_code[2]);

+    u_int32_t 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)

 {

-       if (read_ecc[0] == calc_ecc[0] &&

-           read_ecc[1] == calc_ecc[1] &&

-           read_ecc[2] == calc_ecc[2])

-               return 0;

-       printf("s3c24x0_nand_correct_data: not implemented\n");

-       return -1;

+    struct  s3c24x0_nand *nand = s3c24x0_get_base_nand();

+    u_int32_t  meccdata0, meccdata1, estat0, err_byte_addr;

+    int  ret = -1;

+    u_int8_t  repaired;

+

+    meccdata0= (read_ecc[1] << 16) | read_ecc[0];

+    meccdata1= (read_ecc[3] << 16) | read_ecc[2];

+

+    writel(meccdata0,&nand->nfmeccd0);

+    writel(meccdata1,&nand->nfmeccd1);

+

+    /* Read ecc status */

+

+    estat0= readl(&nand->nfestat0);  

+

+    switch(estat0 & 0x3) {

+    case  0: /* No error */

+        ret= 0;

+        break;

+    case  1:

+        /*

+         * 1 bit 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%d. "

+                "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;

 }

 #endif

猜你喜欢

转载自blog.csdn.net/kunkliu/article/details/81542256