Chinaunix首页 | 论坛 | 博客
  • 博客访问: 143101
  • 博文数量: 29
  • 博客积分: 151
  • 博客等级: 入伍新兵
  • 技术积分: 225
  • 用 户 组: 普通用户
  • 注册时间: 2011-09-20 18:54
文章分类

全部博文(29)

文章存档

2012年(16)

2011年(13)

分类:

2011-09-22 16:21:54

原文地址:基于MTD的NAND驱动开发(一) 作者:caorr

○、说明
 
大约用了两个礼拜不到的时间为公司的IPcamera系统写了基于MTDNAND驱动(linux-2.6.22.10内核),目前已可以在该驱动的支持下跑cramfsjffs2文件系统,另外,该驱动也可以同时支持small page(每页512 Byte)big page(每页2048 Byte)两种NAND芯片。在此整理一下与NAND驱动相关的概念,结构体,驱动框架和流程,同时分析一下基于MTDNAND驱动的部分函数,尤其是其中的nand_scan()函数。(涉及到具体NAND芯片时,若不做说明,将以small pageNAND芯片为例。)
 
注:个人理解,有误难免!—— 笔者:曹荣荣
 
 
MTD 驱动程序是专门针对嵌入式Linux的一种驱动程序,相对于常规块设备驱动程序(比如PC中的IDE硬盘)而言,MTD驱动程序能更好的支持和管理闪存设备,因为它本身就是专为闪存设备而设计的。
具体地讲,基于MTDFLASH驱动,承上可以很好地支持cramfsjffs2yaffs等文件系统,启下也能对FLASH的擦除,读写,FLASH坏块以及损耗平衡进行很好的管理。所谓损耗平衡,是指对NAND的擦写不能总是集中在某一个或某几个block中,这是由NAND芯片有限的擦写次数的特性决定的。
总之,在现阶段,要为FLASH设备开发Linux下的驱动程序,那么基于MTD的开发将几乎是省时又省力的唯一选择!
 
一、NANDNOR的区别
 
Google Nand FlashNor Flash的区别”。
 
简单点说,主要的区别就是:
 
1、  NANDNOR便宜;NAND的容量比NOR大(指相同成本);NAND的擦写次数是NOR的十倍;NAND的擦除和写入速度比NOR快,读取速度比NOR稍慢;
 
2、  NANDNOR的读都可以以字节为单位,但NAND的写以page为单位,而NOR可以随机写每一个字节。NANDNOR的擦除都以block为单位,但一般NANDblockNORblock小。另外,不管是NAND还是NOR,在写入前,都必须先进行擦除操作,但是NOR在擦除前要先写0
 
3、  NAND不能在片内运行程序,而NOR可以。但目前很多CPU都可以在上电时,以硬件的方式先将NAND的第一个block中的内容(一般是程序代码,且也许不足一个block,如2KB大小)自动copyram中,然后再运行,因此只要CPU支持,NAND也可以当成启动设备;
 
4、  NANDNOR都可能发生比特位反转(但NAND反转的几率远大于NOR),因此这两者都必须进行ECC操作;NAND可能会有坏块(出厂时厂家会对坏块做标记),在使用过程中也还有可能会出现新的坏块,因此NAND驱动必须对坏块进行管理。
 
二、内核树中基于MTDNAND驱动代码的布局
 
Linux内核中,MTD源代码放在linux-2.6.22.10/driver/mtd目录中,该目录中包含chipsdevicesmapsnandonenandubi六个子目录。
 
其中只有nandonenand目录中的代码才与NAND驱动相关,不过nand目录中的代码比较通用,而onenand目录中的代码相对于nand中的代码而言则简化了很多,它是针对三星公司开发的另一类Flash芯片,即OneNAND Flash。我尚未对OneNand FLASH有过研究,只是通过网上资料得知,OneNand FLASH克服了传统NAND Flash接口复杂的缺点,具有接口简单、读写速度快、容量大、寿命长、成本低等优点,因此应该是一种较常用NAND先进的FLASH吧,只是目前似乎普及率并不高,本文也将不做讨论。
 
因此,若只是开发基于MTDNAND驱动程序,那么我们需要关注的代码就基本上全在linux-2.6.22.10/drivers/mtd/nand目录中了,而该目录中也不是所有的代码文件都与我们将要开发的NAND驱动有关,除了MakefileKconfig之外,其中真正与NAND驱动有关的代码文件只有6个,即:
 
