这段时间一直在做NAND?Flash相关的一些调试。发现之前很少写block层相关的总结。于是抽出时间将block层相关的东西总结一下。这一节分析一下MTD驱动中NAND?Flash的硬件底层驱动的实现。
?
MTD底层驱动需要给MTD层主要提供三个常用的接口,分别是:
Write、read和erase。对于这三个接口,你可以选择内核提供的标准实现,也可自己驱动函数来实现它。这里主要分析一下内核中的标准实现。
首先分析Write函数,在内核中的标准实现为nand_write,函数调用关系如下图所示。

实际上关于Nand?Flash写操作的主要逻辑都是在中间nand_do_write_ops函数中来实现的,这个函数会判断传入的buf是不是页对齐,并将buf中的内容分为页对齐的一个一个部分。Buf中不对齐的部分存在于buf头和buf尾两个位置,如下图所示。这两个部分的内容需要做特殊处理,处理的方法就是分配一个page大小的缓冲区,将buffer的内容全部初始化为ff,将buf头不对齐的部分拷贝到缓冲区中,再将页大小的这个缓冲区写入到nand?flash。这里用到了一个技巧,就是像nand?flash中写1不改变原来存储颗粒的状态,原来是1还是1原来是0还是0。同理buf尾部不对齐的数据也要做相同的处理。
?

?/**? ?*?nand_do_write_ops?-?[Internal]?NAND?write?with?ECC? ?*?@mtd:????MTD?device?structure? ?*?@to:????????offset?to?write?to? ?*?@ops:????oob?operations?description?structure? ?*? ?*?NAND?write?with?ECC? ?*/? static?int?nand_do_write_ops(struct?mtd_info?*mtd,?loff_t?to,? ?????????????????????????????struct?mtd_oob_ops?*ops)? {? ????int?chipnr,?realpage,?page,?blockmask,?column;? ????struct?nand_chip?*chip?=?mtd->priv;? ????uint32_t?writelen?=?ops->len;? ????uint8_t?*oob?=?ops->oobbuf;? ????uint8_t?*buf?=?ops->datbuf;? ????int?ret,?subpage;? ? ????ops->retlen?=?0;? ????if?(!writelen)? ????????return?0;? ? ????/*?reject?writes,?which?are?not?page?aligned?*/? ????if?(NOTALIGNED(to)?||?NOTALIGNED(ops->len))? ????{? ????????printk(KERN_NOTICE?"nand_write:?"? ???????????????"Attempt?to?write?not?page?aligned?datan");? ????????return?-EINVAL;? ????}? ? ????column?=?to?&?(mtd->writesize?-?1);? ????subpage?=?column?||?(writelen?&?(mtd->writesize?-?1));? ? ????if?(subpage?&&?oob)? ????????return?-EINVAL;? ? ????chipnr?=?(int)(to?>>?chip->chip_shift);? ????chip->select_chip(mtd,?chipnr);? ? ????/*?Check,?if?it?is?write?protected?*/? ????if?(nand_check_wp(mtd))? ????????return?-EIO;? ? ????realpage?=?(int)(to?>>?chip->page_shift);? ????page?=?realpage?&?chip->pagemask;? ????blockmask?=?(1?<<?(chip->phys_erase_shift?-?chip->page_shift))?-?1;? ? ????/*?Invalidate?the?page?cache,?when?we?write?to?the?cached?page?*/? ????if?(to?<=?(chip->pagebuf?<<?chip->page_shift)?&&? ????????????(chip->pagebuf?<<?chip->page_shift)?<?(to?+?ops->len))? ????????chip->pagebuf?=?-/*?If?we're?not?given?explicit?OOB?data,?let?it?be?0xFF?*/? ????if?(likely(!oob))? ????????memset(chip->oob_poi,?0xff,?mtd->oobsize);? ? ????while(1)? ????{? ????????int?bytes?=?mtd->writesize;? ????????int?cached?=?writelen?>?bytes?&&?page?!=?blockmask;? ????????uint8_t?*wbuf?=?buf;? ? ????????/*?Partial?page?write???*/? ????????/*如果是buf开始部分和结尾部分不是整个page的数据,就必须? ??????????制造一个整个的page,将这部分数据填入合适的位置,并将? ??????????其它部分数据全部设置为ff*/? ????????if?(unlikely(column?||?writelen?<?(mtd->writesize?-?1)))? ????????{? ????????????cached?=?0;? ????????????bytes?=?min_t(int,?bytes?-?column,?(int)?writelen);? ????????????chip->pagebuf?=?-1;? ????????????memset(chip->buffers->databuf,?mtd->writesize);? ????????????memcpy(&chip->buffers->databuf[column],?buf,?bytes);? ????????????wbuf?=?chip->buffers->databuf;? ????????}? ? ????????if?(unlikely(oob))? ????????????oob?=?nand_fill_oob(chip,?oob,?ops);? ????????/*实际的写操作*/? ????????ret?=?chip->write_page(mtd,?chip,?wbuf,?cached,? ???????????????????????????????(ops->mode?==?MTD_OOB_RAW));? ????????if?(ret)? ????????????break;? ? ????????writelen?-=?bytes;? ????????if?(!writelen)? ????????????break;? ????????? ????????/*只有buf的头部colnum不为0,自增buf和写入page数目*/? ????????column?=?0;? ????????buf?+=?bytes;? ????????realpage++;? ? ????????page?=?realpage?&?chip->pagemask;? ????????? ????????if?(!page)? ????????{? ????????????chipnr++;? ????????????chip->select_chip(mtd,?-1);? ????????????chip->select_chip(mtd,?chipnr);? ????????}? ????}? ? ????ops->retlen?=?ops->len?-?writelen;? ????if?(unlikely(oob))? ????????ops->oobretlen?=?ops->ooblen;? ????return?ret;? }? |
接下来分析nand_read函数
Nand_read函数会调用到nand_do_read_ops函数,后者完成了读Nand?Flash操作的主要逻辑。这个过程主要是判断读取数据是不是page对齐的。要读取的这段数据在位置往往在开始和结尾部分不是对齐的。对于这种不是page对齐的情况,函数首先分配一个page大小的缓冲区,一次把一个page读取上来,再将实际要读取的page中的部分拷贝出来。

