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