s3c2440 K9F2G08 nandflash支持硬件ECC校验

来源:互联网 发布:淘宝怎么进入微淘 编辑:程序博客网 时间:2024/06/04 20:02

http://blog.csdn.net/hurry_liu/article/details/8741565


S3c2440nandflash K9F2G08是支持硬件ECC的,NandFlash的每一页分为main区和spare区,S3C2440NandFlash控制器支持这两个区的硬件ECC,这里我们实现main区的硬件ECC。在include/configs/fl2440.h文件中,如果我们定义了nandflash的硬件ECC校验,那么我们就可以控制相应的nandflash寄存器,实现硬件ECC。     

nandflash的每一页有两区:main区和spare区,main区用于存储正常的数据,spare区用于存储其他附加信息,其中就包括ECC校验码。

当我们在写入数据的时候,我们就计算这一页数据的ECC校验码,然后把校验码存储到spare区的特定位置中,在下次读取这一页数据的时候,同样我们也计算ECC校验码,然后与spare区中的ECC校验码比较,如果一致则说明读取的数据正确,如果不一致则不正确。

s3c2440能够硬件产生ECC校验码main区的ECC校验码和spare区的ECC校验码。因为K9F2G08U0A8IO口,因此s3c2440共产生4个字节的mainECC2个字节的spareECC

在这里我们规定,在每一页的spare区的第0个地址到第3个地址存储mainECC4个地址和第5个地址存储spareECC。产生ECC校验码的过程为:在读取或写入哪个区的数据之前,先解锁该区的ECC,以便产生该区的ECC。在读取或写入完数据之后,再锁定该区的ECC,这样系统就会把产生的ECC码保存到相应的寄存器中

