nand bad block检测分两个部分:1.bootloader;2.kernel
一。bootloader(u-boot)中的坏块检测
1.总体思路:分别在nand erase,nand read,nand write的时候如果检测到有坏块就跳过,对应的函数分别为:nand_erase_nand,nand_read_ecc,nand_write_ecc;下面只是截取改动的关键部分
//进入擦读写loop
while(len) {
//判断是否超出nand flash边界
ofs = ((loff_t)page << this->page_shift;
if (ofs >= mtd->size) {
#ifdef NAND_NO_IGNORE_ERASE_SIZE
instr->state = MTD_ERASE_FAILED;
#else
instr->state = MTD_EARSE_DONE;
#endif
DEBUG(MTD_DEBUG_LEVEL0, "(%s): out of nand mtd flash bundary\n", __FUNCTION__);
goto erase_exit;
}
if(mtd->block_isbad(mtd,ofs)) {
DEBUG(MTD_DEBUG_LEVEL0, "(%s):Skipping bad block at ox%08x\n",__FUNCTION__,ofs);
page+= pages_per_block;
continue;
}
//擦写过程中遇到坏块
/* See if block erase succeeded */
if (status & 0x01) {
DEBUG (MTD_DEBUG_LEVEL0, "nand_erase: " "Failed erase, page 0x%08x, block is 0x%08x,added to bad block! \n", page,((loff_t)page) >> 5);
nand_default_block_markbad(mtd, ofs);
}
2.遇到的最大问题: 驱动程序读写data区和oob区的时候数据不对,后来查明是在发出读写命令后没有等待就直接调用read_buf,在之间加入udelay(this->chip_delay)就OK啦!
3. bbt table的产生
函数create_bbt()分析:四个block的坏块信息存在一个this->bbt[]数组里二进制的11表示其中一个是坏块,例如:this->bbt[256]=3f表示有三个坏块,分别是block 1024,1025,1026
for (i = startblock; i < numblocks;) {
nand_read_raw (mtd, buf, from, readlen, ooblen);
for (j = 0; j < len; j++) {
if (check_pattern (&buf[j * scanlen], scanlen, mtd->oobblock, bd)) {
this->bbt[i >> 3] |= 0x03 << (i & 0x6);
printk (KERN_WARNING "Bad eraseblock %d at 0x%08x\n",
i >> 1, (unsigned int) from);
break;
}
}
i += 2;
from += (1 << this->bbt_erase_shift);
}
4.函数nand_isbad_bbt()
int nand_isbad_bbt (struct mtd_info *mtd, loff_t offs, int allowbbt)
{
struct nand_chip *this = mtd->priv;
int block;
uint8_t res;
/* Get block number * 2 */
block = (int) (offs >> (this->bbt_erase_shift - 1));
res = (this->bbt[block >> 3] >> (block & 0x06)) & 0x03;
DEBUG (MTD_DEBUG_LEVEL3, "nand_isbad_bbt(): bbt info for offs 0x%08x: (block %d) 0x%02x\n",
(unsigned int)offs, res, block >> 1);
switch ((int)res) {
case 0x00: return 0;
case 0x01: return 1;
case 0x02: return allowbbt ? 0 : 1;
}
return 1;
}
二。kernel
static int do_cached_write (struct mtdblk_dev *mtdblk, unsigned long pos,
int len, const char *buf)
{
struct mtd_info *mtd = mtdblk->mtd;
unsigned int sect_size = mtdblk->cache_size;
size_t retlen;
int ret;
DEBUG(MTD_DEBUG_LEVEL2, "mtdblock: write on \"%s\" at 0x%1x, size 0x%x, sect_size=0x%x\n",
mtd->name, pos+(mtd->current_badblock_num*sect_size), len, sect_size);
//新的写入数据不能超过nand flash 的边界
if( pos+(mtd->current_badblock_num*sect_size) + len > mtd->size) {
DEBUG(MTD_DEBUG_LEVEL0, "pos at 0x%1x,not enough space left on mtd block!\n", pos+(mtd->current_badblock_num*sect_size));
return 0;
}
//跳过坏块,
if (strstr(mtd->name,"rootfs") && mtd->block_isbad(mtd, pos+(mtd->current_badblock_num*sect_size))) {
DEBUG(MTD_DEBUG_LEVEL0, "bad block at 0x%1x,current_badblock_num=%d\n", pos+(mtd->current_badblock_num*sect_size), mtd->current_badblock_num);
mtd->current_badblock_num++;
}
if (!sect_size)
return MTD_WRITE (mtd, pos+(mtd->current_badblock_num*sect_size), len, &retlen, buf);
while (len > 0) {
unsigned long sect_start = ((pos+(mtd->current_badblock_num*sect_size))/sect_size)*sect_size;
unsigned int offset = pos+(mtd->current_badblock_num*sect_size) - sect_start;
unsigned int size = sect_size - offset;
if( size > len )
size = len;
if (size == sect_size) { //一次写入一个block
/*
* We are covering a whole sector. Thus there is no
* need to bother with the cache while it may still be
* useful for other partial writes.
*/
ret = erase_write (mtd, pos+(mtd->current_badblock_num*sect_size), size, buf);
if (ret)
return ret;
} else {
/* Partial sector: need to use the cache */
//清空cache里的数据,不够一个block.
if (mtdblk->cache_state == STATE_DIRTY &&
mtdblk->cache_offset != sect_start) {
ret = write_cached_data(mtdblk);
if (ret)
return ret;
}
//写入一个新的block前,先读出此block的数据,放入cache中
if (mtdblk->cache_state == STATE_EMPTY ||
mtdblk->cache_offset != sect_start) {
/* fill the cache with the current sector */
mtdblk->cache_state = STATE_EMPTY;
ret = MTD_READ(mtd, sect_start, sect_size, &retlen, mtdblk->cache_data);
if (ret)
return ret;
if (retlen != sect_size)
return -EIO;
mtdblk->cache_offset = sect_start;
mtdblk->cache_size = sect_size;
mtdblk->cache_state = STATE_CLEAN;
}
/* write data to our local cache */
memcpy (mtdblk->cache_data + offset, buf, size);
mtdblk->cache_state = STATE_DIRTY;
}
buf += size;
pos += size;
len -= size;
}
return 0;
}