nand flash壞塊管理OOB,BBT,ECC

轉:http://www.cnblogs.com/elect-fans/archive/2012/05/14/2500643.htmlhtml

 

0.NAND的操做管理方式linux

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字節。app

1.爲何會出現壞塊
    因爲NAND Flash的工藝不能保證NAND的Memory Array在其生命週期中保持性能的可靠,所以,在NAND的生產中及使用過程當中會產生壞塊。壞塊的特性是:當編程/擦除這個塊時,會形成Page Program和Block Erase操做時的錯誤,相應地反映到Status Register的相應位。less

2.壞塊的分類
   整體上,壞塊能夠分爲兩大類:(1)固有壞塊:這是生產過程當中產生的壞塊,通常芯片原廠都會在出廠時都會將每一個壞塊第一個page的spare area的第6個byte標記爲不等於0xff的 值。(2)使用壞塊:這是在NAND Flash使用過程當中,若是Block Erase或者Page Program錯誤,就能夠簡單地將這個塊做爲壞塊來處理,這個時候須要把壞塊標記起來。爲了和固有壞塊信息保持一致,將新發現的壞塊的第一個page的 spare area的第6個Byte標記爲非0xff的值。electron

3.壞塊管理
    根據上面的這些敘述,能夠了解NAND Flash出廠時在spare area中已經反映出了壞塊信息,所以, 若是在擦除一個塊以前,必定要先check一下第一頁的spare area的第6個byte是不是0xff,若是是就證實這是一個好塊,能夠擦除;若是是非0xff,那麼就不能擦除,以避免將壞塊標記擦掉。 固然,這樣處理可能會犯一個錯誤―――「錯殺僞壞塊」,由於在芯片操做過程當中可能因爲 電壓不穩定等偶然因素會形成NAND操做的錯誤。可是,爲了數據的可靠性及軟件設計的簡單化,仍是須要遵守這個標準。ide

      能夠用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這個值就是標識擦除成功,是好塊。

  

bbt壞塊管理  
日月 發表於 - 2010-3-2 9:59:00  
2  
推薦  
前面看到在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=2304 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*2335 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的壞塊管理

 

由 於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的。 
一、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。 
二、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成員變量的解釋。 
三、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的參數過程

 

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中被定義。

相關文章
相關標籤/搜索