Chinaunix首页 | 论坛 | 博客
  • 博客访问: 186217
  • 博文数量: 60
  • 博客积分: 652
  • 博客等级: 上士
  • 技术积分: 460
  • 用 户 组: 普通用户
  • 注册时间: 2010-05-15 14:26
文章分类
文章存档

2015年(4)

2014年(16)

2013年(16)

2011年(20)

2010年(4)

分类: 嵌入式

2010-05-17 18:14:24

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;
}
 
    
阅读(5719) | 评论(0) | 转发(1) |
给主人留下些什么吧!~~