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

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 结构体中相应的函数指针:

#ifdef CONFIG_S3C2440_NAND_HWECC
    nand->ecc.hwctl = s3c24x0_nand_enable_hwecc;
    nand->ecc.calculate = s3c24x0_nand_calculate_ecc;
    nand->ecc.correct = s3c24x0_nand_correct_data;
    nand->ecc.mode = NAND_ECC_HW;
    nand->ecc.size = CONFIG_SYS_NAND_ECCSIZE;
    nand->ecc.bytes = CONFIG_SYS_NAND_ECCBYTES;
    nand->ecc.strength = 1;
#else
    nand->ecc.mode = NAND_ECC_SOFT;
#endif

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

比如  nand_read_page_hwecc 函数中的:

for (i = 0; eccsteps; eccsteps--, i += eccbytes, p += eccsize) {
        chip->ecc.hwctl(mtd, NAND_ECC_READ);
        chip->read_buf(mtd, p, eccsize);
        chip->ecc.calculate(mtd, p, &ecc_calc[i]);
    }

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

你可能感兴趣的:(uboot,uboot2016.03,nandflash硬件ecc)