/* command and control functions * * Note, these all use tglx's method of changing the IO_ADDR_W field * to make the code simpler, and use the nand layer's code to issue the * command and address sequences via the proper IO ports. * */
/* Print and store flash device information */ for (i = 0; nand_flash_ids[i].name != NULL; i++) { //保存着nand flash资料的nand_flash_ids表在include/linux/mtd/nand_ids.c文件中,详细见附录 if (nand_dev_id != nand_flash_ids[i].id) //比较设备ID continue;
if (!mtd->name) mtd->name = nand_flash_ids[i].name; //填充设备名 this->chipsize = nand_flash_ids[i].chipsize << 20; //填充设备大小 /* New devices have all the information in additional id bytes */ if (!nand_flash_ids[i].pagesize) { int extid; /* The 3rd id byte contains non relevant data ATM */ extid = this->read_byte(mtd); /* The 4th id byte is the important one */ extid = this->read_byte(mtd); /* Calc pagesize */ mtd->oobblock = 1024 << (extid & 0x3); extid >>= 2; /* Calc oobsize */ mtd->oobsize = (8 << (extid & 0x03)) * (mtd->oobblock / 512); extid >>= 2; /* Calc blocksize. Blocksize is multiples of 64KiB */ mtd->erasesize = (64 * 1024) << (extid & 0x03); extid >>= 2; /* Get buswidth information */ busw = (extid & 0x01) ? NAND_BUSWIDTH_16 : 0; } else { /* Old devices have this data hardcoded in the * device id table */ mtd->erasesize = nand_flash_ids[i].erasesize; //填充檫除单元大小(16k) mtd->oobblock = nand_flash_ids[i].pagesize; //填充页大小(512) mtd->oobsize = mtd->oobblock / 32; //oob大小(512/32=16) busw = nand_flash_ids[i].options & NAND_BUSWIDTH_16;//获取nand flash表中定义的位宽 }
/* Try to identify manufacturer */ //比较生产商ID for (maf_id = 0; nand_manuf_ids[maf_id].id != 0x0; maf_id++) { if (nand_manuf_ids[maf_id].id == nand_maf_id) break; }
/* Check, if buswidth is correct. Hardware drivers should set * this correct ! */ /用户定义的位宽与芯片实际的位宽不一致,取消nand flash的片选 if (busw != (this->options & NAND_BUSWIDTH_16)) { printk (KERN_INFO "NAND device: Manufacturer ID:" " 0x%02x, Chip ID: 0x%02x (%s %s)\n", nand_maf_id, nand_dev_id, nand_manuf_ids[maf_id].name , mtd->name); printk (KERN_WARNING "NAND bus width %d instead %d bit\n", (this->options & NAND_BUSWIDTH_16) ? 16 : 8, busw ? 16 : 8); this->select_chip(mtd, -1);//在s3c2410 nand flash控制器驱动中,此操作为空操作 return 1; } /* Calculate the address shift from the page size */ //计算页、可檫除单元、nand flash大小的偏移值 this->page_shift = ffs(mtd->oobblock) - 1; this->bbt_erase_shift = this->phys_erase_shift = ffs(mtd->erasesize) - 1; this->chip_shift = ffs(this->chipsize) - 1;
/* Set the bad block position */ //标注此nand flash为大页还是小页? this->badblockpos = mtd->oobblock > 512 ? NAND_LARGE_BADBLOCK_POS : NAND_SMALL_BADBLOCK_POS;
/* Get chip options, preserve non chip based options */ //用户没指定的选项从nand flash表中获取补上 this->options &= ~NAND_CHIPOPTIONS_MSK; this->options |= nand_flash_ids[i].options & NAND_CHIPOPTIONS_MSK; /* Set this as a default. Board drivers can override it, if neccecary */ this->options |= NAND_NO_AUTOINCR; /* Check if this is a not a samsung device. Do not clear the options * for chips which are not having an extended id. */ if (nand_maf_id != NAND_MFR_SAMSUNG && !nand_flash_ids[i].pagesize) this->options &= ~NAND_SAMSUNG_LP_OPTIONS; /* Check for AND chips with 4 page planes */ if (this->options & NAND_4PAGE_ARRAY) this->erase_cmd = multi_erase_cmd; else this->erase_cmd = single_erase_cmd;
/* Do not replace user supplied command function ! */ if (mtd->oobblock > 512 && this->cmdfunc == nand_command) this->cmdfunc = nand_command_lp; printk (KERN_INFO "NAND device: Manufacturer ID:" " 0x%02x, Chip ID: 0x%02x (%s %s)\n", nand_maf_id, nand_dev_id, nand_manuf_ids[maf_id].name , nand_flash_ids[i].name); break; }//好的,检测结束^_^
if (!nand_flash_ids[i].name) { printk (KERN_WARNING "No NAND device found!!!\n"); this->select_chip(mtd, -1); return 1; }
//统计一下同种类型的nand flash有多少块(我板上只有一块) for (i=1; i < maxchips; i++) { this->select_chip(mtd, i);
/* Send the command for reading device ID */ this->cmdfunc (mtd, NAND_CMD_READID, 0x00, -1);
/* Read manufacturer and device IDs */ if (nand_maf_id != this->read_byte(mtd) || nand_dev_id != this->read_byte(mtd)) break; } if (i > 1) printk(KERN_INFO "%d NAND chips detected\n", i); /* Allocate buffers, if neccecary */ if (!this->oob_buf) { size_t len; //求出一个檫除单元64K中oob所占用的总空间 len = mtd->oobsize << (this->phys_erase_shift - this->page_shift); this->oob_buf = kmalloc (len, GFP_KERNEL); if (!this->oob_buf) { printk (KERN_ERR "nand_scan(): Cannot allocate oob_buf\n"); return -ENOMEM; } this->options |= NAND_OOBBUF_ALLOC;//oob空间已分配,置相应的标志位 } if (!this->data_buf) { size_t len; len = mtd->oobblock + mtd->oobsize;//512+16=128 this->data_buf = kmalloc (len, GFP_KERNEL); if (!this->data_buf) { if (this->options & NAND_OOBBUF_ALLOC) kfree (this->oob_buf); printk (KERN_ERR "nand_scan(): Cannot allocate data_buf\n"); return -ENOMEM; } this->options |= NAND_DATABUF_ALLOC;//数据空间已分配,置相应的标志位 }
/* Store the number of chips and calc total size for mtd */ this->numchips = i;//记录nand flash片数 mtd->size = i * this->chipsize;//计算出nand flash总大小 /* Convert chipsize to number of pages per chip -1. */ this->pagemask = (this->chipsize >> this->page_shift) - 1;//(64M>>9)-1=128k-1=0x1ffff
/* If no default placement scheme is given, select an * appropriate one */ if (!this->autooob) { //我们选用的是NAND_ECC_SOFT,autooob未设置 /* Select the appropriate default oob placement scheme for * placement agnostic filesystems */ switch (mtd->oobsize) { case 8: this->autooob = &nand_oob_8; break; case 16: this->autooob = &nand_oob_16;//我们的nand flash属于这一类 break; case 64: this->autooob = &nand_oob_64; break; default: printk (KERN_WARNING "No oob scheme defined for oobsize %d\n", mtd->oobsize); BUG(); } } 注: ECC的东西不是很懂,先跳过^_^
/* The number of bytes available for the filesystem to place fs dependend * oob data */ mtd->oobavail = 0; for (i = 0; this->autooob->oobfree[i][1]; i++) mtd->oobavail += this->autooob->oobfree[i][1];
/* * check ECC mode, default to software * if 3byte/512byte hardware ECC is selected and we have 256 byte pagesize * fallback to software ECC */ this->eccsize = 256; /* set default eccsize */ this->eccbytes = 3;
switch (this->eccmode) { case NAND_ECC_HW12_2048: if (mtd->oobblock < 2048) { printk(KERN_WARNING "2048 byte HW ECC not possible on %d byte page size, fallback to SW ECC\n", mtd->oobblock); this->eccmode = NAND_ECC_SOFT; this->calculate_ecc = nand_calculate_ecc; this->correct_data = nand_correct_data; } else this->eccsize = 2048; break;
case NAND_ECC_HW3_512: case NAND_ECC_HW6_512: case NAND_ECC_HW8_512: if (mtd->oobblock == 256) { printk (KERN_WARNING "512 byte HW ECC not possible on 256 Byte pagesize, fallback to SW ECC \n"); this->eccmode = NAND_ECC_SOFT; this->calculate_ecc = nand_calculate_ecc; this->correct_data = nand_correct_data; } else this->eccsize = 512; /* set eccsize to 512 */ break; case NAND_ECC_HW3_256: break; case NAND_ECC_NONE: printk (KERN_WARNING "NAND_ECC_NONE selected by board driver. This is not recommended !!\n"); this->eccmode = NAND_ECC_NONE; break;
case NAND_ECC_SOFT: this->calculate_ecc = nand_calculate_ecc; this->correct_data = nand_correct_data; break;
/* Check hardware ecc function availability and adjust number of ecc bytes per * calculation step */ switch (this->eccmode) { case NAND_ECC_HW12_2048: this->eccbytes += 4; case NAND_ECC_HW8_512: this->eccbytes += 2; case NAND_ECC_HW6_512: this->eccbytes += 3; case NAND_ECC_HW3_512: case NAND_ECC_HW3_256: if (this->calculate_ecc && this->correct_data && this->enable_hwecc) break; printk (KERN_WARNING "No ECC functions supplied, Hardware ECC not possible\n"); BUG(); } mtd->eccsize = this->eccsize; /* Set the number of read / write steps for one page to ensure ECC generation */ switch (this->eccmode) { case NAND_ECC_HW12_2048: this->eccsteps = mtd->oobblock / 2048; break; case NAND_ECC_HW3_512: case NAND_ECC_HW6_512: case NAND_ECC_HW8_512: this->eccsteps = mtd->oobblock / 512; break; case NAND_ECC_HW3_256: case NAND_ECC_SOFT: this->eccsteps = mtd->oobblock / 256; break; case NAND_ECC_NONE: this->eccsteps = 1; break; } /* Initialize state, waitqueue and spinlock */ this->state = FL_READY; init_waitqueue_head (&this->wq); spin_lock_init (&this->chip_lock);
/* De-select the device */ this->select_chip(mtd, -1);
/* Invalidate the pagebuffer reference */ this->pagebuf = -1;
/* and make the autooob the default one */ memcpy(&mtd->oobinfo, this->autooob, sizeof(mtd->oobinfo));
mtd->owner = THIS_MODULE; /* Check, if we should skip the bad block table scan */ if (this->options & NAND_SKIP_BBTSCAN) return 0;
/* Build bad block table */ return this->scan_bbt (mtd); }
/** * nand_command - [DEFAULT] Send command to NAND device * @mtd: MTD device structure * @command: the command to be sent * @column: the column address for this command, -1 if none * @page_addr: the page address for this command, -1 if none * * Send command to NAND device. This function is used for small page * devices (256/512 Bytes per page) */ static void nand_command (struct mtd_info *mtd, unsigned command, int column, int page_addr) { register struct nand_chip *this = mtd->priv;
/* Begin command latch cycle */ this->hwcontrol(mtd, NAND_CTL_SETCLE); //选择写入S3C2410_NFCMD寄存器 /* * Write out the command to the device. */ if (command == NAND_CMD_SEQIN) { int readcmd;
/* Serially input address */ if (column != -1) { /* Adjust columns for 16 bit buswidth */ if (this->options & NAND_BUSWIDTH_16) column >>= 1; this->write_byte(mtd, column); //写入列地址 } if (page_addr != -1) { //写入页地址(分三个字节写入) this->write_byte(mtd, (unsigned char) (page_addr & 0xff)); this->write_byte(mtd, (unsigned char) ((page_addr >> 8) & 0xff)); /* One more address cycle for devices > 32MiB */ if (this->chipsize > (32 << 20)) this->write_byte(mtd, (unsigned char) ((page_addr >> 16) & 0x0f)); } /* Latch in address */ /* 锁存地址 */ this->hwcontrol(mtd, NAND_CTL_CLRALE); }
/* * program and erase have their own busy handlers * status and sequential in needs no delay */ switch (command) { case NAND_CMD_PAGEPROG: case NAND_CMD_ERASE1: case NAND_CMD_ERASE2: case NAND_CMD_SEQIN: case NAND_CMD_STATUS: return;
case NAND_CMD_RESET: //复位操作 // 等待nand flash become ready if (this->dev_ready) //判断nand flash 是否busy(1:ready 0:busy) break; udelay(this->chip_delay); this->hwcontrol(mtd, NAND_CTL_SETCLE); this->write_byte(mtd, NAND_CMD_STATUS); this->hwcontrol(mtd, NAND_CTL_CLRCLE); while ( !(this->read_byte(mtd) & NAND_STATUS_READY)); return;
/* This applies to read commands */ default: /* * If we don't have access to the busy pin, we apply the given * command delay */ if (!this->dev_ready) { udelay (this->chip_delay);//稍作延迟 return; } } /* Apply this short delay always to ensure that we do wait tWB in * any case on any machine. */ ndelay (100);
nand_wait_ready(mtd); }
/* * Wait for the ready pin, after a command * The timeout is catched later. */ static void nand_wait_ready(struct mtd_info *mtd) { struct nand_chip *this = mtd->priv; unsigned long timeo = jiffies + 2;
/* wait until command is processed or timeout occures */ do { if (this->dev_ready(mtd)) //简单调用this->dev_ready(s3c2410_nand_devready)函数 等待nand flash become ready return; touch_softlockup_watchdog(); } while (time_before(jiffies, timeo)); }
/** * nand_wait - [DEFAULT] wait until the command is done * @mtd: MTD device structure * @this: NAND chip structure * @state: state to select the max. timeout value * * Wait for command done. This applies to erase and program only * Erase can take up to 400ms and program up to 20ms according to * general NAND and SmartMedia specs * */ /* 等待知道命令传输完成,适用于檫除和写入命令 */ static int nand_wait(struct mtd_info *mtd, struct nand_chip *this, int state) {
unsigned long timeo = jiffies; int status; if (state == FL_ERASING) timeo += (HZ * 400) / 1000;//檫除操作的话,时间相对要长一些 else timeo += (HZ * 20) / 1000;
/* Apply this short delay always to ensure that we do wait tWB in * any case on any machine. */ ndelay (100);
while (time_before(jiffies, timeo)) { /* Check, if we were interrupted */ if (this->state != state) return 0; /* 等待nand flash become ready */ if (this->dev_ready) { if (this->dev_ready(mtd)) break; } else { if (this->read_byte(mtd) & NAND_STATUS_READY) break; } cond_resched(); } status = (int) this->read_byte(mtd); return status; }
/** * nand_block_bad - [DEFAULT] Read bad block marker from the chip * 检查nand flash中某一页是否为坏块 * @mtd: MTD device structure * @ofs: offset from device start * @getchip: 0, if the chip is already selected * * Check, if the block is bad. */ static int nand_block_bad(struct mtd_info *mtd, loff_t ofs, int getchip) { int page, chipnr, res = 0; struct nand_chip *this = mtd->priv; u16 bad;
if (this->options & NAND_BUSWIDTH_16) { this->cmdfunc (mtd, NAND_CMD_READOOB, this->badblockpos & 0xFE, page & this->pagemask); bad = cpu_to_le16(this->read_word(mtd)); if (this->badblockpos & 0x1) bad >>= 1; if ((bad & 0xFF) != 0xff) res = 1; } else { this->cmdfunc (mtd, NAND_CMD_READOOB, this->badblockpos, page & this->pagemask); /* 发送读oob_data命令(oob_data的badblockpos (第6)位记录着坏块标志) */ if (this->read_byte(mtd) != 0xff)//坏块 res = 1; } if (getchip) { /* Deselect and wake up anyone waiting on the device */ nand_release_device(mtd); } return res; }
/** * nand_default_block_markbad - [DEFAULT] mark a block bad * 标志坏块 * @mtd: MTD device structure * @ofs: offset from device start * * This is the default implementation, which can be overridden by * a hardware specific driver. */ static int nand_default_block_markbad(struct mtd_info *mtd, loff_t ofs) { struct nand_chip *this = mtd->priv; u_char buf[2] = {0, 0}; size_t retlen; int block; /* Get block number */ block = ((int) ofs) >> this->bbt_erase_shift; if (this->bbt) this->bbt[block >> 2] |= 0x01 << ((block & 0x03) << 1); /* 这个暂时不是很好说:内核维护一个标志bad block表,使用2bit来表示1block。 这个表在开机的时候通过扫描nand flash每个block的头两页的oob数据来生成, 发现坏块后至相应的block标志位为非零(有时候至3,但有时候至1,还没搞明白有什么不同) */
/* Do we have a flash based bad block table ? */ if (this->options & NAND_USE_FLASH_BBT)//samsun nand flash不属于这种,暂时不去研究,以后同 return nand_update_bbt (mtd, ofs); /* We write two bytes, so we dont have to mess with 16 bit access */ ofs += mtd->oobsize + (this->badblockpos & ~0x01);//??????????????? return nand_write_oob (mtd, ofs , 2, &retlen, buf); }
/** * nand_verify_buf - [DEFAULT] Verify chip data against buffer * 检验nand flash与buffer的数据是否一致 * @mtd: MTD device structure * @buf: buffer containing the data to compare * @len: number of bytes to compare * * Default verify function for 8bit buswith */ static int nand_verify_buf(struct mtd_info *mtd, const u_char *buf, int len) { int i; struct nand_chip *this = mtd->priv;
for (i=0; i if (buf[i] != readb(this->IO_ADDR_R)) return -EFAULT;
return 0; }
/** * nand_default_bbt - [NAND Interface] Select a default bad block table for the device * @mtd: MTD device structure * * This function selects the default bad block table * support for the device and calls the nand_scan_bbt function * */ int nand_default_bbt (struct mtd_info *mtd) { struct nand_chip *this = mtd->priv; /* Default for AG-AND. We must use a flash based * bad block table as the devices have factory marked * _good_ blocks. Erasing those blocks leads to loss * of the good / bad information, so we _must_ store * this information in a good / bad table during * startup */ if (this->options & NAND_IS_AND) { /* Use the default pattern descriptors */ if (!this->bbt_td) { this->bbt_td = &bbt_main_descr; this->bbt_md = &bbt_mirror_descr; } this->options |= NAND_USE_FLASH_BBT; return nand_scan_bbt (mtd, &agand_flashbased); } /* Is a flash based bad block table requested ? */ 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->oobblock > 512) ? &largepage_flashbased : &smallpage_flashbased; } } else { //samsun nand flash的坏块表不存在与nand flash里面,需要扫描来生成。 this->bbt_td = NULL; this->bbt_md = NULL; if (!this->badblock_pattern) { this->badblock_pattern = (mtd->oobblock > 512) ? &largepage_memorybased : &smallpage_memorybased; } } return nand_scan_bbt (mtd, this->badblock_pattern); }
/** * nand_scan_bbt - [NAND Interface] scan, find, read and maybe create bad block table(s) * @mtd: MTD device structure * @bd: descriptor for 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 for manufacturer * marked good / bad blocks and writes the bad block table(s) to * the selected place. * * The bad block table memory is allocated here. It must be freed * by calling the nand_free_bbt function. * */ int nand_scan_bbt (struct mtd_info *mtd, struct nand_bbt_descr *bd) { struct nand_chip *this = mtd->priv; int len, res = 0; uint8_t *buf; struct nand_bbt_descr *td = this->bbt_td; struct nand_bbt_descr *md = this->bbt_md;
len = mtd->size >> (this->bbt_erase_shift + 2); /* Allocate memory (2bit per block) */ /* 2bit per block=(2/8)byte per block,所以上面要多右移2位 */ this->bbt = kmalloc (len, GFP_KERNEL); if (!this->bbt) { printk (KERN_ERR "nand_scan_bbt: Out of memory\n"); return -ENOMEM; } /* Clear the memory bad block table */ memset (this->bbt, 0x00, len);
/* If no primary table decriptor is given, scan the device * to build a memory based bad block table */ if (!td) { if ((res = nand_memory_bbt(mtd, bd))) { printk (KERN_ERR "nand_bbt: Can't scan flash and build the RAM-based BBT\n"); kfree (this->bbt); this->bbt = NULL; } return res; }
/* Allocate a temporary buffer for one eraseblock incl. oob */ /* 分配1 block所需要的oob data空间 */ len = (1 << this->bbt_erase_shift); len += (len >> this->page_shift) * mtd->oobsize; buf = kmalloc (len, GFP_KERNEL); if (!buf) { printk (KERN_ERR "nand_bbt: Out of memory\n"); kfree (this->bbt); this->bbt = NULL; return -ENOMEM; } //由于td、md均为NULL,一下函数基本不起作用,先不去研究它 /* 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); }
if (res) res = check_create (mtd, buf, bd); /* Prevent the bbt regions from erasing / writing */ mark_bbt_region (mtd, td); if (md) mark_bbt_region (mtd, md); kfree (buf); return res; }
/** * nand_memory_bbt - [GENERIC] create a memory based bad block table * @mtd: MTD device structure * @bd: descriptor for the good/bad block search pattern * * The function creates a memory based bbt by scanning the device * for manufacturer / software marked good / bad blocks */ static inline int nand_memory_bbt (struct mtd_info *mtd, struct nand_bbt_descr *bd) { struct nand_chip *this = mtd->priv;
/** * create_bbt - [GENERIC] Create a bad block table by scanning the device * @mtd: MTD device structure * @buf: temporary buffer * @bd: descriptor for the good/bad block search pattern * @chip: create the table for a specific chip, -1 read all chips. * Applies only if NAND_BBT_PERCHIP option is set * * Create a bad block table by scanning the device * for the given good/bad block identify pattern */ static int create_bbt (struct mtd_info *mtd, uint8_t *buf, struct nand_bbt_descr *bd, int chip) { struct nand_chip *this = mtd->priv; int i, j, numblocks, len, scanlen; int startblock; loff_t from; size_t readlen, ooblen;
printk (KERN_INFO "Scanning device for bad blocks\n");
if (bd->options & NAND_BBT_SCANALLPAGES)//扫描所有都页 len = 1 << (this->bbt_erase_shift - this->page_shift);//求出每block所含的page数 else { if (bd->options & NAND_BBT_SCAN2NDPAGE)//只检查2 page len = 2; else len = 1;//只检查1 page }
if (!(bd->options & NAND_BBT_SCANEMPTY)) { /* We need only read few bytes from the OOB area */ /* 我们只需要检查OOB的某些数据 */ scanlen = ooblen = 0; readlen = bd->len; } else { /* Full page content should be read */ /* 读取整页内容 */ scanlen = mtd->oobblock + mtd->oobsize; readlen = len * mtd->oobblock; ooblen = len * mtd->oobsize; }
if (chip == -1) { /* Note that numblocks is 2 * (real numblocks) here, see i+=2 below as it * makes shifting and masking less painful */ /* 计算出nand flash所包含都block数目(注意这里总数目经过林乘2操作)*/ numblocks = mtd->size >> (this->bbt_erase_shift - 1); startblock = 0; from = 0; } else { if (chip >= this->numchips) { printk (KERN_WARNING "create_bbt(): chipnr (%d) > available chips (%d)\n", chip + 1, this->numchips); return -EINVAL; } numblocks = this->chipsize >> (this->bbt_erase_shift - 1); startblock = chip * numblocks; numblocks += startblock; from = startblock << (this->bbt_erase_shift - 1); } for (i = startblock; i < numblocks;) { int ret; if (bd->options & NAND_BBT_SCANEMPTY) //整页数据读取 if ((ret = nand_read_raw (mtd, buf, from, readlen, ooblen))) return ret;
for (j = 0; j < len; j++) { if (!(bd->options & NAND_BBT_SCANEMPTY)) { size_t retlen; /* Read the full oob until read_oob is fixed to * handle single byte reads for 16 bit buswidth */ /* 读取当前页的oob区的所有数据 */ ret = mtd->read_oob(mtd, from + j * mtd->oobblock, mtd->oobsize, &retlen, buf); if (ret) return ret; /* 检查oob data的bad block标志位,判断是否是坏块 */ if (check_short_pattern (buf, bd)) { this->bbt[i >> 3] |= 0x03 << (i & 0x6); /* 注意:这里i=实际值*2。由于一个block的状态用2bit来表示,那么一个字节可以存放4个block的状态。 这里i>>3刚好是实际block/4,4个block的状态刚好存放在this->bbt所指向的一个字节里面 */ printk (KERN_WARNING "Bad eraseblock %d at 0x%08x\n", i >> 1, (unsigned int) from); break; } } else { 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;//更新block的序号 from += (1 << this->bbt_erase_shift);//更新nand flash的地址 } return 0; }
/** * nand_release - [NAND Interface] Free resources held by the NAND device * @mtd: MTD device structure */ void nand_release (struct mtd_info *mtd) { struct nand_chip *this = mtd->priv;