
基于mtd的nandflash设备驱动底层实现原理分析.doc
61页基于 MTD 的 NANDFLASH 设备驱动底层实现原理分析 (一) 经过 UBOOT 初步的移植,Linux 内核初步的移植,Linux 内核总线设备模型的分析,等一系列痛苦的折腾,目的就是想更好的来分析下 NANDFLASH 的驱动大概一共历经了半个月的时间,慢慢的对 NANDFLASH 驱动程序有感觉了一、MTD 体系结构:Linux 内核提供 MTD 子系统来建立 FLASH 针对 Linux 的统一、抽象接口MTD 将文件系统与底层的 FLASH 存储器进行隔离引入 MTD 后 Linux 系统中对 FLASH 的设备驱动分为 4 层设备节点:用户在/dev 目录下使用 mknod 命令建立 MTD 字符设备节点(主设备号为 90),或者 MTD 块设备节点(主设备号为 31),使用该设备节点即可访问 MTD 设备MTD 设备层:基于 MTD 原始设备层,系统将 MTD 设备可以定义为 MTD 字符(在/mtd/mtdchar.c 中实现,设备号 90)和 MTD 块设备(在/mtd/mtdblock.c 中实现,设备号 31)MTD 原始设备层:MTD 原始设备层由两部分构成,一部分是 MTD 原始设备的通用代码,另一部分是各个特定 Flash 的数据,如分区。
主要构成的文件有:drivers/mtd/mtdcore.c 支持 mtd 字符设备driver/mtd/mtdpart.c 支持 mtd 块设备Flash 硬件驱动层:Flash 硬件驱动层负责对 Flash 硬件的读、写和擦除操作MTD 设备的 Nor Flash 芯片驱动位于 drivers/mtd/chips/子目录下,Nand Flash 芯片的驱动则位于drivers/mtd/nand/子目录下 二、Linux 内核中基于 MTD 的 NANDFLASH 驱动代码布局:在 Linux2.6.35 内核中, MTD 源代码放在 driver/mtd 目录中,该目录中包含chips、devices、maps、nand 、onenand、lpdrr、tests 和 ubi 八个子目录其中只有 nand 和 onenand 目录中的代码才与 NAND 驱动相关,不过 nand 目录中的代码比较通用,而 onenand 目录中的代码相对于 nand 中的代码而言则简化了很多,它是针对三星公司开发的另一类 Flash 芯片,即 OneNAND Flash本文我们需要关注的代码是 linux-2.6.35/drivers/mtd/nand 目录中,在该目录中我们关心的文件如下: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,则该文件也不需理会上面这些内容我是 Copy 别人的我觉得写得太好了,因为一开始我真的很迷茫, 在 nand 目录下有那么多的文件,到底哪个是值得我读的.我真的不值得,读了这个大神的博客后对NANDDLASH 的驱动我不再是那么的迷茫 三、NANDFLASH 的硬件特性要想读懂后面 Linux 系统中对 NANDFLASH 硬件驱动代码,了解 NANDFLASH 的硬件特性这是再好不过的。
1、NANDFLASH 的内部布局2、Nand Flash 的物理存储单元的阵列组织结构 (以开发板上的 K9F2G08 为例) K9F2G08 的大小是 256Ma)block:"Block 是 Nand Flash 的擦除操作的基本/ 最小单位", 一片 NANDFLASH(chip)由很多块(block)组成,块的大小一般是 128KB, 256KB,512KB,此处是 128KB其他的小于 128KB 的,比如 64KB 称之为 small block 的 Nand Flashb)page:"page 是读写操作的最小单位",每一个 block 里面包又含了许多 page(页),每个页的大小,对于现在常见的 Nand Flash 多数是 2KB,最新的 Nand Flash 的是 4KB、8KB 等,这类的页大小大于2KB 的 NandFlash,被称作 big block 的 Nand Flash,对应的发读写命令地址,一共 5个周期(cycle),而老的 Nand Flash,页大小是 256B,512B,,这类的 Nand Flash 被称作 small block的 nandflash地址周期只有 4 个。
c)oob:每一个页,对应还有一块区域,叫做空闲区域(spare area)/冗余区域(redundant area)而Linux 系统中,一般叫做 OOB(Out Of Band),这个区域,是最初基于 Nand Flash 的硬件特性:数据在读写时候相对容易错误,所以为了保证数据的正确性,必须要有对应的检测和纠错机制,此机制被叫做 EDC(Error Detection Code)/ECC(Error Code Correction, 或者Error Checking and Correcting),所以设计了多余的区域,用于放置数据的校验值 Oob 的读写操作,一般是随着页的操作一起完成的,即读写页的时候,对应地就读写了 oob 关于 oob 具体用途,总结起来有: 1、 标记是否是坏快 2、存储 ECC 数据 3、存储一些和文件系统相关的数据如 jffs2 就会用到这些空间存储一些特定信息,4、而 yaffs2 文件系统,会在 oob 中,存放很多和自己文件系统相关的信息 3、K9F2G08 的引脚定义IO7~IO0:用于输入地址/数据/ 命令,输出数据CLE:命令锁存使能位,在发送命令之前要先将模式寄存器中设置 CLE 使能(高电平有效)。
ALE:地址锁存使能位,在发送地址之前, 要先将模式寄存器中设置 ALE 使能(高电平有效 )CE:(nFCE)芯片的片选信号, 操作 nandflash 前应该拉低该位使之选中该芯片RE:(nFRE)读使能, 低电平有效,读之前使 CE 有效WE:(nFWE) 写使能 ,低电平有效,写之前必须使 WE 有效 WP:写保护低电平有效R/B:(R/nB)Ready/Busy Output,就绪/忙, 主要用于在发送完编程/ 擦除命令后, 检测这些操作是否完成,忙, 表示编程/ 擦除操作仍在进行中, 就绪表示操作完成其中就绪:高电平, 忙:低电平)基于 MTD 的 NANDFLASH 设备驱动底层实现原理分析 (二) 四、常见的 NANDFLASH 的操作1、要实现对 Nand Flash 的操作,比如读取一页的数据,写入一页的数据等,都要发送对应的命令,而且要符合硬件的规定,如图:比如说要实现读一页的数据,就要发送 Read 命令,而且分两个周期发送,即分两次发送对应的命令, 第一次是 0x00h,第二次是 0x30h,而两次命令中间,需要发送对应的你所要读取的页的地址,对应地,其他常见的一些操作,比如写一个页的数据(Page Program),就是先发送 0x80h,然后发送要写入的地址,再发送 0x10h。
2、读(Read)nandflash 操作过程分析1)红色竖线穿过的第一行,是 CLE前面介绍命令所存使能 (CLE)的那个引脚将 CLE 置 1,就说明你将要通过 I/O 复用端口发送进入 Nand Flash 的,是命令,而不是地址或者其他类型的数据只有这样将 CLE 置 1,使其有效,才能去通知了内部硬件逻辑,你接下来将收到的是命令,内部硬件逻辑才会直到收到的是命令,放到命令寄存器中,才能实现后面正确的操作,否则,不去将 CLE 置 1 使其有效硬件会无所适从,不知道你传入的到底是数据还是命令了 2)而第二行,是 CE,那一刻的值是 0这个道理很简单,你既然要向 Nand Flash 发命令,那么先要选中它,所以,要保证 CE 为低电平,使其有效,也就是片选有效3)第三行是 WE,意思是写使能因为接下来是往 Nand Flash 里面写命令,所以,要使得 WE 有效所以设为低电平4)第四行,是 ALE 是低电平,而 ALE 是高电平有效,此时意思就是使其无效而对应地,前面介绍的使 CLE 有效,因为将要数据的是命令(此时是发送图示所示的读命令第二周期的 0x30) ,而不是地址。
如果在其他某些场合,比如接下来的要输入地址的时候,就要使其有效,而使 CLE 无效了5)第五行,RE,此时是高电平,无效可以看到,知道后面低 6 阶段,才变成低电平,才有效,因为那时候要发生读取命令,去读取数据6)第六行,就是我们重点要介绍的,复用的输入输出 I/O 端口了,此刻,还没有输入数据,接下来,在不同的阶段,会输入或输出不同的数据/地址7)第七行,R/B,高电平,表示 R(Ready)/ 就绪,因为到了后面的第 5 阶段,硬件内部,在第四阶段,接受了外界的读取命令后,把该页的数据一点点送到页寄存器中,这段时间,属于系统在忙着干活,属于忙的阶段所以,R/B 才变成低,表示 Busy 忙的状态的其他的时序的就类似的理解3、计算我们要读取或者写入的行地址和例地址以 mini2440 开发板上的 K9F2G08 为例,此 Nand Flash,一共有 2048 个块,每个块内有 64 页 ,每个页是 2K+64 Bytes假设,我们要访问其中的第 1000 个块中的第 25 页中的 1208 字节处的地址,此时,我们就要先把具体的地址算出来: 物理地址=块大小 *块号 + 页大小* 页号 。
