在 mach-smdkv210.c 中添加头文件<linux/mtd/mtd.h>
添加 nand_ecclayout 定义 OOB 布局,同时赋值给 smdk_nand_sets, 设置 disable_ecc 属性为假
修改 NAND 驱动 drivers/mtd/nand/s3c2410.c,里面所用到的寄存器索引都在arch/arm/plat-samsung/include/plat/regs-nand.h 中定义,对 s3c2410.c 的修改主要是增加几个函数
添加
/* add by JerryGou */
static void s5pv210_nand_enable_hwecc(struct mtd_info *mtd, int mode)
{
struct s3c2410_nand_info *info = s3c2410_nand_mtd_toinfo(mtd);
u32 cfg;
if (mode == NAND_ECC_READ)
{
/* set 8/12/16bit Ecc direction to Encoding */
cfg = readl(info->regs + S5PV210_NFECCCONT) & (~(0x1 << 16));
writel(cfg, info->regs + S5PV210_NFECCCONT);
/* clear 8/12/16bit ecc encode done */
cfg = readl(info->regs + S5PV210_NFECCSTAT) | (0x1 << 24);
writel(cfg, info->regs + S5PV210_NFECCSTAT);
}
else
{
/* set 8/12/16bit Ecc direction to Encoding */
cfg = readl(info->regs + S5PV210_NFECCCONT) | (0x1 << 16);
writel(cfg, info->regs + S5PV210_NFECCCONT);
/* clear 8/12/16bit ecc encode done */
cfg = readl(info->regs + S5PV210_NFECCSTAT) | (0x1 << 25);
writel(cfg, info->regs + S5PV210_NFECCSTAT);
}
/* Initialize main area ECC decoder/encoder */
cfg = readl(info->regs + S5PV210_NFCONT) | (0x1 << 5);
writel(cfg, info->regs + S5PV210_NFCONT);
/* The ECC message size(For 512-byte message, you should set 511) 8-bit ECC/512B */
writel((511 << 16) | 0x3, info->regs + S5PV210_NFECCCONF);
/* Initialize main area ECC decoder/ encoder */
cfg = readl(info->regs + S5PV210_NFECCCONT) | (0x1 << 2);
writel(cfg, info->regs + S5PV210_NFECCCONT);
/* Unlock Main area ECC */
cfg = readl(info->regs + S5PV210_NFCONT) & (~(0x1 << 7));
writel(cfg, info->regs + S5PV210_NFCONT);
}
/* add by JerryGou */
static int s5pv210_nand_calculate_ecc(struct mtd_info *mtd, const u_char *dat,
u_char *ecc_calc)
{
u32 cfg;
struct s3c2410_nand_info *info = s3c2410_nand_mtd_toinfo(mtd);
u32 nfeccprgecc0 = 0, nfeccprgecc1 = 0, nfeccprgecc2 = 0, nfeccprgecc3 = 0;
/* Lock Main area ECC */
cfg = readl(info->regs + S5PV210_NFCONT) | (0x1 << 7);
writel(cfg, info->regs + S5PV210_NFCONT);
if (ecc_calc) /* NAND_ECC_WRITE */
{
/* ECC encoding is completed */
while (!(readl(info->regs + S5PV210_NFECCSTAT) & (1 << 25)));
/* 读取13 Byte的Ecc Code */
nfeccprgecc0 = readl(info->regs + S5PV210_NFECCPRGECC0);
nfeccprgecc1 = readl(info->regs + S5PV210_NFECCPRGECC1);
nfeccprgecc2 = readl(info->regs + S5PV210_NFECCPRGECC2);
nfeccprgecc3 = readl(info->regs + S5PV210_NFECCPRGECC3);
ecc_calc[0] = nfeccprgecc0 & 0xFF;
ecc_calc[1] = (nfeccprgecc0 >> 8) & 0xFF;
ecc_calc[2] = (nfeccprgecc0 >> 16) & 0xFF;
ecc_calc[3] = (nfeccprgecc0 >> 24) & 0xFF;
ecc_calc[4] = nfeccprgecc1 & 0xFF;
ecc_calc[5] = (nfeccprgecc1 >> 8) & 0xFF;
ecc_calc[6] = (nfeccprgecc1 >> 16) & 0xFF;
ecc_calc[7] = (nfeccprgecc1 >> 24) & 0xFF;
ecc_calc[8] = nfeccprgecc2 & 0xFF;
ecc_calc[9] = (nfeccprgecc2 >> 8) & 0xFF;
ecc_calc[10] = (nfeccprgecc2 >> 16) & 0xFF;
ecc_calc[11] = (nfeccprgecc2 >> 24) & 0xFF;
ecc_calc[12] = nfeccprgecc3 & 0xFF;
}
else /* NAND_ECC_READ */
{
/* ECC decoding is completed */
while (!(readl(info->regs + S5PV210_NFECCSTAT) & (1 << 24)));
}
return 0;
}
/* add by JerryGou */
static int s5pv210_nand_correct_data(struct mtd_info *mtd, u_char *dat,
u_char *read_ecc, u_char *calc_ecc)
{
int ret = 0;
u32 errNo;
u32 erl0, erl1, erl2, erl3, erp0, erp1;
struct s3c2410_nand_info *info = s3c2410_nand_mtd_toinfo(mtd);
/* Wait until the 8-bit ECC decoding engine is Idle */
while (readl(info->regs + S5PV210_NFECCSTAT) & (1 << 31));
errNo = readl(info->regs + S5PV210_NFECCSECSTAT) & 0x1F;
erl0 = readl(info->regs + S5PV210_NFECCERL0);
erl1 = readl(info->regs + S5PV210_NFECCERL1);
erl2 = readl(info->regs + S5PV210_NFECCERL2);
erl3 = readl(info->regs + S5PV210_NFECCERL3);
erp0 = readl(info->regs + S5PV210_NFECCERP0);
erp1 = readl(info->regs + S5PV210_NFECCERP1);
switch (errNo)
{
case 8:
dat[(erl3 >> 16) & 0x3FF] ^= (erp1 >> 24) & 0xFF;
case 7:
dat[erl3 & 0x3FF] ^= (erp1 >> 16) & 0xFF;
case 6:
dat[(erl2 >> 16) & 0x3FF] ^= (erp1 >> 8) & 0xFF;
case 5:
dat[erl2 & 0x3FF] ^= erp1 & 0xFF;
case 4:
dat[(erl1 >> 16) & 0x3FF] ^= (erp0 >> 24) & 0xFF;
case 3:
dat[erl1 & 0x3FF] ^= (erp0 >> 16) & 0xFF;
case 2:
dat[(erl0 >> 16) & 0x3FF] ^= (erp0 >> 8) & 0xFF;
case 1:
dat[erl0 & 0x3FF] ^= erp0 & 0xFF;
case 0:
break;
default:
ret = -1;
printk("ECC uncorrectable error detected:%d\n", errNo);
break;
}
return ret;
}
/* add by JerryGou */
static int s5pv210_nand_read_page_hwecc(struct mtd_info *mtd, struct nand_chip *chip,
uint8_t *buf, int oob_required, int page)
{
int i, eccsize = chip->ecc.size;
int eccbytes = chip->ecc.bytes;
int eccsteps = chip->ecc.steps;
int col = 0;
int stat;
uint8_t *p = buf;
uint8_t *ecc_code = chip->buffers->ecccode;
uint32_t *eccpos = chip->ecc.layout->eccpos;
/* Read the OOB area first */
col = mtd->writesize;
chip->cmdfunc(mtd, NAND_CMD_RNDOUT, col, -1);
chip->read_buf(mtd, chip->oob_poi, mtd->oobsize);
for (i = 0; i < chip->ecc.total; i++)
ecc_code[i] = chip->oob_poi[eccpos[i]];
for (i = 0, col = 0; eccsteps; eccsteps--, i += eccbytes, p += eccsize, col += eccsize)
{
chip->cmdfunc(mtd, NAND_CMD_RNDOUT, col, -1);
chip->ecc.hwctl(mtd, NAND_ECC_READ);
chip->read_buf(mtd, p, eccsize);
chip->write_buf(mtd, ecc_code + i, eccbytes);
chip->ecc.calculate(mtd, NULL, NULL);
stat = chip->ecc.correct(mtd, p, NULL, NULL);
if (stat < 0)
mtd->ecc_stats.failed++;
else
mtd->ecc_stats.corrected += stat;
}
return 0;
}
配置内核支持
NAND
硬件
ECC
Device Drivers --->
<*> Memory Technology Device (MTD) support --->
<*> NAND Device Support --->
[*] Samsung S3C NAND Hardware ECC
重新编译内核,运行测试
成功挂载 jffs2 文件系统,读写 NAND 正常,但这并不代码 ECC 校验功能生效,我们可以使用裸机程序,按照 4.11 的测试方法,把 rootfs 分区的第 1 个一个块的数据全部读出(包括 OOB),然后擦除 rootfs分区的第 1 个块,然后把读出的数据的前面 8 个数据的第 0 位取反,然后不使用 ECC 校验把读出的数据全部写入(包括 OOB),然后启动内核,看挂载文件系统是否正常。如果正常,再次重复测试,这次把 9个数据取反,挂载文件系统时应该有错误信息输出。
注意到“ECC uncorrectable error detected:15”,这是我在 ECC 校验数据修正函数中打印出来的,表明无法修正的错误。 现在只有重新烧写文件系统才能恢复正常。