nand_chip的主要数据结构
IO_ADDR_R/IO_ADDR_W
读/写8根io线的地址 |
read_byte/read_word |
从芯片读一个字节/字 |
read_buf/write_buf |
读芯片读取内容至缓冲区/将缓冲区内容写入芯片 |
verify_buf |
select_chip |
block_bad |
检查是否坏块 |
block_markbad |
标识坏块 |
cmd_ctrl |
硬件相关控制函数 |
init_size |
dev_ready |
cmdfunc |
命令处理函数 |
waitfunc |
erase_cmd |
擦除命令 |
scan_bbt |
扫描坏块 |
errstat |
write_page |
options |
与具体的NAND 芯片相关的选项,如NAND_USE_FLASH_BBT等(nand.h) |
page_shift |
nand_ecclayout类型结构体,ECC布局信息 |
ecc |
nand_ecc_ctrl类型结构体,ECC控制结构 |
? |
nand_ecclayout的主要数据结构
eccbytes
ecc的字节数(For 512B-per-page,eccbytes is 3) |
eccpos |
ecc数据在oob中的位置 |
oobavail |
oob中可用的字节数,MTD 会根据其它三个变量自动计算得到 |
oobfree |
nand_oobfree类型结构体,显示定义空闲的oob 字节 |
? |
MTD相关层实现
MTD设备层
mtd字符设备接口:? mtdchar.c 实现了字符设备接口,通过它,用户可以直接操作Flash 设备。? ? 通过read()、write()系统调用可以读写Flash。? ? 通过一系列IOCTL 命令可以获取Flash 设备信息、擦除Flash、读写NAND 的OOB、获取OOB layout 及检查NAND 坏块等(MEMGETINFO、MEMERASE、MEMREADOOB、MEMWRITEOOB、MEMGETBADBLOCK IOCRL)? tip: mtd_read和mtd_write直接直接调用mtd_info的read 函数,因此,字符设备接口跳过patition这一层
mtd块设备接口:? 主要原理是将Flash的erase block 中的数据在内存中建立映射,然后对其进行修改,最后擦除Flash 上的block,将内存中的映射块写入Flash 块。整个过程被称为read/modify/erase/rewrite 周期。? 但是,这样做是不安全的,当下列操作序列发生时,read/modify/erase/poweroff,就会丢失这个block 块的数据。? 块设备模拟驱动按照block 号和偏移量来定位文件,因此在Flash 上除了文件数据,基本没有额外的控制数据。
?
MTD原始设备层
MTD硬件驱动层
1. NOR Flash驱动结构
Linux系统实现了针对cfi,jedec等接口的通用NOR Flash驱动? 在上述接口驱动基础上,芯片级驱动较简单? ???? 定义具体内存映射结构体map_info,然后通过接口类型后调用do_map_probe()?? 以h720x-flash.c为例(位于drivers/mtd/maps)? - 定义map_info结构体,初始化成员name,size,phys,bankwidth? - 通过ioremap映射成员virt(虚拟内存地址)? - 通过函数simple_map_init初始化map_info成员函数read,write,copy_from,copy_to? - 调用do_map_probe进行cfi接口探测,返回mtd_info结构体? - 通过parse_mtd_partitions,add_mtd_partitions注册mtd原始设备
2. NAND Flash驱动结构
?
Linux实现了通用NAND驱动(drivers/mtd/nand/nand_base.c)? tip: For more,check?内核中的NAND代码布局?? 芯片级驱动需要实现nand_chip结构体? MTD使用nand_chip来表示一个NAND FLASH芯片,该结构体包含了关于Nand Flash的地址信息,读写方法,ECC模式,硬件控制等一系列底层机制。? ? NAND芯片级初始化? 主要有以下几个步骤:? - 分配nand_chip内存,根据目标板及NAND控制器初始化nand_chip中成员函数(若未初始化则使用nand_base.c中的默认函数),将mtd_info中的priv指向nand_chip(或板相关私有结构),设置ecc模式及处理函数? - 以mtd_info为参数调用nand_scan()探测NAND FLash。? ?? nand_scan()会读取nand芯片ID,并根据mtd->priv即nand_chip中成员初始化mtd_info? - 若有分区,则以mtd_info和mtd_partition为参数调用add_mtd_partitions()添加分区信息? -
? 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相关,就是这部分函数与硬件交互,通常需要我们自己来实现。? tip: nand_chip中的读写函数虽然与具体的NAND controller相关,但是MTD也为我们提供了默认的读写函数,如果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中的读写函数。? eg:? 以读为例? 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芯片的其它操作,如写,擦除等,都与读操作类似?
http://www.cnblogs.com/hzl6255/archive/2012/12/18/2824043.html
MTD(memory technology device内存技术设备)是用于访问memory设备(ROM、flash)的Linux的子系统。MTD的主要目的是为了使新的memory设备的驱 动更加简单,为此它在硬件和上层之间提供了一个抽象的接口。MTD的所有源代码在/drivers/mtd子目录下。我将CFI接口的MTD设备分为四层 (从设备节点直到底层硬件驱动),这四层从上到下依次是:设备节点、MTD设备层、MTD原始设备层和硬件驱动层。?
一、Flash硬件驱动层:硬件驱动层负责在init时驱动Flash硬件,Linux MTD设备的NOR Flash芯片驱动遵循CFI接口标准,其驱动程序位于drivers/mtd/chips子目录下。NAND型Flash的驱动程 序则位于/drivers/mtd/nand子目录下。
二、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 中)。?
三、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一一对应。?
四、设备节点:通过mknod在/dev子目录下建立MTD字符设备节点(主设备号为90)和MTD块设备节点(主设备号为31),通过访问此设备节点即可访问MTD字符设备和块设备。?
五、根文件系统:在Bootloader中将JFFS(或JFFS2)的文件系统映像jffs.image(或jffs2.img)烧到flash的 某一个分区中,在/arch/arm/mach-your/arch.c文件的your_fixup函数中将该分区作为根文件系统挂载。?
六、文件系统:内核启动后,通过mount 命令可以将flash中的其余分区作为文件系统挂载到mountpoint上。
http://www.cnblogs.com/hoys/archive/2012/05/30/2526230.html
//----------------------------------------------个人见解----------------------------------------
1、linux nandflash驱动主要工作在于nand_chip结构体填充(linux内核在MTD的下层实现了通用的nand驱动(主要通过drivers/mtd/nand_base.c文件实现),因此芯片级的nand驱动不再需要实现mtd_info中的read、write、read_oob等函数,而主体转移到了nand_chip数据结构体填充上)、内核flash分区表配置两部分,而nand_chip结构体填充驱动一般芯片厂商提供的开发包中会有这个驱动,例如dm365的nand_chip结构体填充驱动就是drivers/mtd/nand/davinvi-nand.c
在davinvi-nand.c驱动中,会涉及到一个ecc布局的结构体struct?nand_ecclayout如下:
static struct nand_ecclayout hwecc4_small __initconst = { .eccbytes = 10, .eccpos = { 0,1,2,3,4, /* offset 5 holds the badblock marker */ 6,7, 13,14,15,}, .oobfree = { {.offset = 8,.length = 5, {.offset = 16, }, };
nand_ecclayout结构体指针,可参考硬件手册的OOB中ecc布局。
下面是相关的一些成员介绍:
struct nand_ecclayout {
uint32_t eccbytes; ? ?//表示使用几个ecc字节
uint32_t eccpos[128]; //表示ecc占用的位置,因为现在大页面4kbyte也就128个,所以这里写了128,
?? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? //以后有更大页面的,这里也要改了。
uint32_t oobavail; ? ? ? //有几个oob可用,这个跟下面的成员有点像,一般用下面的
struct nand_oobfree oobfree[MTD_MAX_OOBFREE_ENTRIES]; ?//定义oob有效个数,从哪开始等
};
?
给个例子:
static struct nand_ecclayout mylayout = {
#ifdef CONFIG_SYS_NAND_PAGE_2K
.eccbytes = 40,
.eccpos = {?
24,25,26,27,28,51); font-family:Arial; font-size:14px; line-height:26px; margin-top:0px; margin-bottom:0px; padding-top:0px; padding-bottom:0px"> 29,30,31,32,33,34,35,36,37,38,51); font-family:Arial; font-size:14px; line-height:26px; margin-top:0px; margin-bottom:0px; padding-top:0px; padding-bottom:0px"> 39,40,41,42,43,44,45,46,47,48,51); font-family:Arial; font-size:14px; line-height:26px; margin-top:0px; margin-bottom:0px; padding-top:0px; padding-bottom:0px"> 49,50,52,53,54,55,56,57,58,51); font-family:Arial; font-size:14px; line-height:26px; margin-top:0px; margin-bottom:0px; padding-top:0px; padding-bottom:0px"> 59,60,61,62,63,?
},51); font-family:Arial; font-size:14px; line-height:26px; margin-top:0px; margin-bottom:0px; padding-top:0px; padding-bottom:0px"> .oobfree = {
{.offset = 2,.length = 22,51); font-family:Arial; font-size:14px; line-height:26px; margin-top:0px; margin-bottom:0px; padding-top:0px; padding-bottom:0px"> #endif
} ?其中,.eccbytes = 40,跟初始化有关系,我们一般这样初始化:
nand->ecc.size = 512;?
nand->ecc.bytes = 10;
恩,这下明朗了,我们需要每512个字节产生10个ecc字节,因此对2kbyte页面的flash来说,一页就是4个512,因此需要4*10=40个ecc字节。
.eccpos就是告诉驱动,这些ecc字节放在哪里,一般是按顺序存放,不要覆盖芯片默认的坏块标记位,对2kbyte的flash来说,厂家说是前两个即第0、1个字节是坏块标志。
所以分配为eccbytes和eccpos后,后面有个oobfree,这样看来也很明白了:
offset=2表示从第2个字节开始(因为前面2个是坏块标志啊~~),length=22表示(从offset开始)共22个ecc字节可以用户随便用。
这下一目了然了吧。
2、有了nand_chip结构体填充驱动了,接下来需要往系统注册一个nand flash设备了,一般情况是在板级初始化的代码中,一般目录是arch/arm/mach****/****.c文件(或者也有可能在devices.c中),在这个文件中,一般会包括定义nandflash分区信息结构,注册nandflash设备,其中还包括了定义一些IO资源,用于主控器驱动获得IO资源配置相应的寄存器以及数据读取。例如dm365的板级初始化文件:arch/arm/mach-davinci/board-dm365-evm.c中:
#define NAND_BLOCK_SIZE (SZ_16K)//32MB or 64MB static struct mtd_partition nand_partitions[] = { /* bootloader (UBL,U-Boot,BBT) in sectors: 0 - 14 */ { .name = "bootloader", .offset = 0, .size = 32 * NAND_BLOCK_SIZE, .mask_flags = MTD_WRITEABLE,/* force read-only */ }, /* bootloader params in the next sector 15 */ { .name = "params", .offset = MTDPART_OFS_APPEND, .size = 96 * NAND_BLOCK_SIZE, /* kernel in sectors: 16 */ { .name = "kernel", .size = SZ_2M, .mask_flags = 0 }, { .name = "filesystem1", .size = SZ_16M,//SZ_16M + SZ_8M, { .name = "filesystem2",//SZ_8M, { .name = "Calibration", .size = MTDPART_SIZ_FULL, .mask_flags = 0 } };
上面是nandflash分区信息定义结构体,在上面结构体中,总共为nandflash分成了6个区,在系统启动后,可以通过ls /dev/mtdblock*查看到所有的分区,看到的mtdblock*设备文件节点从0开始分别对应上面数组中的六个分区,如果需要使用哪个分区,则可以通过mount挂载到文件系统中使用。也能通过系统启动时打印的信息看到分区情况:
Creating 6 MTD partitions on "nand_davinci.0": 0x00000000-0x00080000 : "bootloader" 0x00080000-0x00200000 : "params" 0x00200000-0x00400000 : "kernel" 0x00400000-0x01400000 : "filesystem1" 0x01400000-0x01600000 : "filesystem2" 0x01600000-0x04000000 : "Calibration"
按照上面信息可以看出,文件系统是保存在0x00400000-0x01400000或者0x01400000-0x01600000地址段中(后面假设系统是只用filesystem1),内核是保存0x00200000-0x00400000地址段中,所以在烧录内核时,应该使用nand write 0x80700000?0x00200000 0x00300000(其中0x00300000表示写入大小,0x00200000表示写入nandflash的起始地址,0x80700000表示从内存的这个地址读出内核烧写到对应的nandflash地址中),而烧录文件可以通过网络文件系统启动板子,再将上面的filesystem1挂载在mnt/目录下:mount /dev/mtdblock3 /mnt/mtd3,然后直接将要烧录的文件系统解压到这个filesystem1分区中,也能通过网络上其它方式烧录文件系统,如yaffs jffs2等等,烧钱文件系统前,必须通过nand erase在uboot阶段擦除这块地址段中的flash数据,如擦写filesystem1的数据命令:nand erase?0x00400000 ********* (***表示擦写块大小)?
static struct resource nand_resources[] = { [0] = { /* First memory resource is AEMIF control registers */ .start = DM365_ASYNC_EMIF_CNTRL_BASE, .end = DM365_ASYNC_EMIF_CNTRL_BASE + SZ_4K - 1, .flags = IORESOURCE_MEM, [1] = { /* Second memory resource is NAND I/O window */ .start = DAVINCI_ASYNC_EMIF_DATA_CE0_BASE, .end = DAVINCI_ASYNC_EMIF_DATA_CE0_BASE + SZ_16K - 1, [2] = { /* Third (optional) memory resource is NAND I/O window */ /* for second NAND chip select */ .start = DAVINCI_ASYNC_EMIF_DATA_CE0_BASE + SZ_16K, .end = DAVINCI_ASYNC_EMIF_DATA_CE0_BASE + 2 * SZ_16K - 1, };
上面数组中定义的是nandflash接口(一般接在EMIF上)的IO内存资源,需要注意的是,这个数组如何定义取决于nandflash驱动(drivers/mtd/nand/davinvi-nand.c)中代码是如何使用这些资源的,比如说davinci-nand.c获取资源是platform_get_resource(pdev,IORESOURCE_MEM,0),而获取的这个资源是用于控制寄存器配置的,则上面数组中0成员就必须是EMIF控制寄存器的IO内存地址,如果这个资源是用于访问nandflash数据的,则0数组的成员就必须是EMIF数据寄存器。所以这个数组的定义需要配置davinci-nand.c驱动的编写来更改。
(编辑:李大同)
【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!
|