现在的位置: 首页 > 综合 > 正文

MTD(4)—nand flash的bbt坏块表的建立函数代码分析

2014年11月13日 ⁄ 综合 ⁄ 共 13257字 ⁄ 字号 评论关闭

其实现在Linux kernel的bbt做的也比较简单,就是把整个flash的block在内存里面用2bit位图来标识good/bad,这样,在上层判断一个block是否good时就不需要再去读取flash的oob里面的坏块标记了,只需要读取内存里面的bbt就可以了,这是一个比较重要的优化。

但,我想这只是一个开始,希望将来能够把BBM加入到kernel里面来,让上层不再操心nand flash的坏块和均匀擦写。

 

当然,是否使用bbt,kernel还是给开发者提供了开关接口的,那就是

       /*Check, if we should skip the bad block table scan */

       if(chip->options & NAND_SKIP_BBTSCAN)

              return0;

而我们在chip信息里面的定义是

       .options         = NAND_USE_FLASH_BBT,

也就是定义了使用bbt。

 

我们现在进入nand_default_bbt(),它在Nand_bbt.c (drivers\mtd\nand)里面;

/* Is a flash based bad block tablerequested ? */

       if(this->options & NAND_USE_FLASH_BBT) {

              /*Use the default pattern descriptors */

              if(!this->bbt_td) {

                     this->bbt_td= &bbt_main_descr;

                     this->bbt_md= &bbt_mirror_descr;

              }

              if(!this->badblock_pattern) {

                     this->badblock_pattern= (mtd->writesize > 512) ? &largepage_flashbased :&smallpage_flashbased;     // 这里是针对splp不同的flash,设置不同的bbt参数;

              }

       }

相关的结构体定义如下,首先关注下我标注的红色注释;

//small page(512B)large pagenand flash它们的坏块标记存放位置是不一样的,具体参数还是要阅读flash的硬件手册为准,一般sp是第6个字节,lp是前2个字节;

static struct nand_bbt_descrlargepage_flashbased = {

       .options= NAND_BBT_SCAN2NDPAGE,  // 扫描坏块标记时要扫描每个block的前2page

       .offs= 0,        // 坏块标记存放在每个pageoob里面,offs是坏块标记在oob里面的起始位置;

       .len= 2,         // 坏块标记占2个连续字节

       .pattern= scan_ff_pattern   // 如果坏块标记是0xff, 0xff,则说明这个block是好的

};

static struct nand_bbt_descrsmallpage_flashbased = {

       .options= NAND_BBT_SCAN2NDPAGE,

       .offs= 5,

       .len= 1,

       .pattern= scan_ff_pattern

};

 

/* Generic flash bbt decriptors

*/

static uint8_t bbt_pattern[] = {'B','b', 't', '0' };            //通常使用2bbt,这是主bbt

static uint8_t mirror_pattern[] ={'1', 't', 'b', 'B' };        // 这是备份bbt

 

static struct nand_bbt_descrbbt_main_descr = {

       .options= NAND_BBT_LASTBLOCK | NAND_BBT_CREATE | NAND_BBT_WRITE

              |NAND_BBT_2BIT | NAND_BBT_VERSION | NAND_BBT_PERCHIP,

       .offs=    8,

       .len= 4,

       .veroffs= 12,

       .maxblocks= 4,

       .pattern= bbt_pattern

};

 

static struct nand_bbt_descrbbt_mirror_descr = {

       .options= NAND_BBT_LASTBLOCK | NAND_BBT_CREATE | NAND_BBT_WRITE

              |NAND_BBT_2BIT | NAND_BBT_VERSION | NAND_BBT_PERCHIP,

       .offs=    8,

       .len= 4,

       .veroffs= 12,

       .maxblocks= 4,

       .pattern= mirror_pattern

};

 

/* Define some generic bad / goodblock scan pattern which are used

 * while scanning a device for factory markedgood / bad blocks. */

static uint8_t scan_ff_pattern[] = {0xff, 0xff };

 

 

根据flash类型设置好bbt参数后,就要开始扫描坏块了。

       returnnand_scan_bbt(mtd, this->badblock_pattern);

 

/**

 * nand_scan_bbt - [NAND Interface] scan, find,read and maybe create bad block table(s)

 * @mtd:       MTDdevice structure

 * @bd:         descriptorfor the good/bad block search pattern

 *

 * The function checks, if a bad block table(s)is/are already

 * available. If not it scans the device formanufacturer

 * marked good / bad blocks and writes the badblock table(s) to

 * the selected place.

 *

 * The bad block table memory is allocatedhere. It must be freed

 * by calling the nand_free_bbt function.

 *

*/

int nand_scan_bbt(struct mtd_info*mtd, struct nand_bbt_descr *bd)

函数说明已经很详细了,就是如果flash已经存在bbt了,就读取到内存中,如果没有,就扫描全部flash芯片建立bbt;

那最初的bbt从何而来?2种来源,一种是bootloader启动的时候,创建了bbt并保存到flash中;另外就是kernel第一次启动的时候如果没有找到合法的bbt就扫描flash并建立bbt。

       len= mtd->size >> (this->bbt_erase_shift + 2);

       /*Allocate memory (2bit per block) and clear the memory bad block table */

       this->bbt= kzalloc(len, GFP_KERNEL);

对于K9K8G08U0A,len=2048字节,chip总共有8192 blocks,每个block用2个bit表示;

 

       /*Allocate a temporary buffer for one eraseblock incl. oob */

       len= (1 << this->bbt_erase_shift);     //bbt_erase_shift=17,len=128K

       len+= (len >> this->page_shift) * mtd->oobsize; // len = 128k+64*64

       buf= vmalloc(len);

 

下面的代码,是查找flash中是否有bbt,我们没有定义NAND_BBT_ABSPAGE,所以要search;

       /*Is the bbt at a given page ? */

       if(td->options & NAND_BBT_ABSPAGE) {

              res= read_abs_bbts(mtd, buf, td, md);

       }else {

              /*Search the bad block table using a pattern in oob */

              res= search_read_bbts(mtd, buf, td, md);

       }

 

static int search_read_bbts(structmtd_info *mtd, uint8_t * buf, struct nand_bbt_descr *td, struct nand_bbt_descr*md)

{

       /*Search the primary table */

       search_bbt(mtd,buf, td);

 

       /*Search the mirror table */

       if(md)

              search_bbt(mtd,buf, md);

 

       /*Force result check */

       return1;

}

 

static int search_bbt(structmtd_info *mtd, uint8_t *buf, struct nand_bbt_descr *td)

