一、结构体详解
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);
上一篇:NorFlash驱动
下一篇:块设备驱动程序
推荐阅读最新更新时间:2024-11-12 20:13
设计资源 培训 开发板 精华推荐
- LR8N3-G 高输入电压、可调 3 端子线性稳压器的典型应用
- LT6656BIDC-3、3V 电压基准作为微功率稳压器的典型应用
- ADA4610-1BRZ-RL 正峰值检波器运算放大器的典型应用电路
- LTC3225,一款 12V 电源穿越应用
- LT6656AIS6-3、3V 2 端子电压基准电流源的典型应用
- LT6656AIDC-3、3V 微控制器电压基准和稳压器的典型应用
- DC1422A-A,演示板双路匹配 7MHz 低噪声、低失真二阶 LP 滤波器
- 双路双向有刷电调(RZ7886)
- AT97SC3205T-SDK2,基于AT97SC3205 SAM4S ARM MCU的TPM I2C/SPI开发套件
- 使用 Microchip Technology 的 LM385BCZB-2.5 的参考设计