u-boot-2011.06在基于s3c2440开发板的移植之硬件ECC
2024-06-11 来源:elecfans
为了理解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区数据,同时通过调用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区内。
无论是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。所以对于这三个参数,只需事先定义好前两个参数即可。而这两个参数是在drivers/mtd/nand/s3c2440_nand.c文件中的board_nand_init函数内被定义赋值的,即:
nand->ecc.size = 2048;
nand->ecc.bytes = 4;
u-boot-2011.06对S3C2440的NandFlash控制器的寄存器定义得不完整,而且有错误,因此我们还需要对此进行修改。删除arch/arm/include/asm/arch-s3c24x0/s3c24x0.h文件内的第167行至第178行内容,添加进下面的内容:
struct s3c2440_nand {
u32 nfconf;
u32 nfcont;
u32 nfcmd;
u32 nfaddr;
u32 nfdata;
u32 nfmeccd0;
u32 nfmeccd1;
u32 nfseccd;
u32 nfstat;
u32 nfestat0;
u32 nfestat1;
u32 nfmecc0;
u32 nfmecc1;
u32 nfsecc;
u32 nfsblk;
u32 nfeblk;
};
最后,我们对s3c2440_nand_enable_hwecc函数、s3c2440_nand_calculate_ecc函数和s3c2440_nand_correct_data函数进行修改。
void s3c2440_nand_enable_hwecc(structmtd_info *mtd, int mode)
{
struct s3c2440_nand *nand = s3c2440_get_base_nand();
debugX(1,'s3c2440_nand_enable_hwecc(%p,%d)n', mtd, mode);
writel(readl(&nand->nfcont)| S3C2440_NFCONT_INITECC& ~S3C2440_NFCONT_MECCL,&nand->nfcont);
}
该函数的任务就是初始化ECC(即复位ECC),并解锁main区ECC。
static int s3c2440_nand_calculate_ecc(struct mtd_info *mtd, constu_char *dat,
u_char *ecc_code)
{
struct s3c2440_nand *nand = s3c2440_get_base_nand();
u32 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;
debugX(1,'s3c2440_nand_calculate_hwecc(%p,):0x%02x 0x%02x 0x%02x 0x%02xn',
mtd , ecc_code[0], ecc_code[1], ecc_code[2], ecc_code[3]);
return 0;
}
该函数首先锁定main区ECC,然后读取寄存器NFMECC0,该寄存器存放着由硬件生成的main区ECC,最后把4个1字节的ECC存放到ecc_code数组内。
static int s3c2440_nand_correct_data(struct mtd_info *mtd, u_char*dat,
u_char *read_ecc, u_char *calc_ecc)
{
struct s3c2440_nand *nand = s3c2440_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->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%ld. '
'Correcting from 0x%02x to0x%02x...OKn',
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;
}
该函数首先把read_ecc数组内的ECC存入寄存器NFMECCD0和寄存器NFMECCD1中,这样系统就会自动校验数据,并把状态放入寄存器NFESTAT0中,然后读取该寄存器的后4位,当为0时表示校验正确;当为1时表示发生了1位错误(该类错误可以校正),我们把它校正过来;当为2和3时表示发生其他类型的错误,这类错误是无法校正的。
通过以上内容的修改,我们就实现了NandFlash的硬件ECC。
ECC足以保证所读数据的正确性,并在有些情况下还可以修正错误,但它不能保证所写数据的正确性。为了保证所写数据的正确性,u-boot还可以通过在include/configs/zhaocj2440.h文件内定义宏CONFIG_MTD_NAND_VERIFY_WRITE来实现把所写的数据再读取一遍,然后与被写入的数据之间进行比较来判断所写数据的正确性,这一过程是在drivers/mtd/nand/nand_base.c文件的nand_write_page函数内调用实现的。