NAND Flash驱动
硬件原理及分析
管脚说明
Pin Name | Pin Function |
R/B(RnB) | The R/B output indicates the status of the device operation. When low, it indicates that a program, erase or random read operation is in process and returns to high state upon completion. It is an open drain output and does not float to high-z condition when the chip is deselected or when outputs are disabled. |
CLE(CLE) | The CLE input controls the activating path for commands sent to the command register. When active high, commands are latched into the command register through the I/O ports on the rising edge of the WE signal. |
CE(nFCE) | The CE input is the device selection control. When the device is in the Busy state, CE high is ignored, and the device does not return to standby mode in program or erase operation. |
ALE(ALE) | The ALE input controls the activating path for address to the internal address registers. Addresses are latched on the rising edge of WE with ALE high. |
WE(nFWE) | The WE input controls writes to the I/O port. Commands, address and data are latched on the rising edge of the WE pulse. |
RE(nFRE) | The RE input is the serial data-out control, and when active drives the data onto the I/O bus. Data is valid tREA after the falling edge of RE which also increments the internal column address counter by one. |
I/O(LDATA0-LDATA7) | The I/O pins are used to input command, address and data, and to output data during read operations. The I/O pins float to high-z when the chip is deselected or when the outputs are disabled. |
在U-BOOT上操作Nand Flash
NAND FLASH S3C2440
发命令 选中芯片
CLE设为高电平 NFCMMD=命令值
在DATA0~DATA7上输出命令值
发出一个写脉冲
发地址 选中芯片 NFADDR=地址值
ALE设为高电平
在DATA0~DATA7上输出地址值
发出一个写脉冲
发数据 选中芯片 NFDATA=数据值
ALE,CLE设为低电平
在DATA0~DATA7上输出数据值
发出一个写脉冲
读数据 选中芯片 val=NFDATA
发出读脉冲
读DATA0~DATA7的数据
OpenJTAG> help md
md [.b, .w, .l] address [# of objects]
- memory display
OpenJTAG> help mw
mw [.b, .w, .l] address value [count]
- write memory
•b 1字节
•W 2字节
•l 4字节
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 |
读数据得到device code val=NFDATA | md.b 0x4E000010 1 |
退出读ID的状态 NFCMMD=0xff | mw.b 0x4E000008 0xff |
2. 读内容: 读0地址的数据
使用UBOOT命令:
nand dump 0
Page 00000000 dump:
17 00 00 ea 14 f0 9f e5 14 f0 9f e5 14 f0 9f e5
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 |
读数据得到0x17 val=NFDATA | md.b 0x4E000010 1 |
读数据得到0x00 val=NFDATA | md.b 0x4E000010 1 |
读数据得到0x00 val=NFDATA | md.b 0x4E000010 1 |
读数据得到0xea val=NFDATA | md.b 0x4E000010 1 |
退出读状态 NFCMMD=0xff | mw.b 0x4E000008 0xff |
ECC的作用
S3C2440-Nand Flash Controller Register
S3C2440-Nand Flash Memory Timing
K9F2G08U0C-Key Characteristic
K9F2G08U0C-Operation Timing
驱动程序
driver.c
1 /*
2 * SAMSUNG: K9F2G08U0C
3 * 参考:
4 * .\linux-2.6.22.6\drivers\mtd\nand\s3c2410.c
5 * .\linux-2.6.22.6\drivers\mtd\nand\at91_nand.c
6 */
7 #include <linux/module.h>
8 #include <linux/types.h>
9 #include <linux/init.h>
10 #include <linux/kernel.h>
11 #include <linux/string.h>
12 #include <linux/ioport.h>
13 #include <linux/platform_device.h>
14 #include <linux/delay.h>
15 #include <linux/err.h>
16 #include <linux/slab.h>
17 #include <linux/clk.h>
18
19 #include <linux/mtd/mtd.h>
20 #include <linux/mtd/nand.h>
21 #include <linux/mtd/nand_ecc.h>
22 #include <linux/mtd/partitions.h>
23
24 #include <asm/io.h>
25
26 #include <asm/arch/regs-nand.h>
27 #include <asm/arch/nand.h>
28
29 struct regs_nand {
30 unsigned long NFCONF;
31 unsigned long NFCONT;
32 unsigned long NFCMMD;
33 unsigned long NFADDR;
34 unsigned long NFDATA;
35 unsigned long NFMECCD0;
36 unsigned long NFMECCD1;
37 unsigned long NFSECCD;
38 unsigned long NFSTAT;
39 unsigned long NFESTAT0;
40 unsigned long NFESTAT1;
41 unsigned long NFMECC0;
42 unsigned long NFMECC1;
43 unsigned long NFSECC;
44 unsigned long NFSBLK;
45 unsigned long NFEBLK;
46 };
47
48 static struct nand_chip *nand;
49 static struct mtd_info *mtd;
50 static struct regs_nand *regs_nand;
51
52 //分区信息
53 static struct mtd_partition nand_parts[] = {
54 [0] = {
55 .name = "bootloader",
56 .size = 0x00040000,
57 .offset = 0, /* offset within the master MTD space */
58 },
59
60 [1] = {
61 .name = "params",
62 .offset = MTDPART_OFS_APPEND, //紧跟着上一个分区的大小
63 .size = 0x00020000,
64 },
65
66 [2] = {
67 .name = "kernel",
68 .offset = MTDPART_OFS_APPEND, //紧跟着上一个分区的大小
69 .size = 0x00200000,
70 },
71
72 [3] = {
73 .name = "root",
74 .offset = MTDPART_OFS_APPEND, //紧跟着上一个分区的大小
75 .size = MTDPART_SIZ_FULL,
76 },
77 };
78
79 static void nand_select_chip(struct mtd_info *mtd, int chipnr)
80 {
81 if (chipnr == -1)
82 {
83 //取消选中:NFCONT[1]:0
84 regs_nand->NFCONT |= (1<<1);
85 }
86 else {
87 //选中:NFCONT[1]:1
88 regs_nand->NFCONT &= ~(1<<1);
89 }
90 return;
91 }
92
93 static void nand_cmd_ctrl(struct mtd_info *mtd, int dat, unsigned int ctrl)
94 {
95 if (ctrl & NAND_CLE)
96 {
97 //发命令:NFCMMD=dat
98 regs_nand->NFCMMD = dat;
99 }
100 else
101 {
102 //发地址:NFADDR=dat
103 regs_nand->NFADDR = dat;
104 }
105 return;
106 }
107
108 static int nand_dev_ready(struct mtd_info *mtd)
109 {
110 return (regs_nand->NFSTAT & (1<<0));
111 }
112
113
114 /* 1 出、入口函数 */
115 static int __init nand_init(void)
116 {
117 struct clk *clk;
118
119 /* 2 分配一个nand_chip结构体 */
120 nand = kzalloc(sizeof(struct nand_chip), GFP_KERNEL);
121 /******** 2 end ********/
122
123 regs_nand = ioremap(0x4E000000, sizeof(struct regs_nand));
124
125 /* 3 设置:nand_chip */
126 //提供:选中、发命令、发地址、发数据、读数据、判断状态的功能
127 nand->select_chip = nand_select_chip; //片选
128 nand->cmd_ctrl = nand_cmd_ctrl; //发命令
129 nand->IO_ADDR_R = ®s_nand->NFDATA; //读
130 nand->IO_ADDR_W = ®s_nand->NFDATA; //写
131 nand->dev_ready = nand_dev_ready; //Flash 状态(忙/空闲)
132 nand->ecc.mode = NAND_ECC_SOFT; //ecc软件校验
133 /******** 3 end ********/
134
135 /* 4 硬件相关的操作:时序 */
136 //使能时钟
137 clk = clk_get(NULL, "nand");
138 clk_enable(clk);
139
140 #define TACLS 0
141 #define TWRPH0 1
142 #define TWRPH1 0
143 //时间参数设置
144 regs_nand->NFCONF = (TACLS<<12) | (TWRPH0<<8) | (TWRPH1<<4);
145
146 //控制---使能nand flash控制器,取消片选
147 regs_nand->NFCONT = (1<<0) | (1<<1);
148 /******** 4 end ********/
149
150
151 /* 5 使用:nand_scan */
152 mtd = kzalloc(sizeof(struct mtd_info), GFP_KERNEL);
153 mtd->owner = THIS_MODULE;
154 mtd->priv = nand; //私有数据
155
156 nand_scan(mtd, 1); //识别nand_flash,构造mtd_info结构体
157 /******** 5 end ********/
158
159
160 /* 6 添加分区:add_mtd_partitions */
161 add_mtd_partitions(mtd, nand_parts, 4);
162
163 //不分区的情形
164 //add_mtd_device(mtd);
165 /******** 6 end ********/
166
167 return 0;
168 }
169
170 static void __exit nand_exit(void)
171 {
172 del_mtd_partitions(mtd);
173 kfree(nand);
174 kfree(mtd);
175 iounmap(regs_nand);
176 return;
177 }
178
179 module_init(nand_init);
180 module_exit(nand_exit);
181 MODULE_LICENSE("GPL");
182 /******** 1 end ********/
Makefile
1 KERN_DIR = /work/system/linux-2.6.22.6
2
3 all:
4 make -C $(KERN_DIR) M=`pwd` modules
5
6 clean:
7 make -C $(KERN_DIR) M=`pwd` modules clean
8 rm -rf modules.order
9
10 obj-m += nand_drv.o
调试
pc-linux:
//格式化工具
tar xjf mtd-utils-05.07.23.tar.bz2
cd mtd-utils-05.07.23/util
修改Makefile:
#CROSS=arm-linux-
改为
CROSS=arm-linux-
make
cp flash_erase flash_eraseall /work/nfs_root/fs_mini/bin/
//格式化工具-----end
board-uboot:
nfs 30000000 192.168.0.103:/work/nfs_root/uImage_f
//使用NFS作为根文件系统
set bootargs console=ttySAC0 root=/dev/nfs nfsroot=192.168.0.103:/work/nfs_root/fs_mini ip=192.168.0.33:192.168.0.103:192.168.0.1:255.255.255.0::eth0:off
bootm 30000000
board-linux:
insmod nand_drv.ko
flash_eraseall /dev/mtd3 // yaffs //格式化
mount -t yaffs /dev/mtdblock3 /mnt //挂接