参考基于mtd的nand驱动开发_第1页
参考基于mtd的nand驱动开发_第2页
参考基于mtd的nand驱动开发_第3页
参考基于mtd的nand驱动开发_第4页
参考基于mtd的nand驱动开发_第5页
已阅读5页,还剩41页未读 继续免费阅读

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

基于MTD的NAND驱动开发说明大约用了两个礼拜不到的时间为公司的IPcamera系统写了基于MTD的NAND驱动(linux-0内 核),目前已可以在该驱动的支持下跑cramfs和jffs2文件系统,另 外,该驱动也可以同时支持small page(每页512 Byte)和big page(每页2048 Byte)两种NAND芯片。在此整理一下与NAND驱 动相关的概念,结构体,驱动框架和流程,同时分析一下基于MTD的NAND驱动的部分函数,尤其是其中的nand_scan()函数。(涉及到具体NAND芯片时,若不做说明,将以small page的NAND芯片为例。)注:个人理解,有误难免! 笔者:曹荣荣MTD 驱动程序是专门针对嵌入式Linux的 一种驱动程序,相对于常规块设备驱动程序(比如PC中 的IDE硬盘)而言,MTD驱动程序能更好的支持和管理闪存设备,因为它本身就是专为闪存设备 而设计的。具体地讲,基于MTD的FLASH驱动,承上可以很好地支持cramfs,jffs2和yaffs等文件系统,启下也能对FLASH的擦除,读写,FLASH坏块以及损耗平衡进行很好的管理。所谓损耗平衡,是指对NAND的擦写不能总是集中在某一个或某几个block中,这是由NAND芯片有限的擦写次数的特性决定的。总之,在现阶段,要为FLASH设备开发Linux下的驱动程序,那么基于MTD的开发将几乎是省时又省力的唯一选择!一、NAND和NOR的区别Google “Nand Flash和Nor Flash的区别”。简单点说,主要的区别就是:1、 NAND比NOR便宜;NAND的容量比NOR大(指相同成 本);NAND的擦写次数是NOR的十倍;NAND的擦除和写入速度比NOR快,读取速度比NOR稍 慢;2、 NAND和NOR的读都可以以字节为单位,但NAND的写以page为 单位,而NOR可以随机写每一个字节。NAND和NOR的擦除都以block为单位,但一般NAND的block比NOR的block小。另外,不管是NAND还 是NOR,在写入前,都必须先进行擦除操作,但是NOR在擦除前要先写0;3、 NAND不能在片内运行程序,而NOR可以。但目前很多CPU都可以在上电时,以硬件的方式先将NAND的第一个block中的内容(一般是程 序代码,且也许不足一个block,如2KB大小)自动copy到ram中,然后再运行,因此只要CPU支持,NAND也可以当成启动设备;4、 NAND和NOR都可能发生比特位反转(但NAND反转的几率远大于NOR), 因此这两者都必须进行ECC操作;NAND可能会有坏块(出厂时厂家会对坏块做标记),在使用过程中也还有可能会出现新的坏块,因此NAND驱动必须对坏块进行管理。二、内 核树中基于MTD的NAND驱动代码的布局在Linux内 核中,MTD源代码放在linux-0/driver/mtd目录中,该目录中包含chips、devices、maps、nand、onenand和ubi六 个子目录。其中只有nand和onenand目录中的代码才与NAND驱动相关,不过nand目录中的代码比 较通用,而onenand目录中的代码相对于nand中的代码而言则简化了很多,它是针对三星公司开发的另一类Flash芯片,即 OneNAND Flash。我尚未对OneNand FLASH有过研究,只是通过网上资料得知,OneNand FLASH克服了传统NAND Flash接口复杂的缺点,具有接口简单、读写速度快、容量大、寿命长、成本低等优点,因此应该是一种较常用NAND先进的FLASH吧,只是目前似乎普 及率并不高,本文也将不做讨论。因此,若只是开发基于MTD的NAND驱动程序,那么我们需要关注的 代码就基本上全在linux-0/drivers/mtd/nand目 录中了,而该目录中也不是所有的代码文件都与我们将要开发的NAND驱动有 关,除了Makefile和Kconfig之外,其中真正与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 中有三项属性比较重要,即pagesize、chipsize和erasesize,驱动就是依据这三项属性来决定对NAND芯片进行擦除,读写等操作时的大小的。其中pagesize即NAND芯片的页大 小,一般为256、512或2048;chipsize即NAND芯 片的容量;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.c和s3c2410.c两个,而其中又尤以cafe_nand.c更为详细,另外,nand目 录中也似乎只有cafe_nand.c中的驱动程序在读写NAND芯片时用到了DMA操 作。综上所述,若要研究基于MTD的NAND驱动,其实所需阅读的代码量也 不是很大。另外,在动手写NAND驱 动之前,也许需要读一下以下文档:1、 Linux MTD 源代码分析:该文档可以让我们对MTD有一个直观而又相对具体的认识,但它似乎主要是针对NOR FLASH的,对于实际开发NAND驱动的帮助并不是很大。2、 MTD NAND Driver Programming Interface:/tjuerges/ALMA/Kernel/mtdnand/该文档中关于ECC的 说明很有帮助。3、 MTD的官方网站:/三、NAND相 关原理在我们开始NAND驱 动编写之前,至少应该知道:数据在NAND中是怎样存储的,以及以怎样的方式 从NAND中读写数据时。1、 NAND的存储结构和操作方式这方面的资料可以从任意一种NAND的datasheet中得到,因为基本 上每一种NAND的datasheet都会介绍NAND的组成结构 和操作命令,而且事实上,大多数的NAND datasheet都大同小异, 所不同的大概只是该NAND芯片的容量大小和读写速度等基本特性。这里以每页512字 节的NAND FLASH为例简单说明一下:每一块NAND芯片由n个block组成每 一个block由m个page组成每 一个page由256字 节大小的column1(也称1st half page)、256字节大小 的column2(也称2nd half page)和16字节大小的oob(out-of-band,也称spare area)组成。至于m和n的大小可以查看特定NAND的datasheet。相应的,若给定NAND中的一个字节的地址,我们可以根据这个地址算出block地 址(即第几个block)、page地址(即 该block中的第几个page)和column地址(即1st half page,或2nd half page,或oob中的第几个字节)。在擦除NAND时, 必须每次至少擦除1个block;在写NAND时,必须每次写1个page(有 些NAND也支持写不足一个page大小的数据);在读NAND时,分为三种情况(对 应三种不同的NAND命令),即读column1、读column2和读oob, 那么为什么要分这三种情况呢?假如知道NAND怎样根据给定的地址确定它的存 储单元,那么自然也就能明白原因了,其实也并不复杂,主要是因为给定地址中的A8并 不在NAND的视野范围之内(也许表达并不准确)。事实上,在写基于MTD的NAND驱动时,我们并不需要实现精确到读写某一个byte地址的函数(除了读oob之外), 这是因为:基于MTD的NAND驱动在读写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的第一个page的oob中的第六个字节)来判断该block是 不是一个坏块。所以,我们只有在读oob时,才需要实现精确到读某一个byte地址的函数。由此,我们也可以额外知道一件事,那就是NAND驱动中用到的column地 址只在读oob时才有用,而在其他情况下,column地址都为0。2、 ECC相关的结构体struct nand_ecclayout uint32_t eccbytes; uint32_t eccpos64; uint32_t oobavail; struct nand_oobfree oobfreeMTD_MAX_OOBFREE_ENTRIES;这是用来定义ECC在oob中布局的一个结构体。前面已经提及过,oob中 主要存储两种信息:坏块信息和ECC数据。对与small page的NAND芯 片来说,其中坏块信息占据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 eccbytes:ECC的字节数,对于512B-per-page的NAND来 说,eccbytes = 3,如果你需要额外用到oob中的数据,那么也可以大于3.uint32_t eccpos64:ECC数据在oob中的位置,这里之所以是个64字节的数组,是因为对于2048-per-page的NAND来说,它的oob有64个字节。而对于512B-per-page的NAND来说,可以而且只可以定义它的前16个字节。uint32_t oobavail:oob中可用的字节数,这个值不用赋值,MTD会根据其它三个变量自动计算得到。struct nand_oobfree oobfreeMTD_MAX_OOBFREE_ENTRIES:显示定义空闲的oob字节。四、基于MTD的NAND驱动架构1、platform_device和platform_driver的 定义和注册对于我们的NAND driver,以下是一个典型的例子:static struct platform_driver caorr_nand_driver = .driver = .name = caorr-nand,.owner = THIS_MODULE,.probe = caorr_nand_probe,.remove = caorr_nand_remove,;static int _init caorr_nand_init(void)printk(CAORR NAND Driver, (c) 2008-2009.n);return platform_driver_register(&caorr_nand_driver);static void _exit caorr_nand_exit(void)platform_driver_unregister(&caorr_nand_driver);module_init(caorr_nand_init);module_exit(caorr_nand_exit);与大多数嵌入式Linux驱 动一样,NAND驱动也是从module_init宏 开始。caorr_nand_init是驱动初始化函数,在此函数中注册platform driver结构体,platform driver结构体中自然需要定义probe和remove函数。其实在大多数嵌入式Linux驱 动中,这样的套路基本已经成了一个定式至于module_init有 什么作用,caorr_nand_probe又是何时调用的,以及这个driver是怎么和NAND设备联系起来的, 就不再多说了,这里只提三点:A、以 上代码只是向内核注册了NAND的platform_driver, 即caorr_nand_driver,我们当然还需要一个NAND的platform_device,要 不然caorr_nand_driver的probe函 数就永远不会被执行,因为没有device需要这个driver。B、 向Linux内核注册NAND的platform_device有两种方式:其一是直接定义一个NAND的platform_device结构体,然后调用platform_device_register函 数注册。作为例子,我们可以这样定义NAND的platform_device结 构体:struct platform_device caorr_nand_device = .name = caorr-nand, .id = -1, .num_resources = 0,.resource = NULL,.dev = .platform_data = &caorr_platform_default_nand,; platform_device_register(&caorr_nand_device);其中num_resources和resource与具体的硬件相关,主要包括一些寄存器地址范围和中断的定义。caorr_platform_default_nand待会儿再说。需要注意的是,这个platform_device中name的值 必须与platform_driver-driver-name的值完全一 致,因为platform_bus_type的match函 数是根据这两者的name值来进行匹配的。其二是用platform_device_alloc函 数动态分配一个platform_device,然后再用platform_device_add函数把这个platform_device加 入到内核中去。具体不再细说,Linux内核中有很多例子可以参考。相对来说,第一种方式更加方便和直观一点,而第二种方式则更加灵活一点。C、 在加载NAND驱动时,我们还需要向MTD Core提供一个信息,那就是NAND的分区信息,caorr_platform_default_nand主要就是起这个作用,更加详细的容后再说。2、MTD架构的简单描述MTD(memory technology device存储技术设备)是用于访问memory设备(ROM、flash)的Linux的子系统。MTD的主要目的是为了使新的memory设 备的驱动更加简单,为此它在硬件和上层之间提供了一个抽象的接口。MTD的所有源代码在/drivers/mtd子目录下。MTD设 备可分为四层(从设备节点直到底层硬件驱动),这四层从上到下依次是:设备节点、MTD设备 层、MTD原始设备层和硬件驱动层。A、Flash硬件驱动层:硬件驱动层负责驱动Flash硬件。B、MTD原始设备:原始设备层有两部分组成,一部分是MTD原始设备的通用 代码,另一部分是各个特定的Flash的数据,例如分区。用于描述MTD原始设备的数据结构是mtd_info,这其中定义了大量的关 于MTD的数据和操作函数。mtd_table(mtdcore.c)则是所有MTD原始设备的列表,mtd_part(mtd_part.c)是用于表 示MTD原始设备分区的结构,其中包含了mtd_info,因为每一个分区都是被看成一个MTD原始设备加在mtd_table中 的,mtd_part.mtd_info中的大部分数据都从该分区的主分区mtd_part-master中获得。在drivers/mtd/maps/子目录下存放的是特定的flash的数 据,每一个文件都描述了一块板子上的flash。其中调用add_mtd_device()、del_mtd_device()建立/删除 mtd_info结构并将其加入/删除mtd_table(或者调用add_mtd_partition()、del_mtd_partition() (mtdpart.c)建立/删除mtd_part结构并将mtd_part.mtd_info加入/删除mtd_table 中)。C、MTD设备层:基于MTD原始设备,linux系统可以定义出MTD的块 设备(主设备号31)和字符设备(设备号90)。MTD字符设备的定义在mtdchar.c中实现,通过注册一系列file operation函数(lseek、open、close、read、write)。MTD块设备则是定义了一个描述MTD块设备的结构 mtdblk_dev,并声明了一个名为mtdblks的指针数组,这数组中的每一个mtdblk_dev和mtd_table中的每一个 mtd_info一一对应。D、设备节点:通过mknod在/dev子目录下建立MTD字符设备节点(主 设备号为90)和MTD块设备节点(主设备号为31),通过访问此设备节点即可访问MTD字符设备和块设备。E、根文件系统:在Bootloader中将JFFS(或JFFS2)的文件 系统映像jffs.image(或jffs2.img)烧到flash的某一个分区中,在/arch/arm/mach-your/arch.c文件的 your_fixup函数中将该分区作为根文件系统挂载。F、文件系统:内核启动后,通过mount 命令可以将flash中的其余分区作为文件系统挂载到mountpoint上。以上是从网上找到的一些资料,我只是断断续续地看过一些 code,没有系统地研究过,所以这里只能讲一下MTD原始设备层与FLASH硬件驱动之间的交互。一个MTD原始设备可以通过mtd_part分割成数个MTD原 始设备注册进mtd_table,mtd_table中的每个MTD原始设备都可以被注册成一个MTD设备,有两个函数可以完成这个工作,即 add_mtd_device函数和add_mtd_partitions函数。其中add_mtd_device函数是把整个 NANDFLASH注册进MTD Core,而add_mtd_partitions函数则是把NANDFLASH的各个分区分别注册进MTD Core。add_mtd_partitions函数的原型是:int add_mtd_partitions(struct mtd_info *master,const struct mtd_partition *parts, int nbparts);其中master就是这个MTD原始设备,parts即NAND 的分区信息,nbparts指有几个分区。那么parts和nbparts怎么来?caorr_platform_default_nand就 是起这个作用了。static struct mtd_partition caorr_platform_default_nand = 0 = .name = Boot Strap,.offset = 0,.size = 0x40000, 1 = .name = Bootloader,.offset = MTDPART_OFS_APPEND,.size = 0x40000, , 2 = .name = Partition Table,.offset = MTDPART_OFS_APPEND,.size = 0x40000, , 3 = .name = Linux Kernel,.offset = MTDPART_OFS_APPEND,.size = 0x500000, , 4 = .name = Rootfs,.offset = MTDPART_OFS_APPEND,.size = MTDPART_SIZ_FULL, ,;其中offset是分区开始的偏移地址,在后4个分区我们设为 MTDPART_OFS_APPEND,表示紧接着上一个分区,MTD Core会自动计算和处理分区地址;size是分区的大小,在最后一个分区我们设为MTDPART_SIZ_FULL,表示这个NADN剩下的所有部分。这样配置NAND的分区并不是唯一的,需要视具体的系统而定,我 们可以在kernel中这样显式的指定,也可以使用bootloader传给内核的参数进行配置。另外,MTD对NAND芯片的读写主要分三部分:A、struct mtd_info中的读写函数,如read,write_oob等,这是MTD原始设备层与FLASH硬件层之间的接口;B、struct nand_ecc_ctrl中的读写函数,如read_page_raw,write_page等,主要用来做一些与ecc有关的操作;C、struct nand_chip中的读写函数,如read_buf,cmdfunc等,与具体的NAND controller相关,就是这部分函数与硬件交互,通常需要我们自己来实现。(注:这里提到的read,write_oob,cmdfunc等,其实 都是些函数指针,所以这里所说的函数,是指这些函数指针所指向的函数,以后本文将不再另做说明。)值得一提的是,struct nand_chip中的读写函数虽然与具体的NAND controller相关,但是MTD也为我们提供了default的读写函数,如果你的NAND controller比较通用(使用PIO模式),对NAND芯片的读写与MTD提供的这些函数一致,就不必自己实现这些函数了。这三部分读写函数是相互配合着完成对NAND芯片的读写的。首 先,MTD上层需要读写NAND芯片时,会调用struct mtd_info中的读写函数,接着struct mtd_info中的读写函数就会调用struct nand_chip或struct nand_ecc_ctrl中的读写函数,最后,若调用的是struct nand_ecc_ctrl中的读写函数,那么它又会接着调用struct nand_chip中的读写函数。如下图所示:以读NAND芯片为例,讲解一下这三部分读写函数的工作过程。首先,MTD上层会调用struct mtd_info中的读page函数,即nand_read函数。接着nand_read函数会调用struct nand_chip中cmdfunc函数,这个cmdfunc函数与具体的NAND controller相关,它的作用是使NAND controller向NAND 芯片发出读命令,NAND芯片收到命令后,就会做好准备等待NAND controller下一步的读取。接着nand_read函数又会调用struct nand_ecc_ctrl中的read_page函数,而read_page函数又会调用struct nand_chip中read_buf函数,从而真正把NAND芯片中的数据读取到buffer中(所以这个read_buf的意思其实应该是read into buffer,另外,这个buffer是struct mtd_info中的nand_read函数传下来的)。read_buf函数返回后,read_page函数就会对 buffer中的数据做一些处理,比如校验ecc,以及若数据有错,就根据ecc对数据修正之类的,最后read_page函数返回到nand_read 函数中。对NAND芯片的其它操作,如写,擦除等,都与读操作类似。五、NAND驱 动中的probe函数对于很多嵌入式Linux的 外设driver来说,probe函 数将是我们遇到的第一个与具体硬件打交道,同时也相对复杂的函数。而且根据我的经验,对于很多外设的driver来 说,只要能成功实现probe函数,那基本上完成这个外设的driver也就成功了一多半,基于MTD的NAND driver就是一个典型的例子。稍后就可以看到,在NAND driver的probe函数中,就 已经涉及到了对NAND芯片的读写。在基于MTD的NAND driver的probe函数中, 主要可以分为两部分内容,其一是与很多外设driver类似的一些工作,如申请地址,中断,DMA等资源,kzalloc及初始化一些结 构体,分配DMA用的内存等等;其二就是与MTD相 关的一 些特定的工作,在这里我们将只描述第二部分内容。1、probe函数中与MTD相关的结构体在probe函 数中,我们需要为三个与MTD相关的结构体分配内存以及初始化,它们是struct mtd_info、struct mtd_partition和struct nand_chip。其中前两者已 经在前面做过说明,在此略过,这里只对struct nand_chip做一些介绍。struct nand_chip是一个与NAND芯片密切相关的结构体,主要包含三方面内 容:A. 指 向一些操作NAND芯片的函数的指针,稍后将对这些函数指针作一些说明;B.表 示NAND芯片特性的成员变量,主要有:unsigned int options:与具体的NAND芯片相关的一些选项,如NAND_NO_AUTOINCR,NAND_BUSWIDTH_16等, 至于这些选项具体表示什么含义,可以参考, 那里有较为详细的说明;int page_shift:用位表示的NAND芯片的page大小,如某片NAND芯片的一个page有512 个字节,那么page_shift就是9;int phys_erase_shift:用位表示的NAND芯片的每次可擦除的大 小,如某片NAND芯片每次可擦除16K字 节(通常就是一个block的大小),那么phys_erase_shift就 是14;int bbt_erase_shift:用位表示的bad block table的 大小,通常一个bbt占用一个block, 所以bbt_erase_shift通常与phys_erase_shift相 等;int chip_shift:用位表示的NAND芯片的容量;int numchips:表示系统中有多少片NAND芯片;unsigned long chipsize:NAND芯片的大小;int pagemask:计算page number时的掩码,总是等于chipsize/page大小 1;int pagebuf:用来保存当前读取的NAND芯片的page number,这样一来,下次读取的数据若还是属于同一个page,就不必再从NAND芯片读取了,而是 从data_buf中直接得到;int badblockpos:表示坏块信息保存在oob中的第几个字节。在每个block的第一个page的oob中,通常用1或2个字节来表示这是否为一个坏块。对于绝大多数的NAND芯 片,若page size 512,那么坏块信息从Byte 0开始存储,否则就存储在Byte 5, 即第六个字节。C. 与ecc,oob和bbt (bad block table)相关的一些结构体,对于坏块及坏块管理,将在稍后做专门介 绍。2、对NAND芯 片进行实际操作的函数前面已经说过,MTD为我们提供了许多default的操作NAND的函数,这些函数与具体的硬件(即NAND controller)相关,而现有的NAND controller都有各自的特性和配置方式,MTD当然不可能为所有的NAND controller都提供一套这样的函数,所以在MTD中 定义的这些函数只适用于通用的NAND controller(使用PIO模式)。如果你的NAND controller在操作或者说读写NAND时 有自己独特的方式,那就必须自己定义适用于你的NAND controller的函数。一般来 说,这些与硬件相关的函数都在struct nand_chip结构体中定义,或者应该说是给 此结构体中的函数指针赋值。为了更好的理解,我想有必要对struct nand_chip中 几个重要的函数指针做一些说明。 struct nand_chip void _iomem *IO_ADDR_R;void _iomem *IO_ADDR_W;uint8_t (*read_byte)(struct mtd_info *mtd);u16 (*read_word)(struct mtd_info *mtd);void (*write_buf)(struct mtd_info *mtd, const uint8_t *buf, int len);void (*read_buf)(struct mtd_info *mtd, uint8_t *buf, int len);int (*verify_buf)(struct mtd_info *mtd, const uint8_t *buf, int len);void (*select_chip)(struct mtd_info *mtd, int chip);int (*block_bad)(struct mtd_info *mtd, loff_t ofs, int getchip);int (*block_markbad)(struct mtd_info *mtd, loff_t ofs);void (*cmd_ctrl)(struct mtd_info *mtd, int dat, unsigned int ctrl);int (*dev_ready)(struct mtd_info *mtd);void (*cmdfunc)(struct mtd_info *mtd, unsigned command, int column, int page_addr);int (*waitfunc)(struct mtd_info *mtd, struct nand_chip *this);void (*erase_cmd)(struct mtd_info *mtd, int page);int (*scan_bbt)(struct mtd_info *mtd);int (*errstat)(struct mtd_info *mtd, struct nand_chip *this, int state, int status, int page);int (*write_page)(struct mtd_info *mtd, struct nand_chip *chip, const uint8_t *buf, int page, int cached, int raw);struct nand_ecc_ctrl ecc;IO_ADDR_R和IO_ADDR_W:8位NAND芯片的读写地址,如果你的NAND controller是用PIO模式与NAND芯片交互,那么只要把这两个值赋上合适的地址,就完全可以使用MTD提供的default的读写函数来操作NAND芯片了。所以这两个变量视具体的NAND controller而定,不一定用得着;read_byte和read_word:从NAND芯片读一个字 节或一个字,通常MTD会在读取NAND芯 片的ID,STATUS和OOB中的坏块信息时调用这两个函数,具体是这样的流程,首先MTD调 用cmdfunc函数,发起相应的命令,NAND芯 片收到命令后就会做好准备,最后MTD就会调用read_byte或read_word函数从NAND芯片中读取 芯片的ID,STATUS或者OOB;read_buf、write_buf和verify_buf: 分别是从NAND芯片读取数据到buffer, 把buffer中的数据写入到NAND芯 片,和从NAND芯片中读取数据并验证。调用read_buf时 的流程与read_byte和read_word类 似,MTD也是先调用cmdfunc函 数发起读命令(如NAND_CMD_READ0命 令),接着NAND芯片收到命令后 做好准备,最后MTD再调用read_buf函 数把NAND芯片中的数据读取到buffer中。 调用write_buf函数的流程与read_buf相 似;select_chip: 因为系统中可能有不止一片NAND芯片,所以在对NAND芯 片进行操作前,需要这个函数来指定一片NAND芯片;cmdfunc: 向NAND芯片发起命令;waitfunc:NAND芯片在接收到命令后,并不一定能立即响应NAND controller的下一步动作,对有些命令,比如erase,program等命令,NAND芯片需要一定的 时间来完成,所以就需要这个waitfunc来等待NAND芯 片完成命令,并再次进入准备好状态;write_page: 把一个page的数据写入NAND芯 片,这个函数一般不需我们实现,因为它会调用struct nand_ecc_ctrl中的write_page_raw或者write_page函 数,关于这两个函数将在稍后介绍。以上提到的这些函数指针,都是REPLACEABLE的,也就是说都是可以被替换的,根据你的NAND controller,如果你需要自己实现相应的函数,那么只需要把你的函数赋值给这些函数指针就可以了,如果你没有赋值,那么MTD会把它自己定义的default的函数 赋值给它们。顺便提一下,以上所说的读写NAND芯片的流程并不是唯一的,如果你的NAND controller在读写NAND芯片时有自己独特的方式,那么完全可以按照 自己的方式来做。就比如我们公司芯片的NAND controller,因为它使用DMA的方式从NAND芯片中读写数据,所以 在我的NAND driver中,读数据的流程是这样的:首先在cmdfunc函数中初始化DMA专用的buffer,配置NAND地址,发起命令 等,在cmdfunc中我几乎做了所有需要与NAND芯 片交互的事情,总之等cmdfunc函数返回后,NAND芯 片中的数据就已经在DMA专用的buffer中 了,之后MTD会再调用read_buf函 数,所以我的read_buf函数其实只是把数据从DMA专 用的buffer中,拷贝到MTD提 供的buffer中罢了。最后,struct nand_chip结构体中还包含了一个很重要的结构体,即struct struct nand_ecc_ctrl,该结构体中也定义了几个很重要的函数指针。它的定义如下:struct nand_ecc_ctrl void (*hwctl)(struct mtd_info *mtd, int mode);int (*calculate)(struct mtd_info *mtd, const uint8_t *dat, uint8_t *ecc_code);int (*correct)(struct mtd_info *mtd, uint8_t *dat, uint8_t *read_ecc, uint8_t *calc_ecc);int (*read_page_raw)(struct mtd_info *mtd, struct nand_chip *chip, uint8_t *buf);void (*write_page_raw)(struct mtd_info *mtd, struct nand_chip *chip, const uint8_t *buf);int (*read_page)(struct mtd_info *mtd, struct nand_chip *chip, uint8_t *buf);void (*write_page)(struct mtd_info *mtd, struct nand_chip *chip, const uint8_t *buf);int (*read_oob)(struct mtd_info *mtd, struct nand_chip *chip, int page, int sndcmd);int (*write_oob)(struct mtd_info *mtd, struct nand_chip *chip, int page);

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

评论

0/150

提交评论