diff options
Diffstat (limited to 'drivers/mtd/nand/atmel_nand.c')
-rw-r--r-- | drivers/mtd/nand/atmel_nand.c | 590 |
1 files changed, 590 insertions, 0 deletions
diff --git a/drivers/mtd/nand/atmel_nand.c b/drivers/mtd/nand/atmel_nand.c new file mode 100644 index 000000000000..51b703155db6 --- /dev/null +++ b/drivers/mtd/nand/atmel_nand.c | |||
@@ -0,0 +1,590 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2003 Rick Bronson | ||
3 | * | ||
4 | * Derived from drivers/mtd/nand/autcpu12.c | ||
5 | * Copyright (c) 2001 Thomas Gleixner (gleixner@autronix.de) | ||
6 | * | ||
7 | * Derived from drivers/mtd/spia.c | ||
8 | * Copyright (C) 2000 Steven J. Hill (sjhill@cotw.com) | ||
9 | * | ||
10 | * | ||
11 | * Add Hardware ECC support for AT91SAM9260 / AT91SAM9263 | ||
12 | * Richard Genoud (richard.genoud@gmail.com), Adeneo Copyright (C) 2007 | ||
13 | * | ||
14 | * Derived from Das U-Boot source code | ||
15 | * (u-boot-1.1.5/board/atmel/at91sam9263ek/nand.c) | ||
16 | * (C) Copyright 2006 ATMEL Rousset, Lacressonniere Nicolas | ||
17 | * | ||
18 | * | ||
19 | * This program is free software; you can redistribute it and/or modify | ||
20 | * it under the terms of the GNU General Public License version 2 as | ||
21 | * published by the Free Software Foundation. | ||
22 | * | ||
23 | */ | ||
24 | |||
25 | #include <linux/slab.h> | ||
26 | #include <linux/module.h> | ||
27 | #include <linux/platform_device.h> | ||
28 | #include <linux/mtd/mtd.h> | ||
29 | #include <linux/mtd/nand.h> | ||
30 | #include <linux/mtd/partitions.h> | ||
31 | |||
32 | #include <asm/gpio.h> | ||
33 | #include <asm/io.h> | ||
34 | |||
35 | #include <asm/arch/board.h> | ||
36 | |||
37 | #ifdef CONFIG_MTD_NAND_ATMEL_ECC_HW | ||
38 | #define hard_ecc 1 | ||
39 | #else | ||
40 | #define hard_ecc 0 | ||
41 | #endif | ||
42 | |||
43 | #ifdef CONFIG_MTD_NAND_ATMEL_ECC_NONE | ||
44 | #define no_ecc 1 | ||
45 | #else | ||
46 | #define no_ecc 0 | ||
47 | #endif | ||
48 | |||
49 | /* Register access macros */ | ||
50 | #define ecc_readl(add, reg) \ | ||
51 | __raw_readl(add + AT91_ECC_##reg) | ||
52 | #define ecc_writel(add, reg, value) \ | ||
53 | __raw_writel((value), add + AT91_ECC_##reg) | ||
54 | |||
55 | #include "atmel_nand_ecc.h" /* Hardware ECC registers */ | ||
56 | |||
57 | /* oob layout for large page size | ||
58 | * bad block info is on bytes 0 and 1 | ||
59 | * the bytes have to be consecutives to avoid | ||
60 | * several NAND_CMD_RNDOUT during read | ||
61 | */ | ||
62 | static struct nand_ecclayout at91_oobinfo_large = { | ||
63 | .eccbytes = 4, | ||
64 | .eccpos = {60, 61, 62, 63}, | ||
65 | .oobfree = { | ||
66 | {2, 58} | ||
67 | }, | ||
68 | }; | ||
69 | |||
70 | /* oob layout for small page size | ||
71 | * bad block info is on bytes 4 and 5 | ||
72 | * the bytes have to be consecutives to avoid | ||
73 | * several NAND_CMD_RNDOUT during read | ||
74 | */ | ||
75 | static struct nand_ecclayout at91_oobinfo_small = { | ||
76 | .eccbytes = 4, | ||
77 | .eccpos = {0, 1, 2, 3}, | ||
78 | .oobfree = { | ||
79 | {6, 10} | ||
80 | }, | ||
81 | }; | ||
82 | |||
83 | struct at91_nand_host { | ||
84 | struct nand_chip nand_chip; | ||
85 | struct mtd_info mtd; | ||
86 | void __iomem *io_base; | ||
87 | struct at91_nand_data *board; | ||
88 | struct device *dev; | ||
89 | void __iomem *ecc; | ||
90 | }; | ||
91 | |||
92 | /* | ||
93 | * Enable NAND. | ||
94 | */ | ||
95 | static void at91_nand_enable(struct at91_nand_host *host) | ||
96 | { | ||
97 | if (host->board->enable_pin) | ||
98 | gpio_set_value(host->board->enable_pin, 0); | ||
99 | } | ||
100 | |||
101 | /* | ||
102 | * Disable NAND. | ||
103 | */ | ||
104 | static void at91_nand_disable(struct at91_nand_host *host) | ||
105 | { | ||
106 | if (host->board->enable_pin) | ||
107 | gpio_set_value(host->board->enable_pin, 1); | ||
108 | } | ||
109 | |||
110 | /* | ||
111 | * Hardware specific access to control-lines | ||
112 | */ | ||
113 | static void at91_nand_cmd_ctrl(struct mtd_info *mtd, int cmd, unsigned int ctrl) | ||
114 | { | ||
115 | struct nand_chip *nand_chip = mtd->priv; | ||
116 | struct at91_nand_host *host = nand_chip->priv; | ||
117 | |||
118 | if (ctrl & NAND_CTRL_CHANGE) { | ||
119 | if (ctrl & NAND_NCE) | ||
120 | at91_nand_enable(host); | ||
121 | else | ||
122 | at91_nand_disable(host); | ||
123 | } | ||
124 | if (cmd == NAND_CMD_NONE) | ||
125 | return; | ||
126 | |||
127 | if (ctrl & NAND_CLE) | ||
128 | writeb(cmd, host->io_base + (1 << host->board->cle)); | ||
129 | else | ||
130 | writeb(cmd, host->io_base + (1 << host->board->ale)); | ||
131 | } | ||
132 | |||
133 | /* | ||
134 | * Read the Device Ready pin. | ||
135 | */ | ||
136 | static int at91_nand_device_ready(struct mtd_info *mtd) | ||
137 | { | ||
138 | struct nand_chip *nand_chip = mtd->priv; | ||
139 | struct at91_nand_host *host = nand_chip->priv; | ||
140 | |||
141 | return gpio_get_value(host->board->rdy_pin); | ||
142 | } | ||
143 | |||
144 | /* | ||
145 | * write oob for small pages | ||
146 | */ | ||
147 | static int at91_nand_write_oob_512(struct mtd_info *mtd, | ||
148 | struct nand_chip *chip, int page) | ||
149 | { | ||
150 | int chunk = chip->ecc.bytes + chip->ecc.prepad + chip->ecc.postpad; | ||
151 | int eccsize = chip->ecc.size, length = mtd->oobsize; | ||
152 | int len, pos, status = 0; | ||
153 | const uint8_t *bufpoi = chip->oob_poi; | ||
154 | |||
155 | pos = eccsize + chunk; | ||
156 | |||
157 | chip->cmdfunc(mtd, NAND_CMD_SEQIN, pos, page); | ||
158 | len = min_t(int, length, chunk); | ||
159 | chip->write_buf(mtd, bufpoi, len); | ||
160 | bufpoi += len; | ||
161 | length -= len; | ||
162 | if (length > 0) | ||
163 | chip->write_buf(mtd, bufpoi, length); | ||
164 | |||
165 | chip->cmdfunc(mtd, NAND_CMD_PAGEPROG, -1, -1); | ||
166 | status = chip->waitfunc(mtd, chip); | ||
167 | |||
168 | return status & NAND_STATUS_FAIL ? -EIO : 0; | ||
169 | |||
170 | } | ||
171 | |||
172 | /* | ||
173 | * read oob for small pages | ||
174 | */ | ||
175 | static int at91_nand_read_oob_512(struct mtd_info *mtd, | ||
176 | struct nand_chip *chip, int page, int sndcmd) | ||
177 | { | ||
178 | if (sndcmd) { | ||
179 | chip->cmdfunc(mtd, NAND_CMD_READOOB, 0, page); | ||
180 | sndcmd = 0; | ||
181 | } | ||
182 | chip->read_buf(mtd, chip->oob_poi, mtd->oobsize); | ||
183 | return sndcmd; | ||
184 | } | ||
185 | |||
186 | /* | ||
187 | * Calculate HW ECC | ||
188 | * | ||
189 | * function called after a write | ||
190 | * | ||
191 | * mtd: MTD block structure | ||
192 | * dat: raw data (unused) | ||
193 | * ecc_code: buffer for ECC | ||
194 | */ | ||
195 | static int at91_nand_calculate(struct mtd_info *mtd, | ||
196 | const u_char *dat, unsigned char *ecc_code) | ||
197 | { | ||
198 | struct nand_chip *nand_chip = mtd->priv; | ||
199 | struct at91_nand_host *host = nand_chip->priv; | ||
200 | uint32_t *eccpos = nand_chip->ecc.layout->eccpos; | ||
201 | unsigned int ecc_value; | ||
202 | |||
203 | /* get the first 2 ECC bytes */ | ||
204 | ecc_value = ecc_readl(host->ecc, PR); | ||
205 | |||
206 | ecc_code[eccpos[0]] = ecc_value & 0xFF; | ||
207 | ecc_code[eccpos[1]] = (ecc_value >> 8) & 0xFF; | ||
208 | |||
209 | /* get the last 2 ECC bytes */ | ||
210 | ecc_value = ecc_readl(host->ecc, NPR) & AT91_ECC_NPARITY; | ||
211 | |||
212 | ecc_code[eccpos[2]] = ecc_value & 0xFF; | ||
213 | ecc_code[eccpos[3]] = (ecc_value >> 8) & 0xFF; | ||
214 | |||
215 | return 0; | ||
216 | } | ||
217 | |||
218 | /* | ||
219 | * HW ECC read page function | ||
220 | * | ||
221 | * mtd: mtd info structure | ||
222 | * chip: nand chip info structure | ||
223 | * buf: buffer to store read data | ||
224 | */ | ||
225 | static int at91_nand_read_page(struct mtd_info *mtd, | ||
226 | struct nand_chip *chip, uint8_t *buf) | ||
227 | { | ||
228 | int eccsize = chip->ecc.size; | ||
229 | int eccbytes = chip->ecc.bytes; | ||
230 | uint32_t *eccpos = chip->ecc.layout->eccpos; | ||
231 | uint8_t *p = buf; | ||
232 | uint8_t *oob = chip->oob_poi; | ||
233 | uint8_t *ecc_pos; | ||
234 | int stat; | ||
235 | |||
236 | /* read the page */ | ||
237 | chip->read_buf(mtd, p, eccsize); | ||
238 | |||
239 | /* move to ECC position if needed */ | ||
240 | if (eccpos[0] != 0) { | ||
241 | /* This only works on large pages | ||
242 | * because the ECC controller waits for | ||
243 | * NAND_CMD_RNDOUTSTART after the | ||
244 | * NAND_CMD_RNDOUT. | ||
245 | * anyway, for small pages, the eccpos[0] == 0 | ||
246 | */ | ||
247 | chip->cmdfunc(mtd, NAND_CMD_RNDOUT, | ||
248 | mtd->writesize + eccpos[0], -1); | ||
249 | } | ||
250 | |||
251 | /* the ECC controller needs to read the ECC just after the data */ | ||
252 | ecc_pos = oob + eccpos[0]; | ||
253 | chip->read_buf(mtd, ecc_pos, eccbytes); | ||
254 | |||
255 | /* check if there's an error */ | ||
256 | stat = chip->ecc.correct(mtd, p, oob, NULL); | ||
257 | |||
258 | if (stat < 0) | ||
259 | mtd->ecc_stats.failed++; | ||
260 | else | ||
261 | mtd->ecc_stats.corrected += stat; | ||
262 | |||
263 | /* get back to oob start (end of page) */ | ||
264 | chip->cmdfunc(mtd, NAND_CMD_RNDOUT, mtd->writesize, -1); | ||
265 | |||
266 | /* read the oob */ | ||
267 | chip->read_buf(mtd, oob, mtd->oobsize); | ||
268 | |||
269 | return 0; | ||
270 | } | ||
271 | |||
272 | /* | ||
273 | * HW ECC Correction | ||
274 | * | ||
275 | * function called after a read | ||
276 | * | ||
277 | * mtd: MTD block structure | ||
278 | * dat: raw data read from the chip | ||
279 | * read_ecc: ECC from the chip (unused) | ||
280 | * isnull: unused | ||
281 | * | ||
282 | * Detect and correct a 1 bit error for a page | ||
283 | */ | ||
284 | static int at91_nand_correct(struct mtd_info *mtd, u_char *dat, | ||
285 | u_char *read_ecc, u_char *isnull) | ||
286 | { | ||
287 | struct nand_chip *nand_chip = mtd->priv; | ||
288 | struct at91_nand_host *host = nand_chip->priv; | ||
289 | unsigned int ecc_status; | ||
290 | unsigned int ecc_word, ecc_bit; | ||
291 | |||
292 | /* get the status from the Status Register */ | ||
293 | ecc_status = ecc_readl(host->ecc, SR); | ||
294 | |||
295 | /* if there's no error */ | ||
296 | if (likely(!(ecc_status & AT91_ECC_RECERR))) | ||
297 | return 0; | ||
298 | |||
299 | /* get error bit offset (4 bits) */ | ||
300 | ecc_bit = ecc_readl(host->ecc, PR) & AT91_ECC_BITADDR; | ||
301 | /* get word address (12 bits) */ | ||
302 | ecc_word = ecc_readl(host->ecc, PR) & AT91_ECC_WORDADDR; | ||
303 | ecc_word >>= 4; | ||
304 | |||
305 | /* if there are multiple errors */ | ||
306 | if (ecc_status & AT91_ECC_MULERR) { | ||
307 | /* check if it is a freshly erased block | ||
308 | * (filled with 0xff) */ | ||
309 | if ((ecc_bit == AT91_ECC_BITADDR) | ||
310 | && (ecc_word == (AT91_ECC_WORDADDR >> 4))) { | ||
311 | /* the block has just been erased, return OK */ | ||
312 | return 0; | ||
313 | } | ||
314 | /* it doesn't seems to be a freshly | ||
315 | * erased block. | ||
316 | * We can't correct so many errors */ | ||
317 | dev_dbg(host->dev, "at91_nand : multiple errors detected." | ||
318 | " Unable to correct.\n"); | ||
319 | return -EIO; | ||
320 | } | ||
321 | |||
322 | /* if there's a single bit error : we can correct it */ | ||
323 | if (ecc_status & AT91_ECC_ECCERR) { | ||
324 | /* there's nothing much to do here. | ||
325 | * the bit error is on the ECC itself. | ||
326 | */ | ||
327 | dev_dbg(host->dev, "at91_nand : one bit error on ECC code." | ||
328 | " Nothing to correct\n"); | ||
329 | return 0; | ||
330 | } | ||
331 | |||
332 | dev_dbg(host->dev, "at91_nand : one bit error on data." | ||
333 | " (word offset in the page :" | ||
334 | " 0x%x bit offset : 0x%x)\n", | ||
335 | ecc_word, ecc_bit); | ||
336 | /* correct the error */ | ||
337 | if (nand_chip->options & NAND_BUSWIDTH_16) { | ||
338 | /* 16 bits words */ | ||
339 | ((unsigned short *) dat)[ecc_word] ^= (1 << ecc_bit); | ||
340 | } else { | ||
341 | /* 8 bits words */ | ||
342 | dat[ecc_word] ^= (1 << ecc_bit); | ||
343 | } | ||
344 | dev_dbg(host->dev, "at91_nand : error corrected\n"); | ||
345 | return 1; | ||
346 | } | ||
347 | |||
348 | /* | ||
349 | * Enable HW ECC : unsused | ||
350 | */ | ||
351 | static void at91_nand_hwctl(struct mtd_info *mtd, int mode) { ; } | ||
352 | |||
353 | #ifdef CONFIG_MTD_PARTITIONS | ||
354 | static const char *part_probes[] = { "cmdlinepart", NULL }; | ||
355 | #endif | ||
356 | |||
357 | /* | ||
358 | * Probe for the NAND device. | ||
359 | */ | ||
360 | static int __init at91_nand_probe(struct platform_device *pdev) | ||
361 | { | ||
362 | struct at91_nand_host *host; | ||
363 | struct mtd_info *mtd; | ||
364 | struct nand_chip *nand_chip; | ||
365 | struct resource *regs; | ||
366 | struct resource *mem; | ||
367 | int res; | ||
368 | |||
369 | #ifdef CONFIG_MTD_PARTITIONS | ||
370 | struct mtd_partition *partitions = NULL; | ||
371 | int num_partitions = 0; | ||
372 | #endif | ||
373 | |||
374 | /* Allocate memory for the device structure (and zero it) */ | ||
375 | host = kzalloc(sizeof(struct at91_nand_host), GFP_KERNEL); | ||
376 | if (!host) { | ||
377 | printk(KERN_ERR "at91_nand: failed to allocate device structure.\n"); | ||
378 | return -ENOMEM; | ||
379 | } | ||
380 | |||
381 | mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); | ||
382 | if (!mem) { | ||
383 | printk(KERN_ERR "at91_nand: can't get I/O resource mem\n"); | ||
384 | return -ENXIO; | ||
385 | } | ||
386 | |||
387 | host->io_base = ioremap(mem->start, mem->end - mem->start + 1); | ||
388 | if (host->io_base == NULL) { | ||
389 | printk(KERN_ERR "at91_nand: ioremap failed\n"); | ||
390 | kfree(host); | ||
391 | return -EIO; | ||
392 | } | ||
393 | |||
394 | mtd = &host->mtd; | ||
395 | nand_chip = &host->nand_chip; | ||
396 | host->board = pdev->dev.platform_data; | ||
397 | host->dev = &pdev->dev; | ||
398 | |||
399 | nand_chip->priv = host; /* link the private data structures */ | ||
400 | mtd->priv = nand_chip; | ||
401 | mtd->owner = THIS_MODULE; | ||
402 | |||
403 | /* Set address of NAND IO lines */ | ||
404 | nand_chip->IO_ADDR_R = host->io_base; | ||
405 | nand_chip->IO_ADDR_W = host->io_base; | ||
406 | nand_chip->cmd_ctrl = at91_nand_cmd_ctrl; | ||
407 | |||
408 | if (host->board->rdy_pin) | ||
409 | nand_chip->dev_ready = at91_nand_device_ready; | ||
410 | |||
411 | regs = platform_get_resource(pdev, IORESOURCE_MEM, 1); | ||
412 | if (!regs && hard_ecc) { | ||
413 | printk(KERN_ERR "at91_nand: can't get I/O resource " | ||
414 | "regs\nFalling back on software ECC\n"); | ||
415 | } | ||
416 | |||
417 | nand_chip->ecc.mode = NAND_ECC_SOFT; /* enable ECC */ | ||
418 | if (no_ecc) | ||
419 | nand_chip->ecc.mode = NAND_ECC_NONE; | ||
420 | if (hard_ecc && regs) { | ||
421 | host->ecc = ioremap(regs->start, regs->end - regs->start + 1); | ||
422 | if (host->ecc == NULL) { | ||
423 | printk(KERN_ERR "at91_nand: ioremap failed\n"); | ||
424 | res = -EIO; | ||
425 | goto err_ecc_ioremap; | ||
426 | } | ||
427 | nand_chip->ecc.mode = NAND_ECC_HW_SYNDROME; | ||
428 | nand_chip->ecc.calculate = at91_nand_calculate; | ||
429 | nand_chip->ecc.correct = at91_nand_correct; | ||
430 | nand_chip->ecc.hwctl = at91_nand_hwctl; | ||
431 | nand_chip->ecc.read_page = at91_nand_read_page; | ||
432 | nand_chip->ecc.bytes = 4; | ||
433 | nand_chip->ecc.prepad = 0; | ||
434 | nand_chip->ecc.postpad = 0; | ||
435 | } | ||
436 | |||
437 | nand_chip->chip_delay = 20; /* 20us command delay time */ | ||
438 | |||
439 | if (host->board->bus_width_16) /* 16-bit bus width */ | ||
440 | nand_chip->options |= NAND_BUSWIDTH_16; | ||
441 | |||
442 | platform_set_drvdata(pdev, host); | ||
443 | at91_nand_enable(host); | ||
444 | |||
445 | if (host->board->det_pin) { | ||
446 | if (gpio_get_value(host->board->det_pin)) { | ||
447 | printk ("No SmartMedia card inserted.\n"); | ||
448 | res = ENXIO; | ||
449 | goto out; | ||
450 | } | ||
451 | } | ||
452 | |||
453 | /* first scan to find the device and get the page size */ | ||
454 | if (nand_scan_ident(mtd, 1)) { | ||
455 | res = -ENXIO; | ||
456 | goto out; | ||
457 | } | ||
458 | |||
459 | if (nand_chip->ecc.mode == NAND_ECC_HW_SYNDROME) { | ||
460 | /* ECC is calculated for the whole page (1 step) */ | ||
461 | nand_chip->ecc.size = mtd->writesize; | ||
462 | |||
463 | /* set ECC page size and oob layout */ | ||
464 | switch (mtd->writesize) { | ||
465 | case 512: | ||
466 | nand_chip->ecc.layout = &at91_oobinfo_small; | ||
467 | nand_chip->ecc.read_oob = at91_nand_read_oob_512; | ||
468 | nand_chip->ecc.write_oob = at91_nand_write_oob_512; | ||
469 | ecc_writel(host->ecc, MR, AT91_ECC_PAGESIZE_528); | ||
470 | break; | ||
471 | case 1024: | ||
472 | nand_chip->ecc.layout = &at91_oobinfo_large; | ||
473 | ecc_writel(host->ecc, MR, AT91_ECC_PAGESIZE_1056); | ||
474 | break; | ||
475 | case 2048: | ||
476 | nand_chip->ecc.layout = &at91_oobinfo_large; | ||
477 | ecc_writel(host->ecc, MR, AT91_ECC_PAGESIZE_2112); | ||
478 | break; | ||
479 | case 4096: | ||
480 | nand_chip->ecc.layout = &at91_oobinfo_large; | ||
481 | ecc_writel(host->ecc, MR, AT91_ECC_PAGESIZE_4224); | ||
482 | break; | ||
483 | default: | ||
484 | /* page size not handled by HW ECC */ | ||
485 | /* switching back to soft ECC */ | ||
486 | nand_chip->ecc.mode = NAND_ECC_SOFT; | ||
487 | nand_chip->ecc.calculate = NULL; | ||
488 | nand_chip->ecc.correct = NULL; | ||
489 | nand_chip->ecc.hwctl = NULL; | ||
490 | nand_chip->ecc.read_page = NULL; | ||
491 | nand_chip->ecc.postpad = 0; | ||
492 | nand_chip->ecc.prepad = 0; | ||
493 | nand_chip->ecc.bytes = 0; | ||
494 | break; | ||
495 | } | ||
496 | } | ||
497 | |||
498 | /* second phase scan */ | ||
499 | if (nand_scan_tail(mtd)) { | ||
500 | res = -ENXIO; | ||
501 | goto out; | ||
502 | } | ||
503 | |||
504 | #ifdef CONFIG_MTD_PARTITIONS | ||
505 | #ifdef CONFIG_MTD_CMDLINE_PARTS | ||
506 | mtd->name = "at91_nand"; | ||
507 | num_partitions = parse_mtd_partitions(mtd, part_probes, | ||
508 | &partitions, 0); | ||
509 | #endif | ||
510 | if (num_partitions <= 0 && host->board->partition_info) | ||
511 | partitions = host->board->partition_info(mtd->size, | ||
512 | &num_partitions); | ||
513 | |||
514 | if ((!partitions) || (num_partitions == 0)) { | ||
515 | printk(KERN_ERR "at91_nand: No parititions defined, or unsupported device.\n"); | ||
516 | res = ENXIO; | ||
517 | goto release; | ||
518 | } | ||
519 | |||
520 | res = add_mtd_partitions(mtd, partitions, num_partitions); | ||
521 | #else | ||
522 | res = add_mtd_device(mtd); | ||
523 | #endif | ||
524 | |||
525 | if (!res) | ||
526 | return res; | ||
527 | |||
528 | #ifdef CONFIG_MTD_PARTITIONS | ||
529 | release: | ||
530 | #endif | ||
531 | nand_release(mtd); | ||
532 | |||
533 | out: | ||
534 | iounmap(host->ecc); | ||
535 | |||
536 | err_ecc_ioremap: | ||
537 | at91_nand_disable(host); | ||
538 | platform_set_drvdata(pdev, NULL); | ||
539 | iounmap(host->io_base); | ||
540 | kfree(host); | ||
541 | return res; | ||
542 | } | ||
543 | |||
544 | /* | ||
545 | * Remove a NAND device. | ||
546 | */ | ||
547 | static int __devexit at91_nand_remove(struct platform_device *pdev) | ||
548 | { | ||
549 | struct at91_nand_host *host = platform_get_drvdata(pdev); | ||
550 | struct mtd_info *mtd = &host->mtd; | ||
551 | |||
552 | nand_release(mtd); | ||
553 | |||
554 | at91_nand_disable(host); | ||
555 | |||
556 | iounmap(host->io_base); | ||
557 | iounmap(host->ecc); | ||
558 | kfree(host); | ||
559 | |||
560 | return 0; | ||
561 | } | ||
562 | |||
563 | static struct platform_driver at91_nand_driver = { | ||
564 | .probe = at91_nand_probe, | ||
565 | .remove = at91_nand_remove, | ||
566 | .driver = { | ||
567 | .name = "at91_nand", | ||
568 | .owner = THIS_MODULE, | ||
569 | }, | ||
570 | }; | ||
571 | |||
572 | static int __init at91_nand_init(void) | ||
573 | { | ||
574 | return platform_driver_register(&at91_nand_driver); | ||
575 | } | ||
576 | |||
577 | |||
578 | static void __exit at91_nand_exit(void) | ||
579 | { | ||
580 | platform_driver_unregister(&at91_nand_driver); | ||
581 | } | ||
582 | |||
583 | |||
584 | module_init(at91_nand_init); | ||
585 | module_exit(at91_nand_exit); | ||
586 | |||
587 | MODULE_LICENSE("GPL"); | ||
588 | MODULE_AUTHOR("Rick Bronson"); | ||
589 | MODULE_DESCRIPTION("NAND/SmartMedia driver for AT91 / AVR32"); | ||
590 | MODULE_ALIAS("platform:at91_nand"); | ||