对技术执着
分类: LINUX
2015-03-14 16:26:31
原文地址:NAND Falsh驱动分析 作者:leon_yu
1.驱动结构:
Linux系统中,用MTD(Memory Technology Device,内存技术设备)系统用来建立Flash针对Linux的统一、抽象的接口,Flash驱动及接口分为4层。
①硬件驱动层:负责FLASH硬件设备的读写,擦除。LinuxMTD的NORFLASH在drivers/mtd/chips子目录下,NAND在drivers/mtd/nand/目录下
②MTD原始设备层:包括两部分,一是MTD原始设备的通用代码,另一部分是各个特定的FLASH的数据,如分区
③MTD设备层:
基于MTD原始设备层,Linux系统定义MTD的块设备(设备号31)和字符设备(设备号90),MTD字符设备定义在mtdchar.c,MTD快设备则是定义了一个描述MTD块设备的结构体mtdblk_dev,并声明了一个名为mtdblks的指针数组,这个数组中的每一个mtdblk_dev和mtd_table中的每一个mtd_info一一对应
④设备节点:通过mknod在/dev目录下建立MTD块设备节点31,字符设备节点90。用户通过访问此设备节点访问MTD设备。
2.数据结构
①Mtd_info:用于描述MTD原始设备,这其中定义了大量关于MTD的数据和操作函数。每个分区被认为是一个mtd_info,如果有两个MTD原始设备,每个有3个分区,在系统中就有6个mtd_info结构体,这些mtd_info指针被存放在mtd_table数组里。
mtd_info的type字段表示底层物理设备类型,如MTD_RAM,MTD_NORFlash, MTD_NANDFlash,.
Flag字段表示MTD_ERASEABLE(可擦除)、MTD_WRITEB_WRITEABLE(可编程),MTD_XIP(可片内执行),MTD_OOB(NAND带外数据),MTD_ECC(支持自动ECC)。
Mtd_info中的read(),write(),read_oob(),wreite_oob(),这些函数对NAND,NOR是透明的。
添加和删除MTD设备:
int add_mtd_device(struct mtd_info *mtd);
int del_mtd_device (struct mtd_info *mtd);
②.mtd_part结构,用于描述分区,其mtd_info成员用于描述本分区,它会被加入到mtd_table中,其他部分成员由其主分区mtd_part->master决定,主分区(涵盖所有分区)不作为一个MTD原始设备加入mtd_table
struct mtd_part {
struct mtd_info mtd; //分区信息(大部分有其master决定)
struct mtd_info *master;//该分区的主分区
u_int32_t offset; //该分区的偏移地址
int index; //分区号
struct list_head list;
int registered;
};
③.mtd_partion会在MTD原始设备层调用add_mtd_partions()时传递分区信息用,
struct mtd_partition {
char *name; /* identifier string */
u_int32_t size; /* partition size */
u_int32_t offset; /* offset within the master MTD space */
u_int32_t mask_flags; /* master MTD flags to mask out for this partition */
struct nand_ecclayout *ecclayout; /* out of band layout for this partition (NAND only)*/
struct mtd_info **mtdp; /* pointer to store the MTD object */
};
Flash驱动中使用两个函数注册和注销分区
int add_mtd_partitions(struct mtd_info *master,
const struct mtd_partition *parts,
int nbparts)
int del_mtd_partitions(struct mtd_info *master);
add_mtd_partitions()会对每一个新建分区建立一个新的mtd_part结构体,将其加入mtd_partitions中,并调用add_mtd_device()将此分区作为MTD设备加入mtd_table中,成功返回0,如分配mtd_part时内存不足,则返回-ENOMEM。add_mtd_partitions()新建的mtd_part需要依赖传入的mtd_partion参数对其初始化。
del_mtd_partitions()作用是对于mtd_partions上的每一个分区,若它的主分区是master,则将它从mtd_partions和mtd_table中删除并释放掉,这个函数会调用del_mtd_device()
3.Nor flash驱动:
只需要定义具体的内存映射情况结构体map_info并使用指定接口类型调用do_map_probe(),map_info是NOR flash驱动的核心,它指定了NOR的基址,位宽,大小等信息以及flash的读写函数,甚至NOR驱动代码本质上可以被认为是根据map_info探测芯片的过程
NOR Flash驱动主要工作:
①定义map_info的实例,初始化其中的成员,根据目标板的情况为name,size,bankwidth和phys赋值
②如果Flash要分区,则定义mtd_partition数组,将实际电路板中Flash分区信息记录与其中
③以map_info和探测的接口类型为参数(如“cif_probe”,”jedec_probe”等)为参数调用do_map_probe(),探测Flash得到mtd_info.
Do_map_probe会根据传入的参数name通过get_mtd_chip_driver()得到具体的MTD驱动,调用与接口对应的probe()函数探测设备,利用map_info中的配置,do_map_probe()可以自动支持CFI或JEDEC接口的Flash芯片,MTD以后会自动采用适当的命令参数对flash进行读写或擦除
④在模块初始化时以mtd_info为参数调用add_mtd_deviece()或以mtd_info,mtd_partition数据以及分区数为参数调用add_mtd_partitions()注册设备或分区。当然,在这之前可以调用parse_mtd_partitions()查看flash是否已有分区信息,并将查看出的分区信息通过add_mtd_partition()注册
⑤在模块卸载时调用“反函数”删除设备或分区
4.NAND FLASH驱动
Linux内核在MTD下层实现了通用的NAND驱动(主要通过driver/mtd/nand/nand_base.c实现),因此芯片级的NAND驱动不用再实现mtd_info中的read,write_oob()等函数,主题转移到nand_chip数据结构。
MTD用nand_chip结构表示一个NAND芯片,这个结构体包含了NAND的地址信息,读写方法,ECC模式,硬件控制等一系列底层机制
①如果flash要分区,则定义mtd_partition数组,将实际电路板中的flash分区信息记录其中
②在模块加载时分配nand_chip内存,根据目标板NAND控制器的特殊情况初始化nand_chip中的hwcontrol(),calculate_ecc,read_byte()等,若不赋值会使用nand_base.c中的默认函数,若使用软件ECC,不需要赋值calculate_ecc()和correct_date()成员。
③以mtd_info为参数调用nand_scan()函数探测NAND FLASH的存在。Nand_scan()函数会读取NAND芯片ID,并根据mtd->priv即nand_chip中的成员初始化mtd_info.
④如果要分区,则以mtd_info和mtd_parition为参数调用add_mtd_partition(),添加分区信息
Ps.在NAND芯片级驱动中,若在nand_chip中没有赋值,将使用nand_base.c中默认的参数,比如/* Define default oob placement schemes for large and small page devices */
static struct nand_ecclayout nand_oob_8 = {
.eccbytes = 3,
.eccpos = {0, 1, 2},
.oobfree = {
{.offset = 3,
.length = 2},
{.offset = 6,
.length = 2}}
};
Parse_mtd_partitions()用于解析MTD分区信息,常见的MTD分区解析是命令行解析,即解析在Linux启动命令行中通过mtdparts=传入的MTD分区信息
通过查看/proc/mtd文件可以获知系统中包含的MTD分区
在Linux2.6内核中,s3c2410的NAND被注册为一个平台设备,当NAND驱动模块调用platform_driver_register($s3c2410_nand_driver)注册平台驱动时,其对应的platform_driver结构体的s3c24xx_nand_probe()成员函数被调用,在该函数中将调用前述的s3c2410_nand_init_chip()初始化nand_chip,通过nand_scan()扫描到NAND Flash存储器后,调用s3c2410_nand_add_partition()添加MTD分区和设备信息。
同样地,当nand驱动模块卸载调用platform_driver_unregister($s3c2410_nand_driver)。