Chinaunix首页 | 论坛 | 博客
  • 博客访问: 509841
  • 博文数量: 398
  • 博客积分: 0
  • 博客等级: 民兵
  • 技术积分: 14
  • 用 户 组: 普通用户
  • 注册时间: 2013-08-21 16:02
个人简介

嵌入式屌丝

文章分类

全部博文(398)

文章存档

2013年(398)

我的朋友

分类: LINUX

2013-08-21 16:24:28

Arm-linux东东nand7: nand_scan_tail

.进入这个函数请大家打十二分精神……因为这个函数会跑MFC那样长的路

………………….

int nand_scan_tail(struct mtd_info *mtd)

{

       int i;

       struct nand_chip *chip = mtd->priv;

 

       if (!(chip->options & NAND_OWN_BUFFERS))

              chip->buffers = kmalloc(sizeof(*chip->buffers), GFP_KERNEL);

       if (!chip->buffers)

              return -ENOMEM;

 

       /* Set the internal oob buffer location, just after the page data */

       chip->oob_poi = chip->buffers->databuf + mtd->writesize;

chip里有个buffers:

struct nand_buffers {

       uint8_t    ecccalc[NAND_MAX_OOBSIZE];

       uint8_t    ecccode[NAND_MAX_OOBSIZE];

       uint8_t databuf[NAND_MAX_PAGESIZE + NAND_MAX_OOBSIZE];

};

这里oob_poi就是指向databuf的第512个字节.这个地址是放ECC数据的..下面会看到.

……………………      

if (!chip->ecc.layout) {

              switch (mtd->oobsize) {

              case 8:

                     chip->ecc.layout = &nand_oob_8;

                     break;

              case 16:

                     chip->ecc.layout = &nand_oob_16;

                     break;

              case 64:

                     chip->ecc.layout = &nand_oob_64;

                     break;

              default:

                     printk(KERN_WARNING "No oob scheme defined for "

                            "oobsize %d\n", mtd->oobsize);

                     BUG();

              }

       }

 

       if (!chip->write_page)

              chip->write_page = nand_write_page;

 

       /*

        * check ECC mode, default to software if 3byte/512byte hardware ECC is

        * selected and we have 256 byte pagesize fallback to software ECC

        */

       if (!chip->ecc.read_page_raw)

              chip->ecc.read_page_raw = nand_read_page_raw;

       if (!chip->ecc.write_page_raw)

              chip->ecc.write_page_raw=nand_write_page_raw;…………………………………………………………

layout是有值的.接下来就是ecc内的函数指针定值.还是用到再说.

……………………….

switch (chip->ecc.mode) {

       case NAND_ECC_HW:

              /* Use standard hwecc read page function ? */

              if (!chip->ecc.read_page)

                     chip->ecc.read_page = nand_read_page_hwecc;

              if (!chip->ecc.write_page)

                     chip->ecc.write_page = nand_write_page_hwecc;

              if (!chip->ecc.read_oob)

                     chip->ecc.read_oob = nand_read_oob_std;

              if (!chip->ecc.write_oob)

                     chip->ecc.write_oob = nand_write_oob_std;

 

       case NAND_ECC_HW_SYNDROME:

              if ((!chip->ecc.calculate || !chip->ecc.correct ||

                   !chip->ecc.hwctl) &&

                  (!chip->ecc.read_page ||

                   chip->ecc.read_page == nand_read_page_hwecc ||

                   !chip->ecc.write_page ||

                   chip->ecc.write_page == nand_write_page_hwecc)) {

                     printk(KERN_WARNING "No ECC functions supplied, "

                            "Hardware ECC not possible\n");

                     BUG();

              }

              /* Use standard syndrome read/write page function ? */

              if (!chip->ecc.read_page)

                     chip->ecc.read_page = nand_read_page_syndrome;

              if (!chip->ecc.write_page)

                     chip->ecc.write_page = nand_write_page_syndrome;

              if (!chip->ecc.read_oob)

                     chip->ecc.read_oob = nand_read_oob_syndrome;

              if (!chip->ecc.write_oob)

                     chip->ecc.write_oob = nand_write_oob_syndrome;

 

              if (mtd->writesize >= chip->ecc.size)

                     break;

              printk(KERN_WARNING "%d byte HW ECC not possible on "

                     "%d byte page size, fallback to SW ECC\n",

                     chip->ecc.size, mtd->writesize);

              chip->ecc.mode = NAND_ECC_SOFT;

 

       case NAND_ECC_SOFT:

              chip->ecc.calculate = nand_calculate_ecc;

              chip->ecc.correct = nand_correct_data;

              chip->ecc.read_page = nand_read_page_swecc;

              chip->ecc.write_page = nand_write_page_swecc;

              chip->ecc.read_oob = nand_read_oob_std;

              chip->ecc.write_oob = nand_write_oob_std;

              chip->ecc.size = 256;

              chip->ecc.bytes = 3;

              break;

 

       case NAND_ECC_NONE:

              printk(KERN_WARNING "NAND_ECC_NONE selected by board driver. "

                     "This is not recommended !!\n");

              chip->ecc.read_page = nand_read_page_raw;

              chip->ecc.write_page = nand_write_page_raw;

              chip->ecc.read_oob = nand_read_oob_std;

              chip->ecc.write_oob = nand_write_oob_std;

              chip->ecc.size = mtd->writesize;

              chip->ecc.bytes = 0;

              break;

 

       default:

              printk(KERN_WARNING "Invalid NAND_ECC_MODE %d\n",

                     chip->ecc.mode);

              BUG();

       }

……………………….

ECC又来了.还记得在s3c2410_nand_init_chip中有一句这样的吗:

              chip->ecc.mode         = NAND_ECC_HW;

这里顺便说下:ECC检验错的问题.如果用硬件算ECC会出错.很多众人就干脆不用.为什么呢.因为bootloaderECC算法与这里硬件的算法不同.所以就出错.建议在bootloader中的ECC算法改成由硬件来算.

先把下面的说完.等下还会回来的

……………………

chip->ecc.layout->oobavail = 0;

       for (i = 0; chip->ecc.layout->oobfree[i].length; i++)

              chip->ecc.layout->oobavail +=

                     chip->ecc.layout->oobfree[i].length;

       mtd->oobavail = chip->ecc.layout->oobavail;

 

       /*

        * Set the number of read / write steps for one page depending on ECC

        * mode

        */

       chip->ecc.steps = mtd->writesize / chip->ecc.size;

       if(chip->ecc.steps * chip->ecc.size != mtd->writesize) {

              printk(KERN_WARNING "Invalid ecc parameters\n");

              BUG();

       }

       chip->ecc.total = chip->ecc.steps * chip->ecc.bytes;

……………………..

Ecc.steps就是算ECC有多少步了.如果ecc.size256的那么就需要2.这里的ecc.size512的所以一步就行了. ecc.total就是所有步下来生成的ecc大小…..

:来看上面了.对于硬件算ECC来说

              if (!chip->ecc.read_page)

                     chip->ecc.read_page = nand_read_page_hwecc;

              if (!chip->ecc.write_page)

                     chip->ecc.write_page = nand_write_page_hwecc;

              if (!chip->ecc.read_oob)

                     chip->ecc.read_oob = nand_read_oob_std;

              if (!chip->ecc.write_oob)

                     chip->ecc.write_oob = nand_write_oob_std;

这里只说二个函数就是: nand_write_page_hwecc,, nand_read_page_hwecc

…………………………………………………………….

static void nand_write_page_hwecc(struct mtd_info *mtd, struct nand_chip *chip,

                              const uint8_t *buf)

{

       int i, eccsize = chip->ecc.size;

       int eccbytes = chip->ecc.bytes;

       int eccsteps = chip->ecc.steps;

       uint8_t *ecc_calc = chip->buffers->ecccalc;

       const uint8_t *p = buf;

       uint32_t *eccpos = chip->ecc.layout->eccpos;

 

       for (i = 0; eccsteps; eccsteps--, i += eccbytes, p += eccsize) {

              chip->ecc.hwctl(mtd, NAND_ECC_WRITE);

              chip->write_buf(mtd, p, eccsize);

              chip->ecc.calculate(mtd, p, &ecc_calc[i]);

       }

 

       for (i = 0; i < chip->ecc.total; i++)

              chip->oob_poi[eccpos[i]] = ecc_calc[i];

 

       chip->write_buf(mtd, chip->oob_poi, mtd->oobsize);

}

……………………………………………………….

有多少步for的循环多少次.我没有骗你吧 .    write_buff一次就算一下ECC.

对于nand_read_page_hwecc就比较难了.

……………………………

static int nand_read_page_hwecc(struct mtd_info *mtd, struct nand_chip *chip,

                            uint8_t *buf)

{

       int i, eccsize = chip->ecc.size;

       int eccbytes = chip->ecc.bytes;

       int eccsteps = chip->ecc.steps;

       uint8_t *p = buf;

       uint8_t *ecc_calc = chip->buffers->ecccalc;

       uint8_t *ecc_code = chip->buffers->ecccode;

       uint32_t *eccpos = chip->ecc.layout->eccpos;

 

       for (i = 0; eccsteps; eccsteps--, i += eccbytes, p += eccsize) {

              chip->ecc.hwctl(mtd, NAND_ECC_READ);

              chip->read_buf(mtd, p, eccsize);

              chip->ecc.calculate(mtd, p, &ecc_calc[i]);

       }

       chip->read_buf(mtd, chip->oob_poi, mtd->oobsize);

 

       for (i = 0; i < chip->ecc.total; i++)

              ecc_code[i] = chip->oob_poi[eccpos[i]];

 

       eccsteps = chip->ecc.steps;

       p = buf;

…………………………

这里红色的read_buf就是读上面写进去的ECC.放在chip->oob_poi

上面的那个ecc.calculate就是在ECC寄存器的数据.

ecc.layout这里就用到了.拿出对照一下就清楚了.这里就不说了.

………………..

for (i = 0 ; eccsteps; eccsteps--, i += eccbytes, p += eccsize) {

              int stat;

 

              stat = chip->ecc.correct(mtd, p, &ecc_code[i], &ecc_calc[i]);

              if (stat < 0)

                     mtd->ecc_stats.failed++;

              else

                     mtd->ecc_stats.corrected += stat;

       }

       return 0;

…………………..

接下来就要看一下两个ECC的值能不能对的上.

stat = chip->ecc.correct(mtd, p, &ecc_code[i], &ecc_calc[i]);就是调用:

s3c2410_nand_correct_data

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 int diff0, diff1, diff2;

       unsigned int bit, byte;

 

       pr_debug("%s(%p,%p,%p,%p)\n", __func__, mtd, dat, read_ecc, calc_ecc);

 

       diff0 = read_ecc[0] ^ calc_ecc[0];

       diff1 = read_ecc[1] ^ calc_ecc[1];

       diff2 = read_ecc[2] ^ calc_ecc[2];

 

       pr_debug("%s: rd %02x%02x%02x calc %02x%02x%02x diff %02x%02x%02x\n",

               __func__,

               read_ecc[0], read_ecc[1], read_ecc[2],

               calc_ecc[0], calc_ecc[1], calc_ecc[2],

               diff0, diff1, diff2);

 

       if (diff0 == 0 && diff1 == 0 && diff2 == 0)

              return 0;         /* ECC is ok */

这里diff0 = read_ecc[0] ^ calc_ecc[0];异或是吧.大一的时候老师说了:两个相同的值异或就是0如果read_ecc[0]calc_ecc[0]相同.那么diff0就是0

       if (diff0 == 0 && diff1 == 0 && diff2 == 0)

              return 0;         /* ECC is ok */

如果个个都为0.那么很好ECC没有问题….

如果不为0?接着看:

……………..

if (read_ecc[0] == 0xff && read_ecc[1] == 0xff && read_ecc[2] == 0xff

           && info->platform->ignore_unset_ecc)

              return 0;

………………….

为什么是等于0XFF就说明ECC没有问题呢?

NAND出厂的时候由于没有写过那么所有的内容就是0xFF.如果某个bootleader在写NAND的时候并没有用ECC,那么其内容是不是还是0xFF…OK

……………………

if (((diff0 ^ (diff0 >> 1)) & 0x55) == 0x55 &&

           ((diff1 ^ (diff1 >> 1)) & 0x55) == 0x55 &&

           ((diff2 ^ (diff2 >> 1)) & 0x55) == 0x55) {

              /* calculate the bit position of the error */

 

              bit  = ((diff2 >> 3) & 1) |

                     ((diff2 >> 4) & 2) |

                     ((diff2 >> 5) & 4);

 

              /* calculate the byte position of the error */

 

              byte = ((diff2 << 7) & 0x100) |

                     ((diff1 << 0) & 0x80)  |

                     ((diff1 << 1) & 0x40)  |

                     ((diff1 << 2) & 0x20)  |

                     ((diff1 << 3) & 0x10)  |

                     ((diff0 >> 4) & 0x08)  |

                     ((diff0 >> 3) & 0x04)  |

                     ((diff0 >> 2) & 0x02)  |

                     ((diff0 >> 1) & 0x01);

 

              dev_dbg(info->device, "correcting error bit %d, byte %d\n",

                     bit, byte);

 

              dat[byte] ^= (1 << bit);

              return 1;

       }

 

       /* if there is only one bit difference in the ECC, then

        * one of only a row or column parity has changed, which

        * means the error is most probably in the ECC itself */

 

       diff0 |= (diff1 << 8);

       diff0 |= (diff2 << 16);

 

       if ((diff0 & ~(1<

              return 1;

 

       return -1;

}

…………………………………….

接下来就是有问题的了.但是还能够用ECC把数据恢复过来,谢天谢地.

如果你的程序到了这里,相信这块NAND也不长命的了..

这里的算法不是很懂.所以没有办法说了.

s3c2410_nand_correct_data就完了.一直返回到nand_scan_tail

中来………………..

阅读(540) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~