main区的ECC保存到NFMECC0/1(因为K9F2G08U0A8IO口,因此这里只用到了NFMECC0spare区的ECC保存到NFSECC。对于读操作来说,我们还要继续读取spare区的相应地址内容,已得到上次写操作时所存储的main区和spare区的ECC,并把这些数据分别放入NFMECCD0/1NFSECCD的相应位置中。最后我们就可以通过读取NFESTAT0/1(因为K9F2G08U0A8IO口,因此这里只用到了NFESTAT0中的低4位来判断读取的数据是否正确,其中第0位和第1位为main区指示错误,第2位和第3位为spare区指示错误。

 

     首先我们来了解一下与ECC相关的函数以及他们所在的文件

drivers/mtd/nand/s3c2410_nand.c文件中,有4个相应的函数,执行相应的功能

board_nand_init

(初始化nandflash,其中有调用另外三个函数,并且该函数有关于ECC的一个结构体

nand->ecc.hwctl = s3c2410_nand_enable_hwecc;

nand->ecc.calculate = s3c2410_nand_calculate_ecc;

nand->ecc.correct = s3c2410_nand_correct_data;

nand->ecc.mode = NAND_ECC_HW;

nand->ecc.size = CONFIG_SYS_NAND_ECCSIZE;

nand->ecc.bytes = CONFIG_SYS_NAND_ECCBYTES;

 

s3c2410_nand_enable_hwecc(函数负责使能硬件ECC

s3c2410_nand_calculate_ecc(函数负责计算ECC

s3c2410_nand_correct_data(函数负责进行ECC的校验)

board_nand_init函数会调用其他三个函数,完成硬件ECC的功能

drivers/mtd/nand/nand_base.c文件中,有两个函数

nand_read_page_hwecc

nand_read_page_hwecc

为了理解u-boot是如何进行硬件ECC的,我们先来简要地分析一下相关的函数。NandFlash是以页为最小单位进行读写操作的,支持硬件ECC的读操作最终是由nand_read_page_hwecc函数来完成的,支持硬件ECC的写操作最终是由nand_write_page_hwecc函数来完成的。nand_read_page_hwecc函数的流程为先读取main区数据,同时通过调用s3c2440_nand_calculate_ecc函数来得到硬件ECC;再读取spare区数据;然后提取出储存在spare区内的mainECC;最后通过调用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;

下面我们就来介绍一下这个循环的作用:不同的CPUNandFlash控制器一次所能完成的硬件ECC的字节数是不一样的,例如有些CPU一次只能完成512字节的硬件ECC,但如果开发板上的NandFlash每页有2048个字节,那该怎么办呢?这时就要用到一个循环体,通过循环多次来得到一页的硬件ECC。例如上面这种情况,就要循环4次(2048÷5124),才能得到这个页内数据完整的硬件ECC。另外每一次硬件ECC,不同的CPU所生成的ECC字节数也是不同的,有的是3个字节,有的是4个字节。

那么,上面那三个变量的含义就分别为:

ecc.size:每一次硬件ECC所检验的字节个数

ecc.bytes:每一次硬件ECC所生成的字节个数

ecc.steps:每一页需要进行硬件ECC的次数

对于S3C2440来说,一次硬件ECC可以检验2048个字节,并且生成4个字节的ECC,因此ecc.size应该为2048ecc.bytes应该为4。而ecc.steps是通过计算得到的,即系统上电后能够获知NandFlash的每页的大小,用这个值除以ecc.size就等于ecc.steps。所以对于这三个参数,只需事先定义好前两个参数即可。而这两个参数是在drivers/mtd/nand/s3c2410_nand.c文件中的board_nand_init函数内被定义赋值的。

 

下面我来修改这些函数,实现s3c2440nandflash的硬件ECC校验

include/configs/fl2440.h中添加硬件ECC校验的定义

#define CONFIG_S3C2410_NAND_HWECC

在这之后,

修改arch/arm/include/asm/arch-s3c24x0/s3c24x0.hnandflash寄存器的定义

#if defined (CONFIG_S3C2440)

/*  NAND FLASH (see S3C2440 manual chapter 6) */

struct s3c2410_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;

};

#endif

 

s3c2440_nand_enable_hwecc函数、s3c2440_nand_calculate_ecc函数和s3c2440_nand_correct_data函数进行修改如下

#ifdef CONFIG_S3C2410_NAND_HWECC

-----------------------------------------------------------------------------------------

该函数的任务就是初始化ECC(即复位ECC),并解锁mainECC

-------------------------------------------------------------------

 

void s3c2410_nand_enable_hwecc(struct mtd_info *mtd, int mode)

{

    struct s3c2410_nand *nand = s3c2410_get_base_nand();

    debugX(1, "s3c2410_nand_enable_hwecc(%p, %d)\n", mtd, mode);

#if defined(CONFIG_S3C2410)

    writel(readl(&nand->NFCONF) | S3C2410_NFCONF_INITECC, &nand->NFCONF);

#elif defined(CONFIG_S3C2440)

    writel(readl(&nand->NFCONT) | S3C2410_NFCONT_INITECC & ~S3C2410_NFCONT_MAINECCLOCK, &nand->NFCONT);

#endif

} 

-------------------------------------------------------------------------------------------------------------

该函数首先锁定mainECC,然后读取寄存器NFMECC0,该寄存器存放着由硬件生成的main

ECC,最后把41字节的ECC存放到ecc_code数组内。

--------------------------------------------------------------------------------------------------------------------------------------------------------------------

static int s3c2410_nand_calculate_ecc(struct mtd_info *mtd, const u_char *dat,

                      u_char *ecc_code)

{

    struct s3c2410_nand *nand = s3c2410_get_base_nand();

 

    writel(readl(&nand->NFCONT) | S3C2410_NFCONT_MAINECCLOCK, &nand->NFCONT); /* modify by hurryliu */

 

    ecc_code[0] = readb(&nand->NFMECC0);

    ecc_code[1] = readb(&nand->NFMECC0 + 1);

    ecc_code[2] = readb(&nand->NFMECC0 + 2);

    ecc_code[3] = readb(&nand->NFMECC0 + 3);

 

    debugX(1, "s3c2410_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;

}

 -----------------------------------------------------------------------------------------------------------------------------------------------

该函数首先把read_ecc数组内的ECC存入寄存器NFMECCD0和寄存器NFMECCD1中,这样系统就会自动校验数据,并把状态放入寄存器NFESTAT0中,然后读取该寄存器的后4位,当为0时表示校验正确;当为1时表示发生了1位错误(该类错误可以校正),我们把它校正过来;当为23时表示发生其他类型的错误,这类错误是无法校正的。

------------------------------------------------------------------------------------------------------------

static int s3c2410_nand_correct_data(struct mtd_info *mtd, u_char *dat,

                     u_char *read_ecc, u_char *calc_ecc)

{

    struct s3c2410_nand *nand = s3c2410_get_base_nand();

 

    u32  meccdata0, meccdata1, estat0, err_byte_addr;

    u8   ret = -1;

    u8   repaired;

 

    meccdata0= (read_ecc[1] << 16) | read_ecc[0]; /* modify by hurryliu */

    meccdata1= (read_ecc[3] << 16) | read_ecc[2];

 

    writel(meccdata0,&nand->NFMECCD0);

    writel(meccdata1,&nand->NFMECCD1);

 

    estat0= readl(&nand->NFESTAT0);

 

    switch(estat0 & 0x3) {

           case  0: /*  No error */

               ret= 0;

               break;

           case  1:

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

}

#endif

通过以上内容的修改,我们就实现了NandFlash的硬件ECC

 

原创粉丝点击