当前位置: 首页 > 工具软件 > S3QL > 使用案例 >

s3c2416--nandflash驱动--八位ecc校验

姜旭
2023-12-01

环境:

kerner:linux-2.6.39

uboot:u-boot-1.3.4

1.根据上篇

s3c2146 nandflash 8 bit ECC校验

我们知道了怎么配置和纠正ecc的大概想法。

2.我们可以参考u-boot-1.3.4目录的cpu/s3c24xx/nand.c关于8位ecc的使能,计算ecc,校准ecc。

使能函数:void s3c_nand_enable_hwecc_8bit(struct mtd_info *mtd, int mode)
计算ecc函数:int s3c_nand_calculate_ecc_8bit(struct mtd_info *mtd, const u_char *dat, u_char *ecc_code)
校准ecc函数:int s3c_nand_correct_data_8bit(struct mtd_info *mtd, u_char *dat, u_char *read_ecc, u_char *calc_ecc)
具体的内容请查看uboot中的源码。

3.修改我们的linux-2.6.39中的nand驱动源码目录是:drivers/mtd/nand/s3c2410.c

修改ecc纠错的位数只涉及到三个函数以及两个结构体分别是:

(1)配置校准的快大小和oob区需要的ecc字节数

注:注释的是本次跟改过的关于八位ecc的代码:注释的文字用红色表示。

/**
 * s3c2410_nand_update_chip - post probe update
 * @info: The controller instance.
 * @nmtd: The driver version of the MTD instance.
 *
 * This routine is called after the chip probe has successfully completed
 * and the relevant per-chip information updated. This call ensure that
 * we update the internal state accordingly.
 *
 * The internal state is currently limited to the ECC state information.
*/
static void s3c2410_nand_update_chip(struct s3c2410_nand_info *info,
                                     struct s3c2410_nand_mtd *nmtd)
{
        struct nand_chip *chip = &nmtd->chip;

        dev_dbg(info->device, "chip %p => page shift %d\n",
                chip, chip->page_shift);

        if (chip->ecc.mode != NAND_ECC_HW)       我们选择的是硬件ecc(所以必须先配置为硬件ecc)(make menuconfig->Device Drivers->   <*> Memory Technology Device (MTD) support  --->   <*>   NAND Device Support  ---> <*>   NAND Flash support for Samsung S3C SoCs 和[*]     Samsung S3C NAND Hardware ECC  )
                return;

                /* change the behaviour depending on wether we are using
                 * the large or small page nand device */

        if (chip->page_shift > 10) {
                chip->ecc.size      = 512;                                因为是8位ecc所以只能512bytes,根据s3c2416数据手册(chip->ecc.size:能检测的ecc大小)
                chip->ecc.bytes     = 13;                                根据s3c2416的数据手册当是8位ecc时,根据这个寄存器得到这个值NFM8ECC0                    ,NFM8ECC1,NFM8ECC2,NFM8ECC3(chip->ecc.bytes:所产生的ecc字节数用于存在oob区(spare区))
                chip->ecc.layout    = &nand_hw_eccoob;     //oob关于产生的ecc的布局
        } else {
                chip->ecc.size      = 512;
                chip->ecc.bytes     = 4;
                chip->ecc.layout    = &nand_hw_eccoob;
        }
}
2.配置oob区的布局
      /* new oob placement block for use with hardware ecc generation
 */
static struct nand_ecclayout nand_hw_eccoob = {
        .eccbytes = 13*4,                                                    我们flash是一块2k+64bytes(这个可以查flash的手册,也可以在uboot启动的时候用命令 nand info得到信息,因为512bytes产生13个字节的ecc,所以一块等于13*4)
        .eccpos = {11, 12, 13, 14, 15,
                        16, 17, 18, 19, 20,
                   21, 22, 23, 24, 25, 26, 27, 28, 29, 30,
                   31, 32, 33, 34, 35, 36, 37, 38, 39, 40,
                   41, 42, 43, 44, 45, 46, 47, 48, 49, 50,
                   51, 52, 53, 54, 55, 56, 57, 58, 59, 60,
                   61, 62
                   },                                                          布局随意只要有52个字节即可

        .oobfree = {{2, 9}, {63, 2}}                                    oob空闲的字节数 (64-52=12),(0,1两个字节用于存放bad快标志){(2,9):表示2到10九个字节空间}
};

/* controller and mtd information */       

