加入收藏 | 设为首页 | 会员中心 | 我要投稿 李大同 (https://www.lidatong.com.cn/)- 科技、建站、经验、云计算、5G、大数据,站长网!
当前位置: 首页 > 百科 > 正文

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

发布时间:2020-12-15 17:55:45 所属栏目:百科 来源:网络整理
导读:其实现在Linux kernel的bbt做的也比较简单,就是把整个flash的block在内存里面用2bit位图来标识good/bad,这样,在上层判断一个block是否good时就不需要再去读取flash的oob里面的坏块标记了,只需要读取内存里面的bbt就可以了,这是一个比较重要的优化。 但

其实现在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 (driversmtdnand)里面;

/* 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 = {

?????? .offs= 5,51)">?????? .len= 1,51)">?????? .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','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,51)">?????? .offs=??? 8,51)">?????? .len= 4,51)">?????? .veroffs= 12,51)">?????? .maxblocks= 4,51)">?????? .pattern= bbt_pattern

static struct nand_bbt_descrbbt_mirror_descr = {

?????? .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,51)">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,td);

?????? /*Search the mirror table */

?????? if(md)

????????????? search_bbt(mtd,51)">?????? /*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;

????????????? 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;

????????????? 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,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,loff_t offs,51)">??????????????????????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,&ops);

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

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

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

static int check_pattern(uint8_t*buf,int len,int paglen,51)">???????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;

???????.offs=????8,51)">???????.len= 4,51)">从上面的代码就看明白了,是匹配该block的第一个page的oob里面8-11的4个字节是否是td标记;

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

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

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

??????????????res= check_create(mtd,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,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,from,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,rd,chipsel);??//->read_bbt

static int read_bbt(struct mtd_info*mtd,int page,int num,51)">??????????????????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,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);

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

????????????????????????????gotowrite;

??????????????printk(KERN_ERR"No space left to write bad block tablen");

??????????????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,&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,51)">?* mark_bbt_regions - [GENERIC] mark the badblock table regions

?* @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设备了。

(编辑:李大同)

【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!

    推荐文章
      热点阅读