其实现在Linuxkernel的bbt做的也比较简单,就是把整个flash的block在内存里面用2bit位图来标识good/bad,这样,在上层判断一个block是否good时就不需要再去读取flash的oob里面的坏块标记了,只需要读取内存里面的bbt就可以了,这是一个比较重要的优化。
但,我想这只是一个开始,希望将来能够把BBM加入到kernel里面来,让上层不再操心nandflash的坏块和均匀擦写。
当然,是否使用bbt,kernel还是给开发者提供了开关接口的,那就是
而我们在chip信息里面的定义是
也就是定义了使用bbt。
我们现在进入nand_default_bbt(),它在Nand_bbt.c(drivers\mtd\nand)里面;
相关的结构体定义如下,首先关注下我标注的红色注释;
//small page(512B)和large page的nandflash它们的坏块标记存放位置是不一样的,具体参数还是要阅读flash的硬件手册为准,一般sp是第6个字节,lp是前2个字节;
static struct nand_bbt_descrlargepage_flashbased ={
};
static struct nand_bbt_descrsmallpage_flashbased ={
};
static uint8_t bbt_pattern[] = {'B','b', 't', '0'};
static uint8_t mirror_pattern[] ={'1', 't', 'b','B'};
static struct nand_bbt_descrbbt_main_descr = {
};
static struct nand_bbt_descrbbt_mirror_descr ={
};
static uint8_t scan_ff_pattern[] = {0xff, 0xff};
根据flash类型设置好bbt参数后,就要开始扫描坏块了。
int nand_scan_bbt(struct mtd_info*mtd, structnand_bbt_descr *bd)
函数说明已经很详细了,就是如果flash已经存在bbt了,就读取到内存中,如果没有,就扫描全部flash芯片建立bbt;
那最初的bbt从何而来?2种来源,一种是bootloader启动的时候,创建了bbt并保存到flash中;另外就是kernel第一次启动的时候如果没有找到合法的bbt就扫描flash并建立bbt。
对于K9K8G08U0A,len=2048字节,chip总共有8192blocks,每个block用2个bit表示;
下面的代码,是查找flash中是否有bbt,我们没有定义NAND_BBT_ABSPAGE,所以要search;
staticint
{
}
static int search_bbt(structmtd_info *mtd, uint8_t*buf, struct nand_bbt_descr *td)
{
默认是将bbt存放在最后一个block的,这里startblock是最后一个block序号8191;
下面的代码是2层for循环,分别在每个chip中查找bbt,重点分析如何查找bbt;
由于定义了NAND_BBT_LASTBLOCK,所以bbt是存放在每个chip末尾的。
static int scan_read_raw(structmtd_info *mtd,uint8_t *buf, loff_t offs,
{
}
在上一章我们已经分析过read_oob的代码了,在MTD_OOB_RAW模式下,会自动将oob复制到data的末尾,所以,现在buf的起始部分已经填充了第一个page的data和oob了;
接下来是匹配bbt,是通过nand_bbt_descr来匹配的;
static int check_pattern(uint8_t*buf, int len, intpaglen, struct nand_bbt_descr *td)
{
//
}
static uint8_t bbt_pattern[] = {'B','b', 't', '0'};
static uint8_t mirror_pattern[] ={'1', 't', 'b','B' };
从上面的代码就看明白了,是匹配该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和md都没有匹配到,那么就要重新创建
* create_bbt - [GENERIC] Create abad block table byscanning 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,一般用在大容量的nandflash上;
如果没有指定这2个标志位,就只扫描block的第一个page;
scan_block_fast的代码比较简单,就是读取block的前面1个或2个page的oob,然后匹配badblock_pattern,如果匹配成功,该block是好的,就返回0;
回到check_create,如果在前面的search_read_bbts已经查找到了td或md,那么就要比较他们的版本号version,以版本号大的为准,读取bbt到内存中;
static int read_bbt(struct mtd_info*mtd, uint8_t*buf, int page, int num,
首先从td指定的page开始读取bbt到buf中,从前面的代码我们可以得知,bbt都是从某个block的起始page0开始存放的,通常不会超过一个block,所以下面的代码会将flash上的bbt读取到内存中;
接下来的代码是,有个转换,需要注意,具体原因,我们等到write bbt的时候再讲;
从flash里面读到的bbt位图,uint8_t tmp = (dat >> j)& msk;
3
if (tmp == msk)
否则是坏块;
在前面的check中,如果td和md的版本号一致,那就不用写bbt到flash了,否则要根据以上的情况调用
write_bbt的流程是,先确定要写入bbt的page,如果flash之前就有相应的bbt,就在原来的page重写;否则要寻找一个block,寻找的策略是如果指定了NAND_BBT_LASTBLOCK就从chip末尾往前找,否则从chip最前面往后找,找到一个goodblock为止;
一般我们都要指定NAND_BBT_LASTBLOCK,因为flash前面的block要存放uboot之类的启动程序;
要注意的是,有2个bbt,td和md,他们占用不同的block,所以寻找block的时候,不仅要判断block是否good,还要判断是否已经被别的bbt占用了。
下面这段代码就是寻找写入td的block的流程,里面可能会有风险,td->maxblocks被固定成4了,要是flashchip最后面的badblock超过2个了,就无法写入bbt了!建议扩大这个值。
下面开始写bbt了,略过NAND_BBT_SAVECONTENT
else {
上面是填充oob的内容,pattern和version,其余为0xff;
下面是把内存中的this->bbt转换成flash格式,goodblock,在内存中是0,在flash上是3;
为什么要做这个转换?我猜想可能是因为flasherase后都是0xff,而坏块毕竟是少数,把good置为3,会减少写flash的bit,当然,也可能是因为其他原因:)
下一步擦除该块,
这里的调用把allowbbt赋值为1,是允许擦除bbt所在的block,而在上层应用eraseblock的时候,是不能赋值的,也就是说上层看到的bbt所在的block是坏块,这样会保护bbt,上层应用不会操作到bbt;
该写flash了,
就是把data和oob都写到page里面。
从check_create返回后,我们已经有bbt了;
接下来还有一步,但是因为td->reserved_block_code指定为0了,所以没起什么作用。
* @reserved_block_code: if non-0,this patterndenotes a reserved (rather than
从上面的注释来看,如果使用了td->reserved_block_code,bbt就会把自己的block标记为0x02,上层就不能操作bbt了,除非指定了allowbb;
现在nand_scan_bbt也执行完毕返回了,nand_default_bbt也就返回了,nand_scan_tail也就返回了:)
代码又回到了nand_davinci_probe,现在bbt已经有了,下一步要创建MTD逻辑分区了,注册MTD设备了。
联系客服