3.使能8位Ecc(因为2416用的是这个函数)

 static void s3c2412_nand_enable_hwecc(struct mtd_info *mtd, int mode)
{
        struct s3c2410_nand_info *info = s3c2410_nand_mtd_toinfo(mtd);
        unsigned long ctrl;
         cur_ecc_mode = mode;                                 //知道当前的模式是读还是写flash
        unsigned long msgLength = 0;
        msgLength = readl(info->regs + S3C2410_NFCONF);        ///根据s3c2416数据手册

(1)使用8位ECC校验,首先将MsgLength位设置为0(设置message数据长度为512字节),同时ECCType位设置为“01”(使能8位ECC校验)。每读512字节数据,8位ECC校验模块就会产生ECC校验码。为了初始化8位ECC校验模块,首先要将MainECCLock(NFCONT[7])清零,然后向InitMECC(NFCONT[5])位写1。

MainECCLock位决定了ECC校验码是否产生。

NOTE:在8位ECC校验中,应该先清除MainECCLock位,再清除InitMECC位
        msgLength &= ~(1<<25);
        writel(msgLength, info->regs + S3C2410_NFCONF);

        unsigned long initEccBit = 0;
        initEccBit = readl(info->regs + S3C2410_NFCONF);
        initEccBit &= ~(1<<24);
        initEccBit |= 1<<23;
        writel(initEccBit, info->regs + S3C2410_NFCONF);

        ctrl = readl(info->regs + S3C2440_NFCONT);
        ctrl &= ~S3C2412_NFCONT_MAIN_ECC_LOCK;
        writel(ctrl, info->regs + S3C2440_NFCONT);

        ctrl = readl(info->regs + S3C2440_NFCONT);
        ctrl |= S3C2412_NFCONT_INIT_MAIN_ECC;
        writel(ctrl, info->regs + S3C2440_NFCONT);

        ctrl = readl(info->regs + S3C2440_NFCONT);
        if (NAND_ECC_WRITE == mode)
                ctrl &= ~(S3C2412_NFCONT_ECC4_DIRWR);                 //使能4-bit, 8-bitECC encoding / decoding control 0
                                                                                                                   //0 = Decoding 4-bit, 8bit ECC, It is used for page read(好像反了)
                                                                                                                  //1 = Encoding 4-bit, 8-bit ECC, It is be used for page program

        else
                ctrl |= (S3C2412_NFCONT_ECC4_DIRWR);
        writel(ctrl, info->regs + S3C2440_NFCONT);
       

}

4.计算8位Ecc(因为2416用的是这个函数)

