diff options
-rw-r--r-- | drivers/mtd/nand/Kconfig | 41 | ||||
-rw-r--r-- | drivers/mtd/nand/at91_nand.c | 361 |
2 files changed, 397 insertions, 5 deletions
diff --git a/drivers/mtd/nand/Kconfig b/drivers/mtd/nand/Kconfig index dcbb0decd465..5076faf9ca66 100644 --- a/drivers/mtd/nand/Kconfig +++ b/drivers/mtd/nand/Kconfig | |||
@@ -278,6 +278,47 @@ config MTD_NAND_AT91 | |||
278 | help | 278 | help |
279 | Enables support for NAND Flash / Smart Media Card interface | 279 | Enables support for NAND Flash / Smart Media Card interface |
280 | on Atmel AT91 processors. | 280 | on Atmel AT91 processors. |
281 | choice | ||
282 | prompt "ECC management for NAND Flash / SmartMedia on AT91" | ||
283 | depends on MTD_NAND_AT91 | ||
284 | |||
285 | config MTD_NAND_AT91_ECC_HW | ||
286 | bool "Hardware ECC" | ||
287 | depends on ARCH_AT91SAM9263 || ARCH_AT91SAM9260 | ||
288 | help | ||
289 | Uses hardware ECC provided by the at91sam9260/at91sam9263 chip | ||
290 | instead of software ECC. | ||
291 | The hardware ECC controller is capable of single bit error | ||
292 | correction and 2-bit random detection per page. | ||
293 | |||
294 | NB : hardware and software ECC schemes are incompatible. | ||
295 | If you switch from one to another, you'll have to erase your | ||
296 | mtd partition. | ||
297 | |||
298 | If unsure, say Y | ||
299 | |||
300 | config MTD_NAND_AT91_ECC_SOFT | ||
301 | bool "Software ECC" | ||
302 | help | ||
303 | Uses software ECC. | ||
304 | |||
305 | NB : hardware and software ECC schemes are incompatible. | ||
306 | If you switch from one to another, you'll have to erase your | ||
307 | mtd partition. | ||
308 | |||
309 | config MTD_NAND_AT91_ECC_NONE | ||
310 | bool "No ECC (testing only, DANGEROUS)" | ||
311 | depends on DEBUG_KERNEL | ||
312 | help | ||
313 | No ECC will be used. | ||
314 | It's not a good idea and it should be reserved for testing | ||
315 | purpose only. | ||
316 | |||
317 | If unsure, say N | ||
318 | |||
319 | endchoice | ||
320 | |||
321 | endchoice | ||
281 | 322 | ||
282 | config MTD_NAND_PXA3xx | 323 | config MTD_NAND_PXA3xx |
283 | bool "Support for NAND flash devices on PXA3xx" | 324 | bool "Support for NAND flash devices on PXA3xx" |
diff --git a/drivers/mtd/nand/at91_nand.c b/drivers/mtd/nand/at91_nand.c index 463632ed794c..c3eb203a2ad0 100644 --- a/drivers/mtd/nand/at91_nand.c +++ b/drivers/mtd/nand/at91_nand.c | |||
@@ -9,6 +9,15 @@ | |||
9 | * Derived from drivers/mtd/spia.c | 9 | * Derived from drivers/mtd/spia.c |
10 | * Copyright (C) 2000 Steven J. Hill (sjhill@cotw.com) | 10 | * Copyright (C) 2000 Steven J. Hill (sjhill@cotw.com) |
11 | * | 11 | * |
12 | * | ||
13 | * Add Hardware ECC support for AT91SAM9260 / AT91SAM9263 | ||
14 | * Richard Genoud (richard.genoud@gmail.com), Adeneo Copyright (C) 2007 | ||
15 | * | ||
16 | * Derived from Das U-Boot source code | ||
17 | * (u-boot-1.1.5/board/atmel/at91sam9263ek/nand.c) | ||
18 | * (C) Copyright 2006 ATMEL Rousset, Lacressonniere Nicolas | ||
19 | * | ||
20 | * | ||
12 | * This program is free software; you can redistribute it and/or modify | 21 | * This program is free software; you can redistribute it and/or modify |
13 | * it under the terms of the GNU General Public License version 2 as | 22 | * it under the terms of the GNU General Public License version 2 as |
14 | * published by the Free Software Foundation. | 23 | * published by the Free Software Foundation. |
@@ -29,11 +38,59 @@ | |||
29 | #include <asm/arch/board.h> | 38 | #include <asm/arch/board.h> |
30 | #include <asm/arch/gpio.h> | 39 | #include <asm/arch/gpio.h> |
31 | 40 | ||
41 | #ifdef CONFIG_MTD_NAND_AT91_ECC_HW | ||
42 | #define hard_ecc 1 | ||
43 | #else | ||
44 | #define hard_ecc 0 | ||
45 | #endif | ||
46 | |||
47 | #ifdef CONFIG_MTD_NAND_AT91_ECC_NONE | ||
48 | #define no_ecc 1 | ||
49 | #else | ||
50 | #define no_ecc 0 | ||
51 | #endif | ||
52 | |||
53 | /* Register access macros */ | ||
54 | #define ecc_readl(add, reg) \ | ||
55 | __raw_readl(add + AT91_ECC_##reg) | ||
56 | #define ecc_writel(add, reg, value) \ | ||
57 | __raw_writel((value), add + AT91_ECC_##reg) | ||
58 | |||
59 | #include <asm/arch/at91_ecc.h> /* AT91SAM9260/3 ECC registers */ | ||
60 | |||
61 | /* oob layout for large page size | ||
62 | * bad block info is on bytes 0 and 1 | ||
63 | * the bytes have to be consecutives to avoid | ||
64 | * several NAND_CMD_RNDOUT during read | ||
65 | */ | ||
66 | static struct nand_ecclayout at91_oobinfo_large = { | ||
67 | .eccbytes = 4, | ||
68 | .eccpos = {60, 61, 62, 63}, | ||
69 | .oobfree = { | ||
70 | {2, 58} | ||
71 | }, | ||
72 | }; | ||
73 | |||
74 | /* oob layout for small page size | ||
75 | * bad block info is on bytes 4 and 5 | ||
76 | * the bytes have to be consecutives to avoid | ||
77 | * several NAND_CMD_RNDOUT during read | ||
78 | */ | ||
79 | static struct nand_ecclayout at91_oobinfo_small = { | ||
80 | .eccbytes = 4, | ||
81 | .eccpos = {0, 1, 2, 3}, | ||
82 | .oobfree = { | ||
83 | {6, 10} | ||
84 | }, | ||
85 | }; | ||
86 | |||
32 | struct at91_nand_host { | 87 | struct at91_nand_host { |
33 | struct nand_chip nand_chip; | 88 | struct nand_chip nand_chip; |
34 | struct mtd_info mtd; | 89 | struct mtd_info mtd; |
35 | void __iomem *io_base; | 90 | void __iomem *io_base; |
36 | struct at91_nand_data *board; | 91 | struct at91_nand_data *board; |
92 | struct device *dev; | ||
93 | void __iomem *ecc; | ||
37 | }; | 94 | }; |
38 | 95 | ||
39 | /* | 96 | /* |
@@ -82,6 +139,215 @@ static void at91_nand_disable(struct at91_nand_host *host) | |||
82 | at91_set_gpio_value(host->board->enable_pin, 1); | 139 | at91_set_gpio_value(host->board->enable_pin, 1); |
83 | } | 140 | } |
84 | 141 | ||
142 | /* | ||
143 | * write oob for small pages | ||
144 | */ | ||
145 | static int at91_nand_write_oob_512(struct mtd_info *mtd, | ||
146 | struct nand_chip *chip, int page) | ||
147 | { | ||
148 | int chunk = chip->ecc.bytes + chip->ecc.prepad + chip->ecc.postpad; | ||
149 | int eccsize = chip->ecc.size, length = mtd->oobsize; | ||
150 | int len, pos, status = 0; | ||
151 | const uint8_t *bufpoi = chip->oob_poi; | ||
152 | |||
153 | pos = eccsize + chunk; | ||
154 | |||
155 | chip->cmdfunc(mtd, NAND_CMD_SEQIN, pos, page); | ||
156 | len = min_t(int, length, chunk); | ||
157 | chip->write_buf(mtd, bufpoi, len); | ||
158 | bufpoi += len; | ||
159 | length -= len; | ||
160 | if (length > 0) | ||
161 | chip->write_buf(mtd, bufpoi, length); | ||
162 | |||
163 | chip->cmdfunc(mtd, NAND_CMD_PAGEPROG, -1, -1); | ||
164 | status = chip->waitfunc(mtd, chip); | ||
165 | |||
166 | return status & NAND_STATUS_FAIL ? -EIO : 0; | ||
167 | |||
168 | } | ||
169 | |||
170 | /* | ||
171 | * read oob for small pages | ||
172 | */ | ||
173 | static int at91_nand_read_oob_512(struct mtd_info *mtd, | ||
174 | struct nand_chip *chip, int page, int sndcmd) | ||
175 | { | ||
176 | if (sndcmd) { | ||
177 | chip->cmdfunc(mtd, NAND_CMD_READOOB, 0, page); | ||
178 | sndcmd = 0; | ||
179 | } | ||
180 | chip->read_buf(mtd, chip->oob_poi, mtd->oobsize); | ||
181 | return sndcmd; | ||
182 | } | ||
183 | |||
184 | /* | ||
185 | * Calculate HW ECC | ||
186 | * | ||
187 | * function called after a write | ||
188 | * | ||
189 | * mtd: MTD block structure | ||
190 | * dat: raw data (unused) | ||
191 | * ecc_code: buffer for ECC | ||
192 | */ | ||
193 | static int at91_nand_calculate(struct mtd_info *mtd, | ||
194 | const u_char *dat, unsigned char *ecc_code) | ||
195 | { | ||
196 | struct nand_chip *nand_chip = mtd->priv; | ||
197 | struct at91_nand_host *host = nand_chip->priv; | ||
198 | uint32_t *eccpos = nand_chip->ecc.layout->eccpos; | ||
199 | unsigned int ecc_value; | ||
200 | |||
201 | /* get the first 2 ECC bytes */ | ||
202 | ecc_value = ecc_readl(host->ecc, PR) & AT91_ECC_PARITY; | ||
203 | |||
204 | ecc_code[eccpos[0]] = ecc_value & 0xFF; | ||
205 | ecc_code[eccpos[1]] = (ecc_value >> 8) & 0xFF; | ||
206 | |||
207 | /* get the last 2 ECC bytes */ | ||
208 | ecc_value = ecc_readl(host->ecc, NPR) & AT91_ECC_NPARITY; | ||
209 | |||
210 | ecc_code[eccpos[2]] = ecc_value & 0xFF; | ||
211 | ecc_code[eccpos[3]] = (ecc_value >> 8) & 0xFF; | ||
212 | |||
213 | return 0; | ||
214 | } | ||
215 | |||
216 | /* | ||
217 | * HW ECC read page function | ||
218 | * | ||
219 | * mtd: mtd info structure | ||
220 | * chip: nand chip info structure | ||
221 | * buf: buffer to store read data | ||
222 | */ | ||
223 | static int at91_nand_read_page(struct mtd_info *mtd, | ||
224 | struct nand_chip *chip, uint8_t *buf) | ||
225 | { | ||
226 | int eccsize = chip->ecc.size; | ||
227 | int eccbytes = chip->ecc.bytes; | ||
228 | uint32_t *eccpos = chip->ecc.layout->eccpos; | ||
229 | uint8_t *p = buf; | ||
230 | uint8_t *oob = chip->oob_poi; | ||
231 | uint8_t *ecc_pos; | ||
232 | int stat; | ||
233 | |||
234 | /* read the page */ | ||
235 | chip->read_buf(mtd, p, eccsize); | ||
236 | |||
237 | /* move to ECC position if needed */ | ||
238 | if (eccpos[0] != 0) { | ||
239 | /* This only works on large pages | ||
240 | * because the ECC controller waits for | ||
241 | * NAND_CMD_RNDOUTSTART after the | ||
242 | * NAND_CMD_RNDOUT. | ||
243 | * anyway, for small pages, the eccpos[0] == 0 | ||
244 | */ | ||
245 | chip->cmdfunc(mtd, NAND_CMD_RNDOUT, | ||
246 | mtd->writesize + eccpos[0], -1); | ||
247 | } | ||
248 | |||
249 | /* the ECC controller needs to read the ECC just after the data */ | ||
250 | ecc_pos = oob + eccpos[0]; | ||
251 | chip->read_buf(mtd, ecc_pos, eccbytes); | ||
252 | |||
253 | /* check if there's an error */ | ||
254 | stat = chip->ecc.correct(mtd, p, oob, NULL); | ||
255 | |||
256 | if (stat < 0) | ||
257 | mtd->ecc_stats.failed++; | ||
258 | else | ||
259 | mtd->ecc_stats.corrected += stat; | ||
260 | |||
261 | /* get back to oob start (end of page) */ | ||
262 | chip->cmdfunc(mtd, NAND_CMD_RNDOUT, mtd->writesize, -1); | ||
263 | |||
264 | /* read the oob */ | ||
265 | chip->read_buf(mtd, oob, mtd->oobsize); | ||
266 | |||
267 | return 0; | ||
268 | } | ||
269 | |||
270 | /* | ||
271 | * HW ECC Correction | ||
272 | * | ||
273 | * function called after a read | ||
274 | * | ||
275 | * mtd: MTD block structure | ||
276 | * dat: raw data read from the chip | ||
277 | * read_ecc: ECC from the chip (unused) | ||
278 | * isnull: unused | ||
279 | * | ||
280 | * Detect and correct a 1 bit error for a page | ||
281 | */ | ||
282 | static int at91_nand_correct(struct mtd_info *mtd, u_char *dat, | ||
283 | u_char *read_ecc, u_char *isnull) | ||
284 | { | ||
285 | struct nand_chip *nand_chip = mtd->priv; | ||
286 | struct at91_nand_host *host = nand_chip->priv; | ||
287 | unsigned int ecc_status; | ||
288 | unsigned int ecc_word, ecc_bit; | ||
289 | |||
290 | /* get the status from the Status Register */ | ||
291 | ecc_status = ecc_readl(host->ecc, SR); | ||
292 | |||
293 | /* if there's no error */ | ||
294 | if (likely(!(ecc_status & AT91_ECC_RECERR))) | ||
295 | return 0; | ||
296 | |||
297 | /* get error bit offset (4 bits) */ | ||
298 | ecc_bit = ecc_readl(host->ecc, PR) & AT91_ECC_BITADDR; | ||
299 | /* get word address (12 bits) */ | ||
300 | ecc_word = ecc_readl(host->ecc, PR) & AT91_ECC_WORDADDR; | ||
301 | ecc_word >>= 4; | ||
302 | |||
303 | /* if there are multiple errors */ | ||
304 | if (ecc_status & AT91_ECC_MULERR) { | ||
305 | /* check if it is a freshly erased block | ||
306 | * (filled with 0xff) */ | ||
307 | if ((ecc_bit == AT91_ECC_BITADDR) | ||
308 | && (ecc_word == (AT91_ECC_WORDADDR >> 4))) { | ||
309 | /* the block has just been erased, return OK */ | ||
310 | return 0; | ||
311 | } | ||
312 | /* it doesn't seems to be a freshly | ||
313 | * erased block. | ||
314 | * We can't correct so many errors */ | ||
315 | dev_dbg(host->dev, "at91_nand : multiple errors detected." | ||
316 | " Unable to correct.\n"); | ||
317 | return -EIO; | ||
318 | } | ||
319 | |||
320 | /* if there's a single bit error : we can correct it */ | ||
321 | if (ecc_status & AT91_ECC_ECCERR) { | ||
322 | /* there's nothing much to do here. | ||
323 | * the bit error is on the ECC itself. | ||
324 | */ | ||
325 | dev_dbg(host->dev, "at91_nand : one bit error on ECC code." | ||
326 | " Nothing to correct\n"); | ||
327 | return 0; | ||
328 | } | ||
329 | |||
330 | dev_dbg(host->dev, "at91_nand : one bit error on data." | ||
331 | " (word offset in the page :" | ||
332 | " 0x%x bit offset : 0x%x)\n", | ||
333 | ecc_word, ecc_bit); | ||
334 | /* correct the error */ | ||
335 | if (nand_chip->options & NAND_BUSWIDTH_16) { | ||
336 | /* 16 bits words */ | ||
337 | ((unsigned short *) dat)[ecc_word] ^= (1 << ecc_bit); | ||
338 | } else { | ||
339 | /* 8 bits words */ | ||
340 | dat[ecc_word] ^= (1 << ecc_bit); | ||
341 | } | ||
342 | dev_dbg(host->dev, "at91_nand : error corrected\n"); | ||
343 | return 1; | ||
344 | } | ||
345 | |||
346 | /* | ||
347 | * Enable HW ECC : unsused | ||
348 | */ | ||
349 | static void at91_nand_hwctl(struct mtd_info *mtd, int mode) { ; } | ||
350 | |||
85 | #ifdef CONFIG_MTD_PARTITIONS | 351 | #ifdef CONFIG_MTD_PARTITIONS |
86 | static const char *part_probes[] = { "cmdlinepart", NULL }; | 352 | static const char *part_probes[] = { "cmdlinepart", NULL }; |
87 | #endif | 353 | #endif |
@@ -94,6 +360,8 @@ static int __init at91_nand_probe(struct platform_device *pdev) | |||
94 | struct at91_nand_host *host; | 360 | struct at91_nand_host *host; |
95 | struct mtd_info *mtd; | 361 | struct mtd_info *mtd; |
96 | struct nand_chip *nand_chip; | 362 | struct nand_chip *nand_chip; |
363 | struct resource *regs; | ||
364 | struct resource *mem; | ||
97 | int res; | 365 | int res; |
98 | 366 | ||
99 | #ifdef CONFIG_MTD_PARTITIONS | 367 | #ifdef CONFIG_MTD_PARTITIONS |
@@ -108,8 +376,13 @@ static int __init at91_nand_probe(struct platform_device *pdev) | |||
108 | return -ENOMEM; | 376 | return -ENOMEM; |
109 | } | 377 | } |
110 | 378 | ||
111 | host->io_base = ioremap(pdev->resource[0].start, | 379 | mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); |
112 | pdev->resource[0].end - pdev->resource[0].start + 1); | 380 | if (!mem) { |
381 | printk(KERN_ERR "at91_nand: can't get I/O resource mem\n"); | ||
382 | return -ENXIO; | ||
383 | } | ||
384 | |||
385 | host->io_base = ioremap(mem->start, mem->end - mem->start + 1); | ||
113 | if (host->io_base == NULL) { | 386 | if (host->io_base == NULL) { |
114 | printk(KERN_ERR "at91_nand: ioremap failed\n"); | 387 | printk(KERN_ERR "at91_nand: ioremap failed\n"); |
115 | kfree(host); | 388 | kfree(host); |
@@ -119,6 +392,7 @@ static int __init at91_nand_probe(struct platform_device *pdev) | |||
119 | mtd = &host->mtd; | 392 | mtd = &host->mtd; |
120 | nand_chip = &host->nand_chip; | 393 | nand_chip = &host->nand_chip; |
121 | host->board = pdev->dev.platform_data; | 394 | host->board = pdev->dev.platform_data; |
395 | host->dev = &pdev->dev; | ||
122 | 396 | ||
123 | nand_chip->priv = host; /* link the private data structures */ | 397 | nand_chip->priv = host; /* link the private data structures */ |
124 | mtd->priv = nand_chip; | 398 | mtd->priv = nand_chip; |
@@ -132,7 +406,32 @@ static int __init at91_nand_probe(struct platform_device *pdev) | |||
132 | if (host->board->rdy_pin) | 406 | if (host->board->rdy_pin) |
133 | nand_chip->dev_ready = at91_nand_device_ready; | 407 | nand_chip->dev_ready = at91_nand_device_ready; |
134 | 408 | ||
409 | regs = platform_get_resource(pdev, IORESOURCE_MEM, 1); | ||
410 | if (!regs && hard_ecc) { | ||
411 | printk(KERN_ERR "at91_nand: can't get I/O resource " | ||
412 | "regs\nFalling back on software ECC\n"); | ||
413 | } | ||
414 | |||
135 | nand_chip->ecc.mode = NAND_ECC_SOFT; /* enable ECC */ | 415 | nand_chip->ecc.mode = NAND_ECC_SOFT; /* enable ECC */ |
416 | if (no_ecc) | ||
417 | nand_chip->ecc.mode = NAND_ECC_NONE; | ||
418 | if (hard_ecc && regs) { | ||
419 | host->ecc = ioremap(regs->start, regs->end - regs->start + 1); | ||
420 | if (host->ecc == NULL) { | ||
421 | printk(KERN_ERR "at91_nand: ioremap failed\n"); | ||
422 | res = -EIO; | ||
423 | goto err_ecc_ioremap; | ||
424 | } | ||
425 | nand_chip->ecc.mode = NAND_ECC_HW_SYNDROME; | ||
426 | nand_chip->ecc.calculate = at91_nand_calculate; | ||
427 | nand_chip->ecc.correct = at91_nand_correct; | ||
428 | nand_chip->ecc.hwctl = at91_nand_hwctl; | ||
429 | nand_chip->ecc.read_page = at91_nand_read_page; | ||
430 | nand_chip->ecc.bytes = 4; | ||
431 | nand_chip->ecc.prepad = 0; | ||
432 | nand_chip->ecc.postpad = 0; | ||
433 | } | ||
434 | |||
136 | nand_chip->chip_delay = 20; /* 20us command delay time */ | 435 | nand_chip->chip_delay = 20; /* 20us command delay time */ |
137 | 436 | ||
138 | if (host->board->bus_width_16) /* 16-bit bus width */ | 437 | if (host->board->bus_width_16) /* 16-bit bus width */ |
@@ -149,8 +448,53 @@ static int __init at91_nand_probe(struct platform_device *pdev) | |||
149 | } | 448 | } |
150 | } | 449 | } |
151 | 450 | ||
152 | /* Scan to find existance of the device */ | 451 | /* first scan to find the device and get the page size */ |
153 | if (nand_scan(mtd, 1)) { | 452 | if (nand_scan_ident(mtd, 1)) { |
453 | res = -ENXIO; | ||
454 | goto out; | ||
455 | } | ||
456 | |||
457 | if (nand_chip->ecc.mode == NAND_ECC_HW_SYNDROME) { | ||
458 | /* ECC is calculated for the whole page (1 step) */ | ||
459 | nand_chip->ecc.size = mtd->writesize; | ||
460 | |||
461 | /* set ECC page size and oob layout */ | ||
462 | switch (mtd->writesize) { | ||
463 | case 512: | ||
464 | nand_chip->ecc.layout = &at91_oobinfo_small; | ||
465 | nand_chip->ecc.read_oob = at91_nand_read_oob_512; | ||
466 | nand_chip->ecc.write_oob = at91_nand_write_oob_512; | ||
467 | ecc_writel(host->ecc, MR, AT91_ECC_PAGESIZE_528); | ||
468 | break; | ||
469 | case 1024: | ||
470 | nand_chip->ecc.layout = &at91_oobinfo_large; | ||
471 | ecc_writel(host->ecc, MR, AT91_ECC_PAGESIZE_1056); | ||
472 | break; | ||
473 | case 2048: | ||
474 | nand_chip->ecc.layout = &at91_oobinfo_large; | ||
475 | ecc_writel(host->ecc, MR, AT91_ECC_PAGESIZE_2112); | ||
476 | break; | ||
477 | case 4096: | ||
478 | nand_chip->ecc.layout = &at91_oobinfo_large; | ||
479 | ecc_writel(host->ecc, MR, AT91_ECC_PAGESIZE_4224); | ||
480 | break; | ||
481 | default: | ||
482 | /* page size not handled by HW ECC */ | ||
483 | /* switching back to soft ECC */ | ||
484 | nand_chip->ecc.mode = NAND_ECC_SOFT; | ||
485 | nand_chip->ecc.calculate = NULL; | ||
486 | nand_chip->ecc.correct = NULL; | ||
487 | nand_chip->ecc.hwctl = NULL; | ||
488 | nand_chip->ecc.read_page = NULL; | ||
489 | nand_chip->ecc.postpad = 0; | ||
490 | nand_chip->ecc.prepad = 0; | ||
491 | nand_chip->ecc.bytes = 0; | ||
492 | break; | ||
493 | } | ||
494 | } | ||
495 | |||
496 | /* second phase scan */ | ||
497 | if (nand_scan_tail(mtd)) { | ||
154 | res = -ENXIO; | 498 | res = -ENXIO; |
155 | goto out; | 499 | goto out; |
156 | } | 500 | } |
@@ -179,9 +523,15 @@ static int __init at91_nand_probe(struct platform_device *pdev) | |||
179 | if (!res) | 523 | if (!res) |
180 | return res; | 524 | return res; |
181 | 525 | ||
526 | #ifdef CONFIG_MTD_PARTITIONS | ||
182 | release: | 527 | release: |
528 | #endif | ||
183 | nand_release(mtd); | 529 | nand_release(mtd); |
530 | |||
184 | out: | 531 | out: |
532 | iounmap(host->ecc); | ||
533 | |||
534 | err_ecc_ioremap: | ||
185 | at91_nand_disable(host); | 535 | at91_nand_disable(host); |
186 | platform_set_drvdata(pdev, NULL); | 536 | platform_set_drvdata(pdev, NULL); |
187 | iounmap(host->io_base); | 537 | iounmap(host->io_base); |
@@ -202,6 +552,7 @@ static int __devexit at91_nand_remove(struct platform_device *pdev) | |||
202 | at91_nand_disable(host); | 552 | at91_nand_disable(host); |
203 | 553 | ||
204 | iounmap(host->io_base); | 554 | iounmap(host->io_base); |
555 | iounmap(host->ecc); | ||
205 | kfree(host); | 556 | kfree(host); |
206 | 557 | ||
207 | return 0; | 558 | return 0; |
@@ -233,5 +584,5 @@ module_exit(at91_nand_exit); | |||
233 | 584 | ||
234 | MODULE_LICENSE("GPL"); | 585 | MODULE_LICENSE("GPL"); |
235 | MODULE_AUTHOR("Rick Bronson"); | 586 | MODULE_AUTHOR("Rick Bronson"); |
236 | MODULE_DESCRIPTION("NAND/SmartMedia driver for AT91RM9200"); | 587 | MODULE_DESCRIPTION("NAND/SmartMedia driver for AT91RM9200 / AT91SAM9"); |
237 | MODULE_ALIAS("platform:at91_nand"); | 588 | MODULE_ALIAS("platform:at91_nand"); |