nand flash坏块管理OOB,BBT,ECC

<p><strong>0.NAND的操作管理方式</strong>
</p>
<p><strong> </strong>
NAND FLASH的管理方式:以三星FLASH为例,一片Nand
flash为一个设备(device),1 (Device) = xxxx (Blocks),1 (Block) = xxxx
(Pages),1(Page)=528 (Bytes) = 数据块大小(512Bytes) + OOB
块大小(16Bytes,除OOB第六字节外,通常至少把OOB的前3个字节存放Nand Flash硬件ECC码)。</p>
<p> 关于OOB区,是每个Page都有的。Page大小是512字节的NAND每页分配16字节的OOB;如果NAND物理上是2K的Page,则每个Page分配64字节的OOB。如下图:</p>
<p> <img src="http://hi.csdn.net/attachment/201103/25/0_1301039353CC60.gif" alt=""></p>
<p>以HYNIX为例,图中黑体的是实际探测到的NAND,是个2G
bit(256M)的NAND。PgSize是2K字节,PgsPBlk表示每个BLOCK包含64页,那么每个BLOCK占用的字节数是
64X2K=128K字节;该NAND包好2048个BLOCK,那么可以算出NAND占用的字节数是2048X128K=256M,与实际相符。需要注
意的是SprSize就是OOB大小,也恰好是2K页所用的64字节。</p>
<p><strong>1.为什么会出现坏块<br></strong>
  由于NAND Flash的工艺不能保证NAND的Memory
Array在其生命周期中保持性能的可靠,因此,在NAND的生产中及使用过程中会产生坏块。坏块的特性是:当编程/擦除这个块时,会造成Page
Program和Block Erase操作时的错误,相应地反映到Status Register的相应位。</p>
<p><strong>2.坏块的分类<br></strong>
  总体上,坏块可以分为两大类:(1)固有坏块:这是生产过程中产生的坏块,一般芯片原厂都会在出厂时都会将每个坏块第一个page的spare area的第6个byte标记为<span style="color: #000000;">不等于0xff的</span>
值。(2)使用坏块:这是在NAND Flash使用过程中,<span style="color: #000000;">如果Block Erase或者Page Program错误,就可以简单地将这个块作为坏块来处理,这个时候需要把坏块标记起来。为了和固有坏块信息保持一致,将新发现的坏块的第一个page的 spare area的第6个Byte标记为非0xff的值。</span>
</p>
<p><span style="color: #ff0000;"><span style="color: #000000;"><strong>3.坏块管理<br></strong>
  根据上面的这些叙述,可以了解NAND Flash出厂时在spare area中已经反映出了坏块信息,因此,</span>
<span style="color: #000000;">如果在擦除一个块之前,一定要先check一下第一页的spare area的第6个byte是否是0xff,如果是就证明这是一个好块,可以擦除;如果是非0xff,那么就不能擦除,以免将坏块标记擦掉。</span>
<span style="color: #000000;"><span style="color: #000000;">当然,这样处理可能会犯一个错误―――“错杀伪坏块”,因为在芯片操作过程中可能由于</span>
电压不稳定等偶然因素会造成NAND操作的错误。但是,为了数据的可靠性及软件设计的简单化,还是需要遵照这个标准。</span>
</span>
</p>
<p><span style="color: #ff0000;"><span style="color: #000000;">
可以用BBT:bad block
table,即坏块表来进行管理。各家对nand的坏块管理方法都有差异。比如专门用nand做存储的,会把bbt放到block0,因为第0块一定是好
的块。但是如果nand本身被用来boot,那么第0块就要存放程序,不能放bbt了。</span>
</span>
<span style="color: #ff0000;"><span style="color: #000000;">有的把bbt放到最后一块,当然,这一块坚决不能为坏块。</span>
</span>
<span style="color: #ff0000;"><span style="color: #000000;">bbt的大小跟nand大小有关,nand越大,需要的bbt也就越大。</span>
</span>
</p>
<p><span style="color: #ff0000;"><span style="color: #000000;"> <strong>需要注意的是:OOB是每个页都有的数据,里面存的有ECC(当然不仅仅);而BBT是一个FLASH才有一个;针对每个BLOCK的坏块识别则是该块第一页spare area的第六个字节。</strong>
<br><strong>4.坏块纠正</strong>
</span>
</span>
</p>
<p><span style="color: #003300;"><span style="color: #000000;"> ECC:</span>
</span>
<span style="color: #000000;">NAND

Flash出错的时候一般不会造成整个Block或是Page不能读取或是全部出错,而是整个Page(例如512Bytes)中只有一个或几个bit出
错。一般使用一种比较专用的校验——ECC。ECC能纠正单比特错误和检测双比特错误,而且计算速度很快,但对1比特以上的错误无法纠正,对2比特以上的
错误不保证能检测。<br>
ECC一般每256字节原始数据生成3字节ECC校验数据,这三字节共24比特分成两部分:6比特的列校验和16比特的行校验,多余的两个比特置1。(512生成两组ECC,共6字节)<br>
当往NAND Flash的page中写入数据的时候,每256字节我们生成一个ECC校验和,称之为原ECC校验和,保存到PAGE的<strong>OOB</strong>
(out-
of-band)数据区中。其位置就是eccpos[]。校验的时候,根据上述ECC生成原理不难推断:将从OOB区中读出的原ECC校验和新ECC校验
和按位异或,若结果为0,则表示不存在错(或是出现了ECC无法检测的错误);若3个字节异或结果中存在11个比特位为1,表示存在一个比特错误,且可纠
正;若3个字节异或结果中只存在1个比特位为1,表示OOB区出错;其他情况均表示出现了无法纠正的错误。 <br></span>
<span style="color: #000000;"><strong>5.补充<br></strong>
 
(1)需要对前面由于Page
Program错误发现的坏块进行一下特别说明。如果在对一个块的某个page进行编程的时候发生了错误就要把这个块标记为坏块,首先就要把块里其他好的
面的内容备份到另外一个空的好块里面,然后,把这个块标记为坏块。当然,这可能会犯“错杀”之误,一个补救的办法,就是在进行完块备份之后,再将这个坏块
擦除一遍,如果Block Erase发生错误,那就证明这个块是个真正的坏块,那就毫不犹豫地将它打个“戳”吧!<br>
  (2)可能有人会问,为什么要使用每个块第一页的spare area的第六个byte作为坏块标记。这是NAND Flash生产商的默认约定,你可以看到Samsung,Toshiba,STMicroelectronics都是使用这个Byte作为坏块标记的。</span>
