分类: LINUX
2013-07-13 15:24:47
其实现在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; // 这里是针对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', '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设备了。
http://blog.csdn.net/lidehua1975/article/details/7703488