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 = { ?????? .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' };??????????? //通常使用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, ?????? .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, ?????? .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,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,td); ? ?????? /*Search the mirror table */ ?????? if(md) ????????????? search_bbt(mtd,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,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, ???????????????????? ?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来匹配的; ???????????????????? if(!check_pattern(buf,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','0' }; static uint8_t mirror_pattern[] ={'1','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,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, ????????????? ??? 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); ???????????????????? /*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 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,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设备了。 (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |