0.NAND的操作管理方式

     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码)。

关于OOB区,是每个Page都有的。Page大小是512字节的NAND每页分配16字节的OOB;如果NAND物理上是2K的Page,则每个Page分配64字节的OOB。如下图:

以HYNIX为例,图中黑体的是实际探测到的NAND,是个2G bit(256M)的NAND。PgSize是2K字节,PgsPBlk表示每个BLOCK包含64页,那么每个BLOCK占用的字节数是 64X2K=128K字节;该NAND包好2048个BLOCK,那么可以算出NAND占用的字节数是2048X128K=256M,与实际相符。需要注意的是SprSize就是OOB大小,也恰好是2K页所用的64字节。

1.为什么会出现坏块     由于NAND Flash的工艺不能保证NAND的Memory Array在其生命周期中保持性能的可靠,因此,在NAND的生产中及使用过程中会产生坏块。坏块的特性是:当编程/擦除这个块时,会造成Page Program和Block Erase操作时的错误,相应地反映到Status Register的相应位。

2.坏块的分类    总体上,坏块可以分为两大类:(1)固有坏块:这是生产过程中产生的坏块,一般芯片原厂都会在出厂时都会将每个坏块第一个page的spare area的第6个byte标记为不等于0xff的 值。(2)使用坏块:这是在NAND Flash使用过程中,如果Block Erase或者Page Program错误,就可以简单地将这个块作为坏块来处理,这个时候需要把坏块标记起来。为了和固有坏块信息保持一致,将新发现的坏块的第一个page的 spare area的第6个Byte标记为非0xff的值。

3.坏块管理     根据上面的这些叙述,可以了解NAND Flash出厂时在spare area中已经反映出了坏块信息,因此, 如果在擦除一个块之前,一定要先check一下第一页的spare area的第6个byte是否是0xff,如果是就证明这是一个好块,可以擦除;如果是非0xff,那么就不能擦除,以免将坏块标记擦掉。 当然,这样处理可能会犯一个错误―――“错杀伪坏块”,因为在芯片操作过程中可能由于 电压不稳定等偶然因素会造成NAND操作的错误。但是,为了数据的可靠性及软件设计的简单化,还是需要遵照这个标准。

可以用BBT:bad block table,即坏块表来进行管理。各家对nand的坏块管理方法都有差异。比如专门用nand做存储的,会把bbt放到block0,因为第0块一定是好的块。但是如果nand本身被用来boot,那么第0块就要存放程序,不能放bbt了。 有的把bbt放到最后一块,当然,这一块坚决不能为坏块。 bbt的大小跟nand大小有关,nand越大,需要的bbt也就越大。

需要注意的是:OOB是每个页都有的数据,里面存的有ECC(当然不仅仅);而BBT是一个FLASH才有一个;针对每个BLOCK的坏块识别则是该块第一页spare area的第六个字节。 4.坏块纠正

ECC: NAND Flash出错的时候一般不会造成整个Block或是Page不能读取或是全部出错,而是整个Page(例如512Bytes)中只有一个或几个bit出错。一般使用一种比较专用的校验——ECC。ECC能纠正单比特错误和检测双比特错误,而且计算速度很快,但对1比特以上的错误无法纠正,对2比特以上的错误不保证能检测。       ECC一般每256字节原始数据生成3字节ECC校验数据,这三字节共24比特分成两部分:6比特的列校验和16比特的行校验,多余的两个比特置1。(512生成两组ECC,共6字节)        当往NAND Flash的page中写入数据的时候,每256字节我们生成一个ECC校验和,称之为原ECC校验和,保存到PAGE的OOB (out- of-band)数据区中。其位置就是eccpos[]。校验的时候,根据上述ECC生成原理不难推断:将从OOB区中读出的原ECC校验和新ECC校验和按位异或,若结果为0,则表示不存在错(或是出现了ECC无法检测的错误);若3个字节异或结果中存在11个比特位为1,表示存在一个比特错误,且可纠正;若3个字节异或结果中只存在1个比特位为1,表示OOB区出错;其他情况均表示出现了无法纠正的错误。 5.补充   (1)需要对前面由于Page Program错误发现的坏块进行一下特别说明。如果在对一个块的某个page进行编程的时候发生了错误就要把这个块标记为坏块,首先就要把块里其他好的面的内容备份到另外一个空的好块里面,然后,把这个块标记为坏块。当然,这可能会犯“错杀”之误,一个补救的办法,就是在进行完块备份之后,再将这个坏块擦除一遍,如果Block Erase发生错误,那就证明这个块是个真正的坏块,那就毫不犹豫地将它打个“戳”吧!   (2)可能有人会问,为什么要使用每个块第一页的spare area的第六个byte作为坏块标记。这是NAND Flash生产商的默认约定,你可以看到Samsung,Toshiba,STMicroelectronics都是使用这个Byte作为坏块标记的。

(3)为什么好块用0xff来标记?因为Nand Flash的擦除即是将相应块的位全部变为1,写操作时只能把芯片每一位(bit)只能从1变为0,而不能从0变为1。0XFF这个值就是标识擦除成功,是好块。

====================================================

  1. bbt坏块管理
  2. 日月 发表于 - 2010-3-2 9:59:00
  3. 2
  4. 推荐
  5. 前面看到在nand_scan()函数的最后将会跳至scan_bbt()函数,这个函数在nand_scan里面有定义:
  6. 2415 if (!this->scan_bbt)
  7. 2416 this->scan_bbt = nand_default_bbt;
  8. nand_default_bbt()位于Nand_bbt.c文件中。
  9. 1047 /**
  10. * nand_default_bbt - [NAND Interface] Select a default bad block table for the device
  11. * @mtd: MTD device structure
  12. *
  13. * This selects the default bad block table
  14. * support for the device and calls the nand_scan_bbt
  15.   **/
  16.   int nand_default_bbt (struct mtd_info *mtd)
  17.   {
  18.    struct nand_chip *this = mtd->priv;
  19. 这个函数的作用是建立默认的坏块表。
  20. 1059 /* Default for AG-AND. We must use a flash based
  21. * bad block table as the devices have factory marked
  22. * _good_ blocks. Erasing those blocks leads to loss
  23. * of the good / bad information, so we _must_ store
  24. * this information in a good / bad table during
  25. * startup
  26. */
  27. if (this->options & NAND_IS_AND) {
  28. /* Use the default pattern deors */
  29. if (!this->bbt_td) {
  30. this->bbt_td = &bbt_main_descr;
  31. this->bbt_md = &bbt_mirror_descr;
  32. }
  33. this->options |= NAND_USE_FLASH_BBT;
  34. return nand_scan_bbt (mtd, &agand_flashbased);
  35. }
  36. 如果Flash的类型是AG-AND(这种Flash类型比较特殊,既不是MLC又不是SLC,因此不去深究了,而且好像瑞萨要把它淘汰掉),需要使用默认的模式描述符,最后再进入nand_scan_bbt()函数。
  37. 1078 /* Is a flash based bad block table requested ? */
  38. if (this->options & NAND_USE_FLASH_BBT) {
  39. /* Use the default pattern deors */
  40. if (!this->bbt_td) {
  41. this->bbt_td = &bbt_main_descr;
  42. this->bbt_md = &bbt_mirror_descr;
  43. }
  44. if (!this->badblock_pattern) {
  45. this->badblock_pattern = (mtd->oobblock > 512) ?
  46. &largepage_flashbased : &smallpage_flashbased;
  47. }
  48. } else {
  49. this->bbt_td = NULL;
  50. this->bbt_md = NULL;
  51. if (!this->badblock_pattern) {
  52. this->badblock_pattern = (mtd->oobblock > 512) ?
  53. &largepage_memorybased : &smallpage_memorybased;
  54. }
  55. }
  56. return nand_scan_bbt (mtd, this->badblock_pattern);
  57. 如果Flash芯片需要使用坏块表,对于1208芯片来说是使用smallpage_memorybased。
  58. 985   static struct nand_bbt_descr smallpage_memorybased = {
  59. .options = NAND_BBT_SCAN2NDPAGE,
  60. .offs = 5,
  61. .len = 1,
  62. .pattern = scan_ff_pattern
  63.   };
  64. 暂时没看到如何使用这些赋值,先放着。后面检测坏块时用得着。
  65. 1099 return nand_scan_bbt (mtd, this->badblock_pattern);
  66. 最后将badblock_pattern作为参数,调用nand_can_bbt函数。
  67. 844   /**
  68.   * nand_scan_bbt - [NAND Interface] scan, find, read and maybe create bad block table(s)
  69. * @mtd: MTD device structure
  70. * @bd:   deor for the good/bad block search pattern
  71. *
  72. * The checks, if a bad block table(s) is/are already
  73. * available. If not it scans the device for manufacturer
  74. * marked good / bad blocks and writes the bad block table(s) to
  75. * the selected place.
  76. *
  77. * The bad block table memory is allocated here. It must be freed
  78. * by calling the nand_free_bbt .
  79. *
  80.   */
  81.   int nand_scan_bbt (struct mtd_info *mtd, struct nand_bbt_descr *bd)
  82.   {
  83. 检测、寻找、读取甚至建立坏块表。函数检测是否已经存在一张坏块表,否则建立一张。坏块表的内存分配也在这个函数中。
  84. 860 struct nand_chip *this = mtd->priv;
  85. int len, res = 0;
  86. uint8_t *buf;
  87. struct nand_bbt_descr *td = this->bbt_td;
  88. struct nand_bbt_descr *md = this->bbt_md;
  89. len = mtd->size >> (this->bbt_erase_shift + 2);
  90. /* Allocate memory (2bit per block) */
  91. this->bbt = kmalloc (len, GFP_KERNEL);
  92. if (!this->bbt) {
  93. printk (KERN_ERR "nand_scan_bbt: Out of memory/n");
  94. return -ENOMEM;
  95. }
  96. /* Clear the memory bad block table */
  97. memset (this->bbt, 0x00, len);
  98. 一些赋值、变量声明、内存分配,每个block分配2bit的空间。1208有4096个block,应该分配4096*2bit的空间。
  99. 877 /* If no primary table decriptor is given, scan the device
  100. * to build a memory based bad block table
  101. */
  102. if (!td) {
  103. if ((res = nand_memory_bbt(mtd, bd))) {
  104.     printk (KERN_ERR "nand_bbt: Can't scan flash and build the RAM-based BBT/n");
  105. kfree (this->bbt);
  106. this->bbt = NULL;
  107. }
  108. return res;
  109. }
  110. 如果没有提供ptd,就扫描设备并建立一张。这里调用了nand_memory_bbt()这个内联函数。
  111. 653 /**
  112. * nand_memory_bbt - [GENERIC] create a memory based bad block table
  113. * @mtd: MTD device structure
  114. * @bd:   deor for the good/bad block search pattern
  115. *
  116. * The creates a memory based bbt by scanning the device
  117. * for manufacturer / software marked good / bad blocks
  118.   */
  119.   static inline int nand_memory_bbt (struct mtd_info *mtd, struct nand_bbt_descr *bd)
  120.   {
  121. struct nand_chip *this = mtd->priv;
  122. bd->options &= ~NAND_BBT_SCANEMPTY;
  123. return create_bbt (mtd, this->data_buf, bd, -1);
  124.   }
  125. 函数的作用是建立一张基于memory的坏块表。
  126. 将操作符的NAND_BBT_SCANEMPTY清除,并继续调用creat_bbt()函数。
  127. 271 /**
  128.   * create_bbt - [GENERIC] Create a bad block table by scanning the device
  129. * @mtd: MTD device structure
  130. * @buf: temporary buffer
  131. * @bd:   deor for the good/bad block search pattern
  132. * @chip: create the table for a specific chip, -1 read all chips.
  133. *   Applies only if NAND_BBT_PERCHIP option is set
  134. *
  135. * Create a bad block table by scanning the device
  136. * for the given good/bad block identify pattern
  137. */
  138.   static int create_bbt (struct mtd_info *mtd, uint8_t *buf, struct nand_bbt_descr *bd, int chip)
  139.   {
  140. 真正的建立坏块表函数。chip参数是-1表示读取所有的芯片。
  141. 284 struct nand_chip *this = mtd->priv;
  142. int i, j, numblocks, len, scanlen;
  143. int startblock;
  144. loff_t from;
  145. size_t readlen, ooblen;
  146. printk (KERN_INFO "Scanning device for bad blocks/n");
  147. 一些变量声明,开机时那句话就是在这儿打印出来的。
  148. 292 if (bd->options & NAND_BBT_SCANALLPAGES)
  149. len = 1 << (this->bbt_erase_shift - this->page_shift);
  150. else {
  151. if (bd->options & NAND_BBT_SCAN2NDPAGE)
  152. len = 2;
  153. else
  154. len = 1;
  155. }
  156. 在前面我们定义了smallpage_memorybased这个结构体,现在里面NAND_BBT_SCANALLPAGES的终于用上了,对于1208芯片来说,len=2。
  157. 304 if (!(bd->options & NAND_BBT_SCANEMPTY)) {
  158. /* We need only read few bytes from the OOB area */
  159. scanlen = ooblen = 0;
  160. readlen = bd->len;
  161. } else {
  162. /* Full page content should be read */
  163. scanlen = mtd->oobblock + mtd->oobsize;
  164. readlen = len * mtd->oobblock;
  165. ooblen = len * mtd->oobsize;
  166. }
  167. 前面已经将NAND_BBT_SCANEMPTY清除了,这里肯定执行else的内容。需要将一页内容都读取出来。
  168. 316 if (chip == -1) {
  169. /* Note that numblocks is 2 * (real numblocks) here, see i+=2 below as it
  170. * makes shifting and masking less painful */
  171. numblocks = mtd->size >> (this->bbt_erase_shift - 1);
  172. startblock = 0;
  173. from = 0;
  174. } else {
  175. if (chip >= this->numchips) {
  176. printk (KERN_WARNING "create_bbt(): chipnr (%d) > available chips (%d)/n",
  177. chip + 1, this->numchips);
  178. return -EINVAL;
  179. }
  180. numblocks = this->chipsize >> (this->bbt_erase_shift - 1);
  181. startblock = chip * numblocks;
  182. numblocks += startblock;
  183. from = startblock << (this->bbt_erase_shift - 1);
  184. }
  185. 前面提到chip为-1,实际上我们只有一颗芯片,numblocks这儿是4096*2。
  186. 335 for (i = startblock; i < numblocks;) {
  187. int ret;
  188. if (bd->options & NAND_BBT_SCANEMPTY)
  189. if ((ret = nand_read_raw (mtd, buf, from, readlen, ooblen)))
  190. return ret;
  191. for (j = 0; j < len; j++) {
  192. if (!(bd->options & NAND_BBT_SCANEMPTY)) {
  193. size_t retlen;
  194. /* Read the full oob until read_oob is fixed to
  195. * handle single byte reads for 16 bit buswidth */
  196. ret = mtd->read_oob(mtd, from + j * mtd->oobblock,
  197. mtd->oobsize, &retlen, buf);
  198. if (ret)
  199. return ret;
  200. if (check_short_pattern (buf, bd)) {
  201. this->bbt[i >> 3] |= 0x03 << (i & 0x6);
  202. printk (KERN_WARNING "Bad eraseblock %d at 0x%08x/n",
  203. i >> 1, (unsigned int) from);
  204. break;
  205. }
  206. } else {
  207. if (check_pattern (&buf[j * scanlen], scanlen, mtd->oobblock, bd)) {
  208. this->bbt[i >> 3] |= 0x03 << (i & 0x6);
  209. printk (KERN_WARNING "Bad eraseblock %d at 0x%08x/n",
  210. i >> 1, (unsigned int) from);
  211. break;
  212. }
  213. }
  214. }
  215. i += 2;
  216. from += (1 << this->bbt_erase_shift);
  217. }
  218. return 0;
  219. 检测这4096个block,刚开始的nand_read_raw肯定不会执行。len是2,在j循环要循环2次。
  220. 每次循环真正要做的事情是下面的内容:
  221. ret = mtd->read_oob(mtd, from + j * mtd->oobblock, mtd->oobsize, &retlen, buf);
  222. read_oob()函数在nand_scan()里被指向nand_read_oob(),这个函数在Nand_base.c文件中,看来得回Nand_base.c看看了。
  223. 1397 /**
  224. * nand_read_oob - [MTD Interface] NAND read out-of-band
  225. * @mtd: MTD device structure
  226. * @from: offset to read from
  227. * @len: number of bytes to read
  228. * @retlen: pointer to variable to store the number of read bytes
  229. * @buf: the databuffer to put data
  230. *
  231. * NAND read out-of-band data from the spare area
  232. */
  233. static int nand_read_oob (struct mtd_info *mtd, loff_t from, size_t len, size_t * retlen, u_char * buf)
  234.   {
  235. 才发现oob全称是out-of-band, from是偏移量,len是读取的长度,retlen是存储指针。
  236. 1409 int i, col, page, chipnr;
  237. struct nand_chip *this = mtd->priv;
  238. int blockcheck = (1 << (this->phys_erase_shift - this->page_shift)) - 1;
  239. DEBUG (MTD_DEBUG_LEVEL3, "nand_read_oob: from = 0x%08x, len = %i/n", (unsigned int) from, (int) len);
  240. /* Shift to get page */
  241. page = (int)(from >> this->page_shift);
  242. chipnr = (int)(from >> this->chip_shift);
  243. /* Mask to get column */
  244. col = from & (mtd->oobsize - 1);
  245. /* Initialize return length value */
  246. *retlen = 0;
  247. 一些初始化,blockcheck对于1208应该是(1<<(0xe-0x9)-1)=31。然后通过偏移量计算出要读取oob区的page,chipnr和col。
  248. 1425 /* Do not allow reads past end of device */
  249. if ((from + len) > mtd->size) {
  250. DEBUG (MTD_DEBUG_LEVEL0, "nand_read_oob: Attempt read beyond end of device/n");
  251. *retlen = 0;
  252. return -EINVAL;
  253. }
  254. /* Grab the lock and see if the device is available */
  255. nand_get_device (this, mtd , FL_READING);
  256. /* Select the NAND device */
  257. this->select_chip(mtd, chipnr);
  258. /* Send the read command */
  259. this->cmdfunc (mtd, NAND_CMD_READOOB, col, page & this->pagemask);
  260. 不允许非法的读取,获取芯片控制权,发送读取OOB命令,这儿会调用具体硬件驱动中相关的Nand控制函数。
  261. 1442 /*
  262. * Read the data, if we read more than one page
  263. * oob data, let the device transfer the data !
  264. */
  265. i = 0;
  266. while (i < len) {
  267. int thislen = mtd->oobsize - col;
  268. thislen = min_t(int, thislen, len);
  269. this->read_buf(mtd, &buf[i], thislen);
  270. i += thislen;
  271. /* Read more ? */
  272. if (i < len) {
  273. page++;
  274. col = 0;
  275. /* Check, if we cross a chip boundary */
  276. if (!(page & this->pagemask)) {
  277. chipnr++;
  278. this->select_chip(mtd, -1);
  279. this->select_chip(mtd, chipnr);
  280. }
  281. /* Apply delay or wait for ready/busy pin
  282. * Do this before the AUTOINCR check, so no problems
  283. * arise if a chip which does auto increment
  284. * is marked as NOAUTOINCR by the board driver.
  285. */
  286. if (!this->dev_ready)
  287. udelay (this->chip_delay);
  288. else
  289. nand_wait_ready(mtd);
  290. /* Check, if the chip supports auto page increment
  291. * or if we have hit a block boundary.
  292. */
  293. if (!NAND_CANAUTOINCR(this) || !(page & blockcheck)) {
  294. /* For subsequent page reads set offset to 0 */
  295. this->cmdfunc (mtd, NAND_CMD_READOOB, 0x0, page & this->pagemask);
  296. }
  297. }
  298. }
  299. /* Deselect and wake up anyone waiting on the device */
  300. nand_release_device(mtd);
  301. /* Return happy */
  302. *retlen = len;
  303. return 0;
  304. 开始读取数据,while循环只要获取到oob区大小的数据即可。注意,read_buf才是最底层的读写Nand的函数,在我们的驱动中根据参数可以实现读取528byte全部内容,或者16byte的oob区。
  305. 如果一次没读完,就要继续再读,根据我们实际使用经验好像没出现过这种问题。
  306. 最后Return Happy~回到Nand_bbt.c的creat_bbt()函数,348行,好像都快忘记我们还没出creat_bbt()函数呢,我再把他贴一遍吧:
  307. 346   /* Read the full oob until read_oob is fixed to
  308. * handle single byte reads for 16 bit buswidth */
  309. ret = mtd->read_oob(mtd, from + j * mtd->oobblock,
  310. mtd->oobsize, &retlen, buf);
  311. if (ret)
  312. return ret;
  313. if (check_short_pattern (buf, bd)) {
  314. this->bbt[i >> 3] |= 0x03 << (i & 0x6);
  315. printk (KERN_WARNING "Bad eraseblock %d at 0x%08x/n",
  316. i >> 1, (unsigned int) from);
  317. break;
  318. }
  319. } else {
  320. if (check_pattern (&buf[j * scanlen], scanlen, mtd->oobblock, bd)) {
  321. this->bbt[i >> 3] |= 0x03 << (i & 0x6);
  322. printk (KERN_WARNING "Bad eraseblock %d at 0x%08x/n",
  323. i >> 1, (unsigned int) from);
  324. break;
  325. }
  326. }
  327. }
  328. i += 2;
  329. from += (1 << this->bbt_erase_shift);
  330. }
  331. return 0;
  332.   }
  333. 刚刚如果不是Ruturn Happy,下面的352行就会返回错误了。接着会调用check_short_pattern()这个函数。
  334. 113 /**
  335. * check_short_pattern - [GENERIC] check if a pattern is in the buffer
  336. * @buf: the buffer to search
  337. * @td:   search pattern deor
  338. *
  339. * Check for a pattern at the given place. Used to search bad block
  340. * tables and good / bad block identifiers. Same as check_pattern, but
  341. * no optional empty check
  342. *
  343.   */
  344.   static int check_short_pattern (uint8_t *buf, struct nand_bbt_descr *td)
  345. {
  346. int i;
  347. uint8_t *p = buf;
  348. /* Compare the pattern */
  349. for (i = 0; i < td->len; i++) {
  350. if (p[td->offs + i] != td->pattern[i])
  351. return -1;
  352. }
  353. return 0;
  354. }
  355. 检查读到的oob区是不是坏块就靠这个函数了。前面放了好久的struct nand_bbt_descr smallpage_memorybased终于用上了,挨个对比,有一个不一样直接返回-1,坏块就这样产生了。下面会将坏块的位置打印出来,并且将坏块记录在bbt表里面,在nand_scan_bbt()函数的开始我们就为bbt申请了空间。
  356. this->bbt[i >> 3] |= 0x03 << (i & 0x6);
  357. 为啥要右移3bit呢?首先i要右移1bit,因为前面乘以了2。由于没个block占用2bit的空间,一个char变量8bit,所以还再要右移2bit吧。
  358.   下面的check_pattern()函数调用不到的。
  359. 依次检测完所有block,creat_bbt()函数也顺利返回。
  360. 这样nand_memory_bbt()函数也正确返回。
  361. 接着是nand_scan_bbt()同样顺利结束。
  362. 最后nand_default_bbt()完成。
  363. 整个nand_scan()的工作终于完成咯,好长。
  1. bbt坏块管理日月 发表于 - 2010-3-2 9:59:002推荐前面看到在nand_scan()函数的最后将会跳至scan_bbt()函数,这个函数在nand_scan里面有定义:2415 if (!this->scan_bbt)2416 this->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->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->options & NAND_IS_AND) {   /* Use the default pattern deors */   if (!this->bbt_td) {    this->bbt_td = &bbt_main_descr;    this->bbt_md = &bbt_mirror_descr;   }    this->options |= NAND_USE_FLASH_BBT;    return nand_scan_bbt (mtd, &agand_flashbased);   }如果Flash的类型是AG-AND(这种Flash类型比较特殊,既不是MLC又不是SLC,因此不去深究了,而且好像瑞萨要把它淘汰掉),需要使用默认的模式描述符,最后再进入nand_scan_bbt()函数。1078 /* Is a flash based bad block table requested ? */   if (this->options & NAND_USE_FLASH_BBT) {   /* Use the default pattern deors */   if (!this->bbt_td) {    this->bbt_td = &bbt_main_descr;    this->bbt_md = &bbt_mirror_descr;   }   if (!this->badblock_pattern) {    this->badblock_pattern = (mtd->oobblock > 512) ?     &largepage_flashbased : &smallpage_flashbased;   }   } else {   this->bbt_td = NULL;   this->bbt_md = NULL;   if (!this->badblock_pattern) {    this->badblock_pattern = (mtd->oobblock > 512) ?     &largepage_memorybased : &smallpage_memorybased;   }   }     return nand_scan_bbt (mtd, this->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->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->priv;int len, res = 0;uint8_t *buf;struct nand_bbt_descr *td = this->bbt_td;struct nand_bbt_descr *md = this->bbt_md;len = mtd->size >> (this->bbt_erase_shift + 2);/* Allocate memory (2bit per block) */this->bbt = kmalloc (len, GFP_KERNEL);if (!this->bbt) {   printk (KERN_ERR "nand_scan_bbt: Out of memory/n");   return -ENOMEM;}/* Clear the memory bad block table */memset (this->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->bbt);    this->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->priv;   bd->options &= ~NAND_BBT_SCANEMPTY;   return create_bbt (mtd, this->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->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->options & NAND_BBT_SCANALLPAGES)len = 1 << (this->bbt_erase_shift - this->page_shift);else {   if (bd->options & NAND_BBT_SCAN2NDPAGE)    len = 2;   else    len = 1;}在前面我们定义了smallpage_memorybased这个结构体,现在里面NAND_BBT_SCANALLPAGES的终于用上了,对于1208芯片来说,len=2。304 if (!(bd->options & NAND_BBT_SCANEMPTY)) {   /* We need only read few bytes from the OOB area */   scanlen = ooblen = 0;   readlen = bd->len;} else {   /* Full page content should be read */   scanlen = mtd->oobblock + mtd->oobsize;   readlen = len * mtd->oobblock;   ooblen = len * mtd->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->size >> (this->bbt_erase_shift - 1);   startblock = 0;   from = 0;} else {   if (chip >= this->numchips) {    printk (KERN_WARNING "create_bbt(): chipnr (%d) > available chips (%d)/n",     chip + 1, this->numchips);    return -EINVAL;   }   numblocks = this->chipsize >> (this->bbt_erase_shift - 1);   startblock = chip * numblocks;   numblocks += startblock;   from = startblock << (this->bbt_erase_shift - 1);}前面提到chip为-1,实际上我们只有一颗芯片,numblocks这儿是4096*2。335 for (i = startblock; i < numblocks;) {   int ret;   if (bd->options & NAND_BBT_SCANEMPTY)    if ((ret = nand_read_raw (mtd, buf, from, readlen, ooblen)))     return ret;   for (j = 0; j < len; j++) {    if (!(bd->options & 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->read_oob(mtd, from + j * mtd->oobblock,        mtd->oobsize, &retlen, buf);     if (ret)      return ret;     if (check_short_pattern (buf, bd)) {       this->bbt[i >> 3] |= 0x03 << (i & 0x6);      printk (KERN_WARNING "Bad eraseblock %d at 0x%08x/n",       i >> 1, (unsigned int) from);        break;     }    } else {     if (check_pattern (&buf[j * scanlen], scanlen, mtd->oobblock, bd)) {         this->bbt[i >> 3] |= 0x03 << (i & 0x6);      printk (KERN_WARNING "Bad eraseblock %d at 0x%08x/n",       i >> 1, (unsigned int) from);         break;     }    }   }   i += 2;   from += (1 << this->bbt_erase_shift);}return 0;检测这4096个block,刚开始的nand_read_raw肯定不会执行。len是2,在j循环要循环2次。每次循环真正要做的事情是下面的内容:ret = mtd->read_oob(mtd, from + j * mtd->oobblock, mtd->oobsize, &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->priv;int blockcheck = (1 << (this->phys_erase_shift - this->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 >> this->page_shift);chipnr = (int)(from >> this->chip_shift);/* Mask to get column */col = from & (mtd->oobsize - 1);/* Initialize return length value */*retlen = 0;一些初始化,blockcheck对于1208应该是(1<<(0xe-0x9)-1)=31。然后通过偏移量计算出要读取oob区的page,chipnr和col。1425 /* Do not allow reads past end of device */if ((from + len) > mtd->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->select_chip(mtd, chipnr);/* Send the read command */this->cmdfunc (mtd, NAND_CMD_READOOB, col, page & this->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 < len) {   int thislen = mtd->oobsize - col;   thislen = min_t(int, thislen, len);   this->read_buf(mtd, &buf[i], thislen);   i += thislen;   /* Read more ? */   if (i < len) {    page++;    col = 0;    /* Check, if we cross a chip boundary */    if (!(page & this->pagemask)) {     chipnr++;     this->select_chip(mtd, -1);     this->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->dev_ready)     udelay (this->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 & blockcheck)) {     /* For subsequent page reads set offset to 0 */           this->cmdfunc (mtd, NAND_CMD_READOOB, 0x0, page & this->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->read_oob(mtd, from + j * mtd->oobblock,        mtd->oobsize, &retlen, buf);     if (ret)      return ret;     if (check_short_pattern (buf, bd)) {       this->bbt[i >> 3] |= 0x03 << (i & 0x6);      printk (KERN_WARNING "Bad eraseblock %d at 0x%08x/n",       i >> 1, (unsigned int) from);        break;     }    } else {     if (check_pattern (&buf[j * scanlen], scanlen, mtd->oobblock, bd)) {         this->bbt[i >> 3] |= 0x03 << (i & 0x6);      printk (KERN_WARNING "Bad eraseblock %d at 0x%08x/n",       i >> 1, (unsigned int) from);         break;     }    }   }   i += 2;   from += (1 << this->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 < td->len; i++) {   if (p[td->offs + i] != td->pattern[i])    return -1;}return 0;}检查读到的oob区是不是坏块就靠这个函数了。前面放了好久的struct nand_bbt_descr smallpage_memorybased终于用上了,挨个对比,有一个不一样直接返回-1,坏块就这样产生了。下面会将坏块的位置打印出来,并且将坏块记录在bbt表里面,在nand_scan_bbt()函数的开始我们就为bbt申请了空间。this->bbt[i >> 3] |= 0x03 << (i & 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()的工作终于完成咯,好长。

bbt坏块管理日月 发表于 - 2010-3-2 9:59:002推荐前面看到在nand_scan()函数的最后将会跳至scan_bbt()函数,这个函数在nand_scan里面有定义:2415 if (!this->scan_bbt)2416 this->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->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->options & NAND_IS_AND) { /* Use the default pattern deors */ if (!this->bbt_td) { this->bbt_td = &bbt_main_descr; this->bbt_md = &bbt_mirror_descr; } this->options |= NAND_USE_FLASH_BBT; return nand_scan_bbt (mtd, &agand_flashbased); }如果Flash的类型是AG-AND(这种Flash类型比较特殊,既不是MLC又不是SLC,因此不去深究了,而且好像瑞萨要把它淘汰掉),需要使用默认的模式描述符,最后再进入nand_scan_bbt()函数。1078 /* Is a flash based bad block table requested ? */ if (this->options & NAND_USE_FLASH_BBT) { /* Use the default pattern deors */ if (!this->bbt_td) { this->bbt_td = &bbt_main_descr; this->bbt_md = &bbt_mirror_descr; } if (!this->badblock_pattern) { this->badblock_pattern = (mtd->oobblock > 512) ? &largepage_flashbased : &smallpage_flashbased; } } else { this->bbt_td = NULL; this->bbt_md = NULL; if (!this->badblock_pattern) { this->badblock_pattern = (mtd->oobblock > 512) ? &largepage_memorybased : &smallpage_memorybased; } } return nand_scan_bbt (mtd, this->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->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->priv;int len, res = 0;uint8_t *buf;struct nand_bbt_descr *td = this->bbt_td;struct nand_bbt_descr *md = this->bbt_md;len = mtd->size >> (this->bbt_erase_shift + 2);/* Allocate memory (2bit per block) */this->bbt = kmalloc (len, GFP_KERNEL);if (!this->bbt) { printk (KERN_ERR "nand_scan_bbt: Out of memory/n"); return -ENOMEM;}/* Clear the memory bad block table */memset (this->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->bbt); this->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->priv; bd->options &= ~NAND_BBT_SCANEMPTY; return create_bbt (mtd, this->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->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->options & NAND_BBT_SCANALLPAGES)len = 1 << (this->bbt_erase_shift - this->page_shift);else { if (bd->options & NAND_BBT_SCAN2NDPAGE) len = 2; else len = 1;}在前面我们定义了smallpage_memorybased这个结构体,现在里面NAND_BBT_SCANALLPAGES的终于用上了,对于1208芯片来说,len=2。304 if (!(bd->options & NAND_BBT_SCANEMPTY)) { /* We need only read few bytes from the OOB area */ scanlen = ooblen = 0; readlen = bd->len;} else { /* Full page content should be read */ scanlen = mtd->oobblock + mtd->oobsize; readlen = len * mtd->oobblock; ooblen = len * mtd->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->size >> (this->bbt_erase_shift - 1); startblock = 0; from = 0;} else { if (chip >= this->numchips) { printk (KERN_WARNING "create_bbt(): chipnr (%d) > available chips (%d)/n", chip + 1, this->numchips); return -EINVAL; } numblocks = this->chipsize >> (this->bbt_erase_shift - 1); startblock = chip * numblocks; numblocks += startblock; from = startblock << (this->bbt_erase_shift - 1);}前面提到chip为-1,实际上我们只有一颗芯片,numblocks这儿是4096*2。335 for (i = startblock; i < numblocks;) { int ret; if (bd->options & NAND_BBT_SCANEMPTY) if ((ret = nand_read_raw (mtd, buf, from, readlen, ooblen))) return ret; for (j = 0; j < len; j++) { if (!(bd->options & 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->read_oob(mtd, from + j * mtd->oobblock, mtd->oobsize, &retlen, buf); if (ret) return ret; if (check_short_pattern (buf, bd)) { this->bbt[i >> 3] |= 0x03 << (i & 0x6); printk (KERN_WARNING "Bad eraseblock %d at 0x%08x/n", i >> 1, (unsigned int) from); break; } } else { if (check_pattern (&buf[j * scanlen], scanlen, mtd->oobblock, bd)) { this->bbt[i >> 3] |= 0x03 << (i & 0x6); printk (KERN_WARNING "Bad eraseblock %d at 0x%08x/n", i >> 1, (unsigned int) from); break; } } } i += 2; from += (1 << this->bbt_erase_shift);}return 0;检测这4096个block,刚开始的nand_read_raw肯定不会执行。len是2,在j循环要循环2次。每次循环真正要做的事情是下面的内容:ret = mtd->read_oob(mtd, from + j * mtd->oobblock, mtd->oobsize, &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->priv;int blockcheck = (1 << (this->phys_erase_shift - this->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 >> this->page_shift);chipnr = (int)(from >> this->chip_shift);/* Mask to get column */col = from & (mtd->oobsize - 1);/* Initialize return length value */*retlen = 0;一些初始化,blockcheck对于1208应该是(1<<(0xe-0x9)-1)=31。然后通过偏移量计算出要读取oob区的page,chipnr和col。1425 /* Do not allow reads past end of device */if ((from + len) > mtd->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->select_chip(mtd, chipnr);/* Send the read command */this->cmdfunc (mtd, NAND_CMD_READOOB, col, page & this->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 < len) { int thislen = mtd->oobsize - col; thislen = min_t(int, thislen, len); this->read_buf(mtd, &buf[i], thislen); i += thislen; /* Read more ? */ if (i < len) { page++; col = 0; /* Check, if we cross a chip boundary */ if (!(page & this->pagemask)) { chipnr++; this->select_chip(mtd, -1); this->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->dev_ready) udelay (this->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 & blockcheck)) { /* For subsequent page reads set offset to 0 */ this->cmdfunc (mtd, NAND_CMD_READOOB, 0x0, page & this->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->read_oob(mtd, from + j * mtd->oobblock, mtd->oobsize, &retlen, buf); if (ret) return ret; if (check_short_pattern (buf, bd)) { this->bbt[i >> 3] |= 0x03 << (i & 0x6); printk (KERN_WARNING "Bad eraseblock %d at 0x%08x/n", i >> 1, (unsigned int) from); break; } } else { if (check_pattern (&buf[j * scanlen], scanlen, mtd->oobblock, bd)) { this->bbt[i >> 3] |= 0x03 << (i & 0x6); printk (KERN_WARNING "Bad eraseblock %d at 0x%08x/n", i >> 1, (unsigned int) from); break; } } } i += 2; from += (1 << this->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 < td->len; i++) { if (p[td->offs + i] != td->pattern[i]) return -1;}return 0;}检查读到的oob区是不是坏块就靠这个函数了。前面放了好久的struct nand_bbt_descr smallpage_memorybased终于用上了,挨个对比,有一个不一样直接返回-1,坏块就这样产生了。下面会将坏块的位置打印出来,并且将坏块记录在bbt表里面,在nand_scan_bbt()函数的开始我们就为bbt申请了空间。this->bbt[i >> 3] |= 0x03 << (i & 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()的工作终于完成咯,好长。

===============================================================

MTD的坏块管理(一)-快速了解MTD的坏块管理

1 人收藏此文章, 收藏此文章 发表于2个月前 , 已有65 次阅读 共0 个评论 1 人收藏此文章

由于NAND Flash的现有工艺不能保证NAND的Memory Array在其生命周期中保持性能的可靠,因此在NAND芯片出厂的时候,厂家只能保证block 0不是坏块,对于其它block,则均有可能存在坏块,而且NAND芯片在使用的过程中也很容易产生坏块。因此,我们在读写NAND FLASH 的时候,需要检测坏块,同时还需在NAND驱动中加入坏块管理的功能。     NAND驱动在加载的时候,会调用nand_scan函数,对bad block table的搜寻,建立等操作就是在这个函数的第二部分,即nand_scan_tail函数中完成的。 
在 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函数。 
bbt有两种存储方式,一种是把bbt存储在NAND芯片中,另一种是把bbt存储在内存中。对于前者,好处是驱动加载更快,因为它只会在第一次加载NAND驱动时扫描整个NAND芯片,然后在NAND芯片的某个block中建立bbt,坏处是需要至少消耗NAND芯片一个block的存储容量;而对于后者,好处是不会耗用NAND芯片的容量,坏处是驱动加载稍慢,因为存储在内存中的bbt每次断电后都不会保存,所以在每次加载NAND驱动时,都会扫描整个NAND芯片,以便建立bbt。 
如果你系统中的NAND芯片容量不是太大的话,我建议还是把bbt存储在内存中比较好,因为根据本人的使用经验,对一块容量为2G bits的NAND芯片,分别采用这两种存储方式的驱动的加载速度相差不大,甚至几乎感觉不出来。 
建立bbt后,以后在做擦除等操作时,就不用每次都去验证当前block是否是个坏块了,因为从bbt中就可以得到这个信息。另外,若在读写等操作时,发现产生了新的坏块,那么除了标志这个block是个坏块外,也还需更新bbt。 
接下来,介绍一下MTD是如何查找或者建立bbt的。 
1、MTD中与bbt相关的结构体 
struct nand_chip中的scan_bbt函数指针所指向的函数,即nand_default_bbt函数会首先检查struct nand_chip中options成员变量,如果当前NAND芯片是AG-AND类型的,会强制把bbt存储在NAND芯片中,因为这种类型的NAND 芯片中含有厂家标注的“好块”信息,擦除这些block时会导致丢失坏块信息。 
接着 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存储在内存中。

在struct nand_chip中与bbt相关的结构体如下:

struct nand_chip {     ……     uint8_t     *bbt     struct nand_bbt_descr    *bbt_td;     struct nand_bbt_descr    *bbt_md;     struct nand_bbt_descr    *badblock_pattern;     ……

};

bbt指向一块在nand_default_bbt函数中分配的内存,若options中没有定义NAND_USE_FLASH_BBT,MTD就直接在bbt指向的内存中建立bbt,否则就会先从NAND芯片中查找bbt是否存在,若存在,就把bbt的内容读出来并保存到bbt指向的内存中,若不存在,则在bbt 指向的内存中建立bbt,最后把它写入到NAND芯片中去。 
bbt_td、bbt_md和badblock_pattern就是在nand_default_bbt函数中赋值的3个结构体。它们虽然是相同的结构体类型,但却有不同的作用和含义。 其中bbt_td和bbt_md是主bbt和镜像bbt的描述符(镜像bbt主要用来对bbt的update和备份),它们只在把bbt存储在NAND芯片的情况下使用,用来从NAND芯片中查找bbt。若bbt存储在内存中,bbt_td和bbt_md将会被赋值为NULL。 
badblock_pattern就是坏块信息的pattern,其中定义了坏块信息在oob中的存储位置,以及内容(即用什么值表示这个block是个坏块)。 
通常用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个字节中。 
我不能确定是否所有的NAND芯片都是如此布局,但应该绝大多数NAND芯片是这样的,不过,即使某种NAND芯片的坏块信息不是这样的存储方式也没关系,因为我们可以在badblock_pattern中自己指定坏块信息的存储位置,以及用什么值来标志坏块(其实这个值表示的应该是“好块”,因为MTD会把从oob中坏块信息存储位置读出的内容与这个值做比较,若相等,则表示是个“好块”,否则就是坏块)。

bbt_td、bbt_md和badblock_pattern的结构体类型定义如下:

struct nand_bbt_descr {     int    options;     int    pages[NAND_MAX_CHIPS];     int    offs;     int    veroffs;     uint8_t    version[NAND_MAX_CHIPS];     int    len;     int    maxblocks;     int    reserved_block_code;     uint8_t    *pattern;

};

options:bad block table或者bad block的选项,可用的选择以及各选项具体表示什么含义,可以参考<linux/mtd/nand.h>。 
pages:bbt 专用。在查找bbt的时候,若找到了bbt,就把bbt所在的page号保存在这个成员变量中。若没找到bbt,就会把新建立的bbt的保存位置赋值给它。因为系统中可能会有多个NAND芯片,我们可以为每一片NAND芯片建立一个bbt,也可以只在其中一片NAND芯片中建立唯一的一个bbt,所以这里的pages是个维数为NAND_MAX_CHIPS的数值,用来保存每一片NAND芯片的bbt位置。当然,若只建立了一个bbt,那么就只使用 pages[0]。 
offs、len和pattern:MTD会从oob的offs中读出len长度的内容,然后与pattern指针指向的内容做比较,若相等,则表示找到了bbt,或者表示这个block是好的。 
veroffs和version:bbt专用。MTD会从oob的veroffs中读出一个字节的内容,作为bbt的版本值保存在version中。 
maxblocks:bbt专用。MTD在查找bbt的时候,不会查找NAND芯片中所有的block,而是最多查找maxblocks个block。 
2、bbt存储在内存中时的工作流程 
前文说过,不管bbt是存储在NAND芯片中,还是存储在内存中,nand_default_bbt函数都会调用nand_scan_bbt函数。 
nand_scan_bbt函数会判断bbt_td的值,若是NULL,则表示bbt存储在内存中,它就在调用nand_memory_bbt函数后返回。nand_memory_bbt函数的主要工作就是在内存中建立bbt,其实就是调用了create_bbt函数。 
create_bbt 函数的工作方式很简单,就是扫描NAND芯片所有的block,读取每个block中第一个page的oob内容,然后根据oob中的坏块信息建立起 bbt,可以参见上节关于struct nand_bbt_descr中的offs、len和pattern成员变量的解释。 
3、bbt存储在NAND芯片时的工作流程 
相对于把bbt存储在内存中,这种方式的工作流程稍显复杂一点。 
nand_scan_bbt函数首先从NAND芯片中读取bbt的内容,它读取的方式分为两种: 
其一是调用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就永远不会坏,而且这样也不灵活;

其二是调用那个search_read_bbts函数试着在NAND芯片的maxblocks(请见上文关于struct nand_bbt_descr中maxblocks的说明)个block中查找bbt是否存在,若找到,就可以读取bbt了。 
MTD 查找bbt的过程为:如果你在bbt_td和bbt_md的options 成员变量中定义了 NAND_BBT_LASTBLOCK,那么MTD就会从NAND芯片的最后一个block开始查找(在default情况下,MTD就是这么干的),否则就从第一个block开始查找。 
与查找oob中的坏块信息时类似,MTD会从所查找block的第一个page的oob中读取内容,然后与bbt_td或bbt_md中patter指向的内容做比较,若相等,则表示找到了bbt,否则就继续查找下一个block。顺利的情况下,只需查找一个block中就可以找到bbt,否则MTD最多会查找maxblocks个block。 若找到了bbt,就把该bbt所在的page地址保存到bbt_td或bbt_md的pages成员变量中,否则pages的值为-1。 
如果系统中有多片NAND芯片,并且为每一片NAND芯片都建立一个bbt,那么就会在每片NAND芯片上重复以上过程。 
接着,nand_scan_bbt函数会调用check_create函数,该函数会判断是否找到了bbt,其实就是判断bbt_td或者bbt_md中 pages成员变量的值是否有效。若找到了bbt,就会把bbt从NAND芯片中读取出来,并保存到struct nand_chip中bbt指针指向的内存中;若没找到,就会调用create_bbt函数建立bbt(与bbt存储在内存中时情况一样),同时把bbt 写入到NAND芯片中去。

**** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** ****

MTD坏块管理(二)-内核获取Nandflash的参数过程

1 人收藏此文章, 收藏此文章 发表于2个月前 , 已有133 次阅读 共0 个评论 1 人收藏此文章

MTD坏块管理机制中,起着核心作用的数据结构是nand_chip,在此以TCC8900-Linux中MTD的坏块管理为例作一次介绍。

MTD在Linux内核中同样以模块的形式被启用,TCC_MTD_IO_Init()函数完成了nand_chip初始化、mtd_info初始注册,

坏块表的管理机制建立等工作。

nand_chip在TCC_MTD_IO_Init函数中的实例名称是this,mtd_info 的实例名称为TCC_mtd,这里有一个比较巧妙的处理方法:

TCC_mtd=kmalloc(sizeof(struct mtd_info)+sizeof(struct nand_chip),GFP_KERNEL);

this=(struct nand_chip*)(&TCC_mtd[1]);

在以后的操作中,只需得知TCC_mtd即可找到对应的nan_chip实例。

获得必要的信息后(包括nand_chip方法的绑定),流程进入nand_scan(TCC_mtd,1).

nand_scan(struct mdt_info *mtd, int maxchips);

调用nand_scan_ident(mtd,maxchips)和nand_scan_tail(mtd);

nand_scan_ident(...)调用了一个很重要的函数:nand_get_flash_type(...)

*从nand_get_flash_type(...)函数中可以看出每个nandflash前几个字节所代表的意思都是约定好了的:

第一个字节:制造商ID

第二个字节:设备ID

第三个字节:MLC 数据

第四个字节:extid (比较总要)

其中设备ID是访问nand_flash_ids表的参照,该表在drivers/mtd/nand/nand_ids.c中定义

Linux内核在nand_flash_ids参照表中,通过匹配上述设备ID来查找nandflash的详细信息,

nand_flash_ids中的举例如下:

struct nand_flash_dev nand_flash_ids[]={

......

{"NAND 16MiB 1,8V 8-bit",   0x33, 512, 16, 0x4000, 0},

{"NAND 16MiB 3,3V 8-bit",   0x73, 512, 16, 0x4000, 0},

{"NAND 16MiB 1,8V 16-bit",  0x43, 512, 16, 0x4000, NAND_BUSWIDTH_16},

{"NAND 16MiB 3,3V 16-bit",  0x53, 512, 16, 0x4000, NAND_BUSWIDTH_16},

......

}

466 struct nand_flash_dev {

467     char *name;

468     int id;

469     unsigned long pagesize;

470     unsigned long chipsize;

471     unsigned long erasesize;

472     unsigned long options;

473 };

值得一提的是,MTD子系统会把从nand_flash_ids表中找到的chipsize复制给mtd->size,这在有些应用中显得不合适,

在有些方案中,并不是把nandflash的所有存储空间都划分为MTD分区,Telechips的TCC89XX方案就是这样,4G的nandflash

上,可以划分任意大小的MTD分区,错误的mtd->size的后果非常严重,造成系统启动慢,整个MTD的坏块管理机制瘫痪等等。

随后,nand_get_flash_type通过extid计算出了以下信息:

mtd可写区大小:mtd->writesize=1024<<(extid&0x03);

这里可以看成1024*(1*2的(extid&0x03)次方),

mtdoob区大小:extid>>=2;mtd->oobsize = (8<<(extid&0x1))*(mtd->writesize>>9);

每512字节对应(8*2的(extid&0x1)次方)字节oob数据

mtd擦写块大小:extid>>=2;mtd->erasesize=(64*1024)<<(extid&0x03);

nand数据宽度 :extid>>=2;busw=(extid&0x01)?NAND_BUSEWIDTH_16:0; 现在大多为8位数据宽度

可以看出第四个字节extid的意义:

高|0    |  0        |   00        | 0   | 0         |  00           |低

|无用|数据宽度|擦写块算阶|无用|oob算阶|  可写区算阶|

nand_get_flash_type(...)还确立了nandflash中的坏块标记在oob信息中的位置:

if(mtd->writesize>512||(busw&NAND_BUSWIDTH_16))

chip->badblockpos = NAND_LARGE_BADBLOCKS_POS;//大页面flash的坏块信息存储地址为oob信息中的第1个字节开始处

else

chip->badblockpos = NAND_SMALL_BADBLOCKS_POS;//大页面flash的坏块信息存储地址为oob信息中的第6个字节开始处

对于Samsun和Hynix的MLC型nandflash,坏块标记所在的页是每块的最后一个页,而Samsung,Hynix,和AMD的SLC型nandflash

中,坏块标记分别保存在每块开始的第1,2个页中,其他型号的nandflash大多都保存在第一个也中,为此需要作下标记:

坏块标记保存在块的最后一页中:chip->options |= NAND_BBT_SCANLASTPAGE;

坏块标记保存在块的第1,2页中 :chip->options |= NAND_BBT_SCAN2NDPAGE;

nand_scan之后调用nand_scan_tail(mtd)函数,

nand_scan_tail(...)函数主要完成MTD实例中各种方法的绑定,例如:

3338     mtd->read = nand_read;

3339     mtd->write = nand_write;

3340     mtd->panic_write = panic_nand_write;

3341     mtd->read_oob = nand_read_oob;

3342     mtd->write_oob = nand_write_oob;

3343     mtd->sync = nand_sync;

nand_scan_tail(...)调用chip->scan_bbt()完成坏块表的有关操作。

chip->scan_bbt的绑定过程是在nand_scan_ident()->nand_set_defaults():chip->scan_bbt = nand_default_bbt.

即真正用于坏块操作的是nand_default_bbt函数,该函数在nand_bbt.c中被定义。

nand flash 的oob 及坏块管理的更多相关文章

  1. Nand Flash基础知识与坏块管理机制的研究

    概述 Flash名称的由来,Flash的擦除操作是以block块为单位的,与此相对应的是其他很多存储设备,是以bit位为最小读取/写入的单位,Flash是一次性地擦除整个块:在发送一个擦除命令后,一次 ...

  2. 【转】nand flash坏块管理OOB,BBT,ECC

    0.NAND的操作管理方式      NAND FLASH的管理方式:以三星FLASH为例,一片Nand flash为一个设备(device),1 (Device) = xxxx (Blocks),1 ...

  3. nand flash坏块管理OOB,BBT,ECC

    转:http://www.cnblogs.com/elect-fans/archive/2012/05/14/2500643.html 0.NAND的操作管理方式 NAND FLASH的管理方式:以三 ...

  4. NAND Flash大容量存储器K9F1G08U的坏块管理方法

    转: http://www.360doc.com/content/11/0915/10/7715138_148381804.shtml 在进行数据存储的时候,我们需要保证数据的完整性,而NAND Fl ...

  5. STM32下FatFs的移植,实现了坏块管理,硬件ECC,ECC纠错,并进行擦写均衡分析

    最近因项目需要,做一个数据采集的单片机平台.需要移植 FatFs .现在把最后成果贴上来. 1.摘要 在 STM32 单片机上,成功移植 FatFs 0.12b,使用的 Nand Flash 芯片为 ...

  6. 坏块管理(Bad Block Management,BBM)

    看了很多坏块管理的文章,加上自己的理解,把整个坏块管理做了个总结. 坏块分类 1.出厂坏块 又叫初始坏块,厂商会给点最小有效块值(NVB,mininum number of valid blocks) ...

  7. BBM(Bad Block Management)坏块管理

    不管WL算法如何高明,在使用中都会碰到一个头痛的问题,那就是坏块,所以一个SSD必须要有坏块管理机制.何谓坏块?一个闪存块里包含有不稳定的地址,不能保证读/写/擦时数据的准确性.            ...

  8. DM365视频处理流程/DM368 NAND Flash启动揭秘

    出自http://blog.csdn.net/maopig/article/details/7029930 DM365的视频处理涉及到三个相关处理器,分别是视频采集芯片.ARM处理器和视频图像协处理器 ...

  9. JZ2440 裸机驱动 第8章 NAND Flash控制器

    本章目标  了解NAND Flash 芯片的接口 掌握通过NAND Flash控制器访问NAND Flash的方法 8.1 NAND Flash介绍和NAND Flash控制器使用     NAND ...

随机推荐

  1. Sort HDU - 5884(优先队列+二分)

    Sort Time Limit: 3000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)Total Submis ...

  2. MT【127】点对个数两题之一【图论】

    在平面上有\(n\) 个点$S={x_1,x_2\cdots,x_n}, $ 其中任意两个点之间的距离至少为 \(1\), 证明在这 \(n\) 个点中距离为 \(1\)的点对数不超过 \(3n\). ...

  3. BZOJ 2157: 旅游

    2157: 旅游 Time Limit: 10 Sec  Memory Limit: 259 MBSubmit: 1347  Solved: 619[Submit][Status][Discuss] ...

  4. 使用regsrv32.exe绕过应用程序白名单(多种方法)

    0x00 regsvr简介 regsvr32表示Microsoft注册服务.它是Windows的命令行实用工具.虽然regsvr32有时会导致问题出现,但它是Windows系统文件中的一个重要文件.该 ...

  5. 20135239 益西拉姆 linux内核分析 读书笔记之第四章

    chapter 4 进程调度 4.1 多任务 多任务操作系统就是能同时并发的交互执行多个进程的操作系统. 多任务系统可以划分为两类: - 非抢占式多任务: - 进程会一直执行直到自己主动停止运行(这一 ...

  6. linux内核分析 第七周 Linux内核如何装载和启动一个可执行程序

    一.编译链接的过程和ELF可执行文件格式 vi hello.c gcc -E -o hello.cpp hello.c -m32 //预处理.c文件,预处理包括把include的文件包含进来以及宏替换 ...

  7. loj6070【山东集训第一轮Day4】基因

    题解: 分块对每个块的起点$st[i]$到$n$做一次回文自动机; 由于子串的回文自动机是原串的子图,所以并不需要重新构图,在原来的图上做即可: 做的时候记录某个终点的本质不同的回文串和$sum[i] ...

  8. 音视频处理之FFmpeg+SDL+MFC视频播放器20180411

    一.FFmpeg+SDL+MFC视频播放器 1.MFC知识 1).创建MFC工程的方法 打开VC++ 文件->新建->项目->MFC应用程序 应用程序类型->基于对话框 取消勾 ...

  9. 团体程序设计天梯赛 L1-009. N个数求和

    易错题 #include <stdio.h> #include <stdlib.h> #include <string.h> #include <stdboo ...

  10. shell 变量定义使用

    shell 中变量的几种类型: 1.局部变量:只在当前 shell 可用的变量, 2.环境变量:当前 shell 的子进程也可用的变量 3.shell 变量:一些由 shell 设置的特殊变量,如:$ ...