</p>
<p>
<span style="color: #ff0000;"><span style="color: #000000;"><span style="color: #000000;"> (3)为什么好块用0xff来标记?因为Nand Flash的擦除即是将相应块的位全部变为1,写操作时只能把芯片每一位(bit)只能从1变为0,而不能从0变为1。0XFF这个值就是标识擦除成功,是好块。</span>
</span>
</span>
</p>
<p></p>
<p></p>
<p></p>
<p></p>
<p></p>
<p></p>
<p></p>
<p><span style="color: #ff0000;"><span style="color: #000000;"><span style="color: #000000;">====================================================</span>
</span>
</span>
</p>
<p></p>
<p></p>
<p><textarea cols="50" rows="15" name="code" class="c-sharp">bbt坏块管理
日月 发表于 - 2010-3-2 9:59:00
2
推荐
前面看到在nand_scan()函数的最后将会跳至scan_bbt()函数,这个函数在nand_scan里面有定义:
2415 if (!this-&gt;scan_bbt)
2416 this-&gt;scan_bbt = nand_default_bbt;
nand_default_bbt()位于Nand_bbt.c文件中。
1047 /**
    * nand_default_bbt - [NAND Interface] Select a default bad block table for the device
    * @mtd: MTD device structure
    *
    * This selects the default bad block table
    * support for the device and calls the nand_scan_bbt
  **/
  int nand_default_bbt (struct mtd_info *mtd)
  {
   struct nand_chip *this = mtd-&gt;priv;
这个函数的作用是建立默认的坏块表。
1059 /* Default for AG-AND. We must use a flash based
   * bad block table as the devices have factory marked
   * _good_ blocks. Erasing those blocks leads to loss
   * of the good / bad information, so we _must_ store
* this information in a good / bad table during
* startup
   */
   if (this-&gt;options &amp; NAND_IS_AND) {
   /* Use the default pattern deors */
   if (!this-&gt;bbt_td) {
    this-&gt;bbt_td = &amp;bbt_main_descr;
    this-&gt;bbt_md = &amp;bbt_mirror_descr;
   }
    this-&gt;options |= NAND_USE_FLASH_BBT;
    return nand_scan_bbt (mtd, &amp;agand_flashbased);
   }
如果Flash的类型是AG-AND(这种Flash类型比较特殊,既不是MLC又不是SLC,因此不去深究了,而且好像瑞萨要把它淘汰掉),需要使用默认的模式描述符,最后再进入nand_scan_bbt()函数。
1078 /* Is a flash based bad block table requested ? */
   if (this-&gt;options &amp; NAND_USE_FLASH_BBT) {
   /* Use the default pattern deors */
   if (!this-&gt;bbt_td) {
    this-&gt;bbt_td = &amp;bbt_main_descr;
    this-&gt;bbt_md = &amp;bbt_mirror_descr;
   }
   if (!this-&gt;badblock_pattern) {
    this-&gt;badblock_pattern = (mtd-&gt;oobblock &gt; 512) ?
     &amp;largepage_flashbased : &amp;smallpage_flashbased;
   }
   } else {
   this-&gt;bbt_td = NULL;
   this-&gt;bbt_md = NULL;
   if (!this-&gt;badblock_pattern) {
    this-&gt;badblock_pattern = (mtd-&gt;oobblock &gt; 512) ?
     &amp;largepage_memorybased : &amp;smallpage_memorybased;
   }
   }
 
   return nand_scan_bbt (mtd, this-&gt;badblock_pattern);
如果Flash芯片需要使用坏块表,对于1208芯片来说是使用smallpage_memorybased。
985   static struct nand_bbt_descr smallpage_memorybased = {
   .options = NAND_BBT_SCAN2NDPAGE,
   .offs = 5,
   .len = 1,
   .pattern = scan_ff_pattern
  };
暂时没看到如何使用这些赋值,先放着。后面检测坏块时用得着。
1099 return nand_scan_bbt (mtd, this-&gt;badblock_pattern);
最后将badblock_pattern作为参数,调用nand_can_bbt函数。
844   /**
  * nand_scan_bbt - [NAND Interface] scan, find, read and maybe create bad block table(s)
   * @mtd: MTD device structure
   * @bd:   deor for the good/bad block search pattern
   *
   * The checks, if a bad block table(s) is/are already
   * available. If not it scans the device for manufacturer
   * marked good / bad blocks and writes the bad block table(s) to
   * the selected place.
   *
   * The bad block table memory is allocated here. It must be freed
   * by calling the nand_free_bbt .
   *
  */
  int nand_scan_bbt (struct mtd_info *mtd, struct nand_bbt_descr *bd)
  {
检测、寻找、读取甚至建立坏块表。函数检测是否已经存在一张坏块表,否则建立一张。坏块表的内存分配也在这个函数中。
860 struct nand_chip *this = mtd-&gt;priv;
int len, res = 0;
uint8_t *buf;
struct nand_bbt_descr *td = this-&gt;bbt_td;
struct nand_bbt_descr *md = this-&gt;bbt_md;
len = mtd-&gt;size &gt;&gt; (this-&gt;bbt_erase_shift + 2);
/* Allocate memory (2bit per block) */
this-&gt;bbt = kmalloc (len, GFP_KERNEL);
if (!this-&gt;bbt) {
   printk (KERN_ERR "nand_scan_bbt: Out of memory/n");
   return -ENOMEM;
}
/* Clear the memory bad block table */
memset (this-&gt;bbt, 0x00, len);
一些赋值、变量声明、内存分配,每个block分配2bit的空间。1208有4096个block,应该分配4096*2bit的空间。
877 /* If no primary table decriptor is given, scan the device
* to build a memory based bad block table
*/
if (!td) {
   if ((res = nand_memory_bbt(mtd, bd))) {
    printk (KERN_ERR "nand_bbt: Can't scan flash and build the RAM-based BBT/n");
    kfree (this-&gt;bbt);
    this-&gt;bbt = NULL;
   }
   return res;
}
如果没有提供ptd,就扫描设备并建立一张。这里调用了nand_memory_bbt()这个内联函数。
653 /**
   * nand_memory_bbt - [GENERIC] create a memory based bad block table
   * @mtd: MTD device structure
   * @bd:   deor for the good/bad block search pattern
   *
   * The creates a memory based bbt by scanning the device
   * for manufacturer / software marked good / bad blocks
  */
  static inline int nand_memory_bbt (struct mtd_info *mtd, struct nand_bbt_descr *bd)
  {
   struct nand_chip *this = mtd-&gt;priv;
   bd-&gt;options &amp;= ~NAND_BBT_SCANEMPTY;
   return create_bbt (mtd, this-&gt;data_buf, bd, -1);
  }
函数的作用是建立一张基于memory的坏块表。
将操作符的NAND_BBT_SCANEMPTY清除,并继续调用creat_bbt()函数。
271 /**
  * create_bbt - [GENERIC] Create a bad block table by scanning the device
   * @mtd: MTD device structure
   * @buf: temporary buffer
   * @bd:   deor for the good/bad block search pattern
   * @chip: create the table for a specific chip, -1 read all chips.
   *   Applies only if NAND_BBT_PERCHIP option is set
   *
   * Create a bad block table by scanning the device
   * for the given good/bad block identify pattern
   */
  static int create_bbt (struct mtd_info *mtd, uint8_t *buf, struct nand_bbt_descr *bd, int chip)
  {
真正的建立坏块表函数。chip参数是-1表示读取所有的芯片。
284 struct nand_chip *this = mtd-&gt;priv;
int i, j, numblocks, len, scanlen;
int startblock;
loff_t from;
size_t readlen, ooblen;
printk (KERN_INFO "Scanning device for bad blocks/n");
一些变量声明,开机时那句话就是在这儿打印出来的。
292 if (bd-&gt;options &amp; NAND_BBT_SCANALLPAGES)
len = 1 &lt;&lt; (this-&gt;bbt_erase_shift - this-&gt;page_shift);
else {
   if (bd-&gt;options &amp; NAND_BBT_SCAN2NDPAGE)
    len = 2;
   else
    len = 1;
}
在前面我们定义了smallpage_memorybased这个结构体,现在里面NAND_BBT_SCANALLPAGES的终于用上了,对于1208芯片来说,len=2。
304 if (!(bd-&gt;options &amp; NAND_BBT_SCANEMPTY)) {
   /* We need only read few bytes from the OOB area */
   scanlen = ooblen = 0;
   readlen = bd-&gt;len;
} else {
   /* Full page content should be read */
   scanlen = mtd-&gt;oobblock + mtd-&gt;oobsize;
   readlen = len * mtd-&gt;oobblock;
   ooblen = len * mtd-&gt;oobsize;
}
前面已经将NAND_BBT_SCANEMPTY清除了,这里肯定执行else的内容。需要将一页内容都读取出来。
316 if (chip == -1) {
   /* Note that numblocks is 2 * (real numblocks) here, see i+=2 below as it
   * makes shifting and masking less painful */
   numblocks = mtd-&gt;size &gt;&gt; (this-&gt;bbt_erase_shift - 1);
   startblock = 0;
   from = 0;
} else {
   if (chip &gt;= this-&gt;numchips) {
    printk (KERN_WARNING "create_bbt(): chipnr (%d) &gt; available chips (%d)/n",
     chip + 1, this-&gt;numchips);
    return -EINVAL;
   }
   numblocks = this-&gt;chipsize &gt;&gt; (this-&gt;bbt_erase_shift - 1);
   startblock = chip * numblocks;
   numblocks += startblock;
   from = startblock &lt;&lt; (this-&gt;bbt_erase_shift - 1);
}
前面提到chip为-1,实际上我们只有一颗芯片,numblocks这儿是4096*2。
335 for (i = startblock; i &lt; numblocks;) {
   int ret;
   if (bd-&gt;options &amp; NAND_BBT_SCANEMPTY)
    if ((ret = nand_read_raw (mtd, buf, from, readlen, ooblen)))
     return ret;
   for (j = 0; j &lt; len; j++) {
    if (!(bd-&gt;options &amp; NAND_BBT_SCANEMPTY)) {
     size_t retlen;
     /* Read the full oob until read_oob is fixed to
     * handle single byte reads for 16 bit buswidth */
     ret = mtd-&gt;read_oob(mtd, from + j * mtd-&gt;oobblock,
        mtd-&gt;oobsize, &amp;retlen, buf);
     if (ret)
      return ret;
     if (check_short_pattern (buf, bd)) {
       this-&gt;bbt[i &gt;&gt; 3] |= 0x03 &lt;&lt; (i &amp; 0x6);
      printk (KERN_WARNING "Bad eraseblock %d at 0x%08x/n",
       i &gt;&gt; 1, (unsigned int) from);
        break;
     }
    } else {
     if (check_pattern (&amp;buf[j * scanlen], scanlen, mtd-&gt;oobblock, bd)) {
         this-&gt;bbt[i &gt;&gt; 3] |= 0x03 &lt;&lt; (i &amp; 0x6);
      printk (KERN_WARNING "Bad eraseblock %d at 0x%08x/n",
       i &gt;&gt; 1, (unsigned int) from);
         break;
     }
    }
   }
   i += 2;
   from += (1 &lt;&lt; this-&gt;bbt_erase_shift);
}
return 0;
检测这4096个block,刚开始的nand_read_raw肯定不会执行。len是2,在j循环要循环2次。
每次循环真正要做的事情是下面的内容:
ret = mtd-&gt;read_oob(mtd, from + j * mtd-&gt;oobblock, mtd-&gt;oobsize, &amp;retlen, buf);
read_oob()函数在nand_scan()里被指向nand_read_oob(),这个函数在Nand_base.c文件中,看来得回Nand_base.c看看了。
1397 /**
   * nand_read_oob - [MTD Interface] NAND read out-of-band
   * @mtd: MTD device structure
   * @from: offset to read from
   * @len: number of bytes to read
   * @retlen: pointer to variable to store the number of read bytes
   * @buf: the databuffer to put data
   *
   * NAND read out-of-band data from the spare area
   */
static int nand_read_oob (struct mtd_info *mtd, loff_t from, size_t len, size_t * retlen, u_char * buf)
  {
才发现oob全称是out-of-band, from是偏移量,len是读取的长度,retlen是存储指针。
1409 int i, col, page, chipnr;
struct nand_chip *this = mtd-&gt;priv;
int blockcheck = (1 &lt;&lt; (this-&gt;phys_erase_shift - this-&gt;page_shift)) - 1;
DEBUG (MTD_DEBUG_LEVEL3, "nand_read_oob: from = 0x%08x, len = %i/n", (unsigned int) from, (int) len);
/* Shift to get page */
page = (int)(from &gt;&gt; this-&gt;page_shift);
chipnr = (int)(from &gt;&gt; this-&gt;chip_shift);
/* Mask to get column */
col = from &amp; (mtd-&gt;oobsize - 1);
/* Initialize return length value */
*retlen = 0;
一些初始化,blockcheck对于1208应该是(1&lt;&lt;(0xe-0x9)-1)=31。然后通过偏移量计算出要读取oob区的page,chipnr和col。
1425 /* Do not allow reads past end of device */
if ((from + len) &gt; mtd-&gt;size) {
   DEBUG (MTD_DEBUG_LEVEL0, "nand_read_oob: Attempt read beyond end of device/n");
   *retlen = 0;
   return -EINVAL;
}
/* Grab the lock and see if the device is available */
nand_get_device (this, mtd , FL_READING);
/* Select the NAND device */
this-&gt;select_chip(mtd, chipnr);
/* Send the read command */
this-&gt;cmdfunc (mtd, NAND_CMD_READOOB, col, page &amp; this-&gt;pagemask);
不允许非法的读取,获取芯片控制权,发送读取OOB命令,这儿会调用具体硬件驱动中相关的Nand控制函数。
1442 /*
* Read the data, if we read more than one page
* oob data, let the device transfer the data !
*/
i = 0;
while (i &lt; len) {
   int thislen = mtd-&gt;oobsize - col;
   thislen = min_t(int, thislen, len);
   this-&gt;read_buf(mtd, &amp;buf[i], thislen);
   i += thislen;
   /* Read more ? */
   if (i &lt; len) {
    page++;
    col = 0;
    /* Check, if we cross a chip boundary */
    if (!(page &amp; this-&gt;pagemask)) {
     chipnr++;
     this-&gt;select_chip(mtd, -1);
     this-&gt;select_chip(mtd, chipnr);
    }
    /* Apply delay or wait for ready/busy pin
    * Do this before the AUTOINCR check, so no problems
    * arise if a chip which does auto increment
    * is marked as NOAUTOINCR by the board driver.
    */
    if (!this-&gt;dev_ready)
     udelay (this-&gt;chip_delay);
    else
     nand_wait_ready(mtd);
    /* Check, if the chip supports auto page increment
    * or if we have hit a block boundary.
    */
    if (!NAND_CANAUTOINCR(this) || !(page &amp; blockcheck)) {
     /* For subsequent page reads set offset to 0 */
           this-&gt;cmdfunc (mtd, NAND_CMD_READOOB, 0x0, page &amp; this-&gt;pagemask);
    }
   }
}
/* Deselect and wake up anyone waiting on the device */
nand_release_device(mtd);
/* Return happy */
*retlen = len;
return 0;
开始读取数据,while循环只要获取到oob区大小的数据即可。注意,read_buf才是最底层的读写Nand的函数,在我们的驱动中根据参数可以实现读取528byte全部内容,或者16byte的oob区。
如果一次没读完,就要继续再读,根据我们实际使用经验好像没出现过这种问题。
最后Return Happy~回到Nand_bbt.c的creat_bbt()函数,348行,好像都快忘记我们还没出creat_bbt()函数呢,我再把他贴一遍吧:
346   /* Read the full oob until read_oob is fixed to
   * handle single byte reads for 16 bit buswidth */
   ret = mtd-&gt;read_oob(mtd, from + j * mtd-&gt;oobblock,
        mtd-&gt;oobsize, &amp;retlen, buf);
     if (ret)
      return ret;
     if (check_short_pattern (buf, bd)) {
       this-&gt;bbt[i &gt;&gt; 3] |= 0x03 &lt;&lt; (i &amp; 0x6);
      printk (KERN_WARNING "Bad eraseblock %d at 0x%08x/n",
       i &gt;&gt; 1, (unsigned int) from);
        break;
     }
    } else {
     if (check_pattern (&amp;buf[j * scanlen], scanlen, mtd-&gt;oobblock, bd)) {
         this-&gt;bbt[i &gt;&gt; 3] |= 0x03 &lt;&lt; (i &amp; 0x6);
      printk (KERN_WARNING "Bad eraseblock %d at 0x%08x/n",
       i &gt;&gt; 1, (unsigned int) from);
         break;
     }
    }
   }
   i += 2;
   from += (1 &lt;&lt; this-&gt;bbt_erase_shift);
}
return 0;
  }