1、  nand_base.c
定义了NAND驱动中对NAND芯片最基本的操作函数和操作流程,如擦除、读写page、读写oob等。当然这些函数都只是进行一些default的操作,若你的系统在对NAND操作时有一些特殊的动作,则需要在你自己的驱动代码中进行定义,然后Replace这些default的函数。
 
2、  nand_bbt.c
定义了NAND驱动中与坏块管理有关的函数和结构体。
 
3、  nand_ids.c
定义了两个全局类型的结构体:struct nand_flash_dev nand_flash_ids[ ]struct nand_manufacturers nand_manuf_ids[ ]。其中前者定义了一些NAND芯片的类型,后者定义了NAND芯片的几个厂商。NAND芯片的ID至少包含两项内容:厂商ID和厂商为自己的NAND芯片定义的芯片ID。当NAND驱动被加载的时候,它会去读取具体NAND芯片的ID,然后根据读取的内容到上述定义的nand_manuf_ids[ ]nand_flash_ids[ ]两个结构体中去查找,以此判断该NAND芯片是那个厂商的产品,以及该NAND芯片的类型。若查找不到,则NAND驱动就会加载失败,因此在开发NAND驱动前必须事先将你的NAND芯片添加到这两个结构体中去(其实这两个结构体中已经定义了市场上绝大多数的NAND芯片,所以除非你的NAND芯片实在比较特殊,否则一般不需要额外添加)。值得一提的是,nand_flash_ids[ ]中有三项属性比较重要,即pagesizechipsizeerasesize,驱动就是依据这三项属性来决定对NAND芯片进行擦除,读写等操作时的大小的。其中pagesizeNAND芯片的页大小,一般为2565122048chipsizeNAND芯片的容量;erasesize即每次擦除操作的大小,通常就是NAND芯片的block大小。
 
4、  nand_ecc.c
定义了NAND驱动中与softeware ECC有关的函数和结构体,若你的系统支持hardware ECC,且不需要software ECC,则该文件也不需理会。
 
5、  nandsim.c
定义了Nokia开发的模拟NAND设备,默认是Toshiba NAND 8MiB 1,8V 8-bit(根据ManufactureID),开发普通NAND驱动时不用理会。
 
6、  diskonchip.c
定义了片上磁盘(DOC)相关的一些函数,开发普通NAND驱动时不用理会。
 
除了上述六个文件之外,nand目录中其他文件基本都是特定系统的NAND驱动程序例子,但本人看来真正有参考价值的只有cafe_nand.cs3c2410.c两个,而其中又尤以cafe_nand.c更为详细,另外,nand目录中也似乎只有cafe_nand.c中的驱动程序在读写NAND芯片时用到了DMA操作。
 
综上所述,若要研究基于MTDNAND驱动,其实所需阅读的代码量也不是很大。
 
另外,在动手写NAND驱动之前,也许需要读一下以下文档:
1、  Linux MTD 源代码分析:
该文档可以让我们对MTD有一个直观而又相对具体的认识,但它似乎主要是针对NOR FLASH的,对于实际开发NAND驱动的帮助并不是很大。
2、  MTD NAND Driver Programming Interface
该文档中关于ECC的说明很有帮助。
3、  MTD的官方网站:
 
三、NAND相关原理
 
在我们开始NAND驱动编写之前,至少应该知道:数据在NAND中是怎样存储的,以及以怎样的方式从NAND中读写数据时。
 
1、  NAND的存储结构和操作方式
 
这方面的资料可以从任意一种NANDdatasheet中得到,因为基本上每一种NANDdatasheet都会介绍NAND的组成结构和操作命令,而且事实上,大多数的NAND datasheet都大同小异,所不同的大概只是该NAND芯片的容量大小和读写速度等基本特性。
 
这里以每页512字节的NAND FLASH为例简单说明一下:每一块NAND芯片由nblock组成->每一个blockmpage组成->每一个page256字节大小的column1(也称1st half page)256字节大小的column2(也称2nd half page)16字节大小的oob(out-of-band,也称spare area)组成。至于mn的大小可以查看特定NANDdatasheet。相应的,若给定NAND中的一个字节的地址,我们可以根据这个地址算出block地址(即第几个block)page地址(即该block中的第几个page)column地址(1st half page,或2nd half page,或oob中的第几个字节)
 
