diff options
author | Lars-Peter Clausen <lars@metafoo.de> | 2010-07-17 07:15:29 -0400 |
---|---|---|
committer | Ralf Baechle <ralf@linux-mips.org> | 2010-08-05 08:26:18 -0400 |
commit | ba01d6ec04f6d1d983101eb527caa96318fc1017 (patch) | |
tree | 7bf45737cdcfb86a587133510e8f321712975d81 /drivers | |
parent | 7a92d54521443450b14d89c413ec2072365da5bc (diff) |
MTD: Nand: Add JZ4740 NAND driver
Add support for the NAND controller on JZ4740 SoCs.
Signed-off-by: Lars-Peter Clausen <lars@metafoo.de>
Cc: David Woodhouse <dwmw2@infradead.org>
Cc: linux-mtd@lists.infradead.org
Cc: linux-mips@linux-mips.org
Cc: linux-kernel@vger.kernel.org
Patchwork: https://patchwork.linux-mips.org/patch/1470/
Signed-off-by: Ralf Baechle <ralf@linux-mips.org>
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/mtd/nand/Kconfig | 6 | ||||
-rw-r--r-- | drivers/mtd/nand/Makefile | 1 | ||||
-rw-r--r-- | drivers/mtd/nand/jz4740_nand.c | 516 |
3 files changed, 523 insertions, 0 deletions
diff --git a/drivers/mtd/nand/Kconfig b/drivers/mtd/nand/Kconfig index ffc3720929f1..362d177efe1b 100644 --- a/drivers/mtd/nand/Kconfig +++ b/drivers/mtd/nand/Kconfig | |||
@@ -526,4 +526,10 @@ config MTD_NAND_NUC900 | |||
526 | This enables the driver for the NAND Flash on evaluation board based | 526 | This enables the driver for the NAND Flash on evaluation board based |
527 | on w90p910 / NUC9xx. | 527 | on w90p910 / NUC9xx. |
528 | 528 | ||
529 | config MTD_NAND_JZ4740 | ||
530 | tristate "Support for JZ4740 SoC NAND controller" | ||
531 | depends on MACH_JZ4740 | ||
532 | help | ||
533 | Enables support for NAND Flash on JZ4740 SoC based boards. | ||
534 | |||
529 | endif # MTD_NAND | 535 | endif # MTD_NAND |
diff --git a/drivers/mtd/nand/Makefile b/drivers/mtd/nand/Makefile index e8ab884ba47b..ac83dcdac5d6 100644 --- a/drivers/mtd/nand/Makefile +++ b/drivers/mtd/nand/Makefile | |||
@@ -46,5 +46,6 @@ obj-$(CONFIG_MTD_NAND_NOMADIK) += nomadik_nand.o | |||
46 | obj-$(CONFIG_MTD_NAND_BCM_UMI) += bcm_umi_nand.o nand_bcm_umi.o | 46 | obj-$(CONFIG_MTD_NAND_BCM_UMI) += bcm_umi_nand.o nand_bcm_umi.o |
47 | obj-$(CONFIG_MTD_NAND_MPC5121_NFC) += mpc5121_nfc.o | 47 | obj-$(CONFIG_MTD_NAND_MPC5121_NFC) += mpc5121_nfc.o |
48 | obj-$(CONFIG_MTD_NAND_RICOH) += r852.o | 48 | obj-$(CONFIG_MTD_NAND_RICOH) += r852.o |
49 | obj-$(CONFIG_MTD_NAND_JZ4740) += jz4740_nand.o | ||
49 | 50 | ||
50 | nand-objs := nand_base.o nand_bbt.o | 51 | nand-objs := nand_base.o nand_bbt.o |
diff --git a/drivers/mtd/nand/jz4740_nand.c b/drivers/mtd/nand/jz4740_nand.c new file mode 100644 index 000000000000..67343fc31bd5 --- /dev/null +++ b/drivers/mtd/nand/jz4740_nand.c | |||
@@ -0,0 +1,516 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2009-2010, Lars-Peter Clausen <lars@metafoo.de> | ||
3 | * JZ4740 SoC NAND controller driver | ||
4 | * | ||
5 | * This program is free software; you can redistribute it and/or modify it | ||
6 | * under the terms of the GNU General Public License as published by the | ||
7 | * Free Software Foundation; either version 2 of the License, or (at your | ||
8 | * option) any later version. | ||
9 | * | ||
10 | * You should have received a copy of the GNU General Public License along | ||
11 | * with this program; if not, write to the Free Software Foundation, Inc., | ||
12 | * 675 Mass Ave, Cambridge, MA 02139, USA. | ||
13 | * | ||
14 | */ | ||
15 | |||
16 | #include <linux/ioport.h> | ||
17 | #include <linux/kernel.h> | ||
18 | #include <linux/module.h> | ||
19 | #include <linux/platform_device.h> | ||
20 | #include <linux/slab.h> | ||
21 | |||
22 | #include <linux/mtd/mtd.h> | ||
23 | #include <linux/mtd/nand.h> | ||
24 | #include <linux/mtd/partitions.h> | ||
25 | |||
26 | #include <linux/gpio.h> | ||
27 | |||
28 | #include <asm/mach-jz4740/jz4740_nand.h> | ||
29 | |||
30 | #define JZ_REG_NAND_CTRL 0x50 | ||
31 | #define JZ_REG_NAND_ECC_CTRL 0x100 | ||
32 | #define JZ_REG_NAND_DATA 0x104 | ||
33 | #define JZ_REG_NAND_PAR0 0x108 | ||
34 | #define JZ_REG_NAND_PAR1 0x10C | ||
35 | #define JZ_REG_NAND_PAR2 0x110 | ||
36 | #define JZ_REG_NAND_IRQ_STAT 0x114 | ||
37 | #define JZ_REG_NAND_IRQ_CTRL 0x118 | ||
38 | #define JZ_REG_NAND_ERR(x) (0x11C + ((x) << 2)) | ||
39 | |||
40 | #define JZ_NAND_ECC_CTRL_PAR_READY BIT(4) | ||
41 | #define JZ_NAND_ECC_CTRL_ENCODING BIT(3) | ||
42 | #define JZ_NAND_ECC_CTRL_RS BIT(2) | ||
43 | #define JZ_NAND_ECC_CTRL_RESET BIT(1) | ||
44 | #define JZ_NAND_ECC_CTRL_ENABLE BIT(0) | ||
45 | |||
46 | #define JZ_NAND_STATUS_ERR_COUNT (BIT(31) | BIT(30) | BIT(29)) | ||
47 | #define JZ_NAND_STATUS_PAD_FINISH BIT(4) | ||
48 | #define JZ_NAND_STATUS_DEC_FINISH BIT(3) | ||
49 | #define JZ_NAND_STATUS_ENC_FINISH BIT(2) | ||
50 | #define JZ_NAND_STATUS_UNCOR_ERROR BIT(1) | ||
51 | #define JZ_NAND_STATUS_ERROR BIT(0) | ||
52 | |||
53 | #define JZ_NAND_CTRL_ENABLE_CHIP(x) BIT((x) << 1) | ||
54 | #define JZ_NAND_CTRL_ASSERT_CHIP(x) BIT(((x) << 1) + 1) | ||
55 | |||
56 | #define JZ_NAND_MEM_ADDR_OFFSET 0x10000 | ||
57 | #define JZ_NAND_MEM_CMD_OFFSET 0x08000 | ||
58 | |||
59 | struct jz_nand { | ||
60 | struct mtd_info mtd; | ||
61 | struct nand_chip chip; | ||
62 | void __iomem *base; | ||
63 | struct resource *mem; | ||
64 | |||
65 | void __iomem *bank_base; | ||
66 | struct resource *bank_mem; | ||
67 | |||
68 | struct jz_nand_platform_data *pdata; | ||
69 | bool is_reading; | ||
70 | }; | ||
71 | |||
72 | static inline struct jz_nand *mtd_to_jz_nand(struct mtd_info *mtd) | ||
73 | { | ||
74 | return container_of(mtd, struct jz_nand, mtd); | ||
75 | } | ||
76 | |||
77 | static void jz_nand_cmd_ctrl(struct mtd_info *mtd, int dat, unsigned int ctrl) | ||
78 | { | ||
79 | struct jz_nand *nand = mtd_to_jz_nand(mtd); | ||
80 | struct nand_chip *chip = mtd->priv; | ||
81 | uint32_t reg; | ||
82 | |||
83 | if (ctrl & NAND_CTRL_CHANGE) { | ||
84 | BUG_ON((ctrl & NAND_ALE) && (ctrl & NAND_CLE)); | ||
85 | if (ctrl & NAND_ALE) | ||
86 | chip->IO_ADDR_W = nand->bank_base + JZ_NAND_MEM_ADDR_OFFSET; | ||
87 | else if (ctrl & NAND_CLE) | ||
88 | chip->IO_ADDR_W = nand->bank_base + JZ_NAND_MEM_CMD_OFFSET; | ||
89 | else | ||
90 | chip->IO_ADDR_W = nand->bank_base; | ||
91 | |||
92 | reg = readl(nand->base + JZ_REG_NAND_CTRL); | ||
93 | if (ctrl & NAND_NCE) | ||
94 | reg |= JZ_NAND_CTRL_ASSERT_CHIP(0); | ||
95 | else | ||
96 | reg &= ~JZ_NAND_CTRL_ASSERT_CHIP(0); | ||
97 | writel(reg, nand->base + JZ_REG_NAND_CTRL); | ||
98 | } | ||
99 | if (dat != NAND_CMD_NONE) | ||
100 | writeb(dat, chip->IO_ADDR_W); | ||
101 | } | ||
102 | |||
103 | static int jz_nand_dev_ready(struct mtd_info *mtd) | ||
104 | { | ||
105 | struct jz_nand *nand = mtd_to_jz_nand(mtd); | ||
106 | return gpio_get_value_cansleep(nand->pdata->busy_gpio); | ||
107 | } | ||
108 | |||
109 | static void jz_nand_hwctl(struct mtd_info *mtd, int mode) | ||
110 | { | ||
111 | struct jz_nand *nand = mtd_to_jz_nand(mtd); | ||
112 | uint32_t reg; | ||
113 | |||
114 | writel(0, nand->base + JZ_REG_NAND_IRQ_STAT); | ||
115 | reg = readl(nand->base + JZ_REG_NAND_ECC_CTRL); | ||
116 | |||
117 | reg |= JZ_NAND_ECC_CTRL_RESET; | ||
118 | reg |= JZ_NAND_ECC_CTRL_ENABLE; | ||
119 | reg |= JZ_NAND_ECC_CTRL_RS; | ||
120 | |||
121 | switch (mode) { | ||
122 | case NAND_ECC_READ: | ||
123 | reg &= ~JZ_NAND_ECC_CTRL_ENCODING; | ||
124 | nand->is_reading = true; | ||
125 | break; | ||
126 | case NAND_ECC_WRITE: | ||
127 | reg |= JZ_NAND_ECC_CTRL_ENCODING; | ||
128 | nand->is_reading = false; | ||
129 | break; | ||
130 | default: | ||
131 | break; | ||
132 | } | ||
133 | |||
134 | writel(reg, nand->base + JZ_REG_NAND_ECC_CTRL); | ||
135 | } | ||
136 | |||
137 | static int jz_nand_calculate_ecc_rs(struct mtd_info *mtd, const uint8_t *dat, | ||
138 | uint8_t *ecc_code) | ||
139 | { | ||
140 | struct jz_nand *nand = mtd_to_jz_nand(mtd); | ||
141 | uint32_t reg, status; | ||
142 | int i; | ||
143 | unsigned int timeout = 1000; | ||
144 | static uint8_t empty_block_ecc[] = {0xcd, 0x9d, 0x90, 0x58, 0xf4, | ||
145 | 0x8b, 0xff, 0xb7, 0x6f}; | ||
146 | |||
147 | if (nand->is_reading) | ||
148 | return 0; | ||
149 | |||
150 | do { | ||
151 | status = readl(nand->base + JZ_REG_NAND_IRQ_STAT); | ||
152 | } while (!(status & JZ_NAND_STATUS_ENC_FINISH) && --timeout); | ||
153 | |||
154 | if (timeout == 0) | ||
155 | return -1; | ||
156 | |||
157 | reg = readl(nand->base + JZ_REG_NAND_ECC_CTRL); | ||
158 | reg &= ~JZ_NAND_ECC_CTRL_ENABLE; | ||
159 | writel(reg, nand->base + JZ_REG_NAND_ECC_CTRL); | ||
160 | |||
161 | for (i = 0; i < 9; ++i) | ||
162 | ecc_code[i] = readb(nand->base + JZ_REG_NAND_PAR0 + i); | ||
163 | |||
164 | /* If the written data is completly 0xff, we also want to write 0xff as | ||
165 | * ecc, otherwise we will get in trouble when doing subpage writes. */ | ||
166 | if (memcmp(ecc_code, empty_block_ecc, 9) == 0) | ||
167 | memset(ecc_code, 0xff, 9); | ||
168 | |||
169 | return 0; | ||
170 | } | ||
171 | |||
172 | static void jz_nand_correct_data(uint8_t *dat, int index, int mask) | ||
173 | { | ||
174 | int offset = index & 0x7; | ||
175 | uint16_t data; | ||
176 | |||
177 | index += (index >> 3); | ||
178 | |||
179 | data = dat[index]; | ||
180 | data |= dat[index+1] << 8; | ||
181 | |||
182 | mask ^= (data >> offset) & 0x1ff; | ||
183 | data &= ~(0x1ff << offset); | ||
184 | data |= (mask << offset); | ||
185 | |||
186 | dat[index] = data & 0xff; | ||
187 | dat[index+1] = (data >> 8) & 0xff; | ||
188 | } | ||
189 | |||
190 | static int jz_nand_correct_ecc_rs(struct mtd_info *mtd, uint8_t *dat, | ||
191 | uint8_t *read_ecc, uint8_t *calc_ecc) | ||
192 | { | ||
193 | struct jz_nand *nand = mtd_to_jz_nand(mtd); | ||
194 | int i, error_count, index; | ||
195 | uint32_t reg, status, error; | ||
196 | uint32_t t; | ||
197 | unsigned int timeout = 1000; | ||
198 | |||
199 | t = read_ecc[0]; | ||
200 | |||
201 | if (t == 0xff) { | ||
202 | for (i = 1; i < 9; ++i) | ||
203 | t &= read_ecc[i]; | ||
204 | |||
205 | t &= dat[0]; | ||
206 | t &= dat[nand->chip.ecc.size / 2]; | ||
207 | t &= dat[nand->chip.ecc.size - 1]; | ||
208 | |||
209 | if (t == 0xff) { | ||
210 | for (i = 1; i < nand->chip.ecc.size - 1; ++i) | ||
211 | t &= dat[i]; | ||
212 | if (t == 0xff) | ||
213 | return 0; | ||
214 | } | ||
215 | } | ||
216 | |||
217 | for (i = 0; i < 9; ++i) | ||
218 | writeb(read_ecc[i], nand->base + JZ_REG_NAND_PAR0 + i); | ||
219 | |||
220 | reg = readl(nand->base + JZ_REG_NAND_ECC_CTRL); | ||
221 | reg |= JZ_NAND_ECC_CTRL_PAR_READY; | ||
222 | writel(reg, nand->base + JZ_REG_NAND_ECC_CTRL); | ||
223 | |||
224 | do { | ||
225 | status = readl(nand->base + JZ_REG_NAND_IRQ_STAT); | ||
226 | } while (!(status & JZ_NAND_STATUS_DEC_FINISH) && --timeout); | ||
227 | |||
228 | if (timeout == 0) | ||
229 | return -1; | ||
230 | |||
231 | reg = readl(nand->base + JZ_REG_NAND_ECC_CTRL); | ||
232 | reg &= ~JZ_NAND_ECC_CTRL_ENABLE; | ||
233 | writel(reg, nand->base + JZ_REG_NAND_ECC_CTRL); | ||
234 | |||
235 | if (status & JZ_NAND_STATUS_ERROR) { | ||
236 | if (status & JZ_NAND_STATUS_UNCOR_ERROR) | ||
237 | return -1; | ||
238 | |||
239 | error_count = (status & JZ_NAND_STATUS_ERR_COUNT) >> 29; | ||
240 | |||
241 | for (i = 0; i < error_count; ++i) { | ||
242 | error = readl(nand->base + JZ_REG_NAND_ERR(i)); | ||
243 | index = ((error >> 16) & 0x1ff) - 1; | ||
244 | if (index >= 0 && index < 512) | ||
245 | jz_nand_correct_data(dat, index, error & 0x1ff); | ||
246 | } | ||
247 | |||
248 | return error_count; | ||
249 | } | ||
250 | |||
251 | return 0; | ||
252 | } | ||
253 | |||
254 | |||
255 | /* Copy paste of nand_read_page_hwecc_oob_first except for different eccpos | ||
256 | * handling. The ecc area is for 4k chips 72 bytes long and thus does not fit | ||
257 | * into the eccpos array. */ | ||
258 | static int jz_nand_read_page_hwecc_oob_first(struct mtd_info *mtd, | ||
259 | struct nand_chip *chip, uint8_t *buf, int page) | ||
260 | { | ||
261 | int i, eccsize = chip->ecc.size; | ||
262 | int eccbytes = chip->ecc.bytes; | ||
263 | int eccsteps = chip->ecc.steps; | ||
264 | uint8_t *p = buf; | ||
265 | unsigned int ecc_offset = chip->page_shift; | ||
266 | |||
267 | /* Read the OOB area first */ | ||
268 | chip->cmdfunc(mtd, NAND_CMD_READOOB, 0, page); | ||
269 | chip->read_buf(mtd, chip->oob_poi, mtd->oobsize); | ||
270 | chip->cmdfunc(mtd, NAND_CMD_READ0, 0, page); | ||
271 | |||
272 | for (i = ecc_offset; eccsteps; eccsteps--, i += eccbytes, p += eccsize) { | ||
273 | int stat; | ||
274 | |||
275 | chip->ecc.hwctl(mtd, NAND_ECC_READ); | ||
276 | chip->read_buf(mtd, p, eccsize); | ||
277 | |||
278 | stat = chip->ecc.correct(mtd, p, &chip->oob_poi[i], NULL); | ||
279 | if (stat < 0) | ||
280 | mtd->ecc_stats.failed++; | ||
281 | else | ||
282 | mtd->ecc_stats.corrected += stat; | ||
283 | } | ||
284 | return 0; | ||
285 | } | ||
286 | |||
287 | /* Copy-and-paste of nand_write_page_hwecc with different eccpos handling. */ | ||
288 | static void jz_nand_write_page_hwecc(struct mtd_info *mtd, | ||
289 | struct nand_chip *chip, const uint8_t *buf) | ||
290 | { | ||
291 | int i, eccsize = chip->ecc.size; | ||
292 | int eccbytes = chip->ecc.bytes; | ||
293 | int eccsteps = chip->ecc.steps; | ||
294 | const uint8_t *p = buf; | ||
295 | unsigned int ecc_offset = chip->page_shift; | ||
296 | |||
297 | for (i = ecc_offset; eccsteps; eccsteps--, i += eccbytes, p += eccsize) { | ||
298 | chip->ecc.hwctl(mtd, NAND_ECC_WRITE); | ||
299 | chip->write_buf(mtd, p, eccsize); | ||
300 | chip->ecc.calculate(mtd, p, &chip->oob_poi[i]); | ||
301 | } | ||
302 | |||
303 | chip->write_buf(mtd, chip->oob_poi, mtd->oobsize); | ||
304 | } | ||
305 | |||
306 | #ifdef CONFIG_MTD_CMDLINE_PARTS | ||
307 | static const char *part_probes[] = {"cmdline", NULL}; | ||
308 | #endif | ||
309 | |||
310 | static int jz_nand_ioremap_resource(struct platform_device *pdev, | ||
311 | const char *name, struct resource **res, void __iomem **base) | ||
312 | { | ||
313 | int ret; | ||
314 | |||
315 | *res = platform_get_resource_byname(pdev, IORESOURCE_MEM, name); | ||
316 | if (!*res) { | ||
317 | dev_err(&pdev->dev, "Failed to get platform %s memory\n", name); | ||
318 | ret = -ENXIO; | ||
319 | goto err; | ||
320 | } | ||
321 | |||
322 | *res = request_mem_region((*res)->start, resource_size(*res), | ||
323 | pdev->name); | ||
324 | if (!*res) { | ||
325 | dev_err(&pdev->dev, "Failed to request %s memory region\n", name); | ||
326 | ret = -EBUSY; | ||
327 | goto err; | ||
328 | } | ||
329 | |||
330 | *base = ioremap((*res)->start, resource_size(*res)); | ||
331 | if (!*base) { | ||
332 | dev_err(&pdev->dev, "Failed to ioremap %s memory region\n", name); | ||
333 | ret = -EBUSY; | ||
334 | goto err_release_mem; | ||
335 | } | ||
336 | |||
337 | return 0; | ||
338 | |||
339 | err_release_mem: | ||
340 | release_mem_region((*res)->start, resource_size(*res)); | ||
341 | err: | ||
342 | *res = NULL; | ||
343 | *base = NULL; | ||
344 | return ret; | ||
345 | } | ||
346 | |||
347 | static int __devinit jz_nand_probe(struct platform_device *pdev) | ||
348 | { | ||
349 | int ret; | ||
350 | struct jz_nand *nand; | ||
351 | struct nand_chip *chip; | ||
352 | struct mtd_info *mtd; | ||
353 | struct jz_nand_platform_data *pdata = pdev->dev.platform_data; | ||
354 | #ifdef CONFIG_MTD_PARTITIONS | ||
355 | struct mtd_partition *partition_info; | ||
356 | int num_partitions = 0; | ||
357 | #endif | ||
358 | |||
359 | nand = kzalloc(sizeof(*nand), GFP_KERNEL); | ||
360 | if (!nand) { | ||
361 | dev_err(&pdev->dev, "Failed to allocate device structure.\n"); | ||
362 | return -ENOMEM; | ||
363 | } | ||
364 | |||
365 | ret = jz_nand_ioremap_resource(pdev, "mmio", &nand->mem, &nand->base); | ||
366 | if (ret) | ||
367 | goto err_free; | ||
368 | ret = jz_nand_ioremap_resource(pdev, "bank", &nand->bank_mem, | ||
369 | &nand->bank_base); | ||
370 | if (ret) | ||
371 | goto err_iounmap_mmio; | ||
372 | |||
373 | if (pdata && gpio_is_valid(pdata->busy_gpio)) { | ||
374 | ret = gpio_request(pdata->busy_gpio, "NAND busy pin"); | ||
375 | if (ret) { | ||
376 | dev_err(&pdev->dev, | ||
377 | "Failed to request busy gpio %d: %d\n", | ||
378 | pdata->busy_gpio, ret); | ||
379 | goto err_iounmap_mem; | ||
380 | } | ||
381 | } | ||
382 | |||
383 | mtd = &nand->mtd; | ||
384 | chip = &nand->chip; | ||
385 | mtd->priv = chip; | ||
386 | mtd->owner = THIS_MODULE; | ||
387 | mtd->name = "jz4740-nand"; | ||
388 | |||
389 | chip->ecc.hwctl = jz_nand_hwctl; | ||
390 | chip->ecc.calculate = jz_nand_calculate_ecc_rs; | ||
391 | chip->ecc.correct = jz_nand_correct_ecc_rs; | ||
392 | chip->ecc.mode = NAND_ECC_HW_OOB_FIRST; | ||
393 | chip->ecc.size = 512; | ||
394 | chip->ecc.bytes = 9; | ||
395 | |||
396 | chip->ecc.read_page = jz_nand_read_page_hwecc_oob_first; | ||
397 | chip->ecc.write_page = jz_nand_write_page_hwecc; | ||
398 | |||
399 | if (pdata) | ||
400 | chip->ecc.layout = pdata->ecc_layout; | ||
401 | |||
402 | chip->chip_delay = 50; | ||
403 | chip->cmd_ctrl = jz_nand_cmd_ctrl; | ||
404 | |||
405 | if (pdata && gpio_is_valid(pdata->busy_gpio)) | ||
406 | chip->dev_ready = jz_nand_dev_ready; | ||
407 | |||
408 | chip->IO_ADDR_R = nand->bank_base; | ||
409 | chip->IO_ADDR_W = nand->bank_base; | ||
410 | |||
411 | nand->pdata = pdata; | ||
412 | platform_set_drvdata(pdev, nand); | ||
413 | |||
414 | writel(JZ_NAND_CTRL_ENABLE_CHIP(0), nand->base + JZ_REG_NAND_CTRL); | ||
415 | |||
416 | ret = nand_scan_ident(mtd, 1, NULL); | ||
417 | if (ret) { | ||
418 | dev_err(&pdev->dev, "Failed to scan nand\n"); | ||
419 | goto err_gpio_free; | ||
420 | } | ||
421 | |||
422 | if (pdata && pdata->ident_callback) { | ||
423 | pdata->ident_callback(pdev, chip, &pdata->partitions, | ||
424 | &pdata->num_partitions); | ||
425 | } | ||
426 | |||
427 | ret = nand_scan_tail(mtd); | ||
428 | if (ret) { | ||
429 | dev_err(&pdev->dev, "Failed to scan nand\n"); | ||
430 | goto err_gpio_free; | ||
431 | } | ||
432 | |||
433 | #ifdef CONFIG_MTD_PARTITIONS | ||
434 | #ifdef CONFIG_MTD_CMDLINE_PARTS | ||
435 | num_partitions = parse_mtd_partitions(mtd, part_probes, | ||
436 | &partition_info, 0); | ||
437 | #endif | ||
438 | if (num_partitions <= 0 && pdata) { | ||
439 | num_partitions = pdata->num_partitions; | ||
440 | partition_info = pdata->partitions; | ||
441 | } | ||
442 | |||
443 | if (num_partitions > 0) | ||
444 | ret = add_mtd_partitions(mtd, partition_info, num_partitions); | ||
445 | else | ||
446 | #endif | ||
447 | ret = add_mtd_device(mtd); | ||
448 | |||
449 | if (ret) { | ||
450 | dev_err(&pdev->dev, "Failed to add mtd device\n"); | ||
451 | goto err_nand_release; | ||
452 | } | ||
453 | |||
454 | dev_info(&pdev->dev, "Successfully registered JZ4740 NAND driver\n"); | ||
455 | |||
456 | return 0; | ||
457 | |||
458 | err_nand_release: | ||
459 | nand_release(&nand->mtd); | ||
460 | err_gpio_free: | ||
461 | platform_set_drvdata(pdev, NULL); | ||
462 | gpio_free(pdata->busy_gpio); | ||
463 | err_iounmap_mem: | ||
464 | iounmap(nand->bank_base); | ||
465 | err_iounmap_mmio: | ||
466 | iounmap(nand->base); | ||
467 | err_free: | ||
468 | kfree(nand); | ||
469 | return ret; | ||
470 | } | ||
471 | |||
472 | static int __devexit jz_nand_remove(struct platform_device *pdev) | ||
473 | { | ||
474 | struct jz_nand *nand = platform_get_drvdata(pdev); | ||
475 | |||
476 | nand_release(&nand->mtd); | ||
477 | |||
478 | /* Deassert and disable all chips */ | ||
479 | writel(0, nand->base + JZ_REG_NAND_CTRL); | ||
480 | |||
481 | iounmap(nand->bank_base); | ||
482 | release_mem_region(nand->bank_mem->start, resource_size(nand->bank_mem)); | ||
483 | iounmap(nand->base); | ||
484 | release_mem_region(nand->mem->start, resource_size(nand->mem)); | ||
485 | |||
486 | platform_set_drvdata(pdev, NULL); | ||
487 | kfree(nand); | ||
488 | |||
489 | return 0; | ||
490 | } | ||
491 | |||
492 | struct platform_driver jz_nand_driver = { | ||
493 | .probe = jz_nand_probe, | ||
494 | .remove = __devexit_p(jz_nand_remove), | ||
495 | .driver = { | ||
496 | .name = "jz4740-nand", | ||
497 | .owner = THIS_MODULE, | ||
498 | }, | ||
499 | }; | ||
500 | |||
501 | static int __init jz_nand_init(void) | ||
502 | { | ||
503 | return platform_driver_register(&jz_nand_driver); | ||
504 | } | ||
505 | module_init(jz_nand_init); | ||
506 | |||
507 | static void __exit jz_nand_exit(void) | ||
508 | { | ||
509 | platform_driver_unregister(&jz_nand_driver); | ||
510 | } | ||
511 | module_exit(jz_nand_exit); | ||
512 | |||
513 | MODULE_LICENSE("GPL"); | ||
514 | MODULE_AUTHOR("Lars-Peter Clausen <lars@metafoo.de>"); | ||
515 | MODULE_DESCRIPTION("NAND controller driver for JZ4740 SoC"); | ||
516 | MODULE_ALIAS("platform:jz4740-nand"); | ||