Root/
Source at commit 7605fd125c7932323c6d1c6072d7639456d7936f created 13 years 11 months ago. By Lars-Peter Clausen, Add jz4740 nand driver | |
---|---|
1 | /* |
2 | * Copyright (C) 2009-2010, Lars-Peter Clausen <lars@metafoo.de> |
3 | * JZ4720/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/mtd/jz4740_nand.h> |
27 | #include <linux/gpio.h> |
28 | |
29 | #define JZ_REG_NAND_CTRL 0x50 |
30 | #define JZ_REG_NAND_ECC_CTRL 0x100 |
31 | #define JZ_REG_NAND_DATA 0x104 |
32 | #define JZ_REG_NAND_PAR0 0x108 |
33 | #define JZ_REG_NAND_PAR1 0x10C |
34 | #define JZ_REG_NAND_PAR2 0x110 |
35 | #define JZ_REG_NAND_IRQ_STAT 0x114 |
36 | #define JZ_REG_NAND_IRQ_CTRL 0x118 |
37 | #define JZ_REG_NAND_ERR(x) (0x11C + (x << 2)) |
38 | |
39 | #define JZ_NAND_ECC_CTRL_PAR_READY BIT(4) |
40 | #define JZ_NAND_ECC_CTRL_ENCODING BIT(3) |
41 | #define JZ_NAND_ECC_CTRL_RS BIT(2) |
42 | #define JZ_NAND_ECC_CTRL_RESET BIT(1) |
43 | #define JZ_NAND_ECC_CTRL_ENABLE BIT(0) |
44 | |
45 | #define JZ_NAND_STATUS_ERR_COUNT (BIT(31) | BIT(30) | BIT(29)) |
46 | #define JZ_NAND_STATUS_PAD_FINISH BIT(4) |
47 | #define JZ_NAND_STATUS_DEC_FINISH BIT(3) |
48 | #define JZ_NAND_STATUS_ENC_FINISH BIT(2) |
49 | #define JZ_NAND_STATUS_UNCOR_ERROR BIT(1) |
50 | #define JZ_NAND_STATUS_ERROR BIT(0) |
51 | |
52 | #define JZ_NAND_CTRL_ENABLE_CHIP(x) BIT(x << 1) |
53 | #define JZ_NAND_CTRL_ASSERT_CHIP(x) BIT((x << 1) + 1) |
54 | |
55 | #define JZ_NAND_DATA_ADDR ((void __iomem *)0xB8000000) |
56 | #define JZ_NAND_CMD_ADDR (JZ_NAND_DATA_ADDR + 0x8000) |
57 | #define JZ_NAND_ADDR_ADDR (JZ_NAND_DATA_ADDR + 0x10000) |
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 | struct jz_nand_platform_data *pdata; |
66 | bool is_reading; |
67 | }; |
68 | |
69 | static inline struct jz_nand *mtd_to_jz_nand(struct mtd_info *mtd) |
70 | { |
71 | return container_of(mtd, struct jz_nand, mtd); |
72 | } |
73 | |
74 | static void jz_nand_cmd_ctrl(struct mtd_info *mtd, int dat, unsigned int ctrl) |
75 | { |
76 | struct jz_nand *nand = mtd_to_jz_nand(mtd); |
77 | struct nand_chip *chip = mtd->priv; |
78 | uint32_t reg; |
79 | |
80 | if (ctrl & NAND_CTRL_CHANGE) { |
81 | BUG_ON((ctrl & NAND_ALE) && (ctrl & NAND_CLE)); |
82 | if (ctrl & NAND_ALE) |
83 | chip->IO_ADDR_W = JZ_NAND_ADDR_ADDR; |
84 | else if (ctrl & NAND_CLE) |
85 | chip->IO_ADDR_W = JZ_NAND_CMD_ADDR; |
86 | else |
87 | chip->IO_ADDR_W = JZ_NAND_DATA_ADDR; |
88 | |
89 | reg = readl(nand->base + JZ_REG_NAND_CTRL); |
90 | if (ctrl & NAND_NCE) |
91 | reg |= JZ_NAND_CTRL_ASSERT_CHIP(0); |
92 | else |
93 | reg &= ~JZ_NAND_CTRL_ASSERT_CHIP(0); |
94 | writel(reg, nand->base + JZ_REG_NAND_CTRL); |
95 | } |
96 | if (dat != NAND_CMD_NONE) |
97 | writeb(dat, chip->IO_ADDR_W); |
98 | } |
99 | |
100 | static int jz_nand_dev_ready(struct mtd_info *mtd) |
101 | { |
102 | struct jz_nand *nand = mtd_to_jz_nand(mtd); |
103 | return gpio_get_value_cansleep(nand->pdata->busy_gpio); |
104 | } |
105 | |
106 | static void jz_nand_hwctl(struct mtd_info *mtd, int mode) |
107 | { |
108 | struct jz_nand *nand = mtd_to_jz_nand(mtd); |
109 | uint32_t reg; |
110 | |
111 | |
112 | writel(0, nand->base + JZ_REG_NAND_IRQ_STAT); |
113 | reg = readl(nand->base + JZ_REG_NAND_ECC_CTRL); |
114 | |
115 | reg |= JZ_NAND_ECC_CTRL_RESET; |
116 | reg |= JZ_NAND_ECC_CTRL_ENABLE; |
117 | reg |= JZ_NAND_ECC_CTRL_RS; |
118 | |
119 | switch (mode) { |
120 | case NAND_ECC_READ: |
121 | reg &= ~JZ_NAND_ECC_CTRL_ENCODING; |
122 | nand->is_reading = true; |
123 | break; |
124 | case NAND_ECC_WRITE: |
125 | reg |= JZ_NAND_ECC_CTRL_ENCODING; |
126 | nand->is_reading = false; |
127 | break; |
128 | default: |
129 | break; |
130 | } |
131 | |
132 | writel(reg, nand->base + JZ_REG_NAND_ECC_CTRL); |
133 | } |
134 | |
135 | |
136 | static int jz_nand_calculate_ecc_rs(struct mtd_info* mtd, const uint8_t *dat, |
137 | uint8_t *ecc_code) |
138 | { |
139 | struct jz_nand *nand = mtd_to_jz_nand(mtd); |
140 | uint32_t reg, status; |
141 | int i; |
142 | static uint8_t empty_block_ecc[] = {0xcd, 0x9d, 0x90, 0x58, 0xf4, |
143 | 0x8b, 0xff, 0xb7, 0x6f}; |
144 | |
145 | if (nand->is_reading) |
146 | return 0; |
147 | |
148 | do { |
149 | status = readl(nand->base + JZ_REG_NAND_IRQ_STAT); |
150 | } while (!(status & JZ_NAND_STATUS_ENC_FINISH)); |
151 | |
152 | reg = readl(nand->base + JZ_REG_NAND_ECC_CTRL); |
153 | reg &= ~JZ_NAND_ECC_CTRL_ENABLE; |
154 | writel(reg, nand->base + JZ_REG_NAND_ECC_CTRL); |
155 | |
156 | for (i = 0; i < 9; ++i) |
157 | ecc_code[i] = readb(nand->base + JZ_REG_NAND_PAR0 + i); |
158 | |
159 | /* If the written data is completly 0xff, we also want to write 0xff as |
160 | * ecc, otherwise we will get in trouble when doing subpage writes. */ |
161 | if (memcmp(ecc_code, empty_block_ecc, 9) == 0) |
162 | memset(ecc_code, 0xff, 9); |
163 | |
164 | return 0; |
165 | } |
166 | |
167 | /*#define printkd printk*/ |
168 | #define printkd(...) |
169 | |
170 | static void correct_data(uint8_t *dat, int index, int mask) |
171 | { |
172 | int offset = index & 0x7; |
173 | uint16_t data; |
174 | printkd("correct: "); |
175 | |
176 | index += (index >> 3); |
177 | |
178 | data = dat[index]; |
179 | data |= dat[index+1] << 8; |
180 | |
181 | printkd("0x%x -> ", data); |
182 | |
183 | mask ^= (data >> offset) & 0x1ff; |
184 | data &= ~(0x1ff << offset); |
185 | data |= (mask << offset); |
186 | |
187 | printkd("0x%x\n", data); |
188 | |
189 | dat[index] = data & 0xff; |
190 | dat[index+1] = (data >> 8) & 0xff; |
191 | } |
192 | |
193 | static int jz_nand_correct_ecc_rs(struct mtd_info *mtd, uint8_t *dat, |
194 | uint8_t *read_ecc, uint8_t *calc_ecc) |
195 | { |
196 | struct jz_nand *nand = mtd_to_jz_nand(mtd); |
197 | int i, error_count, index; |
198 | uint32_t reg, status, error; |
199 | uint32_t t; |
200 | |
201 | t = read_ecc[0]; |
202 | |
203 | if (t == 0xff) { |
204 | for (i = 1; i < 9; ++i) |
205 | t &= read_ecc[i]; |
206 | |
207 | t &= dat[0]; |
208 | t &= dat[nand->chip.ecc.size / 2]; |
209 | t &= dat[nand->chip.ecc.size - 1]; |
210 | |
211 | if (t == 0xff) { |
212 | for (i = 1; i < nand->chip.ecc.size - 1; ++i) |
213 | t &= dat[i]; |
214 | if (t == 0xff) |
215 | return 0; |
216 | } |
217 | } |
218 | |
219 | for (i = 0; i < 9; ++i) |
220 | writeb(read_ecc[i], nand->base + JZ_REG_NAND_PAR0 + i); |
221 | |
222 | reg = readl(nand->base + JZ_REG_NAND_ECC_CTRL); |
223 | reg |= JZ_NAND_ECC_CTRL_PAR_READY; |
224 | writel(reg, nand->base + JZ_REG_NAND_ECC_CTRL); |
225 | |
226 | do { |
227 | status = readl(nand->base + JZ_REG_NAND_IRQ_STAT); |
228 | } while (!(status & JZ_NAND_STATUS_DEC_FINISH)); |
229 | |
230 | reg = readl(nand->base + JZ_REG_NAND_ECC_CTRL); |
231 | reg &= ~JZ_NAND_ECC_CTRL_ENABLE; |
232 | writel(reg, nand->base + JZ_REG_NAND_ECC_CTRL); |
233 | |
234 | if (status & JZ_NAND_STATUS_ERROR) { |
235 | if (status & JZ_NAND_STATUS_UNCOR_ERROR) { |
236 | printkd("uncorrectable ecc:"); |
237 | for (i = 0; i < 9; ++i) |
238 | printkd(" 0x%x", read_ecc[i]); |
239 | printkd("\n"); |
240 | printkd("uncorrectable data:"); |
241 | for (i = 0; i < 32; ++i) |
242 | printkd(" 0x%x", dat[i]); |
243 | printkd("\n"); |
244 | return -1; |
245 | } |
246 | |
247 | error_count = (status & JZ_NAND_STATUS_ERR_COUNT) >> 29; |
248 | |
249 | printkd("error_count: %d %x\n", error_count, status); |
250 | |
251 | for (i = 0; i < error_count; ++i) { |
252 | error = readl(nand->base + JZ_REG_NAND_ERR(i)); |
253 | index = ((error >> 16) & 0x1ff) - 1; |
254 | if (index >= 0 && index < 512) |
255 | correct_data(dat, index, error & 0x1ff); |
256 | } |
257 | |
258 | return error_count; |
259 | } |
260 | |
261 | return 0; |
262 | } |
263 | |
264 | |
265 | |
266 | #ifdef CONFIG_MTD_CMDLINE_PARTS |
267 | static const char *part_probes[] = {"cmdline", NULL}; |
268 | #endif |
269 | |
270 | static int __devinit jz_nand_probe(struct platform_device *pdev) |
271 | { |
272 | int ret; |
273 | struct jz_nand *nand; |
274 | struct nand_chip *chip; |
275 | struct mtd_info *mtd; |
276 | struct jz_nand_platform_data *pdata = pdev->dev.platform_data; |
277 | #ifdef CONFIG_MTD_PARTITIONS |
278 | struct mtd_partition *partition_info; |
279 | int num_partitions = 0; |
280 | #endif |
281 | |
282 | nand = kzalloc(sizeof(*nand), GFP_KERNEL); |
283 | if (!nand) { |
284 | dev_err(&pdev->dev, "Failed to allocate device structure.\n"); |
285 | return -ENOMEM; |
286 | } |
287 | |
288 | nand->mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); |
289 | if (!nand->mem) { |
290 | dev_err(&pdev->dev, "Failed to get platform mmio memory\n"); |
291 | ret = -ENOENT; |
292 | goto err_free; |
293 | } |
294 | |
295 | nand->mem = request_mem_region(nand->mem->start, |
296 | resource_size(nand->mem), pdev->name); |
297 | |
298 | if (!nand->mem) { |
299 | dev_err(&pdev->dev, "Failed to request mmio memory region\n"); |
300 | ret = -EBUSY; |
301 | goto err_free; |
302 | } |
303 | |
304 | nand->base = ioremap(nand->mem->start, resource_size(nand->mem)); |
305 | |
306 | if (!nand->base) { |
307 | dev_err(&pdev->dev, "Failed to ioremap mmio memory region\n"); |
308 | ret = -EBUSY; |
309 | goto err_release_mem; |
310 | } |
311 | |
312 | if (pdata && gpio_is_valid(pdata->busy_gpio)) { |
313 | ret = gpio_request(pdata->busy_gpio, "NAND busy pin"); |
314 | if (ret) { |
315 | dev_err(&pdev->dev, |
316 | "Failed to request busy gpio %d: %d\n", |
317 | pdata->busy_gpio, ret); |
318 | goto err_iounmap; |
319 | } |
320 | } |
321 | |
322 | mtd = &nand->mtd; |
323 | chip = &nand->chip; |
324 | mtd->priv = chip; |
325 | mtd->owner = THIS_MODULE; |
326 | mtd->name = "jz4740-nand"; |
327 | |
328 | chip->ecc.hwctl = jz_nand_hwctl; |
329 | |
330 | chip->ecc.calculate = jz_nand_calculate_ecc_rs; |
331 | chip->ecc.correct = jz_nand_correct_ecc_rs; |
332 | chip->ecc.mode = NAND_ECC_HW_OOB_FIRST; |
333 | chip->ecc.size = 512; |
334 | chip->ecc.bytes = 9; |
335 | if (pdata) |
336 | chip->ecc.layout = pdata->ecc_layout; |
337 | |
338 | chip->chip_delay = 50; |
339 | chip->cmd_ctrl = jz_nand_cmd_ctrl; |
340 | |
341 | if (pdata && gpio_is_valid(pdata->busy_gpio)) |
342 | chip->dev_ready = jz_nand_dev_ready; |
343 | |
344 | chip->IO_ADDR_R = JZ_NAND_DATA_ADDR; |
345 | chip->IO_ADDR_W = JZ_NAND_DATA_ADDR; |
346 | |
347 | nand->pdata = pdata; |
348 | platform_set_drvdata(pdev, nand); |
349 | |
350 | ret = nand_scan_ident(mtd, 1); |
351 | if (ret) { |
352 | dev_err(&pdev->dev, "Failed to scan nand\n"); |
353 | goto err_gpio_free; |
354 | } |
355 | |
356 | if (pdata && pdata->ident_callback) { |
357 | pdata->ident_callback(pdev, chip, &pdata->partitions, |
358 | &pdata->num_partitions); |
359 | } |
360 | |
361 | ret = nand_scan_tail(mtd); |
362 | if (ret) { |
363 | dev_err(&pdev->dev, "Failed to scan nand\n"); |
364 | goto err_gpio_free; |
365 | } |
366 | |
367 | #ifdef CONFIG_MTD_PARTITIONS |
368 | #ifdef CONFIG_MTD_CMDLINE_PARTS |
369 | num_partitions = parse_mtd_partitions(mtd, part_probes, |
370 | &partition_info, 0); |
371 | #endif |
372 | if (num_partitions <= 0 && pdata) { |
373 | num_partitions = pdata->num_partitions; |
374 | partition_info = pdata->partitions; |
375 | } |
376 | |
377 | if (num_partitions > 0) |
378 | ret = add_mtd_partitions(mtd, partition_info, num_partitions); |
379 | else |
380 | #endif |
381 | ret = add_mtd_device(mtd); |
382 | |
383 | if (ret) { |
384 | dev_err(&pdev->dev, "Failed to add mtd device\n"); |
385 | goto err_nand_release; |
386 | } |
387 | |
388 | dev_info(&pdev->dev, "Successfully registered JZ4740 NAND driver\n"); |
389 | |
390 | return 0; |
391 | err_nand_release: |
392 | nand_release(&nand->mtd); |
393 | err_gpio_free: |
394 | platform_set_drvdata(pdev, NULL); |
395 | gpio_free(pdata->busy_gpio); |
396 | err_iounmap: |
397 | iounmap(nand->base); |
398 | err_release_mem: |
399 | release_mem_region(nand->mem->start, resource_size(nand->mem)); |
400 | err_free: |
401 | kfree(nand); |
402 | return ret; |
403 | } |
404 | |
405 | static void __devexit jz_nand_remove(struct platform_device *pdev) |
406 | { |
407 | struct jz_nand *nand = platform_get_drvdata(pdev); |
408 | |
409 | nand_release(&nand->mtd); |
410 | |
411 | iounmap(nand->base); |
412 | |
413 | release_mem_region(nand->mem->start, resource_size(nand->mem)); |
414 | |
415 | platform_set_drvdata(pdev, NULL); |
416 | kfree(nand); |
417 | } |
418 | |
419 | struct platform_driver jz_nand_driver = { |
420 | .probe = jz_nand_probe, |
421 | .remove = __devexit_p(jz_nand_probe), |
422 | .driver = { |
423 | .name = "jz4740-nand", |
424 | .owner = THIS_MODULE, |
425 | }, |
426 | }; |
427 | |
428 | static int __init jz_nand_init(void) |
429 | { |
430 | return platform_driver_register(&jz_nand_driver); |
431 | } |
432 | module_init(jz_nand_init); |
433 | |
434 | static void __exit jz_nand_exit(void) |
435 | { |
436 | platform_driver_unregister(&jz_nand_driver); |
437 | } |
438 | module_exit(jz_nand_exit); |
439 | |
440 | MODULE_LICENSE("GPL"); |
441 | MODULE_AUTHOR("Lars-Peter Clausen <lars@metafoo.de>"); |
442 | MODULE_DESCRIPTION("NAND controller driver for JZ4720/JZ4740 SoC"); |
443 | MODULE_ALIAS("platform:jz4740-nand"); |
444 | MODULE_ALIAS("platform:jz4720-nand"); |
445 |
Branches:
ben-wpan
ben-wpan-stefan
javiroman/ks7010
jz-2.6.34
jz-2.6.34-rc5
jz-2.6.34-rc6
jz-2.6.34-rc7
jz-2.6.35
jz-2.6.36
jz-2.6.37
jz-2.6.38
jz-2.6.39
jz-3.0
jz-3.1
jz-3.11
jz-3.12
jz-3.13
jz-3.15
jz-3.16
jz-3.18-dt
jz-3.2
jz-3.3
jz-3.4
jz-3.5
jz-3.6
jz-3.6-rc2-pwm
jz-3.9
jz-3.9-clk
jz-3.9-rc8
jz47xx
jz47xx-2.6.38
master
Tags:
od-2011-09-04
od-2011-09-18
v2.6.34-rc5
v2.6.34-rc6
v2.6.34-rc7
v3.9