加入收藏 | 设为首页 | 会员中心 | 我要投稿 李大同 (https://www.lidatong.com.cn/)- 科技、建站、经验、云计算、5G、大数据,站长网!
当前位置: 首页 > 百科 > 正文

MTD子系统-硬件驱动层主要函数分析 (start--------2)

发布时间:2020-12-15 17:25:14 所属栏目:百科 来源:网络整理
导读:这段时间一直在做 NAND?Flash 相关的一些调试。发现之前很少写 block 层相关的总结。于是抽出时间将 block 层相关的东西总结一下。这一节分析一下 MTD 驱动中 NAND?Flash 的硬件底层驱动的实现。 ? MTD 底层驱动需要给 MTD 层主要提供三个常用的接口,分别是

这段时间一直在做NAND?Flash相关的一些调试。发现之前很少写block层相关的总结。于是抽出时间将block层相关的东西总结一下。这一节分析一下MTD驱动中NAND?Flash的硬件底层驱动的实现。

?

MTD底层驱动需要给MTD层主要提供三个常用的接口,分别是:

Writereaderase。对于这三个接口,你可以选择内核提供的标准实现,也可自己驱动函数来实现它。这里主要分析一下内核中的标准实现。

首先分析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大小进行擦除。这里不再进行仔细分析。

(编辑:李大同)

【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!

    推荐文章
      热点阅读