刚刚如果不是Ruturn Happy,下面的352行就会返回错误了。接着会调用check_short_pattern()这个函数。
113 /**
   * check_short_pattern - [GENERIC] check if a pattern is in the buffer
   * @buf: the buffer to search
   * @td:   search pattern deor
   *
   * Check for a pattern at the given place. Used to search bad block
   * tables and good / bad block identifiers. Same as check_pattern, but
   * no optional empty check
   *
  */
  static int check_short_pattern (uint8_t *buf, struct nand_bbt_descr *td)
{
int i;
uint8_t *p = buf;
/* Compare the pattern */
for (i = 0; i &lt; td-&gt;len; i++) {
   if (p[td-&gt;offs + i] != td-&gt;pattern[i])
    return -1;
}
return 0;
}
检查读到的oob区是不是坏块就靠这个函数了。前面放了好久的struct nand_bbt_descr smallpage_memorybased终于用上了,挨个对比,有一个不一样直接返回-1,坏块就这样产生了。下面会将坏块的位置打印出来,并且将坏块记录在bbt表里面,在nand_scan_bbt()函数的开始我们就为bbt申请了空间。
this-&gt;bbt[i &gt;&gt; 3] |= 0x03 &lt;&lt; (i &amp; 0x6);
为啥要右移3bit呢?首先i要右移1bit,因为前面乘以了2。由于没个block占用2bit的空间,一个char变量8bit,所以还再要右移2bit吧。
  下面的check_pattern()函数调用不到的。