??*?nand_do_read_ops?-?[Internal]?Read?data?with?ECC? ?*?@from:????offset?to?read?from? oob?ops?structure? ?*?Internal?function.?Called?with?chip?held.? int?nand_do_read_ops( ???????????????????????????? ????struct?nand_chip?*chip?=?mtd->priv;? ????struct?mtd_ecc_stats?stats;? ????int?blkcheck?=?(1;? ????int?sndcmd?=?int?ret?=?0;? ????uint32_t?readlen?=?ops->len;? ????uint32_t?oobreadlen?=?ops->ooblen;? ????uint8_t?*bufpoi,?*oob,?*buf;? ? ????stats?=?mtd->ecc_stats;? ? ????chipnr?=?(int)(from?>>?chip->chip_shift);? ????chip->select_chip(mtd,?chipnr);? ? ????realpage?=?(int)(from?>>?chip->page_shift);? ????page?=?realpage?&?chip->pagemask;? ? ????col?=?(int)(from?&?(mtd->writesize?-?1));? ? ????buf?=?ops->datbuf;? ????oob?=?ops->oobbuf;? ? ????1)? ????{? ????????bytes?=?min(mtd->writesize?-?col,?readlen);? ????????aligned?=?(bytes?==?mtd->writesize);? ? ????????/*?Is?the?current?page?in?the?buffer???*/? ????????if?(realpage?!=?chip->pagebuf?||?oob)? ????????{? ????????????/*如果不是page对齐的读取,使用额外的databuf来读取整个page*/? ????????????bufpoi?=?aligned???buf?:?chip->buffers->databuf;? ? ????????????if?(likely(sndcmd))? ????????????{? ????????????????chip->cmdfunc(mtd,?NAND_CMD_READ0,0); font-size:7.5pt; font-family:Consolas">0x00,?page);? ????????????????sndcmd?=?0;? ????????????}? ? ????????????/*?Now?read?the?page?into?the?buffer?*/? ????????????if?(unlikely(ops->mode?==?MTD_OOB_RAW))? ????????????????ret?=?chip->ecc.read_page_raw(mtd,?bufpoi);? ????????????else?if?(!aligned?&&?NAND_SUBPAGE_READ(chip)?&&?!oob)? ????????????????ret?=?chip->ecc.read_subpage(mtd,255); font-size:7.5pt; font-family:Consolas">else? ????????????????ret?=?chip->ecc.read_page(mtd,255); font-size:7.5pt; font-family:Consolas">if?(ret?<?0)? ????????????????break;? ? ????????????/*?Transfer?not?aligned?data?*/? ????????????/*非对齐的读取需要将读出的整页内容*/? ????????????if?(!aligned)? ????????????{? ????????????????if?(!NAND_SUBPAGE_READ(chip)?&&?!oob)? ????????????????????chip->pagebuf?=?realpage;? ????????????????memcpy(buf,?chip->buffers->databuf?+?col,?bytes);? ????????????}? ????????????/*读入buf后,重新改变buf位置*/? ????????????buf?+=?bytes;? ? ????????????if?(unlikely(oob))? ????????????{? ????????????????/*?Raw?mode?does?data:oob:data:oob?*/? ????????????????if?(ops->mode?!=?MTD_OOB_RAW)? ????????????????{? ????????????????????int?toread?=?min(oobreadlen,? ?????????????????????????????????????chip->ecc.layout->oobavail);? ????????????????????if?(toread)? ????????????????????{? ????????????????????????oob?=?nand_transfer_oob(chip,? ????????????????????????????????????????????????oob,?ops,?toread);? ????????????????????????oobreadlen?-=?toread;? ????????????????????}? ????????????????}? ????????????????else? ????????????????????buf?=?nand_transfer_oob(chip,? ????????????????????????????????????????????buf,?mtd->oobsize);? ????????????}? ? ????????????if?(!(chip->options?&?NAND_NO_READRDY))? ????????????{? ????????????????/*? ?????????????????*?Apply?delay?or?wait?for?ready/busy?pin.?Do? ?????????????????*?this?before?the?AUTOINCR?check,?so?no? ?????????????????*?problems?arise?if?a?chip?which?does?auto? ?????????????????*?increment?is?marked?as?NOAUTOINCR?by?the? ?????????????????*?board?driver.? ?????????????????*/? ????????????????if?(!chip->dev_ready)? ????????????????????udelay(chip->chip_delay);? ????????????????else? ????????????????????nand_wait_ready(mtd);? ????????????}? ????????}? ????????else? ????????{? ????????????memcpy(buf,?bytes);? ????????????buf?+=?bytes;? ????????}? ????????? ????????/*?减去已经读取的长度*/? ????????readlen?-=?bytes;? ? ????????if?(!readlen)? ????????????break;? ? ????????/*?For?subsequent?reads?align?to?page?boundary.?*/? ????????col?=?0;? ????????/*?Increment?page?address?*/? ????????realpage++;? ? ????????page?=?realpage?&?chip->pagemask;? ???????? ????????}? ? ???????? ?????????*?or?if?we?have?hit?a?block?boundary.? ?????????*/? ????????if?(!NAND_CANAUTOINCR(chip)?||?!(page?&?blkcheck))? ????????????sndcmd?=?1;? ????}? ? ????ops->retlen?=?ops->len?-?(size_t)?readlen;? ????if?(oob)? ????????ops->oobretlen?=?ops->ooblen?-?oobreadlen;? ? ????if?(ret)? ????????return?ret;? ? ????if?(mtd->ecc_stats.failed?-?stats.failed)? ????????return?-EBADMSG;? ? ????return??mtd->ecc_stats.corrected?-?stats.corrected???-EUCLEAN?:?0;? }? |
Nand_erase函数实现比较简单,主要是按block大小进行擦除。这里不再进行仔细分析。
(编辑:李大同)
【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!
|