一、结构体详解
MTD体系结构:
在linux中提供了MTD(Memory Technology Device,内存技术设备)系统来建立Flash针对linux的统一、抽象的接口
引入MTD后,linux系统中的Flash设备驱动及接口可分为4层:
设备节点
MTD设备层
MTD原始设备层
硬件驱动层
硬件驱动层:Flash硬件驱动层负责底层硬件设备实际的读、写、擦除,Linux MTD设备的NAND型Flash驱动位于driver/mtd/nand子目录下
s3c2410对应的nand Flash驱动为s3c2410.c
MTD原始设备层:MTD原始设备层由两部分构成,一部分是MTD原始设备的通用代码,另一部分是各个特定Flash的数据,比如分区
主要构成的文件有:
drivers/mtd/mtdcore.c 支持mtd字符设备
driver/mtd/mtdpart.c 支持mtd块设备
MTD设备层:基于MTD原始设备,Linux系统可以定义出MTD的块设备(主设备号31) 和字符设备(设备号90),构成MTD设备层
简单的说就是:使用一个mtd层来作为具体的硬件设备驱动和上层文件系统的桥梁。mtd给出了系统中所有mtd设备(nand,nor,diskonchip)的统一组织方式。
1. struct mtd_info *
mtd层用一个数组struct mtd_info *mtd_table[MAX_MTD_DEVICES]保存系统中所有的设备,mtd设备利用struct mtd_info 这个结构来描述,该结构中描述了存储设备的基本信息和具体操作所需要的内核函数,mtd系统的那个机制主要就是围绕这个结构来实现的。结构体在include/linux/mtd/mtd.h中定义:
struct mtd_info { u_char type; //MTD 设备类型 u_int32_t flags; //MTD设备属性标志 u_int32_t size; //标示了这个mtd设备的大小 u_int32_t erasesize; //MTD设备的擦除单元大小,对于NandFlash来说就是Block的大小 u_int32_t oobblock; //oob区在页内的位置,对于512字节一页的nand来说是512 u_int32_t oobsize; //oob区的大小,对于512字节一页的nand来说是16 u_int32_t ecctype; //ecc校验类型 u_int32_t eccsize; //ecc的大小
char *name; //设备的名字 int index; //设备在MTD列表中的位置
struct nand_oobinfo oobinfo; //oob区的信息,包括是否使用ecc,ecc的大小
//以下是关于mtd的一些读写函数,将在nand_base中的nand_scan中重载 int (*erase) int (*read) int (*write) int (*read_ecc) int (*write_ecc) int (*read_oob) int (*read_oob)
void *priv;//设备私有数据指针,对于NandFlash来说指nand芯片的结构 } |
2. struct nand_chip
下面看nand_chip结构,nand_chip主要是定义了一写操作函数,在include/linux/mtd/nand.h中定义:
struct nand_chip { void __iomem *IO_ADDR_R; //这是nandflash的读写寄存器 void __iomem *IO_ADDR_W; //以下都是nandflash的操作函数,这些函数将根据相应的配置进行重载 u_char (*read_byte)(struct mtd_info *mtd); void (*write_byte)(struct mtd_info *mtd, u_char byte); u16 (*read_word)(struct mtd_info *mtd); void (*write_word)(struct mtd_info *mtd, u16 word); void (*write_buf)(struct mtd_info *mtd, const u_char *buf, int len); void (*read_buf)(struct mtd_info *mtd, u_char *buf, int len); int (*verify_buf)(struct mtd_info *mtd, const u_char *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 (*hwcontrol)(struct mtd_info *mtd, int cmd); 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, int state); int (*calculate_ecc)(struct mtd_info *mtd, const u_char *dat, u_char *ecc_code); int (*correct_data)(struct mtd_info *mtd, u_char *dat, u_char *read_ecc, u_char *calc_ecc); void (*enable_hwecc)(struct mtd_info *mtd, int mode); void (*erase_cmd)(struct mtd_info *mtd, int page); int (*scan_bbt)(struct mtd_info *mtd); int eccmode; //ecc的校验模式(软件,硬件) int chip_delay; //芯片时序延迟参数 int page_shift; //页偏移,对于512B/页的,一般是9 u_char *data_buf; //数据缓存区 } |
跟NAND操作相关的函数:
1、 nand_base.c:
定义了NAND驱动中对NAND芯片最基本的操作函数和操作流程,如擦除、读写page、读写oob等。当然这些函数都只是进行一些常规的操作,若你的系统在对NAND操作时有一些特殊的动作,则需要在你自己的驱动代码中进行定义。
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加载时会找这两个结构体,读出ID,如果找不到,就会加载失败。
4、 nand_ecc.c:
定义了NAND驱动中与softeware ECC有关的函数和结构体,若你的系统支持hardware ECC,且不需要software ECC,则该文件也不需理会。
二、驱动程序解析
1. 定义结构体
如图所示,
①定义了芯片寄存器的一些指针,便于程序中直接使用
②定义了nand_chip结构体,功能是定义了一些读写操作函数,读写寄存器等
③定义了存储设备的基本信息和具体的操作所需要的内核函数
2.在初始化函数中初始化nand_chip结构体
如图所示:
①分配一个nand_chip结构体所需的内存
②映射芯片的寄存器地址到nand_chip结构体
③初始nand_chip结构体中的函数指针
3.定义nand_flash时钟
如图所示:
首先从内核中找到nand的时钟信息,根据芯片手册设置读写时钟脉冲信号。
4.设置nand_mtd结构体
mtd设备利用struct mtd_info 这个结构来描述,该结构中描述了存储设备的基本信息和具体操作所需要的内核函数
在130行中,将我们的nand_mtd结构体与nand_chip结构体联系在一起。
5.实现片选函数
6.实现命令控制函数,用于发送地址和数据
7.在exit函数中释放相应的内存
8.总结流程
①:定义nand_chip结构体实现读写等操作函数
②:初始nand_flash相关的时钟,以及初始化nand_flash的数据时钟总脉冲信号
③:初始化nand_mtd结构体,用于保存扫描得到的nandflash的相关信息
实现了以上三步就可以大致实现了nand-flash的简易驱动程序了
初始化后,实现对nand的基本硬件操作就可以了,包括以下函数:
s3c2410_nand_inithw //初始化硬件,在probe中调用
s3c2410_nand_select_chip //片选
s3c2440_nand_hwcontrol //硬件控制,其实就是片选
s3c2440_nand_devready //设备就绪
s3c2440_nand_enable_hwecc //使能硬件ECC校验
s3c2440_nand_calculate_ecc //计算ECC
s3c2440_nand_read_buf s3c2440_nand_write_buf
9.加载
insmod nand_dev.ko
附驱动源程序1
nand1.c
三、【高级】增加块设备分区结构体
andFlash还有一个分区表结构体,mtd_partition,这个是在arch/arm/plat-s3c24XX/common-smdk.c中定义的。
static struct mtd_partition tiny_nand_part[] = { [0] = { .name = 'bootloader', .size = SZ_4M, .offset = 0, }, [1] = { .name = 'kernel', .size = SZ_8M, .offset = MTDPART_OFS_APPEND, }, [2] = { .name = 'root', .size = MTDPART_SIZ_FULL, .offset = MTDPART_OFS_APPEND, }, }; |
记录了当前的nand flash有几个分区,每个分区的名字,大小,偏移量是多少
系统就是依靠这些分区表找到各个文件系统的
这些分区表nand中的文件系统没有必然关系,分区表只是把flash分成不同的部分
如果自己编写一个nandflash驱动,只需要填充这三个结构体:
Mtd_info nand_chip mtd_partition
并实现对物理设备的控制,上层的驱动控制已由mtd做好了,不需要关心
然后使用
add_mtd_partitions(tiny_nand_mtd, tiny_nand_part, 3);
添加分区信息。
自然在exit函数中就要del_mtd_partitions(tiny_nand_mtd); //删除分区
附驱动源程序2见后
1 #include
2 #include
3 #include
4 #include
5 #include
6 #include
7 #include
8 #include
9 #include
10 #include
11 #include
12 #include
13
14 #include
15 #include
16 #include
17 #include
18
19 #include
20
21 #include
22 #include
23
24 static unsigned long *clk_gate_ip1;
25 static unsigned long *clk_gate_block;
26 static unsigned long *mp0_3con;
27
28 static struct mtd_partition tiny_nand_part[] = {
29 [0] = {
30 .name = 'bootloader',
31 .size = SZ_4M,
32 .offset = 0,
33 },
34 [1] = {
35 .name = 'kernel',
36 .size = SZ_8M,
37 .offset = MTDPART_OFS_APPEND,
38 },
39 [2] = {
40 .name = 'root',
41 .size = MTDPART_SIZ_FULL,
42 .offset = MTDPART_OFS_APPEND,
43 },
44 };
45
46 struct nand_regs {
47 unsigned long nfconf;
48 unsigned long nfcont;
49 unsigned long nfcmmd;
50 unsigned long nfaddr;
51 unsigned long nfdata;
52 unsigned long nfmeccd0;
53 unsigned long nfmeccd1;
54 unsigned long nfseccd;
55 unsigned long nfsblk;
56 unsigned long nfeblk;
57 unsigned long nfstat;
58 unsigned long nfeccerr0;
59 unsigned long nfeccerr1;
60 };
61
62 static struct nand_regs *nand_regs;
63 static struct nand_chip *tiny_nand_chip;
64 static struct mtd_info *tiny_nand_mtd;
65
66 static void tiny_nand_select_chip(struct mtd_info *mtd, int chipnr)
67 {
68 if(chipnr == -1)
69 {
70 /*取消选择*/
71 nand_regs->nfcont |= (1<<1);
72 }
73 else
74 {
75 /*选中芯片*/
76 nand_regs->nfcont &= ~(1<<1);
77 }
78 }
79
80 static void tiny_nand_cmd_ctrl(struct mtd_info *mtd, int dat,
81 unsigned int ctrl)
82 {
83
84 if (ctrl & NAND_CLE)
85 {
86 /*发命令*/
87 nand_regs->nfcmmd = dat;
88 }
89 else
90 {
91 /*发地址*/
92 nand_regs->nfaddr = dat;
93 }
94 }
95
96 static int tiny_nand_dev_ready(struct mtd_info *mtd)
97 {
98 /*等待命令的操作完成*/
99 return (nand_regs->nfstat & (1<<0));
100 }
101
102 static int tiny_nand_init(void)
103 {
104 /*1.分配一个nand_chip结构体*/
105 tiny_nand_chip = kzalloc(sizeof(struct nand_chip),GFP_KERNEL);