diff options
Diffstat (limited to 'drivers/mtd/nand/au1550nd.c')
-rw-r--r-- | drivers/mtd/nand/au1550nd.c | 477 |
1 files changed, 477 insertions, 0 deletions
diff --git a/drivers/mtd/nand/au1550nd.c b/drivers/mtd/nand/au1550nd.c new file mode 100644 index 000000000000..4c7719ce3f48 --- /dev/null +++ b/drivers/mtd/nand/au1550nd.c | |||
@@ -0,0 +1,477 @@ | |||
1 | /* | ||
2 | * drivers/mtd/nand/au1550nd.c | ||
3 | * | ||
4 | * Copyright (C) 2004 Embedded Edge, LLC | ||
5 | * | ||
6 | * $Id: au1550nd.c,v 1.11 2004/11/04 12:53:10 gleixner Exp $ | ||
7 | * | ||
8 | * This program is free software; you can redistribute it and/or modify | ||
9 | * it under the terms of the GNU General Public License version 2 as | ||
10 | * published by the Free Software Foundation. | ||
11 | * | ||
12 | */ | ||
13 | |||
14 | #include <linux/slab.h> | ||
15 | #include <linux/init.h> | ||
16 | #include <linux/module.h> | ||
17 | #include <linux/mtd/mtd.h> | ||
18 | #include <linux/mtd/nand.h> | ||
19 | #include <linux/mtd/partitions.h> | ||
20 | #include <asm/io.h> | ||
21 | |||
22 | /* fixme: this is ugly */ | ||
23 | #if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 0) | ||
24 | #include <asm/mach-au1x00/au1000.h> | ||
25 | #ifdef CONFIG_MIPS_PB1550 | ||
26 | #include <asm/mach-pb1x00/pb1550.h> | ||
27 | #endif | ||
28 | #ifdef CONFIG_MIPS_DB1550 | ||
29 | #include <asm/mach-db1x00/db1x00.h> | ||
30 | #endif | ||
31 | #else | ||
32 | #include <asm/au1000.h> | ||
33 | #ifdef CONFIG_MIPS_PB1550 | ||
34 | #include <asm/pb1550.h> | ||
35 | #endif | ||
36 | #ifdef CONFIG_MIPS_DB1550 | ||
37 | #include <asm/db1x00.h> | ||
38 | #endif | ||
39 | #endif | ||
40 | |||
41 | /* | ||
42 | * MTD structure for NAND controller | ||
43 | */ | ||
44 | static struct mtd_info *au1550_mtd = NULL; | ||
45 | static void __iomem *p_nand; | ||
46 | static int nand_width = 1; /* default x8*/ | ||
47 | |||
48 | #define NAND_CS 1 | ||
49 | |||
50 | /* | ||
51 | * Define partitions for flash device | ||
52 | */ | ||
53 | const static struct mtd_partition partition_info[] = { | ||
54 | #ifdef CONFIG_MIPS_PB1550 | ||
55 | #define NUM_PARTITIONS 2 | ||
56 | { | ||
57 | .name = "Pb1550 NAND FS 0", | ||
58 | .offset = 0, | ||
59 | .size = 8*1024*1024 | ||
60 | }, | ||
61 | { | ||
62 | .name = "Pb1550 NAND FS 1", | ||
63 | .offset = MTDPART_OFS_APPEND, | ||
64 | .size = MTDPART_SIZ_FULL | ||
65 | } | ||
66 | #endif | ||
67 | #ifdef CONFIG_MIPS_DB1550 | ||
68 | #define NUM_PARTITIONS 2 | ||
69 | { | ||
70 | .name = "Db1550 NAND FS 0", | ||
71 | .offset = 0, | ||
72 | .size = 8*1024*1024 | ||
73 | }, | ||
74 | { | ||
75 | .name = "Db1550 NAND FS 1", | ||
76 | .offset = MTDPART_OFS_APPEND, | ||
77 | .size = MTDPART_SIZ_FULL | ||
78 | } | ||
79 | #endif | ||
80 | }; | ||
81 | |||
82 | |||
83 | /** | ||
84 | * au_read_byte - read one byte from the chip | ||
85 | * @mtd: MTD device structure | ||
86 | * | ||
87 | * read function for 8bit buswith | ||
88 | */ | ||
89 | static u_char au_read_byte(struct mtd_info *mtd) | ||
90 | { | ||
91 | struct nand_chip *this = mtd->priv; | ||
92 | u_char ret = readb(this->IO_ADDR_R); | ||
93 | au_sync(); | ||
94 | return ret; | ||
95 | } | ||
96 | |||
97 | /** | ||
98 | * au_write_byte - write one byte to the chip | ||
99 | * @mtd: MTD device structure | ||
100 | * @byte: pointer to data byte to write | ||
101 | * | ||
102 | * write function for 8it buswith | ||
103 | */ | ||
104 | static void au_write_byte(struct mtd_info *mtd, u_char byte) | ||
105 | { | ||
106 | struct nand_chip *this = mtd->priv; | ||
107 | writeb(byte, this->IO_ADDR_W); | ||
108 | au_sync(); | ||
109 | } | ||
110 | |||
111 | /** | ||
112 | * au_read_byte16 - read one byte endianess aware from the chip | ||
113 | * @mtd: MTD device structure | ||
114 | * | ||
115 | * read function for 16bit buswith with | ||
116 | * endianess conversion | ||
117 | */ | ||
118 | static u_char au_read_byte16(struct mtd_info *mtd) | ||
119 | { | ||
120 | struct nand_chip *this = mtd->priv; | ||
121 | u_char ret = (u_char) cpu_to_le16(readw(this->IO_ADDR_R)); | ||
122 | au_sync(); | ||
123 | return ret; | ||
124 | } | ||
125 | |||
126 | /** | ||
127 | * au_write_byte16 - write one byte endianess aware to the chip | ||
128 | * @mtd: MTD device structure | ||
129 | * @byte: pointer to data byte to write | ||
130 | * | ||
131 | * write function for 16bit buswith with | ||
132 | * endianess conversion | ||
133 | */ | ||
134 | static void au_write_byte16(struct mtd_info *mtd, u_char byte) | ||
135 | { | ||
136 | struct nand_chip *this = mtd->priv; | ||
137 | writew(le16_to_cpu((u16) byte), this->IO_ADDR_W); | ||
138 | au_sync(); | ||
139 | } | ||
140 | |||
141 | /** | ||
142 | * au_read_word - read one word from the chip | ||
143 | * @mtd: MTD device structure | ||
144 | * | ||
145 | * read function for 16bit buswith without | ||
146 | * endianess conversion | ||
147 | */ | ||
148 | static u16 au_read_word(struct mtd_info *mtd) | ||
149 | { | ||
150 | struct nand_chip *this = mtd->priv; | ||
151 | u16 ret = readw(this->IO_ADDR_R); | ||
152 | au_sync(); | ||
153 | return ret; | ||
154 | } | ||
155 | |||
156 | /** | ||
157 | * au_write_word - write one word to the chip | ||
158 | * @mtd: MTD device structure | ||
159 | * @word: data word to write | ||
160 | * | ||
161 | * write function for 16bit buswith without | ||
162 | * endianess conversion | ||
163 | */ | ||
164 | static void au_write_word(struct mtd_info *mtd, u16 word) | ||
165 | { | ||
166 | struct nand_chip *this = mtd->priv; | ||
167 | writew(word, this->IO_ADDR_W); | ||
168 | au_sync(); | ||
169 | } | ||
170 | |||
171 | /** | ||
172 | * au_write_buf - write buffer to chip | ||
173 | * @mtd: MTD device structure | ||
174 | * @buf: data buffer | ||
175 | * @len: number of bytes to write | ||
176 | * | ||
177 | * write function for 8bit buswith | ||
178 | */ | ||
179 | static void au_write_buf(struct mtd_info *mtd, const u_char *buf, int len) | ||
180 | { | ||
181 | int i; | ||
182 | struct nand_chip *this = mtd->priv; | ||
183 | |||
184 | for (i=0; i<len; i++) { | ||
185 | writeb(buf[i], this->IO_ADDR_W); | ||
186 | au_sync(); | ||
187 | } | ||
188 | } | ||
189 | |||
190 | /** | ||
191 | * au_read_buf - read chip data into buffer | ||
192 | * @mtd: MTD device structure | ||
193 | * @buf: buffer to store date | ||
194 | * @len: number of bytes to read | ||
195 | * | ||
196 | * read function for 8bit buswith | ||
197 | */ | ||
198 | static void au_read_buf(struct mtd_info *mtd, u_char *buf, int len) | ||
199 | { | ||
200 | int i; | ||
201 | struct nand_chip *this = mtd->priv; | ||
202 | |||
203 | for (i=0; i<len; i++) { | ||
204 | buf[i] = readb(this->IO_ADDR_R); | ||
205 | au_sync(); | ||
206 | } | ||
207 | } | ||
208 | |||
209 | /** | ||
210 | * au_verify_buf - Verify chip data against buffer | ||
211 | * @mtd: MTD device structure | ||
212 | * @buf: buffer containing the data to compare | ||
213 | * @len: number of bytes to compare | ||
214 | * | ||
215 | * verify function for 8bit buswith | ||
216 | */ | ||
217 | static int au_verify_buf(struct mtd_info *mtd, const u_char *buf, int len) | ||
218 | { | ||
219 | int i; | ||
220 | struct nand_chip *this = mtd->priv; | ||
221 | |||
222 | for (i=0; i<len; i++) { | ||
223 | if (buf[i] != readb(this->IO_ADDR_R)) | ||
224 | return -EFAULT; | ||
225 | au_sync(); | ||
226 | } | ||
227 | |||
228 | return 0; | ||
229 | } | ||
230 | |||
231 | /** | ||
232 | * au_write_buf16 - write buffer to chip | ||
233 | * @mtd: MTD device structure | ||
234 | * @buf: data buffer | ||
235 | * @len: number of bytes to write | ||
236 | * | ||
237 | * write function for 16bit buswith | ||
238 | */ | ||
239 | static void au_write_buf16(struct mtd_info *mtd, const u_char *buf, int len) | ||
240 | { | ||
241 | int i; | ||
242 | struct nand_chip *this = mtd->priv; | ||
243 | u16 *p = (u16 *) buf; | ||
244 | len >>= 1; | ||
245 | |||
246 | for (i=0; i<len; i++) { | ||
247 | writew(p[i], this->IO_ADDR_W); | ||
248 | au_sync(); | ||
249 | } | ||
250 | |||
251 | } | ||
252 | |||
253 | /** | ||
254 | * au_read_buf16 - read chip data into buffer | ||
255 | * @mtd: MTD device structure | ||
256 | * @buf: buffer to store date | ||
257 | * @len: number of bytes to read | ||
258 | * | ||
259 | * read function for 16bit buswith | ||
260 | */ | ||
261 | static void au_read_buf16(struct mtd_info *mtd, u_char *buf, int len) | ||
262 | { | ||
263 | int i; | ||
264 | struct nand_chip *this = mtd->priv; | ||
265 | u16 *p = (u16 *) buf; | ||
266 | len >>= 1; | ||
267 | |||
268 | for (i=0; i<len; i++) { | ||
269 | p[i] = readw(this->IO_ADDR_R); | ||
270 | au_sync(); | ||
271 | } | ||
272 | } | ||
273 | |||
274 | /** | ||
275 | * au_verify_buf16 - Verify chip data against buffer | ||
276 | * @mtd: MTD device structure | ||
277 | * @buf: buffer containing the data to compare | ||
278 | * @len: number of bytes to compare | ||
279 | * | ||
280 | * verify function for 16bit buswith | ||
281 | */ | ||
282 | static int au_verify_buf16(struct mtd_info *mtd, const u_char *buf, int len) | ||
283 | { | ||
284 | int i; | ||
285 | struct nand_chip *this = mtd->priv; | ||
286 | u16 *p = (u16 *) buf; | ||
287 | len >>= 1; | ||
288 | |||
289 | for (i=0; i<len; i++) { | ||
290 | if (p[i] != readw(this->IO_ADDR_R)) | ||
291 | return -EFAULT; | ||
292 | au_sync(); | ||
293 | } | ||
294 | return 0; | ||
295 | } | ||
296 | |||
297 | |||
298 | static void au1550_hwcontrol(struct mtd_info *mtd, int cmd) | ||
299 | { | ||
300 | register struct nand_chip *this = mtd->priv; | ||
301 | |||
302 | switch(cmd){ | ||
303 | |||
304 | case NAND_CTL_SETCLE: this->IO_ADDR_W = p_nand + MEM_STNAND_CMD; break; | ||
305 | case NAND_CTL_CLRCLE: this->IO_ADDR_W = p_nand + MEM_STNAND_DATA; break; | ||
306 | |||
307 | case NAND_CTL_SETALE: this->IO_ADDR_W = p_nand + MEM_STNAND_ADDR; break; | ||
308 | case NAND_CTL_CLRALE: | ||
309 | this->IO_ADDR_W = p_nand + MEM_STNAND_DATA; | ||
310 | /* FIXME: Nobody knows why this is neccecary, | ||
311 | * but it works only that way */ | ||
312 | udelay(1); | ||
313 | break; | ||
314 | |||
315 | case NAND_CTL_SETNCE: | ||
316 | /* assert (force assert) chip enable */ | ||
317 | au_writel((1<<(4+NAND_CS)) , MEM_STNDCTL); break; | ||
318 | break; | ||
319 | |||
320 | case NAND_CTL_CLRNCE: | ||
321 | /* deassert chip enable */ | ||
322 | au_writel(0, MEM_STNDCTL); break; | ||
323 | break; | ||
324 | } | ||
325 | |||
326 | this->IO_ADDR_R = this->IO_ADDR_W; | ||
327 | |||
328 | /* Drain the writebuffer */ | ||
329 | au_sync(); | ||
330 | } | ||
331 | |||
332 | int au1550_device_ready(struct mtd_info *mtd) | ||
333 | { | ||
334 | int ret = (au_readl(MEM_STSTAT) & 0x1) ? 1 : 0; | ||
335 | au_sync(); | ||
336 | return ret; | ||
337 | } | ||
338 | |||
339 | /* | ||
340 | * Main initialization routine | ||
341 | */ | ||
342 | int __init au1550_init (void) | ||
343 | { | ||
344 | struct nand_chip *this; | ||
345 | u16 boot_swapboot = 0; /* default value */ | ||
346 | int retval; | ||
347 | |||
348 | /* Allocate memory for MTD device structure and private data */ | ||
349 | au1550_mtd = kmalloc (sizeof(struct mtd_info) + | ||
350 | sizeof (struct nand_chip), GFP_KERNEL); | ||
351 | if (!au1550_mtd) { | ||
352 | printk ("Unable to allocate NAND MTD dev structure.\n"); | ||
353 | return -ENOMEM; | ||
354 | } | ||
355 | |||
356 | /* Get pointer to private data */ | ||
357 | this = (struct nand_chip *) (&au1550_mtd[1]); | ||
358 | |||
359 | /* Initialize structures */ | ||
360 | memset((char *) au1550_mtd, 0, sizeof(struct mtd_info)); | ||
361 | memset((char *) this, 0, sizeof(struct nand_chip)); | ||
362 | |||
363 | /* Link the private data with the MTD structure */ | ||
364 | au1550_mtd->priv = this; | ||
365 | |||
366 | |||
367 | /* MEM_STNDCTL: disable ints, disable nand boot */ | ||
368 | au_writel(0, MEM_STNDCTL); | ||
369 | |||
370 | #ifdef CONFIG_MIPS_PB1550 | ||
371 | /* set gpio206 high */ | ||
372 | au_writel(au_readl(GPIO2_DIR) & ~(1<<6), GPIO2_DIR); | ||
373 | |||
374 | boot_swapboot = (au_readl(MEM_STSTAT) & (0x7<<1)) | | ||
375 | ((bcsr->status >> 6) & 0x1); | ||
376 | switch (boot_swapboot) { | ||
377 | case 0: | ||
378 | case 2: | ||
379 | case 8: | ||
380 | case 0xC: | ||
381 | case 0xD: | ||
382 | /* x16 NAND Flash */ | ||
383 | nand_width = 0; | ||
384 | break; | ||
385 | case 1: | ||
386 | case 9: | ||
387 | case 3: | ||
388 | case 0xE: | ||
389 | case 0xF: | ||
390 | /* x8 NAND Flash */ | ||
391 | nand_width = 1; | ||
392 | break; | ||
393 | default: | ||
394 | printk("Pb1550 NAND: bad boot:swap\n"); | ||
395 | retval = -EINVAL; | ||
396 | goto outmem; | ||
397 | } | ||
398 | #endif | ||
399 | |||
400 | /* Configure RCE1 - should be done by YAMON */ | ||
401 | au_writel(0x5 | (nand_width << 22), 0xB4001010); /* MEM_STCFG1 */ | ||
402 | au_writel(NAND_TIMING, 0xB4001014); /* MEM_STTIME1 */ | ||
403 | au_sync(); | ||
404 | |||
405 | /* setup and enable chip select, MEM_STADDR1 */ | ||
406 | /* we really need to decode offsets only up till 0x20 */ | ||
407 | au_writel((1<<28) | (NAND_PHYS_ADDR>>4) | | ||
408 | (((NAND_PHYS_ADDR + 0x1000)-1) & (0x3fff<<18)>>18), | ||
409 | MEM_STADDR1); | ||
410 | au_sync(); | ||
411 | |||
412 | p_nand = ioremap(NAND_PHYS_ADDR, 0x1000); | ||
413 | |||
414 | /* Set address of hardware control function */ | ||
415 | this->hwcontrol = au1550_hwcontrol; | ||
416 | this->dev_ready = au1550_device_ready; | ||
417 | /* 30 us command delay time */ | ||
418 | this->chip_delay = 30; | ||
419 | this->eccmode = NAND_ECC_SOFT; | ||
420 | |||
421 | this->options = NAND_NO_AUTOINCR; | ||
422 | |||
423 | if (!nand_width) | ||
424 | this->options |= NAND_BUSWIDTH_16; | ||
425 | |||
426 | this->read_byte = (!nand_width) ? au_read_byte16 : au_read_byte; | ||
427 | this->write_byte = (!nand_width) ? au_write_byte16 : au_write_byte; | ||
428 | this->write_word = au_write_word; | ||
429 | this->read_word = au_read_word; | ||
430 | this->write_buf = (!nand_width) ? au_write_buf16 : au_write_buf; | ||
431 | this->read_buf = (!nand_width) ? au_read_buf16 : au_read_buf; | ||
432 | this->verify_buf = (!nand_width) ? au_verify_buf16 : au_verify_buf; | ||
433 | |||
434 | /* Scan to find existence of the device */ | ||
435 | if (nand_scan (au1550_mtd, 1)) { | ||
436 | retval = -ENXIO; | ||
437 | goto outio; | ||
438 | } | ||
439 | |||
440 | /* Register the partitions */ | ||
441 | add_mtd_partitions(au1550_mtd, partition_info, NUM_PARTITIONS); | ||
442 | |||
443 | return 0; | ||
444 | |||
445 | outio: | ||
446 | iounmap ((void *)p_nand); | ||
447 | |||
448 | outmem: | ||
449 | kfree (au1550_mtd); | ||
450 | return retval; | ||
451 | } | ||
452 | |||
453 | module_init(au1550_init); | ||
454 | |||
455 | /* | ||
456 | * Clean up routine | ||
457 | */ | ||
458 | #ifdef MODULE | ||
459 | static void __exit au1550_cleanup (void) | ||
460 | { | ||
461 | struct nand_chip *this = (struct nand_chip *) &au1550_mtd[1]; | ||
462 | |||
463 | /* Release resources, unregister device */ | ||
464 | nand_release (au1550_mtd); | ||
465 | |||
466 | /* Free the MTD device structure */ | ||
467 | kfree (au1550_mtd); | ||
468 | |||
469 | /* Unmap */ | ||
470 | iounmap ((void *)p_nand); | ||
471 | } | ||
472 | module_exit(au1550_cleanup); | ||
473 | #endif | ||
474 | |||
475 | MODULE_LICENSE("GPL"); | ||
476 | MODULE_AUTHOR("Embedded Edge, LLC"); | ||
477 | MODULE_DESCRIPTION("Board-specific glue layer for NAND flash on Pb1550 board"); | ||