{

       structnand_chip *this = mtd->priv;

       inti, chips;

       intbits, startblock, block, dir;

       intscanlen = mtd->writesize + mtd->oobsize;   //2048+64

       intbbtblocks;

       intblocktopage = this->bbt_erase_shift - this->page_shift;    //17-11

 

       /*Search direction top -> down ? */

       if(td->options & NAND_BBT_LASTBLOCK) {

              startblock= (mtd->size >> this->bbt_erase_shift) - 1;// total blocks-1

              dir= -1;

       }else {

              startblock= 0;

              dir= 1;

       }

默认是将bbt存放在最后一个block的,这里startblock是最后一个block序号8191;

       /*Do we have a bbt per chip ? */

       if(td->options & NAND_BBT_PERCHIP) {

              chips= this->numchips;

              bbtblocks= this->chipsize >> this->bbt_erase_shift;

              startblock&= bbtblocks - 1;

       }else {

              chips= 1;

              bbtblocks= mtd->size >> this->bbt_erase_shift; // total blocks

       }

       /*Number of bits for each erase block in the bbt */

       bits= td->options & NAND_BBT_NRBITS_MSK;

 

下面的代码是2层for循环,分别在每个chip中查找bbt,重点分析如何查找bbt;

/* Scan the maximum number of blocks */

              for(block = 0; block < td->maxblocks; block++) {

                     intactblock = startblock + dir * block;

                     loff_toffs = (loff_t)actblock << this->bbt_erase_shift;

                     /*Read first page */

                     scan_read_raw(mtd,buf, offs, mtd->writesize);

                     if(!check_pattern(buf, scanlen, mtd->writesize, td)) {

                            td->pages[i]= actblock << blocktopage;

                            if(td->options & NAND_BBT_VERSION) {

                                   td->version[i]= buf[mtd->writesize + td->veroffs];

                            }

                            break;

                     }

              }

由于定义了NAND_BBT_LASTBLOCK,所以bbt是存放在每个chip末尾的。
/*

 * Scan read raw data from flash

 */

static int scan_read_raw(structmtd_info *mtd, uint8_t *buf, loff_t offs,

                      size_t len)

{

       structmtd_oob_ops ops;

 

       ops.mode= MTD_OOB_RAW;

       ops.ooboffs= 0;

       ops.ooblen= mtd->oobsize;

       ops.oobbuf= buf;

       ops.datbuf= buf;

       ops.len= len;

 

       returnmtd->read_oob(mtd, offs, &ops);

}

在上一章我们已经分析过read_oob的代码了,在MTD_OOB_RAW模式下,会自动将oob复制到data的末尾,所以,现在buf的起始部分已经填充了第一个page的data和oob了;

接下来是匹配bbt,是通过nand_bbt_descr来匹配的;

                     if(!check_pattern(buf, scanlen, mtd->writesize, td))

       intscanlen = mtd->writesize + mtd->oobsize;

static int check_pattern(uint8_t*buf, int len, int paglen, struct nand_bbt_descr *td)

{

       inti, end = 0;

       uint8_t*p = buf;

 

       end= paglen + td->offs;

       p+= end;

// 略过andflash的处理。。。

       /*Compare the pattern */

       for(i = 0; i < td->len; i++) {

              if(p[i] != td->pattern[i])

                     return-1;

       }

       return0;

}

static uint8_t bbt_pattern[] = {'B','b', 't', '0' };

static uint8_t mirror_pattern[] ={'1', 't', 'b', 'B' };

       .offs=    8,

       .len= 4,

 

从上面的代码就看明白了,是匹配该block的第一个page的oob里面8-11的4个字节是否是td标记;

要注意,这里用的是ROW模式,oob里面是绝对偏移量,不过,还好了,我查看了几种flash的nand_ecclayout,8-11的4个字节都是free的;但nand_oob_8除外!这种小容量的flash也不需要bbt。

匹配到td后,将该block序号保存起来,并记录version;

                            td->pages[i]= actblock << blocktopage;

                            if(td->options & NAND_BBT_VERSION) {

                                   td->version[i]= buf[mtd->writesize + td->veroffs];

                            }

 

回到 nand_scan_bbt,因为search_read_bbts总是return 1,下面会调用

              res= check_create(mtd, buf, bd);

这个函数的功能是

/**

 * check_create - [GENERIC] create and writebbt(s) if necessary

 * @mtd:       MTDdevice structure

 * @buf: temporarybuffer

 * @bd:         descriptorfor the good/bad block search pattern

 *

 * The function checks the results of theprevious call to read_bbt

 * and creates / updates the bbt(s) ifnecessary

 * Creation is necessary if no bbt was foundfor the chip/device

 * Update is necessary if one of the tables ismissing or the

 * version nr. of one table is less than theother

*/

如果在前面的代码里面td和md都没有匹配到,那么就要重新创建 bbt了;

                     if(td->pages[i] == -1 && md->pages[i] == -1) {

                            writeops= 0x03;

                            gotocreate;

                     }

       create:

              /*Create the table in memory by scanning the chip(s) */

              create_bbt(mtd,buf, bd, chipsel);

 

* create_bbt - [GENERIC] Create abad block table by scanning the device

扫描所有的block里面的坏块标记,填充bbt位图,2bits一个block;

如果block是good,2bits是0,如果是bad,2bits是0x3;

this->bbt[i >> 3] |= 0x03<< (i & 0x6);

扫描的策略是由bd->options确定的,

NAND_BBT_SCANALLPAGES是扫描所有的page,只在NAND_IS_AND上使用;

NAND_BBT_SCAN2NDPAGE是扫描前2个page,一般用在大容量的nand flash上;

如果没有指定这2个标志位,就只扫描block的第一个page;

              ret= scan_block_fast(mtd, bd, from, buf, len);

scan_block_fast的代码比较简单,就是读取block的前面1个或2个page的oob,然后匹配badblock_pattern,如果匹配成功,该block是好的,就返回0;

 

回到check_create,如果在前面的search_read_bbts已经查找到了td或md,那么就要比较他们的版本号version,以版本号大的为准,读取bbt到内存中;

              read_abs_bbt(mtd,buf, rd, chipsel);  //->read_bbt

 

static int read_bbt(struct mtd_info*mtd, uint8_t *buf, int page, int num,

                  int bits, int offs, intreserved_block_code)

首先从td指定的page开始读取bbt到buf中,从前面的代码我们可以得知,bbt都是从某个block的起始page 0开始存放的,通常不会超过一个block,所以下面的代码会将flash上的bbt读取到内存中;

              len= min(totlen, (size_t) (1 << this->bbt_erase_shift));

              res= mtd->read(mtd, from, len, &retlen, buf);

接下来的代码是/* Analyse data*/,有个转换,需要注意,具体原因,我们等到write bbt的时候再讲;

从flash里面读到的bbt位图,uint8_t tmp = (dat >> j) & msk;

3 表示是good,对应的this->bbt[offs + (act >> 3)]是0;

if (tmp == msk)

                                   continue;

否则是坏块;

                            /*Factory marked bad or worn out ? */

                            if(tmp == 0)

                                   this->bbt[offs+ (act >> 3)] |= 0x3 << (act & 0x06);

                            else

                                   this->bbt[offs+ (act >> 3)] |= 0x1 << (act & 0x06);

 

在前面的check中,如果td和md的版本号一致,那就不用写bbt到flash了,否则要根据以上的情况调用 write_bbt 重写bbt;重新的依据是writeops,将版本低的bbt重新入flash,使得td和md一致;

write_bbt的流程是,先确定要写入bbt的page,如果flash之前就有相应的bbt,就在原来的page重写;否则要寻找一个block,寻找的策略是如果指定了NAND_BBT_LASTBLOCK就从chip末尾往前找,否则从chip最前面往后找,找到一个good block为止;

一般我们都要指定NAND_BBT_LASTBLOCK,因为flash前面的block要存放uboot之类的启动程序;

要注意的是,有2个bbt,td和md,他们占用不同的block,所以寻找block的时候,不仅要判断block是否good,还要判断是否已经被别的bbt占用了。

下面这段代码就是寻找写入td的block的流程,里面可能会有风险,td->maxblocks被固定成4了,要是flash chip最后面的badblock超过2个了,就无法写入bbt了!建议扩大这个值。

              for(i = 0; i < td->maxblocks; i++) {

                     intblock = startblock + dir * i;

                     /*Check, if the block is bad */

                     switch((this->bbt[block >> 2] >>

                             (2 * (block & 0x03))) & 0x03) {

                     case0x01:

                     case0x03:

                            continue;

                     }

                     page= block <<

                            (this->bbt_erase_shift- this->page_shift);

                     /*Check, if the block is used by the mirror table */

                     if(!md || md->pages[chip] != page)

                            gotowrite;

              }

              printk(KERN_ERR"No space left to write bad block table\n");

              return-ENOSPC;

下面开始写bbt了,略过NAND_BBT_SAVECONTENT

else {

                     /*Calc length */

                     len= (size_t) (numblocks >> sft);

                     /*Make it page aligned ! */

                     len= (len + (mtd->writesize - 1)) &

                            ~(mtd->writesize- 1);

                     /*Preset the buffer with 0xff */

                     memset(buf,0xff, len +

                            (len >> this->page_shift)*mtd->oobsize);

                     offs= 0;

                     ooboffs= len;

                     /*Pattern is located in oob area of first page */

                     memcpy(&buf[ooboffs+ td->offs], td->pattern, td->len);

              }

              if(td->options & NAND_BBT_VERSION)

                     buf[ooboffs+ td->veroffs] = td->version[chip];

上面是填充oob的内容,pattern和version,其余为0xff;

下面是把内存中的this->bbt转换成flash格式,good block,在内存中是0,在flash上是3;

为什么要做这个转换?我猜想可能是因为flasherase后都是0xff,而坏块毕竟是少数,把good置为3,会减少写flash的bit,当然,也可能是因为其他原因:)

              /*walk through the memory table */

              for(i = 0; i < numblocks;) {

                     uint8_tdat;

                     dat= this->bbt[bbtoffs + (i >> 2)];

                     for(j = 0; j < 4; j++, i++) {

                            intsftcnt = (i << (3 - sft)) & sftmsk;

                            /*Do not store the reserved bbt blocks ! */

                            buf[offs+ (i >> sft)] &=

                                   ~(msk[dat& 0x03] << sftcnt);

                            dat>>= 2;

                     }

              }

