MINI2440 开发板使用的是8bit,256M blocksize= 128k,pagesize =2k的nandflash。
宽带为8bit ,由S3C2440 datasheet 可知硬件生产的ecc为4byte。
S3C2440 硬件ecc 操作流程:
1.先读出NFMECC0寄存器中由硬件生产的ecc data。
2.将读出的ecc数据,进行处理之后,分别写入NFMECCD0和NFMECCD1寄存器,
硬件自动检测,检测结果可从NFESTAT0 寄存器读取。
3. 读取NFESTAT0寄存器,进行错误类别判断。
4.如果是1bit error 进行数据纠正,数据错误>2bit无法纠正。
在上一篇关于“支持NandFlash读写”的文章中,我们很好地完成了u-boot对NandFlash的读写,但这个读写进行的是软件ECC,即用软件编程的方法实现ECC。我们知道S3C24x0的NandFlash控制器是支持硬件ECC的,因此在这里我们就来讲解如何实现硬件ECC。
NandFlash的每一页分为main区和spare区,S3C24x0的NandFlash控制器支持这两个区的硬件ECC,但只实现main区的硬件ECC。
为了实现硬件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;
}