NAND FLASH驱动程序
15年6月3日10:50:55
首先在u-boot中体验nand flash的读写:
其中u-boot的内置函数md.l mw.b等可以在u-boot中通过help md或者help mw来查看。
(1)读ID:
S3C2440 u_boot
选中 NFCONT的bit1设为0 md.l 0x4E000004 1 mw.l 0x4E000004 1
发出命令0x90 NFCMMD = 0x90 mw.b 0x4E000008 0x90
发出地址0x00 NFADDR = 0x00 mw.b 0x4E00000C 0x00
读数据得到0xEC val = NFDATA md.b 0x4E000010 1
读数据得到devices code md.b 0x4E000010 1
退出读ID的状态 NFCMMD = 0xff mw.b 0x4E000008 0xff
(2)读内容,读0地址的数据:
使用u_boot的命令:nand dump 0:
Page 00000000 dump:
01 da a0 e3 09 00 00 eb 0c 00 00 eb 26 00 00 eb
S3C2440 u_boot
选中 NFCONT的bit1设为0 md.l 0x4E000004 1 mw.l 0x4E000004 1
发出命令0x00 NFCMMD = 0x00 mw.b 0x4E000008 0x00
发出地址0x00 NFADDR = 0x00 mw.b 0x4E00000C 0x00
发出地址0x00 NFADDR = 0x00 mw.b 0x4E00000C 0x00
发出地址0x00 NFADDR = 0x00 mw.b 0x4E00000C 0x00
发出地址0x00 NFADDR = 0x00 mw.b 0x4E00000C 0x00
发出地址0x00 NFADDR = 0x00 mw.b 0x4E00000C 0x00
发出命令0x30 NFCMMD = 0x30 mw.b 0x4E000008 0x30
读数据得到0x01 val = NFDATA md.b 0x4E000010 1
读数据得到0xda val = NFDATA md.b 0x4E000010 1
读数据得到0xa0 val = NFDATA md.b 0x4E000010 1
读数据得到0xe3 val = NFDATA md.b 0x4E000010 1
…............
退出读状态 NFCMMD = 0xff mw.b 0x4E000008 0xff
结果如下图所示:
NAND FLASH的驱动程序如下:
1
2 #include
3 #include
4 #include
5 #include
6 #include
7 #include
8 #include
9 #include
10 #include
11 #include
12 #include
13 #include
14 #include
15 #include
16 #include
17 #include
18 #include
19 #include
20
21 struct s3c_nand_regs {
22 unsigned long nfconf ;
23 unsigned long nfcont ;
24 unsigned long nfcmd ;
25 unsigned long nfaddr ;
26 unsigned long nfdata ;
27 unsigned long nfeccd0 ;
28 unsigned long nfeccd1 ;
29 unsigned long nfeccd ;
30 unsigned long nfstat ;
31 unsigned long nfestat0;
32 unsigned long nfestat1;
33 unsigned long nfmecc0 ;
34 unsigned long nfmecc1 ;
35 unsigned long nfsecc ;
36 unsigned long nfsblk ;
37 unsigned long nfeblk ;
38 };
39
40 static struct nand_chip *s3c_nand;
41 static struct mtd_info *s3c_mtd;
42 static struct s3c_nand_regs *s3c_nand_regs;
43
44 static struct mtd_partition s3c_nand_parts[] = {
45 [0] = {
46 .name = "bootloader",
47 .size = 0x00040000,
48 .offset = 0,
49 },
50 [1] = {
51 .name = "params",
52 .offset = MTDPART_OFS_APPEND,
53 .size = 0x00020000,
54 },
55 [2] = {
56 .name = "kernel",
57 .offset = MTDPART_OFS_APPEND,
58 .size = 0x00200000,
59 },
60 [3] = {
61 .name = "root",
62 .offset = MTDPART_OFS_APPEND,
63 .size = MTDPART_SIZ_FULL,
64 }
65 };
66
67 static void s3c2440_select_chip(struct mtd_info *mtd, int chip)
68 {
69 if (chip == -1)
70 {
71 s3c_nand_regs->nfcont |= (1<<1);
72 }
73 else
74 {
75 s3c_nand_regs->nfcont &= ~(1<<1);
76 }
77
78 }
79
80 static void s3c2440_cmd_ctrl(struct mtd_info *mtd, int dat, unsigned int ctrl)
81 {
82 if (ctrl & NAND_CLE)
83 {
84 s3c_nand_regs->nfcmd = dat;
85 }
86 else
87 {
88 s3c_nand_regs->nfaddr = dat;
89 }
90 }
91
92 static int s3c2440_dev_ready(struct mtd_info *mtd)
93 {
94 return (s3c_nand_regs->nfstat & (1<<0));
95 }
96
97 static int s3c_nand_init(void)
98 {
99 struct clk *clk;
100
101 /* 1. 分配一个 nand_chip 结构体 */
102 s3c_nand = kzalloc(sizeof(struct nand_chip), GFP_KERNEL);
103
104 s3c_nand_regs = ioremap(0x4E000000, sizeof(s3c_nand_regs));
105
106 /* 2. 设置 */
107 s3c_nand->select_chip = s3c2440_select_chip;
108 s3c_nand->cmd_ctrl = s3c2440_cmd_ctrl;
109 s3c_nand->IO_ADDR_R = &s3c_nand_regs->nfdata;
110 s3c_nand->IO_ADDR_W = &s3c_nand_regs->nfdata;
111 s3c_nand->dev_ready = s3c2440_dev_ready;
112 s3c_nand->ecc.mode = NAND_ECC_SOFT;
113
114 /* 3. 硬件相关操作 */
115 /* she zhi zong shi zhong */
116 clk = clk_get(NULL, "nand");
117 clk_enable(clk);
118
119 /*设置 nand 的时钟参数 */
120 s3c_nand_regs->nfconf = (0<<12) | (1<<8) | (0<<4);
121 s3c_nand_regs->nfcont = (1<<1) | (1<<0);
122
123
124 /* 4. 使用 nand_scan */
125 s3c_mtd = kzalloc(sizeof(struct mtd_info), GFP_KERNEL);
126 s3c_mtd->owner = THIS_MODULE;
127 s3c_mtd->priv = s3c_nand;
128
129 nand_scan(s3c_mtd, 1);
130
131 /* 5. add_mtd_partitions */
132 add_mtd_partitions(s3c_mtd, s3c_nand_parts, 4);
133
134 return 0;
135 }
136
137 static void s3c_nand_exit(void)
138 {
del_mtd_partitions(s3c_mtd);
139 kfree(s3c_mtd);
140 iounmap(s3c_nand_regs);
141 kfree(s3c_nand);
142 }
143
144 module_init(s3c_nand_init);
145 module_exit(s3c_nand_exit);
146
147 MODULE_LICENSE("GPL");
148
(一)在linux系统中提供了MTD系统来建立Flash针对llinux的统一抽象的接口。MTD将文件系统与底层的flash存储器进行隔离,这样,在写nand flash驱动的时候就不用关心flash作为字符设备和块设备与linux内核的接口,直接操作MTD即可。
MTD的数据结构是mtd_info,定义了MTD的数据和操作函数。mtd_info是表示MTD原始设备的结构体,每个分区也被认为是一个mtd_info。例如,有两个MTD原始设备,而每个上面有3个分区,在系统中就有6个mtd_info结构体,这些mtd_info的指针放在名为mtd_table的数组里面。
不过在NOR或者NAND的驱动程序中,几乎看不到mtd_info的成员函数,这时因为MTD的下层实现了针对NOR和NAND的通用mtd_info成员,我们只需要分别关心NOR或者NAND在mtd_info结构体中的私有函数即可。
Flash驱动程序中使用下面两个函数来注册和注销MTD设备:
int add_mtd_device(struct mtd_info *mtd);
int del_mtd_device (struct mtd_info *mtd);
(二)如果不想让Flash分区的话,用上面两个函数注册就行,如果想分区,需要用到下面的函数:
int add_mtd_partitions(struct mtd_info *master, const struct mtd_partition *parts, int nbparts);
int del_mtd_partitions(struct mtd_info *master);
看第一个注册函数的参数,第一个参数为 mtd_info结构体,第二个参数是一个 mtd_partition结构体,这个结构体显示了想要把Flash分成几个分区,其中每个分区怎么分的信息:
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 */
};
在调用注册函数之前,需要将这个结构体初始化了。第三个参数是指分区的个数。
(三)注册了mtd_info结构体以后,就需要使用它, nand_scan( )函数探测nand flash的存在,并去读取NAND芯片的厂商,ID等信息,函数如下所示:
int nand_scan(struct mtd_info *mtd, int maxchips);
(四)上面提到了MTD的下层实现了通用的NAND的驱动,我们需要关心的就是NAND的私有函数,这个私有的主体就是nand_chip结构体。这个结构体如下所示:
struct nand_chip {
void __iomem *IO_ADDR_R;
void __iomem *IO_ADDR_W;
uint8_t (*read_byte)(struct mtd_info *mtd);
u16 (*read_word)(struct mtd_info *mtd);
void (*write_buf)(struct mtd_info *mtd, const uint8_t *buf, int len);
void (*read_buf)(struct mtd_info *mtd, uint8_t *buf, int len);
int (*verify_buf)(struct mtd_info *mtd, const uint8_t *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 (*cmd_ctrl)(struct mtd_info *mtd, int dat,
unsigned int ctrl);
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);
void (*erase_cmd)(struct mtd_info *mtd, int page);
int (*scan_bbt)(struct mtd_info *mtd);
int (*errstat)(struct mtd_info *mtd, struct nand_chip *this, int state, int status, int page);
int (*write_page)(struct mtd_info *mtd, struct nand_chip *chip,
const uint8_t *buf, int page, int cached, int raw);
int chip_delay;
unsigned int options;
int page_shift;
int phys_erase_shift;
int bbt_erase_shift;
int chip_shift;
int numchips;
unsigned long chipsize;
int pagemask;
int pagebuf;
int subpagesize;
uint8_t cellinfo;
int badblockpos;
nand_state_t state;
uint8_t *oob_poi;
struct nand_hw_control *controller;
struct nand_ecclayout *ecclayout;
struct nand_ecc_ctrl ecc;
struct nand_buffers *buffers;
struct nand_hw_control hwcontrol;
struct mtd_oob_ops ops;
uint8_t *bbt;
struct nand_bbt_descr *bbt_td;
struct nand_bbt_descr *bbt_md;
struct nand_bbt_descr *badblock_pattern;
void *priv;
};
在写NAND驱动程序的时候,首先需要动态分配这个nand_chip结构体,然后分别初始化设置这个结构体里面我们需要用到的数据或函数。
(五)下面总结写NAND FLASH驱动的步骤:
(1)定义一个nand_chip结构体,并动态分配。
(2)设置nand_chip结构体中一些相关的函数。
(3)硬件相关的操作,包括设置总的nand时钟,设置一些寄存器的参数等。
(3)定义mtd_info结构体,并且将它的priv 函数指向我们刚才注册的nand_chip结构体:
s3c_mtd->priv = s3c_nand;
并且调用nand_scan函数,来探测nand flash的存在,并读取NAND芯片的厂商,ID等信息。
(4)如果要分区,就先定义mtd_partition数组,将分区信息记录在里面,然后调用add_mtd_partitions()函数来分区。
(5)在出口函数中做相反的事情。
测试:需要从新配置内核,去掉MTD中的NAND驱动。
Make menuconfig
Device Drivers --->
<*> Memory Technology Device (MTD) support --->
<*> NAND Device Support --->
< > NAND Flash support for S3C2410/S3C2440 SoC
然后从新“make uImage”,用新内核启动以后测试:
可以看到,没有mtd设备:
装载驱动以后,可以看到有分区信息输出:
这时候,可以看到,设备节点都已经建立起来了:
用分区工具格式化:
格式化以后挂载:
阅读(1587) | 评论(0) | 转发(0) |