Linux MTD 源代码分析
http://blogimg.chinaunix.net/blog/upfile/070511172139.pdf
MTD 原始设备与 FLASH 硬件驱动的对话
MTD 原始设备与 FLASH 硬件驱动的对话 - 续
mtd.h
重要结构体:
★struct erase_info
如果擦除失败,fail_addr将指示坏块地址。
★struct mtd_info
mtd层函数指针存放处。
nand.h
Nand基本指令:
#define NAND_CMD_READ0 0
#define NAND_CMD_READ1 1
#define NAND_CMD_PAGEPROG 0x10
#define NAND_CMD_READOOB 0x50
#define NAND_CMD_ERASE1 0x60
#define NAND_CMD_STATUS 0x70
#define NAND_CMD_STATUS_MULTI 0x71
#define NAND_CMD_SEQIN 0x80
#define NAND_CMD_READID 0x90
#define NAND_CMD_ERASE2 0xd0
#define NAND_CMD_RESET 0xff
和K9F1208指令对比
重要结构体:
★struct nand_chip
具体操作Nand的函数指针都在这个结构体里面。
★ struct nand_bbt_descr
Nand坏块表?具体如何使用还不清楚。
nand_base.c
◆int nand_scan (struct mtd_info *mtd, int maxchips)
{
struct nand_chip *this = mtd->priv;
priv是mtd_info结构体里面的一个空指针,现在指向this。
if (this->cmdfunc == NULL)
this->cmdfunc = nand_command;
判断驱动编写者是否提供了command函数,后来几个类似。
this->cmdfunc (mtd, NAND_CMD_READID, 0X00, -1);
读取Nand芯片信息,包括厂商信息的芯片ID,对于K9F1208是0xEC和0x76。
对应nand_ids.c中的{"NAND 64MiB 3,3V 8-bit", 0x76, 512, 64, 0x4000, 0}。
含义:三星的这颗Nand芯片是64MB的,3.3V供电,8bit位宽,ID为0x76,每一页大小为512Byte,64MB容量,擦除块尺寸为0x4000,操作0。
对擦除块为0x4000的解释:这颗Nand芯片的容量是这样划分的,512Byte x 32 x 4096 = 64MB,一共有4096个块(block),因此每一个块的大小为512Byte x 32 = 16384Byte = 0x4000Byte。
这些信息接下来都会被MTD层获得,如果全部没有问题,则在启动时会打印:
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);
/* Calculate the address shift from the page size */
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;
▼这一段不太明白,翻译过来是根据页面大小计算地址变化?
我在启动时将其打印了出来:
mtd->oobblock is 0x200
mtd->oobsize is 0x10
mtd->erasesize is 0x4000
this->page_shift is 0x9
this->bbt_erase_shift is 0xe
this->chip_shift is 0x1a
ffs函数第一次见到,看看是什么东西:
#define ffs(x) generic_ffs(x)
继续,蛮有意思的函数:
static inline int generic_ffs(int x)
{
int r = 1;
if (!x)
return 0;
if (!(x & 0xffff)) {
x >>= 16;
r += 16;
}
if (!(x & 0xff)) {
x >>= 8;
r += 8;
}
if (!(x & 0xf)) {
x >>= 4;
r += 4;
}
if (!(x & 3)) {
x >>= 2;
r += 2;
}
if (!(x & 1)) {
x >>= 1;
r += 1;
}
return r;
}
这函数人如其名,找到第一个bit位(find first bit set),比如0x80,将返回7。
/* Set the bad block position */
this->badblockpos = mtd->oobblock > 512 ?
NAND_LARGE_BADBLOCK_POS : NAND_SMALL_BADBLOCK_POS;
确定坏块标记的位置,如果大于512,在oob区的位置0,否则是在oob区的位置5。
/* Do not replace user supplied command function ! */
if (mtd->oobblock > 512 && this->cmdfunc == nand_command)
this->cmdfunc = nand_command_lp;
这一段没有什么意义,因为我们的底层驱动里面提供了命令函数。
if (!nand_flash_ids[i].name) {
printk (KERN_WARNING "No NAND device found!!!/n");
this->select_chip(mtd, -1);
return 1;
}
如果没有发现芯片,会提示找不到芯片,我刚开始做u-boot驱动时,读不到正确的芯片ID,就报这个错误,并且直接返回1,下面的程序不再执行。
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;
}
如果有多块芯片,这里会去读它们的ID信息。
/* Allocate buffers, if neccecary */
if (!this->oob_buf) {
size_t len;
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;
}
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;
}
如果前面没有分配,在这儿分配数据区和oob区的空间。说说这个size_t,是为了方便移植而的设定的,其实就是unsigned int。oob区的大小是mtd->oobsize << (this->phys_erase_shift - this->page_shift),数据区的大小是mtd->oobblock + mtd->oobsize。这儿在计算oob区该分配多大时用到了前面定义的this->page_shift和this-> phys_erase_shift。
具体计算方法?
这时候用得上前面print出来的内容:
mtd->oobblock is 0x200
mtd->oobsize is 0x10
mtd->erasesize is 0x4000
this->page_shift is 0x9
this->bbt_erase_shift is 0xe
this->chip_shift is 0x1a
len = mtd->oobsize << (this->phys_erase_shift - this->page_shift);
这句话应该是计算oob_buf的长度,计算结果应该是(16 << 5)=512,奇怪了,oob区的大小应该是16才对,为何要左移5位变成512呢?
暂且放下,现在还没看到oob_buf的用途,继续看下面的内容。
/* 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;
/* Preset the internal oob buffer */
memset(this->oob_buf, 0xff, mtd->oobsize << (this->phys_erase_shift - this->page_shift));
存储芯片的数目并计算mtd的总大小。
将芯片大小换算成页数,这时我才看懂this->page_shift的意思,就是9bit,因为便于移位操作,所以才用ffs函数将512变换为9的。
最后将oob_buf全部填充了0xff。
/* 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();
}
}
根据oobsize填充autooob,我们的oobsize是16,填充的是nand_oob_16这个结构体的内容:
static struct nand_oobinfo nand_oob_16 = {
.useecc = MTD_NANDECC_AUTOPLACE,
.eccbytes = 6,
.eccpos = {0, 1, 2, 3, 6, 7},
.oobfree = { {8, 8} }
};
结构体中规定了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];
文件系统的oob数据放在oob的free区里面。
/* * 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;
default:
printk (KERN_WARNING "Invalid NAND_ECC_MODE %d/n", this->eccmode);
BUG();
}
默认的eccsize为256,eccbytes为3。
开始判断驱动中提供的eccmode,我们以前用的是NAND_ECC_SOFT,现在为了使用yaffs,改用NAND_ECC_NONE,其他硬件的都不用看。如果是NONE的话,直接printk一个warning,如果是SOFT的,需要填充:
this->calculate_ecc = nand_calculate_ecc;
this->correct_data = nand_correct_data;
这是两个函数哦,不是变量,mark下后面要跟。
/* 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;
没用到硬件ecc,这儿应该直接跳过了。
/* 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;
}
设置每一页的ecc校验的steps。NAND_ECC_NONE是1,NAND_ECC_SOFT是2。
/* Initialize state, waitqueue and spinlock */
this->state = FL_READY;
init_waitqueue_head (&this->wq);
spin_lock_init (&this->chip_lock);
初始化状态机、等待列队和自旋锁。
/* 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;
mtd->readv = NULL;
mtd->writev = nand_writev;
mtd->writev_ecc = nand_writev_ecc;
mtd->sync = nand_sync;
mtd->lock = NULL;
mtd->unlock = NULL;
mtd->suspend = nand_suspend;
mtd->resume = nand_resume;
mtd->block_isbad = nand_block_isbad;
mtd->block_markbad = nand_block_markbad;
填充MTD结构体的其他成员及函数,我看完nand scan如果没有突破点,就应该一个一个看这里面的内容。
/* Check, if we should skip the bad block table scan */
if (this->options & NAND_SKIP_BBTSCAN)
return 0;
这儿比较重要,我正想u-boot在开机能不能跳过scan坏块呢,只要定义了NAND_SKIP_BBTSCAN就可以跳过坏块了。但是这个Linux下的nand_base.c,刚又看了下u-boot里面的nand_base.c,发现没有这个判断,奇怪。
/* Build bad block table */
return this->scan_bbt (mtd);
虽然返回,但没有结束,跳去执行scan_bbt这个函数了,下一步目标:scan_bbt!