在擦除NAND时,必须每次至少擦除1block;在写NAND时,必须每次写1page(有些NAND也支持写不足一个page大小的数据);在读NAND时,分为三种情况(对应三种不同的NAND命令),即读column1、读column2和读oob,那么为什么要分这三种情况呢?假如知道NAND怎样根据给定的地址确定它的存储单元,那么自然也就能明白原因了,其实也并不复杂,主要是因为给定地址中的A8并不在NAND的视野范围之内(也许表达并不准确)
 
事实上,在写基于MTDNAND驱动时,我们并不需要实现精确到读写某一个byte地址的函数(除了读oob之外),这是因为:
 
基于MTDNAND驱动在读写NAND时,可以分两种情况,即:(1)不进行ECC检测时,一次读写一整个page中的MAIN部分(也就是那真实存储数据的512字节)(2)进行ECC检测时(不管是hardware ECC还是software ECC),一次读写一整个page(包括16字节的oob部分)。所以部分NAND所支持的写不足一个page大小数据的功能,对MTD来说是用不着的。
 
那么,如果只需要读写不足一个page大小的数据怎么办?这是MTD更上层的部分需要处理的事。也就是说,对于NAND驱动来说,它只会读写整整一个page的数据!
 
最后值得一提的是,NAND驱动有可能只去读oob部分,这是因为除了ECC信息之外,坏块信息也存储在oob之中,NAND驱动需要读取oob中描述坏块的那个字节(通常是每个block的第一个pageoob中的第六个字节)来判断该block是不是一个坏块。所以,我们只有在读oob时,才需要实现精确到读某一个byte地址的函数。
 
由此,我们也可以额外知道一件事,那就是NAND驱动中用到的column地址只在读oob时才有用,而在其他情况下,column地址都为0
 
2、  ECC相关的结构体
struct nand_ecclayout {
           uint32_t eccbytes;
           uint32_t eccpos[64];
           uint32_t oobavail;
           struct nand_oobfree oobfree[MTD_MAX_OOBFREE_ENTRIES];
};
这是用来定义ECCoob中布局的一个结构体。
 
前面已经提及过,oob中主要存储两种信息:坏块信息和ECC数据。对与small pageNAND芯片来说,其中坏块信息占据1个字节(一般固定在第六个字节)ECC数据占据三个字节。所以sturct nand_ecclayout这个结构体,也就是用来告诉那些与ECC操作无关的函数,Nand芯片的oob部分中,哪些字节是用来存储ECC(即不可用作它用的),哪些字节是空闲的,即可用的。
 
其实之所以有这个结构体,主要是因为硬件ECC的缘故。以写数据为例,在使用硬件ECC的情况下,那三个字节的ECC数据是由硬件计算得到,并且写到NAND芯片的oob中去的,同时也是由硬件决定写到oob的哪三个字节中去。这些都是由硬件做的,而NAND驱动并不知道,所以就需要用这个结构体来告诉驱动了。
 
所以,在写NAND驱动时,就有可能需要对这个结构体进行赋值。这里说“有可能”,是因为MTD对这个结构体有一个默认的赋值,假如这个赋值所定义的ECC位置与你的硬件一致的话,那就不必在你的驱动中手动赋值了。其实对大多数硬件(这里所说的硬件,不是指NAND芯片,而是NAND控制器)来说,是不必手动赋值的,但也有许多例外。
 
值得一提的是,这个结构体不仅仅用来定义ECC布局,也可以用来将你的驱动在oob中需要额外用到的字节位置保护起来。
 
现在对struct nand_ecclayout 这个结构体进行一下说明。
 
uint32_t eccbytesECC的字节数,对于512B-per-pageNAND来说,eccbytes = 3,如果你需要额外用到oob中的数据,那么也可以大于3.
uint32_t eccpos[64]ECC数据在oob中的位置,这里之所以是个64字节的数组,是因为对于2048-per-pageNAND来说,它的oob64个字节。而对于512B-per-pageNAND来说,可以而且只可以定义它的前16个字节。
uint32_t oobavailoob中可用的字节数,这个值不用赋值,MTD会根据其它三个变量自动计算得到。
struct nand_oobfree oobfree[MTD_MAX_OOBFREE_ENTRIES]:显示定义空闲的oob字节。
 
完了,似乎有点不想写下去了:(
阅读(1028) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~