static int s3c2412_nand_calculate_ecc(struct mtd_info *mtd, const u_char *dat, u_char *ecc_code)
{
        struct s3c2410_nand_info *info = s3c2410_nand_mtd_toinfo(mtd);

        /** ytq----------2016-09-19----------------------start*/
        unsigned long ctrl;
        unsigned long nfm8ecc0 = 0, nfm8ecc1 = 0, nfm8ecc2 = 0, nfm8ecc3 = 0;
        unsigned long nEccDirect = 0;

           /**

在读取512字节数据之后(不包括spare区数据),你必须把MainECCLock位设置为“1”(禁止产生ECC)并读取NANDFlash中保存的针对此512字节数据的ECC校验值

       */

        ctrl = readl(info->regs + S3C2440_NFCONT);
        ctrl |= S3C2412_NFCONT_MAIN_ECC_LOCK;                         
        writel(ctrl, info->regs + S3C2440_NFCONT);
        if (cur_ecc_mode == NAND_ECC_READ)
        {

            /*When ECCDecDone (NFSTAT[6]) is set (‘1’), NFECCERR0 indicates whether error bit exist or not. If any
error exists, you can fix it by referencing NFECCERR0/1 and NFMLCBITPT registe*/


                while (!(readl(info->regs + S3C2412_NFSTAT) & S3C2412_NFSTAT_ECC_DECDONE)) {}
        }
       else
       {

                  /*根据数据手册--在写入了512字节数据之后(不包括spare区数据),ECC校验码就会自动更新到寄存器NF8MECC0, NFMECC1, NFMECC2, NFMECC3中。如果你使用的NAND flash一页只有512字节数据,那么你就可以将产生的ECC校验码立即写入对应的spare区。如果你使用的NAND flash一页为2k或者4k, 那么只能先将ECC校验码存到内存里(可以建立一个数组)。等到将整页的数据完全写完后,再将保存在内存里的ECC校验码写入对应的spare区。


                   /*


                while (!(readl(info->regs + S3C2412_NFSTAT) & (S3C2412_NFSTAT_ECC_ENCDONE))) {}
                nfm8ecc0 = readl(info->regs + S3C2412_NFM8ECC0);
                 ecc_code[0] = (nfm8ecc0 & 0xff);
                ecc_code[1] = ((nfm8ecc0 >> 8) & 0xff);
                ecc_code[2] = ((nfm8ecc0 >> 16) & 0xff);
                ecc_code[3] = ((nfm8ecc0 >> 24) & 0xff);

                nfm8ecc1 = readl(info->regs + S3C2412_NFM8ECC1);
                ecc_code[4] = (nfm8ecc1 & 0xff);
                ecc_code[5] = ((nfm8ecc1 >> 8) & 0xff);
                ecc_code[6] = ((nfm8ecc1 >> 16) & 0xff);
                ecc_code[7] = ((nfm8ecc1 >> 24) & 0xff);

                nfm8ecc2 = readl(info->regs + S3C2412_NFM8ECC2);
                ecc_code[8] = (nfm8ecc2 & 0xff);
                ecc_code[9] = ((nfm8ecc2 >> 8) & 0xff);
                ecc_code[10] = ((nfm8ecc2 >> 16) & 0xff);
                ecc_code[11] = ((nfm8ecc2 >> 24) & 0xff);

                nfm8ecc3 = readl(info->regs + S3C2412_NFM8ECC3);
                ecc_code[12] = (nfm8ecc3 & 0xff);
        }

          return 0;

}                                                        
 5.校准8位Ecc(因为2416用的是这个函数)                                                        
static int s3c2410_nand_correct_data(struct mtd_info *mtd, u_char *dat,
                                     u_char *read_ecc, u_char *calc_ecc)
{
        struct s3c2410_nand_info *info = s3c2410_nand_mtd_toinfo(mtd);
        unsigned long nErrType = 0;
        unsigned long nErrByte = 0;
        unsigned long nErrBitPat = 0;
        unsigned long nEccDecodeDone = 0;
        unsigned long nEccDecodeDone1 = 0;
        unsigned long nf8eccerr0, nf8eccerr1, nf8eccerr2;
        unsigned long nfmlc8bitpt0, nfmlc8bitpt1;
        int ret = -1; 

       /*Indicates the 4-bit ECC decoding engine is searching whether a error exists or not Initial State 0 0 = Idle 1 = Busy*/

        while (readl(info->regs +S3C2412_NF8ECC_ERR0) & (1<<31)) {}

       /*当ECCDecDone位被置为“1”时, NF8ECCERR0寄存器能够表明是否存在错误。如果错误存在,你可以通过寄存器NF8ECCERR0/1/2 和 NFMLCBITPT0/1 的值来矫正这些错误。*/
        nf8eccerr0 = readl(info->regs + S3C2412_NF8ECC_ERR0);
        nf8eccerr1 = readl(info->regs + S3C2412_NF8ECC_ERR1);
        nf8eccerr2 = readl(info->regs + S3C2412_NF8ECC_ERR2);
        nfmlc8bitpt0 = readl(info->regs + S3C2412_NFMLC8BIT_PT0);
        nfmlc8bitpt1 = readl(info->regs + S3C2412_NFMLC8BIT_PT1);

        nErrType = ((nf8eccerr0 >> 25) & 0xf);       //根据数据手册判断出现几位错误

        if((nf8eccerr0 >> 29) & 0x1)
                nErrType = 0;
        switch(nErrType)
        {
          case 9:
                ret = -1;
                printk("s3c-nand: ECC uncorrectable error detected\n");
                break;
          case 8:
                dat[((nf8eccerr2 >> 22) & 0x3ff)] ^= ((nfmlc8bitpt1 >> 24) & 0xff);
          case 7:
                dat[((nf8eccerr2 >> 11) & 0x3ff)] ^= ((nfmlc8bitpt1 >> 16) & 0xff);
          case 6:

         dat[(nf8eccerr2 & 0x3ff)] ^= ((nfmlc8bitpt1 >> 8) & 0xff);
          case 5:
                dat[((nf8eccerr1 >> 22) & 0x3ff)] ^= (nfmlc8bitpt1 & 0xff);
          case 4:
                dat[((nf8eccerr1 >> 11) & 0x3ff)] ^= ((nfmlc8bitpt0 >> 24) & 0xff);
          case 3:
                dat[(nf8eccerr1 & 0x3ff)] ^= ((nfmlc8bitpt0 >> 16) & 0xff);
          case 2:
                dat[((nf8eccerr0 >> 15) & 0x3ff)] ^= ((nfmlc8bitpt0 >> 8) & 0xff);
          case 1:
                printk("s3c-nand: %d bit(s) error detected, corrected successfully\n", nErrType);
                dat[(nf8eccerr0 & 0x3ff)] ^= (nfmlc8bitpt0 & 0xff);
                ret = nErrType;
                break;
          case 0:

   ret = 0;
                break;
        }
        return ret;
}


                                  


 类似资料: