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. 关于dismissViewControllerAnimated值得注意的一点(deinit)

    在使用dismissViewControllerAnimated退出当前视图的时候,理论上,该视图对象就会被清除了, 也就是说会进去当前类的析构函数deinit里面.但是有时候会发现,dismiss之 ...

  2. 【BZOJ4774】修路(动态规划,斯坦纳树)

    [BZOJ4774]修路(动态规划,斯坦纳树) 题面 BZOJ 题解 先讲怎么求解最小斯坦纳树. 先明白什么是斯坦纳树. 斯坦纳树可以认为是最小生成树的一般情况.最小生成树是把所有给定点都要加入到联通 ...

  3. 使用Java连接HBASE过程中问题总结

    本文是我个人在连接服务器的HBASE过程的一些问题总结. 一.用户和主机名的设置 1.1 报错:Insufficient permissions(user=Administartor) 原因:本地ho ...

  4. 洛谷 P1053 逛公园 解题报告

    P3953 逛公园 问题描述 策策同学特别喜欢逛公园. 公园可以看成一张\(N\)个点\(M\)条边构成的有向图,且没有自环和重边.其中1号点是公园的入口,\(N\)号点是公园的出口,每条边有一个非负 ...

  5. go数据类型之基本类型

    基本数据类型 数值型 整数 int 64位操作系统默认为int64,32位操作系统为int32,但是类型检查时时int int8 (byte 1字节) int16 (short 2字节) int32 ...

  6. python学习(25) BeautifulSoup介绍和实战

    BeautifulSoup是python的html解析库,处理html非常方便 BeautifulSoup 安装 pip install beautifulsoup4 BeautifulSoup 配合 ...

  7. Android dumpsys命令详细使用

    一.dumpsys命令介绍 1.命令说明 Dumpsys用户系统诊断,它运行在设备上,并提供系统服务状态信息 命令格式: adb shell dumpsys [system serbices] 2.系 ...

  8. Qt error ------ no matching function for call to QObject::connect(QSpinBox*&, <unresolved overloaded function type>, QSlider*&, void (QAbstractSlider::*)(int))

    connect(ui->spinBox_luminosity,&QSpinBox::valueChanged, ui->horizontalSlider_luminosity, & ...

  9. array_merge 优化调整

    function dealed_array_merge($a,$b){ if ($a && !$b){ return $a; } if (!$a && $b){ ret ...

  10. a标签--超链接

    一.链接到其他网站 <body> <a href="https://www.baidu.com" target="_blank">百度& ...