u-boot-2016.03 在mini2440上移植之nandflash 硬件ecc

时间:2021-04-27 17:12:36

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;
}