依次检测完所有block,creat_bbt()函数也顺利返回。
这样nand_memory_bbt()函数也正确返回。
接着是nand_scan_bbt()同样顺利结束。
最后nand_default_bbt()完成。
整个nand_scan()的工作终于完成咯,好长。</textarea></p>
<p></p>
<p></p>
<p></p>
<p>===============================================================</p>
<p></p>
<p></p>
<p></p>
<p></p>
<p></p>
<div class="BlogTitle">
<h1>MTD的坏块管理(一)-快速了解MTD的坏块管理</h1>
<div class="BlogStat">
    <span class="admin">
<em id="p_attention_count">1</em>
人收藏此文章,
<span id="attention_it">
<a>收藏此文章</a>
</span>
    </span>
发表于2个月前 ,
已有<strong>65</strong>
次阅读 
共<strong><a href="http://my.oschina.net/u/130864/blog/15328#comments">0</a>
</strong>
个评论
<strong>1</strong>
人收藏此文章
</div>
</div>
<p>
  <span style="color: #64451d; font-family: Verdana; font-size: 12px;">由
于NAND Flash的现有工艺不能保证NAND的Memory
Array在其生命周期中保持性能的可靠,因此在NAND芯片出厂的时候,厂家只能保证block
0不是坏块,对于其它block,则均有可能存在坏块,而且NAND芯片在使用的过程中也很容易产生坏块。因此,我们在读写NAND FLASH
的时候,需要检测坏块,同时还需在NAND驱动中加入坏块管理的功能。</span>
<br><span style="color: #64451d; font-family: Verdana; font-size: 12px;"></span>
<br><span style="color: #64451d; font-family: Verdana; font-size: 12px;">NAND驱动在加载的时候,会调用nand_scan函数,对bad block table的搜寻,建立等操作就是在这个函数的第二部分,即nand_scan_tail函数中完成的。</span>
<br><br><span style="color: #64451d; font-family: Verdana; font-size: 12px;">在
nand_scan_tail函数中,会首先检查struct
nand_chip结构体中的options成员变量是否被赋上了NAND_SKIP_BBTSCAN,这个宏表示跳过扫描bbt。所以,只有当你的
driver中没有为options定义NAND_SKIP_BBTSCAN时,MTD才会继续与bbt相关工作,即调用struct
nand_chip中的scan_bbt函数指针所指向的函数,在MTD中,这个函数指针指向nand_default_bbt函数。</span>
<br><br><span style="color: #64451d; font-family: Verdana; font-size: 12px;">bbt有两种存储
方式,一种是把bbt存储在NAND芯片中,另一种是把bbt存储在内存中。对于前者,好处是驱动加载更快,因为它只会在第一次加载NAND驱动时扫描整
个NAND芯片,然后在NAND芯片的某个block中建立bbt,坏处是需要至少消耗NAND芯片一个block的存储容量;而对于后者,好处是不会耗
用NAND芯片的容量,坏处是驱动加载稍慢,因为存储在内存中的bbt每次断电后都不会保存,所以在每次加载NAND驱动时,都会扫描整个NAND芯片,
以便建立bbt。</span>
<br><br><span style="color: #64451d; font-family: Verdana; font-size: 12px;">如果你系统中的NAND芯片容量不是太大的话,我建议还是把bbt存储在内存中比较好,因为根据本人的使用经验,对一块容量为2G bits的NAND芯片,分别采用这两种存储方式的驱动的加载速度相差不大,甚至几乎感觉不出来。</span>
<br><br><span style="color: #64451d; font-family: Verdana; font-size: 12px;">建立bbt后,以后在做擦除等操作时,就不用每次都去验证当前block是否是个坏块了,因为从bbt中就可以得到这个信息。另外,若在读写等操作时,发现产生了新的坏块,那么除了标志这个block是个坏块外,也还需更新bbt。</span>
<br><br><span style="color: #64451d; font-family: Verdana; font-size: 12px;">接下来,介绍一下MTD是如何查找或者建立bbt的。</span>
<br><br><span style="color: #64451d; font-family: Verdana; font-size: 12px;">1、MTD中与bbt相关的结构体</span>
<br><br><span style="color: #64451d; font-family: Verdana; font-size: 12px;">struct
nand_chip中的scan_bbt函数指针所指向的函数,即nand_default_bbt函数会首先检查struct
nand_chip中options成员变量,如果当前NAND芯片是AG-AND类型的,会强制把bbt存储在NAND芯片中,因为这种类型的NAND
芯片中含有厂家标注的“好块”信息,擦除这些block时会导致丢失坏块信息。</span>
<br><br><span style="color: #64451d; font-family: Verdana; font-size: 12px;">接着
nand_default_bbt函数会再次检查struct
nand_chip中options成员变量,根据它是否定义了NAND_USE_FLASH_BBT,而为struct
nand_chip中3个与bbt相关的结构体附上不同的值,然后再统一调用nand_scan_bbt函数,nand_scan_bbt函数会那3个结
构体的不同的值做不同的动作,或者把bbt存储在NAND芯片中,或者把bbt存储在内存中。</span></p>
<p><span style="color: #64451d; font-family: Verdana; font-size: 12px;">在struct nand_chip中与bbt相关的结构体如下:</span>
</p>
<p><span style="color: #64451d; font-family: Verdana; font-size: 12px;">structnand_chip{</span>
<br><span style="color: #64451d; font-family: Verdana; font-size: 12px;">……</span>
<br><span style="color: #64451d; font-family: Verdana; font-size: 12px;">uint8_t*bbt</span>
<br><span style="color: #64451d; font-family: Verdana; font-size: 12px;">structnand_bbt_descr*bbt_td;</span>
<br><span style="color: #64451d; font-family: Verdana; font-size: 12px;">structnand_bbt_descr*bbt_md;</span>
<br><span style="color: #64451d; font-family: Verdana; font-size: 12px;">structnand_bbt_descr*badblock_pattern;</span>
<br><span style="color: #64451d; font-family: Verdana; font-size: 12px;">……</span>
</p>
<p><span style="color: #64451d; font-family: Verdana; font-size: 12px;">};</span>
</p>
<p><span style="color: #64451d; font-family: Verdana; font-size: 12px;">bbt指向
一块在nand_default_bbt函数中分配的内存,若options中没有定义NAND_USE_FLASH_BBT,MTD就直接在bbt指向
的内存中建立bbt,否则就会先从NAND芯片中查找bbt是否存在,若存在,就把bbt的内容读出来并保存到bbt指向的内存中,若不存在,则在bbt
指向的内存中建立bbt,最后把它写入到NAND芯片中去。</span>
<br><br><span style="color: #64451d; font-family: Verdana; font-size: 12px;">bbt_td、bbt_md和badblock_pattern就是在nand_default_bbt函数中赋值的3个结构体。它们虽然是相同的结构体类型,但却有不同的作用和含义。</span>
<br><span style="color: #64451d; font-family: Verdana; font-size: 12px;">其
中bbt_td和bbt_md是主bbt和镜像bbt的描述符(镜像bbt主要用来对bbt的update和备份),它们只在把bbt存储在NAND芯片
的情况下使用,用来从NAND芯片中查找bbt。若bbt存储在内存中,bbt_td和bbt_md将会被赋值为NULL。</span>
<br><br><span style="color: #64451d; font-family: Verdana; font-size: 12px;">badblock_pattern就是坏块信息的pattern,其中定义了坏块信息在oob中的存储位置,以及内容(即用什么值表示这个block是个坏块)。</span>
<br><br><span style="color: #64451d; font-family: Verdana; font-size: 12px;">通
常用1或2个字节来标志一个block是否为坏块,这1或2个字节就是坏块信息,如果这1或2个字节的内容是0xff,那就说明这个block是好的,否
则就是坏块。对于坏块信息在NAND芯片中的存储位置,small page(每页512 Byte)和big page(每页2048
Byte)的两种NAND芯片不尽相同。一般来说,small
page的NAND芯片,坏块信息存储在每个block的第一个page的oob的第六个字节中,而big
page的NAND芯片,坏块信息存储在每个block的第一个page的oob的第1和第2个字节中。</span>
<br><br><span style="color: #64451d; font-family: Verdana; font-size: 12px;">我
不能确定是否所有的NAND芯片都是如此布局,但应该绝大多数NAND芯片是这样的,不过,即使某种NAND芯片的坏块信息不是这样的存储方式也没关系,
因为我们可以在badblock_pattern中自己指定坏块信息的存储位置,以及用什么值来标志坏块(其实这个值表示的应该是“好块”,因为MTD会
把从oob中坏块信息存储位置读出的内容与这个值做比较,若相等,则表示是个“好块”,否则就是坏块)。</span></p>
<p><span style="color: #64451d; font-family: Verdana; font-size: 12px;">bbt_td、bbt_md和badblock_pattern的结构体类型定义如下:</span>
</p>
<p><span style="color: #64451d; font-family: Verdana; font-size: 12px;">structnand_bbt_descr{</span>
<br><span style="color: #64451d; font-family: Verdana; font-size: 12px;">intoptions;</span>
<br><span style="color: #64451d; font-family: Verdana; font-size: 12px;">intpages[NAND_MAX_CHIPS];</span>
<br><span style="color: #64451d; font-family: Verdana; font-size: 12px;">intoffs;</span>
<br><span style="color: #64451d; font-family: Verdana; font-size: 12px;">intveroffs;</span>
<br><span style="color: #64451d; font-family: Verdana; font-size: 12px;">uint8_tversion[NAND_MAX_CHIPS];</span>
<br><span style="color: #64451d; font-family: Verdana; font-size: 12px;">intlen;</span>
<br><span style="color: #64451d; font-family: Verdana; font-size: 12px;">intmaxblocks;</span>
<br><span style="color: #64451d; font-family: Verdana; font-size: 12px;">intreserved_block_code;</span>
<br><span style="color: #64451d; font-family: Verdana; font-size: 12px;">uint8_t*pattern;</span>
</p>
<p><span style="color: #64451d; font-family: Verdana; font-size: 12px;">};</span>
</p>
<p><span style="color: #64451d; font-family: Verdana; font-size: 12px;">options:bad block table或者bad block的选项,可用的选择以及各选项具体表示什么含义,可以参考&lt;linux/mtd/nand.h&gt;。</span>
<br><br><span style="color: #64451d; font-family: Verdana; font-size: 12px;">pages:bbt
专用。在查找bbt的时候,若找到了bbt,就把bbt所在的page号保存在这个成员变量中。若没找到bbt,就会把新建立的bbt的保存位置赋值给
它。因为系统中可能会有多个NAND芯片,我们可以为每一片NAND芯片建立一个bbt,也可以只在其中一片NAND芯片中建立唯一的一个bbt,所以这
里的pages是个维数为NAND_MAX_CHIPS的数值,用来保存每一片NAND芯片的bbt位置。当然,若只建立了一个bbt,那么就只使用
pages[0]。</span>
<br><br><span style="color: #64451d; font-family: Verdana; font-size: 12px;">offs、len和pattern:MTD会从oob的offs中读出len长度的内容,然后与pattern指针指向的内容做比较,若相等,则表示找到了bbt,或者表示这个block是好的。</span>
<br><br><span style="color: #64451d; font-family: Verdana; font-size: 12px;">veroffs和version:bbt专用。MTD会从oob的veroffs中读出一个字节的内容,作为bbt的版本值保存在version中。</span>
<br><br><span style="color: #64451d; font-family: Verdana; font-size: 12px;">maxblocks:bbt专用。MTD在查找bbt的时候,不会查找NAND芯片中所有的block,而是最多查找maxblocks个block。</span>
<br><br><span style="color: #64451d; font-family: Verdana; font-size: 12px;">2、bbt存储在内存中时的工作流程</span>
<br><br><span style="color: #64451d; font-family: Verdana; font-size: 12px;">前文说过,不管bbt是存储在NAND芯片中,还是存储在内存中,nand_default_bbt函数都会调用nand_scan_bbt函数。</span>
<br><br><span style="color: #64451d; font-family: Verdana; font-size: 12px;">nand_scan_bbt函数会判断bbt_td的值,若是NULL,则表示bbt存储在内存中,它就在调用nand_memory_bbt函数后返回。nand_memory_bbt函数的主要工作就是在内存中建立bbt,其实就是调用了create_bbt函数。</span>
<br><br><span style="color: #64451d; font-family: Verdana; font-size: 12px;">create_bbt
函数的工作方式很简单,就是扫描NAND芯片所有的block,读取每个block中第一个page的oob内容,然后根据oob中的坏块信息建立起
bbt,可以参见上节关于struct nand_bbt_descr中的offs、len和pattern成员变量的解释。</span>
<br><br><span style="color: #64451d; font-family: Verdana; font-size: 12px;">3、bbt存储在NAND芯片时的工作流程</span>
<br><br><span style="color: #64451d; font-family: Verdana; font-size: 12px;">相对于把bbt存储在内存中,这种方式的工作流程稍显复杂一点。</span>
<br><br><span style="color: #64451d; font-family: Verdana; font-size: 12px;">nand_scan_bbt函数首先从NAND芯片中读取bbt的内容,它读取的方式分为两种:</span>
<br><br><span style="color: #64451d; font-family: Verdana; font-size: 12px;">其
一是调用read_abs_bbts函数直接从给定的page地址读取,那么这个page地址在什么时候指定呢?就是在你的NAND
driver中指定。前文说过,在struct
nand_chip结构体中有两个成员变量,分别是bbt_td和bbt_md,MTD为它们附上了default的值,但是你也可以根据你的需要为它们
附上你自己定义的值。假如你为bbt_td和bbt_md的options成员变量定义了NAND_BBT_ABSPAGE,同时又把你的bbt所在的
page地址保存在bbt_td和bbt_md的pages成员变量中,MTD就可以直接在这个page地址中读取bbt了。值得一提的是,在实际使用时
一般不这么干,因为你不能保证你保存bbt的那个block就永远不会坏,而且这样也不灵活;</span>
</p>
<p><br><span style="color: #64451d; font-family: Verdana; font-size: 12px;">其二是调用那个search_read_bbts函数试着在NAND芯片的maxblocks(请见上文关于struct nand_bbt_descr中maxblocks的说明)个block中查找bbt是否存在,若找到,就可以读取bbt了。</span>
<br><br><span style="color: #64451d; font-family: Verdana; font-size: 12px;">MTD
查找bbt的过程为:如果你在bbt_td和bbt_md的options 成员变量中定义了
NAND_BBT_LASTBLOCK,那么MTD就会从NAND芯片的最后一个block开始查找(在default情况下,MTD就是这么干的),否
则就从第一个block开始查找。</span>
<br><br><span style="color: #64451d; font-family: Verdana; font-size: 12px;">与
查找oob中的坏块信息时类似,MTD会从所查找block的第一个page的oob中读取内容,然后与bbt_td或bbt_md中patter指向的
内容做比较,若相等,则表示找到了bbt,否则就继续查找下一个block。顺利的情况下,只需查找一个block中就可以找到bbt,否则MTD最多会
查找maxblocks个block。</span>
<br><span style="color: #64451d; font-family: Verdana; font-size: 12px;">若找到了bbt,就把该bbt所在的page地址保存到bbt_td或bbt_md的pages成员变量中,否则pages的值为-1。</span>
<br><br><span style="color: #64451d; font-family: Verdana; font-size: 12px;">如果系统中有多片NAND芯片,并且为每一片NAND芯片都建立一个bbt,那么就会在每片NAND芯片上重复以上过程。</span>
<br><br><span style="color: #64451d; font-family: Verdana; font-size: 12px;">接
着,nand_scan_bbt函数会调用check_create函数,该函数会判断是否找到了bbt,其实就是判断bbt_td或者bbt_md中
pages成员变量的值是否有效。若找到了bbt,就会把bbt从NAND芯片中读取出来,并保存到struct
nand_chip中bbt指针指向的内存中;若没找到,就会调用create_bbt函数建立bbt(与bbt存储在内存中时情况一样),同时把bbt
写入到NAND芯片中去。</span>
</p>
<p></p>
<p></p>
<p><span style="color: #ff0000;"><span style="color: #000000;"><span style="color: #000000;">****</span>
</span>
</span>
<span style="color: #ff0000;"><span style="color: #000000;"><span style="color: #000000;">****</span>
</span>
</span>
<span style="color: #ff0000;"><span style="color: #000000;"><span style="color: #000000;">****</span>
</span>
</span>
<span style="color: #ff0000;"><span style="color: #000000;"><span style="color: #000000;">****</span>
</span>
</span>
<span style="color: #ff0000;"><span style="color: #000000;"><span style="color: #000000;">****</span>
</span>
</span>
<span style="color: #ff0000;"><span style="color: #000000;"><span style="color: #000000;">****</span>
</span>
</span>
<span style="color: #ff0000;"><span style="color: #000000;"><span style="color: #000000;">****</span>
</span>
</span>
<span style="color: #ff0000;"><span style="color: #000000;"><span style="color: #000000;">****</span>
</span>
</span>
<span style="color: #ff0000;"><span style="color: #000000;"><span style="color: #000000;">****</span>
</span>
</span>
<span style="color: #ff0000;"><span style="color: #000000;"><span style="color: #000000;">****</span>
</span>
</span>
<span style="color: #ff0000;"><span style="color: #000000;"><span style="color: #000000;">****</span>
</span>
</span>
<span style="color: #ff0000;"><span style="color: #000000;"><span style="color: #000000;">****</span>
</span>
</span>
<span style="color: #ff0000;"><span style="color: #000000;"><span style="color: #000000;">****</span>
</span>
</span>
<span style="color: #ff0000;"><span style="color: #000000;"><span style="color: #000000;">****</span>
</span>
</span>
<span style="color: #ff0000;"><span style="color: #000000;"><span style="color: #000000;">****</span>
</span>
</span>
<span style="color: #ff0000;"><span style="color: #000000;"><span style="color: #000000;">****</span>
</span>
</span>
<span style="color: #ff0000;"><span style="color: #000000;"><span style="color: #000000;">****</span>
</span>
</span>
<span style="color: #ff0000;"><span style="color: #000000;"><span style="color: #000000;">****</span>
</span>
</span>
<span style="color: #ff0000;"><span style="color: #000000;"><span style="color: #000000;">****</span>
</span>
</span>
<span style="color: #ff0000;"><span style="color: #000000;"><span style="color: #000000;">****</span>
</span>
</span>
<span style="color: #ff0000;"><span style="color: #000000;"><span style="color: #000000;">****</span>
</span>
</span>
</p>
<p></p>
<div class="BlogTitle">
<h1>MTD坏块管理(二)-内核获取Nandflash的参数过程</h1>
<div class="BlogStat">
    <span class="admin">