下一步擦除该块,        res= nand_erase_nand(mtd, &einfo, 1);

这里的调用把allowbbt赋值为1,是允许擦除bbt所在的block,而在上层应用erase block的时候,是不能赋值的,也就是说上层看到的bbt所在的block是坏块,这样会保护bbt,上层应用不会操作到bbt;

该写flash了,              res =scan_write_bbt(mtd, to, len, buf, &buf[len]);

就是把data和oob都写到page里面。

从check_create返回后,我们已经有bbt了;

接下来还有一步,但是因为td->reserved_block_code指定为0了,所以没起什么作用。

* @reserved_block_code: if non-0,this pattern denotes a reserved (rather than

 *             bad) block in the stored bbt

       /*Prevent the bbt regions from erasing / writing */

       mark_bbt_region(mtd,td);

/**

 * mark_bbt_regions - [GENERIC] mark the badblock table regions

 * @mtd:       MTDdevice structure

 * @td:          badblock table descriptor

 *

 * The bad block table regions are marked as"bad" to prevent

 * accidental erasures / writes. The regionsare identified by

 * the mark 0x02.

*/

从上面的注释来看,如果使用了td->reserved_block_code,bbt就会把自己的block标记为0x02,上层就不能操作bbt了,除非指定了allowbb;

现在nand_scan_bbt也执行完毕返回了,nand_default_bbt也就返回了,nand_scan_tail也就返回了:)

代码又回到了nand_davinci_probe,现在bbt已经有了,下一步要创建MTD逻辑分区了,注册MTD设备了。

抱歉!评论已关闭.