下面分析nand_scan函数(在文件driver/mtd/nand/nand_base.c中,此文件提供一些默认的nand flash操作函数) ,这个函数主要 用默认函数填充一些未初始化的函数指针,读取flash id 使用默认数据填充mtd,chip相关结构体(如果板级文件没有提供相关支持)。此函数的分析直接写在代码注释中。
/**
* nand_scan - [NAND Interface] Scan for the NAND device
* @mtd:
MTD device structure
* @maxchips:
Number of chips to scan for
*
* This fills out all the not initialized function pointers
* with the defaults.
* The flash ID is read and the mtd/chip structures are
* filled with the appropriate values. Buffers are allocated if
* they are not provided by the board driver
*
*/
int nand_scan (struct mtd_info *mtd,int maxchips)
{
int i,j,nand_maf_id,nand_dev_id,busw;
/* Get buswidth to select the correct functions*/
busw = this->options & NAND_BUSWIDTH_16;
//=0
/* check for proper chip_delay setup,set 20us if not */
if (!this->chip_delay)
this->chip_delay = 20;
//延时默认20毫秒
/* check,if a user supplied command function given */
if (this->cmdfunc == NULL)
//在board_nand_init中设置为
sep4020_nand_command
this->cmdfunc = nand_command;
/* check,if a user supplied wait function given */
if (this->waitfunc == NULL)
if (!this->select_chip)
//在board_nand_init中设置为 sep4020_nand_select_chip;
this->select_chip = nand_select_chip;
if (!this->write_byte)
//在board_nand_init中设置为 sep4020_nand_write_byte;
this->write_byte = busw ? nand_write_byte16 : nand_write_byte;
if (!this->read_byte)
//在board_nand_init中设置为 sep4020_nand_read_byte
this->read_byte = busw ? nand_read_byte16 : nand_read_byte;
if (!this->write_word)
this->write_word = nand_write_word;
//
if (!this->read_word)
this->read_word = nand_read_word;
//
if (!this->block_bad)
if (!this->block_markbad)
if (!this->write_buf)
//在board_nand_init中设置为 sep4020_nand_write_buf
this->write_buf = busw ? nand_write_buf16 : nand_write_buf;
if (!this->read_buf)
//在board_nand_init中设置为 sep4020_nand_read_buf
this->read_buf = busw ? nand_read_buf16 : nand_read_buf;
if (!this->verify_buf)
//
this->verify_buf = busw ? nand_verify_buf16 : nand_verify_buf;
if (!this->scan_bbt)
this->scan_bbt = nand_default_bbt;
/* Select the device */
/* Send the command for reading device ID */
this->cmdfunc (mtd,NAND_CMD_READID,0x00,-1);
//flash命令发送函数,读flash ID命令
/* Read manufacturer and device IDs */
#ifdef CONfig_MTD_NAND_SEP4020
//这里是sep4020专用的读id
/* Read manufacturer and device IDs for sep4020 */
nand_maf_id = this->read_byte(mtd);
//读flash 一个字节
nand_dev_id = (EMI_NAND_ID)>>8;
//EMI_NAND_ID是sep4020 flashID寄存器,8:15位是device code
//printf("read id! id is 0x%x\n",nand_dev_id);
#else
nand_maf_id = this->read_byte(mtd);
nand_dev_id = this->read_byte(mtd);
#endif
/* Print and store flash device information */
for (i = 0; nand_flash_ids[i].name != NULL; i++) {
//在driver/mtd/nand/nand_ids.c 中定义nand_flash_ids数组,类型是nand_flash_dev,包含信息有name id 页大小 整片容量 块大小和option
if (nand_dev_id != nand_flash_ids[i].id)
//如果不相等id跳过下面代码继续在表中找,相等继续往下执行
continue;
if (!mtd->name) mtd->name = nand_flash_ids[i].name;
//复制table中的名字给 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);
//此周期包含附加信息
#ifdef CONfig_MTD_NAND_SEP4020
extid = 0x15;
//如果是sep4020 根本不管读出的信息,直接赋值0x15 = 0--0--01--0--1--01
//访问间隔50ns--8位结构--块大小128K--XX--每512字节对应的oob大小,16--页大小2K
#endif
/* Calc pagesize */
mtd->oobblock = 1024 << (extid & 0x3);
//2K
extid >>= 2;
/* Calc oobsize */
mtd->oobsize = (8 << (extid & 0x01)) * (mtd->oobblock / 512);
//64
extid >>= 2;
/* Calc blocksize. Blocksize is multiples of 64KiB */
mtd->erasesize = (64 * 1024) << (extid & 0x03);
//128K
extid >>= 2;
/* Get buswidth information */
busw = (extid & 0x01) ? NAND_BUSWIDTH_16 : 0;
//8位数据宽度
} else {
/* Old devices have this data hardcoded in the
//小页的flash设置
* device id table */
mtd->erasesize = nand_flash_ids[i].erasesize;
mtd->oobblock = nand_flash_ids[i].pagesize;
mtd->oobsize = mtd->oobblock / 32;
busw = nand_flash_ids[i].options & NAND_BUSWIDTH_16;
}
/* Check,if buswidth is correct. Hardware drivers should set
if (busw != (this->options & NAND_BUSWIDTH_16)) {
printk (KERN_INFO "NAND device: Manufacturer ID:"
nand_manuf_ids[i].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);
return 1;
}
/* Calculate the address shift from the page size *///address shift :地址偏移位 ,
this->page_shift = ffs(mtd->oobblock) - 1; //ffs测试第一个位为真的位置,2K的第12位为1 减1 则为11
this->bbt_erase_shift = this->phys_erase_shift = ffs(mtd->erasesize) - 1;
this->chip_shift = ffs(this->chipsize) - 1;
/* Set the bad block position */
this->badblockpos = mtd->oobblock > 512 ?
NAND_LARGE_BADBLOCK_POS : NAND_SMALL_BADBLOCK_POS;
//坏块位位置表示,512页大小的在oob区的第6个位,大页的在oob区域的第1位
/* Get chip options,preserve non chip based options */
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 */
//this will change if the chip is sep4020
//如果定义了sep4020则不要这段
#ifndef CONfig_MTD_NAND_SEP4020
if (this->options & NAND_4PAGE_ARRAY)
this->erase_cmd = multi_erase_cmd;
else
this->erase_cmd = single_erase_cmd;
#endif
/* Do not replace user supplied command function ! */
//如果是大页,并且用户没有指定cmdfunc,则cmdfunc被替
//换成nand_command_lp,大页专用
if (mtd->oobblock > 512 && this->cmdfunc == nand_command)
this->cmdfunc = nand_command_lp;
/* Try to identify manufacturer */
//留了个框架 啥都没干。(处理厂商id)
for (j = 0; nand_manuf_ids[j].id != 0x0; j++) {
if (nand_manuf_ids[j].id == nand_maf_id)
break;
}
break;
//在找到对应芯片id后,设置完了,终于break出去继续别的活了。
}
//-------------------------
//nand_flash_ids[i].name为空说明循环到了最后出去了还没找到配对的id,
//打印出错信息返回
if (!nand_flash_ids[i].name) {
#ifndef CFG_NAND_QUIET_TEST
printk (KERN_WARNING "No NAND device found!!!\n");
#endif
this->select_chip(mtd,-1);
return 1;
}
//怎么又读id,又留个框架,什么都不干。
for (i=1; i < maxchips; i++) {
this->select_chip(mtd,i);
/* Send the command for reading device ID */
this->cmdfunc (mtd,-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)
//flash多于一个的话
printk(KERN_INFO "%d NAND chips detected\n",i);
/* Allocate buffers,if neccecary */
if (!this->oob_buf) {
size_t len;
len = mtd->oobsize << (this->phys_erase_shift - this->page_shift);
//this->phys_erase_shift - this->page_shift 一块内多少个页的位置偏移。oobsize左移6位即乘64.
//一个块有64个页 (2K大页 举例)
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;
}
if (!this->data_buf) {
//分配一个页的内存当缓冲区
size_t len;
len = mtd->oobblock + mtd->oobsize;
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;
mtd->size = i * this->chipsize;
/* Convert chipsize to number of pages per chip -1. */
this->pagemask = (this->chipsize >> this->page_shift) - 1; //pagemask 页号的掩码
/* Preset the internal oob buffer */
memset(this->oob_buf,0xff,mtd->oobsize << (this->phys_erase_shift - this->page_shift));
/* If no default placement scheme is given,select an
* appropriate one */
if (!this->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;
break;
case 64:
this->autooob = &nand_oob_64;
break;
default:
printk (KERN_WARNING "No oob scheme defined for oobsize %d\n",
mtd->oobsize);
/*
BUG(); */
}
}
/****************************
* 这里刚好看一下oob的分布
*
static struct nand_oobinfo nand_oob_64 = {
*
.useecc = MTD_NANDECC_AutopLACE,
*
.eccbytes = 24,
*
.eccpos = {
*
40,41,42,43,44,45,46,47,
*
48,49,50,51,52,53,54,55,
*
56,57,58,59,60,61,62,63},
*
.oobfree = { {2,38} }
*
};
*
* 从上面的可以看出64字节的oob区域中,ECC中占24字节,
* (256字节数据,生成ECC码 3字节)[40:63]字节,
*
{2,38}表示第2位开始有38个字节空闲
*
*
***************************/
/* 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++)
//统计空闲oob区域
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 *///256字节 3字节
this->eccbytes = 3;
switch (this->eccmode) {
//在board_nand_init中 设置为NAND_ECC_SOFT
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->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->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:
break;
default:
printk (KERN_WARNING "Invalid NAND_ECC_MODE %d\n",this->eccmode);
/*
BUG(); */
}
/* Check hardware ecc function availability and adjust number of ecc bytes per
* calculation step
*/
switch (this->eccmode) {
//是否支持硬件ECC
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;
}
/* XXX U-BOOT XXX */
#if 0
/* Initialize state,waitqueue and spinlock */
this->state = FL_READY;
init_waitqueue_head (&this->wq);
spin_lock_init (&this->chip_lock);
#endif
/* De-select the device */
this->select_chip(mtd,-1);
/* Invalidate the pagebuffer reference */
this->pagebuf = -1;
/* Fill in remaining MTD driver data */
mtd->type = MTD_NANDFLASH;
mtd->flags = MTD_CAP_NANDFLASH | MTD_ECC;
mtd->ecctype = MTD_ECC_SW;
mtd->erase = nand_erase;
mtd->point = NULL;
mtd->unpoint = NULL;
mtd->read = nand_read;
mtd->write = nand_write;
mtd->read_ecc = nand_read_ecc;
mtd->write_ecc = nand_write_ecc;
mtd->read_oob = nand_read_oob;
mtd->write_oob = nand_write_oob;
/* XXX U-BOOT XXX */
#if 0
mtd->readv = NULL;
mtd->writev = nand_writev;
mtd->writev_ecc = nand_writev_ecc;
#endif
mtd->sync = nand_sync;
/* XXX U-BOOT XXX */
#if 0
mtd->lock = NULL;
mtd->unlock = NULL;
mtd->suspend = NULL;
mtd->resume = NULL;
#endif
mtd->block_isbad = nand_block_isbad;
//从bbt中查出是否是坏块
mtd->block_markbad = nand_block_markbad;
//标记坏块
/* and make the autooob the default one */
memcpy(&mtd->oobinfo,this->autooob,sizeof(mtd->oobinfo));
//把nand_chip的nand_oobinfo信息拷贝给mtd
/* XXX U-BOOT XXX */
#if 0
mtd->owner = THIS_MODULE;
#endif
/* Build bad block table */
return this->scan_bbt (mtd);
//扫描整个flash,建立bbt表
}
至此返回到nand_init中我们发现nand的初始化已经差不多完毕了。