Chinaunix首页 | 论坛 | 博客
  • 博客访问: 64060
  • 博文数量: 23
  • 博客积分: 0
  • 博客等级: 民兵
  • 技术积分: 45
  • 用 户 组: 普通用户
  • 注册时间: 2014-12-29 10:06
文章分类

全部博文(23)

文章存档

2015年(21)

2014年(2)

我的朋友

分类: LINUX

2015-03-27 22:07:25

原文地址:uboot之nand flash相关(2) 作者:gc5084

下面分析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;
struct nand_chip *this = mtd->priv;  //在调用函数nand_init_chip中mtd->priv = nand;

/* 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)
this->waitfunc = nand_wait; //默认的flash 擦除 烧写 等待函数 //wc
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)
this->block_bad = nand_block_bad; //默认坏块检查函数 //wc
if (!this->block_markbad)
this->block_markbad = nand_default_block_markbad; //默认标记坏块函数 //wc
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 */
this->select_chip(mtd, 0); //选择flash 因为只有一个flash,sep4020_nand_select_chip函数是空的
/* 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
* this correct ! */ //检查读出的数据总线和table中储存的是否一致,不一致就打印错误,并函数返回
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[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则不要这段
//其实修改nand_base.c文件 理论上是不应该的 这个文件中定义的操作都是默认的标准,有差异的板级修改都在可以在board_nand_init中等函数修改。
#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, 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) //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->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; //软件计算ECC
this->correct_data = nand_correct_data; //使用ECC发现和纠正位错误(发现2位错误,纠正1位的错误)
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的初始化已经差不多完毕了。
阅读(1014) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~