<em id="p_attention_count">1</em>
人收藏此文章,
<span id="attention_it">
<a>收藏此文章</a>
</span>
    </span>
发表于2个月前 ,
已有<strong>133</strong>
次阅读 
共<strong><a href="http://my.oschina.net/u/130864/blog/15330#comments">0</a>
</strong>
个评论
<strong>1</strong>
人收藏此文章
</div>
</div>
<p>
  <span class="Apple-style-span" style="font-family: verdana,sans-serif; font-size: 17px; line-height: 25px;">
<p style="padding: 0px; margin: 1em 0px 0.5em;"><span style="color: #64451d; font-family: Verdana; font-size: 12px;">MTD坏块管理机制中,起着核心作用的数据结构是nand_chip,在此以TCC8900-Linux中MTD的坏块管理为例作一次介绍。</span>
</p>
<p><span style="color: #64451d; font-family: Verdana; font-size: 12px;">MTD在Linux内核中同样以模块的形式被启用,TCC_MTD_IO_Init()函数完成了nand_chip初始化、mtd_info初始注册,</span>
</p>
<p><span style="color: #64451d; font-family: Verdana; font-size: 12px;">坏块表的管理机制建立等工作。</span>
</p>
<p><span style="color: #64451d; font-family: Verdana; font-size: 12px;">nand_chip在TCC_MTD_IO_Init函数中的实例名称是this,mtd_info 的实例名称为TCC_mtd,这里有一个比较巧妙的处理方法:</span>
</p>
<p><span style="color: #64451d; font-family: Verdana; font-size: 12px;">TCC_mtd=kmalloc(sizeof(struct mtd_info)+sizeof(struct nand_chip),GFP_KERNEL);</span>
</p>
<p><span style="color: #64451d; font-family: Verdana; font-size: 12px;">this=(struct nand_chip*)(&amp;TCC_mtd[1]);</span>
</p>
<p><span style="color: #64451d; font-family: Verdana; font-size: 12px;">在以后的操作中,只需得知TCC_mtd即可找到对应的nan_chip实例。</span>
</p>
<p><span style="color: #64451d; font-family: Verdana; font-size: 12px;">获得必要的信息后(包括nand_chip方法的绑定),流程进入nand_scan(TCC_mtd,1).</span>
</p>
<p><span style="color: #64451d; font-family: Verdana; font-size: 12px;">nand_scan(struct mdt_info *mtd, int maxchips);</span>
</p>
<p><span style="color: #64451d; font-family: Verdana; font-size: 12px;">调用nand_scan_ident(mtd,maxchips)和nand_scan_tail(mtd);</span>
</p>
<p><span style="color: #64451d; font-family: Verdana; font-size: 12px;">nand_scan_ident(...)调用了一个很重要的函数:nand_get_flash_type(...)</span>
</p>
<p><span style="color: #64451d; font-family: Verdana; font-size: 12px;">*从nand_get_flash_type(...)函数中可以看出每个nandflash前几个字节所代表的意思都是约定好了的:</span>
</p>
<p><span style="color: #64451d; font-family: Verdana; font-size: 12px;">第一个字节:制造商ID</span>
</p>
<p><span style="color: #64451d; font-family: Verdana; font-size: 12px;">第二个字节:设备ID</span>
</p>
<p><span style="color: #64451d; font-family: Verdana; font-size: 12px;">第三个字节:MLC 数据</span>
</p>
<p><span style="color: #64451d; font-family: Verdana; font-size: 12px;">第四个字节:extid (比较总要)</span>
</p>
<p><span style="color: #64451d; font-family: Verdana; font-size: 12px;">其中设备ID是访问nand_flash_ids表的参照,该表在drivers/mtd/nand/nand_ids.c中定义</span>
</p>
<p><span style="color: #64451d; font-family: Verdana; font-size: 12px;">Linux内核在nand_flash_ids参照表中,通过匹配上述设备ID来查找nandflash的详细信息,</span>
</p>
<p><span style="color: #64451d; font-family: Verdana; font-size: 12px;">nand_flash_ids中的举例如下:</span>
</p>
<p><span style="color: #64451d; font-family: Verdana; font-size: 12px;">struct nand_flash_dev nand_flash_ids[]={</span>
</p>
<p><span style="color: #64451d; font-family: Verdana; font-size: 12px;">......</span>
</p>
<p><span style="color: #64451d; font-family: Verdana; font-size: 12px;">{"NAND 16MiB 1,8V 8-bit",  0x33, 512, 16, 0x4000, 0},</span>
</p>
<p><span style="color: #64451d; font-family: Verdana; font-size: 12px;">{"NAND 16MiB 3,3V 8-bit",  0x73, 512, 16, 0x4000, 0},</span>
</p>
<p><span style="color: #64451d; font-family: Verdana; font-size: 12px;">{"NAND 16MiB 1,8V 16-bit", 0x43, 512, 16, 0x4000, NAND_BUSWIDTH_16},</span>
</p>
<p><span style="color: #64451d; font-family: Verdana; font-size: 12px;">{"NAND 16MiB 3,3V 16-bit", 0x53, 512, 16, 0x4000, NAND_BUSWIDTH_16},</span>
</p>
<p><span style="color: #64451d; font-family: Verdana; font-size: 12px;">......</span>
</p>
<p><span style="color: #64451d; font-family: Verdana; font-size: 12px;">}</span>
</p>
<p><span style="color: #64451d; font-family: Verdana; font-size: 12px;">466 struct nand_flash_dev {</span>
</p>
<p><span style="color: #64451d; font-family: Verdana; font-size: 12px;">467   char *name;</span>
</p>
<p><span style="color: #64451d; font-family: Verdana; font-size: 12px;">468   int id;</span>
</p>
<p><span style="color: #64451d; font-family: Verdana; font-size: 12px;">469   unsigned long pagesize;</span>
</p>
<p><span style="color: #64451d; font-family: Verdana; font-size: 12px;">470   unsigned long chipsize;</span>
</p>
<p><span style="color: #64451d; font-family: Verdana; font-size: 12px;">471   unsigned long erasesize;  </span>
</p>
<p><span style="color: #64451d; font-family: Verdana; font-size: 12px;">472   unsigned long options;   </span>
</p>
<p><span style="color: #64451d; font-family: Verdana; font-size: 12px;">473 };</span>
</p>
<p><span style="color: #64451d; font-family: Verdana; font-size: 12px;">值得一提的是,MTD子系统会把从nand_flash_ids表中找到的chipsize复制给mtd-&gt;size,这在有些应用中显得不合适,</span>
</p>
<p><span style="color: #64451d; font-family: Verdana; font-size: 12px;">在有些方案中,并不是把nandflash的所有存储空间都划分为MTD分区,Telechips的TCC89XX方案就是这样,4G的nandflash</span>
</p>
<p><span style="color: #64451d; font-family: Verdana; font-size: 12px;">上,可以划分任意大小的MTD分区,错误的mtd-&gt;size的后果非常严重,造成系统启动慢,整个MTD的坏块管理机制瘫痪等等。</span>
</p>
<p><span style="color: #64451d; font-family: Verdana; font-size: 12px;">随后,nand_get_flash_type通过extid计算出了以下信息:</span>
</p>
<p><span style="color: #64451d; font-family: Verdana; font-size: 12px;">mtd可写区大小:mtd-&gt;writesize=1024&lt;&lt;(extid&amp;0x03);</span>
</p>
<p><span style="color: #64451d; font-family: Verdana; font-size: 12px;">这里可以看成1024*(1*2的(extid&amp;0x03)次方),</span>
</p>
<p><span style="color: #64451d; font-family: Verdana; font-size: 12px;">mtdoob区大小:extid&gt;&gt;=2;mtd-&gt;oobsize = (8&lt;&lt;(extid&amp;0x1))*(mtd-&gt;writesize&gt;&gt;9);</span>
</p>
<p><span style="color: #64451d; font-family: Verdana; font-size: 12px;">每512字节对应(8*2的(extid&amp;0x1)次方)字节oob数据</span>
</p>
<p><span style="color: #64451d; font-family: Verdana; font-size: 12px;">mtd擦写块大小:extid&gt;&gt;=2;mtd-&gt;erasesize=(64*1024)&lt;&lt;(extid&amp;0x03);</span>
</p>
<p><span style="color: #64451d; font-family: Verdana; font-size: 12px;">nand数据宽度 :extid&gt;&gt;=2;busw=(extid&amp;0x01)?NAND_BUSEWIDTH_16:0; 现在大多为8位数据宽度</span>
</p>
<p><span style="color: #64451d; font-family: Verdana; font-size: 12px;">可以看出第四个字节extid的意义:</span>
</p>
<p><span style="color: #64451d; font-family: Verdana; font-size: 12px;">高|0  | 0    |  00    | 0  | 0     | 00      |低</span>
</p>
<p><span style="color: #64451d; font-family: Verdana; font-size: 12px;"> |无用|数据宽度|擦写块算阶|无用|oob算阶| 可写区算阶|</span>
</p>
<p><span style="color: #64451d; font-family: Verdana; font-size: 12px;">nand_get_flash_type(...)还确立了nandflash中的坏块标记在oob信息中的位置:</span>
</p>
<p><span style="color: #64451d; font-family: Verdana; font-size: 12px;">if(mtd-&gt;writesize&gt;512||(busw&amp;NAND_BUSWIDTH_16))</span>
</p>
<p><span style="color: #64451d; font-family: Verdana; font-size: 12px;"> chip-&gt;badblockpos = NAND_LARGE_BADBLOCKS_POS;//大页面flash的坏块信息存储地址为oob信息中的第1个字节开始处</span>
</p>
<p><span style="color: #64451d; font-family: Verdana; font-size: 12px;">else</span>
</p>
<p><span style="color: #64451d; font-family: Verdana; font-size: 12px;"> chip-&gt;badblockpos = NAND_SMALL_BADBLOCKS_POS;//大页面flash的坏块信息存储地址为oob信息中的第6个字节开始处</span>
</p>
<p><span style="color: #64451d; font-family: Verdana; font-size: 12px;">对于Samsun和Hynix的MLC型nandflash,坏块标记所在的页是每块的最后一个页,而Samsung,Hynix,和AMD的SLC型nandflash</span>
</p>
<p><span style="color: #64451d; font-family: Verdana; font-size: 12px;">中,坏块标记分别保存在每块开始的第1,2个页中,其他型号的nandflash大多都保存在第一个也中,为此需要作下标记:</span>
</p>
<p><span style="color: #64451d; font-family: Verdana; font-size: 12px;">坏块标记保存在块的最后一页中:chip-&gt;options |= NAND_BBT_SCANLASTPAGE;</span>
</p>
<p><span style="color: #64451d; font-family: Verdana; font-size: 12px;">坏块标记保存在块的第1,2页中 :chip-&gt;options |= NAND_BBT_SCAN2NDPAGE;</span>
</p>
<p><span style="color: #64451d; font-family: Verdana; font-size: 12px;">nand_scan之后调用nand_scan_tail(mtd)函数,</span>
</p>
<p><span style="color: #64451d; font-family: Verdana; font-size: 12px;">nand_scan_tail(...)函数主要完成MTD实例中各种方法的绑定,例如:</span>
</p>
<p><span style="color: #64451d; font-family: Verdana; font-size: 12px;">3338   mtd-&gt;read = nand_read;</span>
</p>
<p><span style="color: #64451d; font-family: Verdana; font-size: 12px;">3339   mtd-&gt;write = nand_write;</span>
</p>
<p><span style="color: #64451d; font-family: Verdana; font-size: 12px;">3340   mtd-&gt;panic_write = panic_nand_write;</span>
</p>
<p><span style="color: #64451d; font-family: Verdana; font-size: 12px;">3341   mtd-&gt;read_oob = nand_read_oob;</span>
</p>
<p><span style="color: #64451d; font-family: Verdana; font-size: 12px;">3342   mtd-&gt;write_oob = nand_write_oob;</span>
</p>
<p><span style="color: #64451d; font-family: Verdana; font-size: 12px;">3343   mtd-&gt;sync = nand_sync;</span>
</p>
<p><span style="color: #64451d; font-family: Verdana; font-size: 12px;">nand_scan_tail(...)调用chip-&gt;scan_bbt()完成坏块表的有关操作。</span>
</p>
<p><span style="color: #64451d; font-family: Verdana; font-size: 12px;">chip-&gt;scan_bbt的绑定过程是在nand_scan_ident()-&gt;nand_set_defaults():chip-&gt;scan_bbt = nand_default_bbt.</span>
</p>
<p><span style="color: #64451d; font-family: Verdana; font-size: 12px;">即真正用于坏块操作的是nand_default_bbt函数,该函数在nand_bbt.c中被定义。</span>
</p>
<div>
  </div>
</span>
</p>
<p><span style="color: #ff0000;"><span style="color: #000000;"><span style="color: #000000;"></span>
</span>
</span>
</p>

你可能感兴趣的:(Flash)