aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorWan ZongShun <mcuos.com@gmail.com>2009-07-10 03:17:27 -0400
committerDavid Woodhouse <David.Woodhouse@intel.com>2009-09-19 16:17:05 -0400
commit8bff82cbc30884fc52969608d090d874641e7196 (patch)
treee1fff7ae38250be5c7a10896ee85fa5d657c9ed3
parentfc28c39f0ef59bfb649ddfd633275be8e45c0f9c (diff)
mtd: add nand support for w90p910 (v2)
Add w90p910 NAND driver for w90p910 evaluation board based on w90p910,there is a K8F1G08 NAND on my board. [dwmw2: depend on MTD_PARTITIONS] Signed-off-by: Wan ZongShun <mcuos.com@gmail.com> Signed-off-by: David Woodhouse <David.Woodhouse@intel.com>
-rw-r--r--drivers/mtd/nand/Kconfig7
-rw-r--r--drivers/mtd/nand/Makefile1
-rw-r--r--drivers/mtd/nand/w90p910_nand.c382
3 files changed, 390 insertions, 0 deletions
diff --git a/drivers/mtd/nand/Kconfig b/drivers/mtd/nand/Kconfig
index ce96c091f01b..707d7ee495df 100644
--- a/drivers/mtd/nand/Kconfig
+++ b/drivers/mtd/nand/Kconfig
@@ -452,4 +452,11 @@ config MTD_NAND_SOCRATES
452 help 452 help
453 Enables support for NAND Flash chips wired onto Socrates board. 453 Enables support for NAND Flash chips wired onto Socrates board.
454 454
455config MTD_NAND_W90P910
456 tristate "Support for NAND on w90p910 evaluation board."
457 depends on ARCH_W90X900 && MTD_PARTITIONS
458 help
459 This enables the driver for the NAND Flash on evaluation board based
460 on w90p910.
461
455endif # MTD_NAND 462endif # MTD_NAND
diff --git a/drivers/mtd/nand/Makefile b/drivers/mtd/nand/Makefile
index f3a786b3cff3..4f7b1890f83c 100644
--- a/drivers/mtd/nand/Makefile
+++ b/drivers/mtd/nand/Makefile
@@ -40,5 +40,6 @@ obj-$(CONFIG_MTD_NAND_SH_FLCTL) += sh_flctl.o
40obj-$(CONFIG_MTD_NAND_MXC) += mxc_nand.o 40obj-$(CONFIG_MTD_NAND_MXC) += mxc_nand.o
41obj-$(CONFIG_MTD_NAND_SOCRATES) += socrates_nand.o 41obj-$(CONFIG_MTD_NAND_SOCRATES) += socrates_nand.o
42obj-$(CONFIG_MTD_NAND_TXX9NDFMC) += txx9ndfmc.o 42obj-$(CONFIG_MTD_NAND_TXX9NDFMC) += txx9ndfmc.o
43obj-$(CONFIG_MTD_NAND_W90P910) += w90p910_nand.o
43 44
44nand-objs := nand_base.o nand_bbt.o 45nand-objs := nand_base.o nand_bbt.o
diff --git a/drivers/mtd/nand/w90p910_nand.c b/drivers/mtd/nand/w90p910_nand.c
new file mode 100644
index 000000000000..7680e731348a
--- /dev/null
+++ b/drivers/mtd/nand/w90p910_nand.c
@@ -0,0 +1,382 @@
1/*
2 * Copyright (c) 2009 Nuvoton technology corporation.
3 *
4 * Wan ZongShun <mcuos.com@gmail.com>
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation;version 2 of the License.
9 *
10 */
11
12#include <linux/slab.h>
13#include <linux/init.h>
14#include <linux/module.h>
15#include <linux/interrupt.h>
16#include <linux/io.h>
17#include <linux/platform_device.h>
18#include <linux/delay.h>
19#include <linux/clk.h>
20#include <linux/err.h>
21
22#include <linux/mtd/mtd.h>
23#include <linux/mtd/nand.h>
24#include <linux/mtd/partitions.h>
25
26#define REG_FMICSR 0x00
27#define REG_SMCSR 0xa0
28#define REG_SMISR 0xac
29#define REG_SMCMD 0xb0
30#define REG_SMADDR 0xb4
31#define REG_SMDATA 0xb8
32
33#define RESET_FMI 0x01
34#define NAND_EN 0x08
35#define READYBUSY (0x01 << 18)
36
37#define SWRST 0x01
38#define PSIZE (0x01 << 3)
39#define DMARWEN (0x03 << 1)
40#define BUSWID (0x01 << 4)
41#define ECC4EN (0x01 << 5)
42#define WP (0x01 << 24)
43#define NANDCS (0x01 << 25)
44#define ENDADDR (0x01 << 31)
45
46#define read_data_reg(dev) \
47 __raw_readl((dev)->reg + REG_SMDATA)
48
49#define write_data_reg(dev, val) \
50 __raw_writel((val), (dev)->reg + REG_SMDATA)
51
52#define write_cmd_reg(dev, val) \
53 __raw_writel((val), (dev)->reg + REG_SMCMD)
54
55#define write_addr_reg(dev, val) \
56 __raw_writel((val), (dev)->reg + REG_SMADDR)
57
58struct w90p910_nand {
59 struct mtd_info mtd;
60 struct nand_chip chip;
61 void __iomem *reg;
62 struct clk *clk;
63 spinlock_t lock;
64};
65
66static const struct mtd_partition partitions[] = {
67 {
68 .name = "NAND FS 0",
69 .offset = 0,
70 .size = 8 * 1024 * 1024
71 },
72 {
73 .name = "NAND FS 1",
74 .offset = MTDPART_OFS_APPEND,
75 .size = MTDPART_SIZ_FULL
76 }
77};
78
79static unsigned char w90p910_nand_read_byte(struct mtd_info *mtd)
80{
81 unsigned char ret;
82 struct w90p910_nand *nand;
83
84 nand = container_of(mtd, struct w90p910_nand, mtd);
85
86 ret = (unsigned char)read_data_reg(nand);
87
88 return ret;
89}
90
91static void w90p910_nand_read_buf(struct mtd_info *mtd,
92 unsigned char *buf, int len)
93{
94 int i;
95 struct w90p910_nand *nand;
96
97 nand = container_of(mtd, struct w90p910_nand, mtd);
98
99 for (i = 0; i < len; i++)
100 buf[i] = (unsigned char)read_data_reg(nand);
101}
102
103static void w90p910_nand_write_buf(struct mtd_info *mtd,
104 const unsigned char *buf, int len)
105{
106 int i;
107 struct w90p910_nand *nand;
108
109 nand = container_of(mtd, struct w90p910_nand, mtd);
110
111 for (i = 0; i < len; i++)
112 write_data_reg(nand, buf[i]);
113}
114
115static int w90p910_verify_buf(struct mtd_info *mtd,
116 const unsigned char *buf, int len)
117{
118 int i;
119 struct w90p910_nand *nand;
120
121 nand = container_of(mtd, struct w90p910_nand, mtd);
122
123 for (i = 0; i < len; i++) {
124 if (buf[i] != (unsigned char)read_data_reg(nand))
125 return -EFAULT;
126 }
127
128 return 0;
129}
130
131static int w90p910_check_rb(struct w90p910_nand *nand)
132{
133 unsigned int val;
134 spin_lock(&nand->lock);
135 val = __raw_readl(REG_SMISR);
136 val &= READYBUSY;
137 spin_unlock(&nand->lock);
138
139 return val;
140}
141
142static int w90p910_nand_devready(struct mtd_info *mtd)
143{
144 struct w90p910_nand *nand;
145 int ready;
146
147 nand = container_of(mtd, struct w90p910_nand, mtd);
148
149 ready = (w90p910_check_rb(nand)) ? 1 : 0;
150 return ready;
151}
152
153static void w90p910_nand_command_lp(struct mtd_info *mtd,
154 unsigned int command, int column, int page_addr)
155{
156 register struct nand_chip *chip = mtd->priv;
157 struct w90p910_nand *nand;
158
159 nand = container_of(mtd, struct w90p910_nand, mtd);
160
161 if (command == NAND_CMD_READOOB) {
162 column += mtd->writesize;
163 command = NAND_CMD_READ0;
164 }
165
166 write_cmd_reg(nand, command & 0xff);
167
168 if (column != -1 || page_addr != -1) {
169
170 if (column != -1) {
171 if (chip->options & NAND_BUSWIDTH_16)
172 column >>= 1;
173 write_addr_reg(nand, column);
174 write_addr_reg(nand, column >> 8 | ENDADDR);
175 }
176 if (page_addr != -1) {
177 write_addr_reg(nand, page_addr);
178
179 if (chip->chipsize > (128 << 20)) {
180 write_addr_reg(nand, page_addr >> 8);
181 write_addr_reg(nand, page_addr >> 16 | ENDADDR);
182 } else {
183 write_addr_reg(nand, page_addr >> 8 | ENDADDR);
184 }
185 }
186 }
187
188 switch (command) {
189 case NAND_CMD_CACHEDPROG:
190 case NAND_CMD_PAGEPROG:
191 case NAND_CMD_ERASE1:
192 case NAND_CMD_ERASE2:
193 case NAND_CMD_SEQIN:
194 case NAND_CMD_RNDIN:
195 case NAND_CMD_STATUS:
196 case NAND_CMD_DEPLETE1:
197 return;
198
199 case NAND_CMD_STATUS_ERROR:
200 case NAND_CMD_STATUS_ERROR0:
201 case NAND_CMD_STATUS_ERROR1:
202 case NAND_CMD_STATUS_ERROR2:
203 case NAND_CMD_STATUS_ERROR3:
204 udelay(chip->chip_delay);
205 return;
206
207 case NAND_CMD_RESET:
208 if (chip->dev_ready)
209 break;
210 udelay(chip->chip_delay);
211
212 write_cmd_reg(nand, NAND_CMD_STATUS);
213 write_cmd_reg(nand, command);
214
215 while (!w90p910_check_rb(nand))
216 ;
217
218 return;
219
220 case NAND_CMD_RNDOUT:
221 write_cmd_reg(nand, NAND_CMD_RNDOUTSTART);
222 return;
223
224 case NAND_CMD_READ0:
225
226 write_cmd_reg(nand, NAND_CMD_READSTART);
227 default:
228
229 if (!chip->dev_ready) {
230 udelay(chip->chip_delay);
231 return;
232 }
233 }
234
235 /* Apply this short delay always to ensure that we do wait tWB in
236 * any case on any machine. */
237 ndelay(100);
238
239 while (!chip->dev_ready(mtd))
240 ;
241}
242
243
244static void w90p910_nand_enable(struct w90p910_nand *nand)
245{
246 unsigned int val;
247 spin_lock(&nand->lock);
248 __raw_writel(RESET_FMI, (nand->reg + REG_FMICSR));
249
250 val = __raw_readl(nand->reg + REG_FMICSR);
251
252 if (!(val & NAND_EN))
253 __raw_writel(val | NAND_EN, REG_FMICSR);
254
255 val = __raw_readl(nand->reg + REG_SMCSR);
256
257 val &= ~(SWRST|PSIZE|DMARWEN|BUSWID|ECC4EN|NANDCS);
258 val |= WP;
259
260 __raw_writel(val, nand->reg + REG_SMCSR);
261
262 spin_unlock(&nand->lock);
263}
264
265static int __devinit w90p910_nand_probe(struct platform_device *pdev)
266{
267 struct w90p910_nand *w90p910_nand;
268 struct nand_chip *chip;
269 int retval;
270 struct resource *res;
271
272 retval = 0;
273
274 w90p910_nand = kzalloc(sizeof(struct w90p910_nand), GFP_KERNEL);
275 if (!w90p910_nand)
276 return -ENOMEM;
277 chip = &(w90p910_nand->chip);
278
279 w90p910_nand->mtd.priv = chip;
280 w90p910_nand->mtd.owner = THIS_MODULE;
281 spin_lock_init(&w90p910_nand->lock);
282
283 w90p910_nand->clk = clk_get(&pdev->dev, NULL);
284 if (IS_ERR(w90p910_nand->clk)) {
285 retval = -ENOENT;
286 goto fail1;
287 }
288 clk_enable(w90p910_nand->clk);
289
290 chip->cmdfunc = w90p910_nand_command_lp;
291 chip->dev_ready = w90p910_nand_devready;
292 chip->read_byte = w90p910_nand_read_byte;
293 chip->write_buf = w90p910_nand_write_buf;
294 chip->read_buf = w90p910_nand_read_buf;
295 chip->verify_buf = w90p910_verify_buf;
296 chip->chip_delay = 50;
297 chip->options = 0;
298 chip->ecc.mode = NAND_ECC_SOFT;
299
300 res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
301 if (!res) {
302 retval = -ENXIO;
303 goto fail1;
304 }
305
306 if (!request_mem_region(res->start, resource_size(res), pdev->name)) {
307 retval = -EBUSY;
308 goto fail1;
309 }
310
311 w90p910_nand->reg = ioremap(res->start, resource_size(res));
312 if (!w90p910_nand->reg) {
313 retval = -ENOMEM;
314 goto fail2;
315 }
316
317 w90p910_nand_enable(w90p910_nand);
318
319 if (nand_scan(&(w90p910_nand->mtd), 1)) {
320 retval = -ENXIO;
321 goto fail3;
322 }
323
324 add_mtd_partitions(&(w90p910_nand->mtd), partitions,
325 ARRAY_SIZE(partitions));
326
327 platform_set_drvdata(pdev, w90p910_nand);
328
329 return retval;
330
331fail3: iounmap(w90p910_nand->reg);
332fail2: release_mem_region(res->start, resource_size(res));
333fail1: kfree(w90p910_nand);
334 return retval;
335}
336
337static int __devexit w90p910_nand_remove(struct platform_device *pdev)
338{
339 struct w90p910_nand *w90p910_nand = platform_get_drvdata(pdev);
340 struct resource *res;
341
342 iounmap(w90p910_nand->reg);
343
344 res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
345 release_mem_region(res->start, resource_size(res));
346
347 clk_disable(w90p910_nand->clk);
348 clk_put(w90p910_nand->clk);
349
350 kfree(w90p910_nand);
351
352 platform_set_drvdata(pdev, NULL);
353
354 return 0;
355}
356
357static struct platform_driver w90p910_nand_driver = {
358 .probe = w90p910_nand_probe,
359 .remove = __devexit_p(w90p910_nand_remove),
360 .driver = {
361 .name = "w90p910-fmi",
362 .owner = THIS_MODULE,
363 },
364};
365
366static int __init w90p910_nand_init(void)
367{
368 return platform_driver_register(&w90p910_nand_driver);
369}
370
371static void __exit w90p910_nand_exit(void)
372{
373 platform_driver_unregister(&w90p910_nand_driver);
374}
375
376module_init(w90p910_nand_init);
377module_exit(w90p910_nand_exit);
378
379MODULE_AUTHOR("Wan ZongShun <mcuos.com@gmail.com>");
380MODULE_DESCRIPTION("w90p910 nand driver!");
381MODULE_LICENSE("GPL");
382MODULE_ALIAS("platform:w90p910-fmi");