MTD(4)---nand flash的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;?????//?这里是针对sp和lp不同的flash,设置不同的bbt参数; ?????? } 相关的结构体定义如下,首先关注下我标注的红色注释; //small page(512B)和large page的nand flash它们的坏块标记存放位置是不一样的,具体参数还是要阅读flash的硬件手册为准,一般sp是第6个字节,lp是前2个字节; static struct nand_bbt_descrlargepage_flashbased = { ?????? .options= NAND_BBT_SCAN2NDPAGE,??//?扫描坏块标记时要扫描每个block的前2个page; ?????? .offs= 0,????????//?坏块标记存放在每个page的oob里面,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' };????????????//通常使用2个bbt,这是主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设备了。 (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |