原创 如何编写linux下nand flash驱动

2008-7-29 10:03 3529 4 4 分类: MCU/ 嵌入式

【简介】如何编写linuxnand flash驱动


Version: V0.1


Time:10/06/2008


Authorgreen-waste@163.com


【编写驱动之前要了解的知识】


1.硬件方面:


Flash的种类】


Flash主要分nand flashnor flash


除了网上最流行的这个解释之外:


NANDNOR的比较


我再多说几句,nor的成本相对高,比较适合应用于存储少量的代码。


Nand flash相对成本低,因此可以用来存储大量的数据,其在嵌入式系统中的作用,相当于PC上的硬盘,用于存储大量数据。


Nor flash,有类似于dram之类的地址总线,因此可以直接和CPU相连,CPU可以直接通过地址总线对nor flash进行访问,而nand flash没有这类的总线,只有IO接口,只能通过IO接口发送命令和地址,对nand flash内部数据进行访问。相比之下,nor flash就像是并行访问,nand flash就是串行访问,所以相对来说,前者的速度更快些。


但是由于物理制程/制造方面的原因,导致nor nand在一些具体操作方面的特性不同:



Nand Nor Flash 的区别



 


NOR


NAND


(备注)


接口


总线


I/O接口


这个两者最大的区别


单个cell大小




 


单个Cell成本




 


读耗时




 


单字节的编程时间




 


多字节的编程时间




 


擦除时间




 


功耗



低,但是需要额外的RAM


 


是否可以执行代码



不行, 但是一些新的芯片,可以在第一页之外执行一些小的loader


即是否允许,芯片内执行(XIP, eXecute In Place)


位反转Bit twiddling


几乎无限制


1-3次,也称作 “部分页编程限制”


也就是数据错误了?


在芯片出厂时候是否允许坏块


不允许


允许


 


 


Nand Flash的种类】


具体再分,又可以分为


1)Bare NAND chips:裸片,单独的nand 芯片


2)SmartMediaCards =裸片+一层薄塑料,常用于数码相机和MP3播放器中。之所以称smart,是由于其软件smart,而不是硬件本身有啥smart之处。^_^


3)DiskOnChip:裸片+glue logicglue logic=硬件ECC产生器+用于静态的nand 芯片控制的寄存器+直接访问一小片地址窗口,那块地址中包含了引导代码的stub桩,其可以从nand flash中拷贝真正的引导代码。


 


Nand flash的特点】


Nand flash的操作,和其他一些常见的设备,如硬盘等,不同,其有自己特殊的方式。


其特殊就在于:


1.Nand flash的最小单位是页page,而不是其他很多设备所说的位bit


2.写入数据之前必须先进行擦除erase操作


3.写的时候,最小单位是页page,对也进行写操作,也称作“页编程”,page programming


4.擦除的最小单位是块block


5.由于物理特性,容易出错,所以无论是读还是写,都要采取检测和校验,即EDC


6.nand flash出厂时候,就有一定坏的块block,成为换块,并且做了一定标记。


7.nand flash中有个额外的空间,叫做spare area/oob


 


spare area/oob


Nand由于最初硬件设计时候考虑到,额外的错误校验等需要空间,专门对应每个页,额外设计了叫做spare area空区域,在其他地方,比如jffs2文件系统中,也叫做oobout of band)数据。


其具体用途,总结起来有:


1.       标记是否是坏快


2.       存储ECC数据


3.       存储一些和文件系统相关的数据,如jffs2就会用到这些空间存储一些特定信息


 


 


【常见Nand Flash的大小及参数】


常见的nand flash 的大小,由最开始的小于256M,到现在的常见的1G2G,甚至更大。


以前的nand flash


Pagesize页大小,多为512B+16Boobblock大小为64*512B+16B=32KB+1KB


现在目前市场上见到的,绝大多数,都是新的nand falsh,其Pagesize页大小多为2KB+64Boobblock大小多为64pages=64*2K+64B=128KB+4KB,一个nand flash中的芯片,一般含有4096个块,比如samsungK9F4G08U0M,所以这个nand flash大小就是


4096 Blocks = 4096 * 64 *2K+64B=512MB


即:


1 Page = (2K + 64)Bytes


1 Block = (2K + 64)B x 64 Pages


= (128K + 4K) Bytes


1 Device = (2K+64)B x 64Pages x 4,096 Blocks


= 4,224 Mbits =512MB


 


Nand flash工作原理】


所谓工作原理,其实也就是对应对其如何操作的。


还是以上面提到的samsungK9F4G08U0Mnand flash为例,简单描述如下:


1.nand flash定义了一些引脚,使得你可以发送命令过去,实现对nand flash的操控:


点击看大图


上面这些引脚,需要解释的主要是


CLE,使能,只有使能有效,你才能进行后续的操作。只能选中了这个nand芯片,才能进行后续的读写。


ALE,在发送地址时,要锁住地址总线,才可以操作。


REWE,在读写之前,要对应的引脚有效,才可以进行读写的。


其他见解释皆可。


 


所有如上的引脚,都是驱动中,通过发送命令,具体内部控制逻辑去实现的。


而驱动开发者要关心的,是何时去发送对应的命令。具体想要实现一操作,要何时发送什么命令才能实现,要继续看下面的datasheet中的解释:


 


如果想要对nand flash操作,就要根据datasheet中的规范进行,比如对页的写操作/写编程要根据这个顺序去操作:


3955567847715587543.jpg


其具体的细节,包含了哪些操作,可以看这个时序图:


点击看大图


从上图可以看出来,要先发80h的命令,再继续后面的操作。


这里的80h命令的含义,参考这个图表:


点击看大图


从这个图表,可以看到,80h就是page program的第一个命令。


而时序图中的先两个col列地址,后三个row行地址,是根据下面这个图得来的:


点击看大图


此时才能定位到你所要操作的页,才能进行写操作。


再多说几句,上图表示了,如果想要对这个nand flash操作,读或写,需要进行3次列地址和三次的行地址,才能定位到你所要操作的地方,才能读写。同时显示了,每次每个位,对应代表的地址位。


此处的2个列地址和3个行地址,是由硬件设计决定的。


老的nand flash由于每个页只有512B,所以,最后的操作,只需要1个列地址和3个行地址就可以定位了。感兴趣的可以去网上下载samsung一些老的nand flash,可以对比着看,更明白些。


 


而每个操作,比如上面的写操作,都是需要一定时间的,其具体硬件操作所需的时间,见这个图:




点击看大图

此处可以看到,写一个页,即页编程,一般需要200us,最大需要700us,所以驱动开发者在开发的时候,要知道这个细节,要等待对应的时间,再去判断,是否写操作顺利完成了,才能接下来继续后面其他的操作的。


 


上述的具体代码实现,可以去看


drivers\mtd\nand\nand_base.c中的nand_command_lp()函数中的代码:


 


/* Command latch cycle */


//对命令锁存,然后才能发送命令


       chip->cmd_ctrl(mtd, command & 0xff,


                     NAND_NCE | NAND_CLE | NAND_CTRL_CHANGE);


 


       if (column != -1 || page_addr != -1) {


              int ctrl = NAND_CTRL_CHANGE | NAND_NCE | NAND_ALE;


 


//先发送2col地址,构成整个列地址


              /* Serially input address */


              if (column != -1) {


                     /* Adjust columns for 16 bit buswidth */


                     if (chip->options & NAND_BUSWIDTH_16)


                            column >>= 1;


                     chip->cmd_ctrl(mtd, column, ctrl);


                     ctrl &= ~NAND_CTRL_CHANGE;


                     chip->cmd_ctrl(mtd, column >> 8, ctrl);


              }


//再发送3row地址,构成整个行地址


              if (page_addr != -1) {


                     chip->cmd_ctrl(mtd, page_addr, ctrl);


                     chip->cmd_ctrl(mtd, page_addr >> 8,


                                   NAND_NCE | NAND_ALE);


                     /* One more address cycle for devices > 128MiB */


                     if (chip->chipsize > (128 << 20))


                            chip->cmd_ctrl(mtd, page_addr >> 16,


                                          NAND_NCE | NAND_ALE);


              }


       }


这样就可以定位到我们要操作的位置,进行操作了。


 


2.软件方面:


Linux驱动原理


具体内部很多实现,已经包含在drivers\mtd\nand\nand_base.c中了


nand flash驱动加载识别nand类型过程】


在驱动加载的时候,会去调用:


nand_get_flash_type()


其中,就会对nand的类型和其他相关参数进行检查。


1)  选中对应设备,如果此时只有一个nand 芯片,则此步可以省略


       /* Select the device */


       chip->select_chip(mtd, 0);


x


2)  发读命令,去读取设备类型代码


       /* Send the command for reading device ID */


       chip->cmdfunc(mtd, NAND_CMD_READID, 0x00, -1);


3)  判断是哪个厂商的,哪个类型的flash


       /* Read manufacturer and device IDs */


       *maf_id = chip->read_byte(mtd);


       dev_id = chip->read_byte(mtd);


 


4)  在事先已经定义好的nand flash类型中查找属于何种厂商和型号


       /* Lookup the flash id */


       for (i = 0; nand_flash_ids.name != NULL; i++) {


              if (dev_id == nand_flash_ids.id) {


                     type =  &nand_flash_ids;


                     break;


              }


       }


 


5)  继续判断具体nand flash的各个参数,包括


芯片信息,Pagesize页大小,oobsizeoob的大小,blocksize块大小,buswidth总线宽度是8位还是16位。


 


如果页大小不是之前老的nand512B,而是新的nand2K或更大,则后面对应的发送给nand flash命令的的时候,调用的函数就由nand_command()变成nand_command_lp()了。


后者主要比前者多一发个命令:


chip->cmd_ctrl(mtd, column >> 8, ctrl);


即,多发一个列地址命令。


因为大页面(>2KB)的寻址需要2column,而小页面(512B)只需要1次的列地址。


具体可以参考nand flashdatasheet


 


6)  接着会做一些其他初始化操作,包括最后调用nand_set_defaults()去实现的默认函数的挂载,如果你的nand flash驱动没有实现的话,就是挂载默认的了。


 


 


【模块加载原理】


这个内容太大,此处只是简单说说。


驱动加载的功能主要是probe函数实现的,主要去识别设备的类型和各个参数,并且为设备的使用进行正常的初始化。


对应卸载时候执行的remove函数,施放对应的,之前申请的一些资源。


 


MTD设备】


Linux下,将nand Flash等设备归属到MTD设备下进行统一管理。


Mtd,即memory technology deveice,即将nand看出是存储设备来管理。


之所以会这么说和这么做,是因为前面提高的nand flash和普通硬盘等设备的特殊性:


IO接口,最小单位是页,写前需擦除等,导致了,不能像平常对待硬盘等操作一样去操作nand flash,只能采取一些特殊方法,这就诞生了mtd设备的统一抽象层,将nand flashnor flash和其他类型的flash等设备,统一抽象成mtd设备来管理,根据这些设备的特点,上层实现了常见的操作函数封装,底层具体的内部实现,就需要驱动设计者自己来实现了。


 


MTD设备和硬盘设备之前的区别



HARD drives


MTD device


连续的扇区


连续的可擦除块


扇区都很小(512B,1024B)


可擦除块比较大 (32KB,128KB)


主要通过两个操作对其维护操作:读扇区,写扇区


主要通过三个操作对其维护操作:从擦除块中读,写入擦除块,擦写可擦除块


坏快被重新映射,并且被硬件隐藏起来了(至少是在如今常见的LBA硬盘设备中是如此)


坏的可擦除块没有被隐藏,软件中要处理对应的坏块问题。


HDD扇区没有擦写寿命超出的问题。


可擦除块是有擦除次数限制的,大概是104-105.


 


Linuxnand flash驱动编写步骤简介】


1.       了解硬件的nand flash的各个参数和工作原理


具体参考nand flashdatasheet,主要包括,自己nand flash的厂商,型号等。


Nand flash的页大小,oob大小,块大小,位宽8bit还是16bit


工作原理,上面已经做了一定描述,不清楚的,可以参考datasheet,多看看,就会明白很多。


 


2.       按照linux下驱动编写规范编写nand flash驱动,


可以参考其他已经有的驱动,比如内核源码中已经有的


drivers\mtd\nand\s3c2410.c


就是个很好的例子。


自己以其为模板,实现自己板子的nand flash驱动。


其实主要工作就是,实现


static struct platform_driver s3c2410_nand_driver = {


       .probe            = s3c2410_nand_probe,


       .remove          = s3c2410_nand_remove,


       .suspend  = s3c24xx_nand_suspend,


       .resume          = s3c24xx_nand_resume,


       .driver            = {


              .name      = "s3c2410-nand",


              .owner    = THIS_MODULE,


       },


};


中的


XXX_nand_probe函数


XXX_nand_remove函数


XXX_nand_enable_hwecc,如果支持硬件ecc的话。


nand flash的读写,这两个函数,实现了对nand的具体操作。


 


LinuxNand Flash驱动编写简单步骤】


软件和硬件知识,都已经了解的话,由于上层的linux mtd框架中,已经完全封装好了,对nand flashwrite pagewrite oob等相关函数的实现,那么剩下的只是相对来说已经是很少量的,关于nand 驱动具体内部操作方面的工作:


1.初始化


先是在nand 芯片初始化的时候,对其


XXX_nand_init_chip()


给对应的芯片chip赋给对应的


XXX_nand_read_bufXXX_nand_write_buf等函数:


              chip->cmd_ctrl  = XXX_nand_hwcontrol;


              chip->dev_ready = XXX_nand_devready;


              chip->read_buf  = XXX_nand_read_buf;


              chip->write_buf  = XXX_nand_write_buf;


以实现后续的对nand芯片的操作。


 


然后根据ecc类型,赋给对应的ecc的校验与纠错函数:


                    chip->ecc.hwctl     = XXX_nand_enable_hwecc;


                    chip->ecc.calculate = XXX _nand_calculate_ecc;


 


3.       实现上面提到的对应的各个函数,关于如何实现,参考一下其他nand驱动,就会理解很多了。


4.       驱动测试,参考具体的 ldd3Linux Device Driver version 3)的测试相关部分内容。


 


说得很乱,希望对大家有些帮助。


 


MTD设备驱动推荐文章】


关于mtd设备驱动,感兴趣的可以去参考


MTD原始设备与FLASH硬件驱动的对话


MTD原始设备与FLASH硬件驱动的对话-


那里,算是比较详细地介绍了整个流程,方便大家理解整个mtd框架和nand flash驱动。


 


【引用文章】


Brief Intro of Nand Flash


Samsung K9F4G08U0M.pdf Samsung Nand Flash Datasheet nand型号是K9F4G08U0M


nand falsh read operation


PARTNER CONTENT

文章评论0条评论)

登录后参与讨论
EE直播间
更多
我要评论
0
4
关闭 站长推荐上一条 /3 下一条