pxa310 nand驱动三
时间:2010-06-09 来源:tonyen
1 nand写入
1.1 yaffs2写函数分析
struct mtd_oob_ops ops;
… … …
yaffs_PackedTags2 pt;
… … …
ops.mode = MTD_OOB_AUTO;
ops.ooblen = (dev->inbandTags) ? 0 : sizeof(pt);
ops.len = dev->totalBytesPerChunk;
ops.ooboffs = 0;
ops.datbuf = (__u8 *)data;
ops.oobbuf = (dev->inbandTags) ? NULL : (void *)&pt;
retval = mtd->write_oob(mtd, addr, &ops);
pt存放OOB数据,调用的是mtd->write_oob。在nand_scan_tail里,mtd->read_oob被初始化为nand_write_oob
1.2 nand_write_oob
如果ops->datbuf不为空,调用nand_do_write_oob,这个函数只写OOB数据。否则,调用nand_do_write_ops。
if (!ops->datbuf)
ret = nand_do_write_oob(mtd, to, ops);
else
ret = nand_do_write_ops(mtd, to, ops);
在我们的这种情况,ops->datbuf不为空,所以调用nand_do_write_ops。
1.3 nand_do_write_ops
static int nand_do_write_ops(struct mtd_info *mtd, loff_t to,
struct mtd_oob_ops *ops)
下图是该函数的流程图,其中调用了nand_fill_oob和write_page。
2 Nand OOB
page大小为2k的nand包含一个64个字节的OOB区域,pxa310在使用yaffs2时,OOB区域是这样分配的:
3 nand坏块管理
3.1 坏块表描述符
坏块表本身也需要存储在nand上,坏块表描述符用来表示该表在nand上的一些信息,pxa310定义了两个坏块表,一个主坏块表和一个映像。
主坏块表:
static struct nand_bbt_descr monahans_bbt_main = {
.options = NAND_BBT_LASTBLOCK | NAND_BBT_CREATE | NAND_BBT_WRITE | NAND_BBT_2BIT | NAND_BBT_VERSION,
.veroffs = 6,
.maxblocks = 2,
.offs = 2,
.len = 4,
.pattern = scan_main_bbt_pattern,
};
options表示坏块表的参数。NAND_BBT_LASTBLOCK表示该表在该设备上最后一个好的block上,NAND_BBT_CREATE表示如果该坏块表不存在的话,就创建它,NAND_BBT_WRITE表示该表可以写,NAND_BBT_2BIT表示每个block用两个bit表示,NAND_BBT_VERSION表示该表有版本信息,版本信息存储在oob区域,在OOB的偏移为veroffs。offs为pattern在OOB区域的偏移,len则为pattern的长度。
同样,映像表的描述符定义如下:
static struct nand_bbt_descr monahans_bbt_mirror = {
.options = NAND_BBT_LASTBLOCK | NAND_BBT_CREATE | NAND_BBT_WRITE
| NAND_BBT_2BIT | NAND_BBT_VERSION,
.veroffs = 6,
.maxblocks = 4,
.offs = 2,
.len = 4,
.pattern = scan_mirror_bbt_pattern,
};
具体含义我们就不解释了。
3.2 扫描坏块表
3.2.1 nand_scan_bbt
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为需要的bbt长度,每个block两个bit*/
len = mtd->size >> (this->bbt_erase_shift + 2);
/* Allocate memory (2bit per block) and clear the memory bad block table */
this->bbt = kzalloc(len, GFP_KERNEL);
if (!this->bbt) {
printk(KERN_ERR "nand_scan_bbt: Out of memory\n");
return -ENOMEM;
}
… … …
/*这里的len被初始化为一个块的大小,这个大小包含OOB。*/
/* Allocate a temporary buffer for one eraseblock incl. oob */
len = (1 << this->bbt_erase_shift);
len += (len >> this->page_shift) * mtd->oobsize;
buf = vmalloc(len);
if (!buf) {
printk(KERN_ERR "nand_bbt: Out of memory\n");
kfree(this->bbt);
this->bbt = NULL;
return -ENOMEM;
}
res = search_read_bbts(mtd, buf, td, md);
/*search_read_bbts总是会返回1,所以,check_create总是会执行*/
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);
vfree(buf);
return res;
}
3.2.2 search_bbt
search_bbt查找在nand上查找匹配特定模式的坏块表,需要匹配的模式存放在参数td中。
static int search_bbt(struct mtd_info *mtd, uint8_t *buf, struct nand_bbt_descr *td)
{
struct nand_chip *this = mtd->priv;
int i, chips;
int bits, startblock, block, dir;
int scanlen = mtd->writesize + mtd->oobsize;
int bbtblocks;
int blocktopage = this->bbt_erase_shift - this->page_shift;
/*如果设置了NAND_BBT_LASTBLOCK ,表示重最后一个块开始搜索,搜索的块是存放在td->maxblocks */
/* Search direction top -> down ? */
if (td->options & NAND_BBT_LASTBLOCK) {
startblock = (mtd->size >> this->bbt_erase_shift) - 1;
dir = -1;
} else {
startblock = 0;
dir = 1;
}
/* Do we have a bbt per chip ? */
if (td->options & NAND_BBT_PERCHIP) {
chips = this->numchips;
bbtblocks = this->chipsize >> this->bbt_erase_shift;
startblock &= bbtblocks - 1;
} else {
chips = 1;
bbtblocks = mtd->size >> this->bbt_erase_shift;
}
/* Number of bits for each erase block in the bbt */
bits = td->options & NAND_BBT_NRBITS_MSK;
for (i = 0; i < chips; i++) {
/* Reset version information */
td->version[i] = 0;
td->pages[i] = -1;/*从这里可知,每个td->pages存放一个nand的坏块储存位置*/
/* Scan the maximum number of blocks */
for (block = 0; block < td->maxblocks; block++) {
int actblock = startblock + dir * block;
loff_t offs = (loff_t)actblock << this->bbt_erase_shift;
/* Read first page */
scan_read_raw(mtd, buf, offs, mtd->writesize);/*读取该块的第一个page*/
/*check_pattern做的事情比较简单,检查该page的oob区域中的pattern是否和td中存储的相匹配*/
if (!check_pattern(buf, scanlen, mtd->writesize, td)) {
td->pages[i] = actblock << blocktopage;
if (td->options & NAND_BBT_VERSION) {
td->version[i] = buf[mtd->writesize + td->veroffs];
}
break;
}
}
startblock += this->chipsize >> this->bbt_erase_shift;
}
/* Check, if we found a bbt for each requested chip */
for (i = 0; i < chips; i++) {
if (td->pages[i] == -1)
printk(KERN_WARNING "Bad block table not found for chip %d\n", i);
else
printk(KERN_DEBUG "Bad block table found at page %d, version 0x%02X\n", td->pages[i],
td->version[i]);
}
return 0;
}
3.2.3 check_create
check_create创建或者更新坏块表。
static int check_create(struct mtd_info *mtd, uint8_t *buf, struct nand_bbt_descr *bd)
{
int i, chips, writeops, chipsel, res;
struct nand_chip *this = mtd->priv;
struct nand_bbt_descr *td = this->bbt_td;
struct nand_bbt_descr *md = this->bbt_md;
struct nand_bbt_descr *rd, *rd2;
/* Do we have a bbt per chip ? */
if (td->options & NAND_BBT_PERCHIP)
chips = this->numchips;
else
chips = 1;
/*我们这里的chips永远都是1。*/
for (i = 0; i < chips; i++) {
writeops = 0;
rd = NULL;
rd2 = NULL;
/* Per chip or per device ? */
chipsel = (td->options & NAND_BBT_PERCHIP) ? i : -1;
/* Mirrored table avilable ? */
if (md) {
/*如果主坏块表和映像坏块表都没有找到,writeops置为3,程序到create处,一般来说,只有nand初次被使用是才会出现这种情况*/
if (td->pages[i] == -1 && md->pages[i] == -1) {
writeops = 0x03;
goto create;
}
/*如果没有找到主坏块表,则将writeops置1*,rd置为md,表示md需要先被读取,然后才能更新/
if (td->pages[i] == -1) {
rd = md;
td->version[i] = md->version[i];
writeops = 1;
goto writecheck;
}
/*如果没有找到映像坏块表,则将writeops置2*/
if (md->pages[i] == -1) {
rd = td;
md->version[i] = td->version[i];
writeops = 2;
goto writecheck;
}
/*程序执行到这里,主坏块表和映像坏块表都找到了,比较他们的version一样的话,则两个坏块表都需要先被读取,然后才能更新*/
if (td->version[i] == md->version[i]) {
rd = td;
if (!(td->options & NAND_BBT_VERSION))
rd2 = md;
goto writecheck;
}
if (((int8_t) (td->version[i] - md->version[i])) > 0) {
rd = td;
md->version[i] = td->version[i];
writeops = 2;
} else {
rd = md;
td->version[i] = md->version[i];
writeops = 1;
}
goto writecheck;
} else {
if (td->pages[i] == -1) {
writeops = 0x01;
goto create;
}
rd = td;
goto writecheck;
}
create:
/* Create the bad block table by scanning the device ? */
if (!(td->options & NAND_BBT_CREATE))
continue;
/* Create the table in memory by scanning the chip(s) */
create_bbt(mtd, buf, bd, chipsel);
td->version[i] = 1;
if (md)
md->version[i] = 1;
writecheck:
/*如果有需要先读取的情况,调用read_abs_bbt先读取坏块信息*/
/* read back first ? */
if (rd)
read_abs_bbt(mtd, buf, rd, chipsel);
/* If they weren't versioned, read both. */
if (rd2)
read_abs_bbt(mtd, buf, rd2, chipsel);
/*下面这种情况是找到了映像坏块表,没有找到主坏块表*/
/* Write the bad block table to the device ? */
if ((writeops & 0x01) && (td->options & NAND_BBT_WRITE)) {
res = write_bbt(mtd, buf, td, md, chipsel);
if (res < 0)
return res;
}
/*下面这种情况是找到主坏块表,没有找到映像坏块表*/
/* Write the mirror bad block table to the device ? */
if ((writeops & 0x02) && md && (md->options & NAND_BBT_WRITE)) {
res = write_bbt(mtd, buf, md, td, chipsel);
if (res < 0)
return res;
}
}
return 0;
}