Root/
Source at commit 8786de52702888b95da904e5cccc5b754bf5f4ff created 13 years 21 days ago. By Peter Zotov, Add JZ4750 NAND driver. | |
---|---|
1 | /* |
2 | * Copyright (c) 2011 Peter Zotov <whitequark@whitequark.org> |
3 | * JZ4750 SoC NAND controller driver |
4 | * |
5 | * Based on JZ4740 NAND controller driver, |
6 | * (C) 2009-2010, Lars-Peter Clausen <lars@metafoo.de> |
7 | * |
8 | * This program is free software; you can redistribute it and/or modify it |
9 | * under the terms of the GNU General Public License as published by the |
10 | * Free Software Foundation; either version 2 of the License, or (at your |
11 | * option) any later version. |
12 | * |
13 | * You should have received a copy of the GNU General Public License along |
14 | * with this program; if not, write to the Free Software Foundation, Inc., |
15 | * 675 Mass Ave, Cambridge, MA 02139, USA. |
16 | * |
17 | */ |
18 | |
19 | #include <linux/ioport.h> |
20 | #include <linux/kernel.h> |
21 | #include <linux/module.h> |
22 | #include <linux/platform_device.h> |
23 | #include <linux/slab.h> |
24 | |
25 | #include <linux/mtd/mtd.h> |
26 | #include <linux/mtd/nand.h> |
27 | #include <linux/mtd/partitions.h> |
28 | |
29 | #include <linux/gpio.h> |
30 | |
31 | #include <jz4750_nand.h> |
32 | |
33 | #define JZ_REG_NAND_CTRL 0x50 |
34 | |
35 | #define JZ_NAND_CTRL_ENABLE_CHIP(x) BIT((x) << 1) |
36 | #define JZ_NAND_CTRL_ASSERT_CHIP(x) BIT(((x) << 1) + 1) |
37 | |
38 | #define JZ_NAND_MEM_ADDR_OFFSET 0x10000 |
39 | #define JZ_NAND_MEM_CMD_OFFSET 0x08000 |
40 | |
41 | #define JZ_REG_BCH_CR 0x00 |
42 | #define JZ_REG_BCH_CR_SET 0x04 |
43 | #define JZ_REG_BCH_CR_CLR 0x08 |
44 | #define JZ_REG_BCH_CNT 0x0C |
45 | #define JZ_REG_BCH_DR 0x10 |
46 | #define JZ_REG_BCH_PAR(i) (0x14 + (i)) |
47 | #define JZ_REG_BCH_STATUS 0x24 |
48 | #define JZ_REG_BCH_ERR(i) (0x28 + ((i) << 2)) |
49 | |
50 | #define JZ_BCH_ENABLE BIT(0) |
51 | #define JZ_BCH_RESET BIT(1) |
52 | #define JZ_BCH_8BIT BIT(2) |
53 | #define JZ_BCH_ENCODE BIT(3) |
54 | #define JZ_BCH_USE_DMA BIT(4) |
55 | |
56 | #define JZ_BCH_STATUS_ERROR BIT(0) |
57 | #define JZ_BCH_STATUS_UNCOR BIT(1) |
58 | #define JZ_BCH_STATUS_ENC_FINISH BIT(2) |
59 | #define JZ_BCH_STATUS_DEC_FINISH BIT(3) |
60 | #define JZ_BCH_STATUS_ALL_F BIT(4) |
61 | #define JZ_BCH_STATUS_ALL_0 BIT(5) |
62 | #define JZ_BCH_STATUS_ERR_COUNT 0xf0000000 |
63 | |
64 | struct jz_nand { |
65 | struct mtd_info mtd; |
66 | struct nand_chip chip; |
67 | void __iomem *base; |
68 | struct resource *mem; |
69 | |
70 | void __iomem *bank_base; |
71 | struct resource *bank_mem; |
72 | |
73 | void __iomem *bch_base; |
74 | struct resource *bch_mem; |
75 | |
76 | struct jz_nand_platform_data *pdata; |
77 | bool is_reading; |
78 | }; |
79 | |
80 | static inline struct jz_nand *mtd_to_jz_nand(struct mtd_info *mtd) |
81 | { |
82 | return container_of(mtd, struct jz_nand, mtd); |
83 | } |
84 | |
85 | static void jz_nand_cmd_ctrl(struct mtd_info *mtd, int dat, unsigned int ctrl) |
86 | { |
87 | struct jz_nand *nand = mtd_to_jz_nand(mtd); |
88 | struct nand_chip *chip = mtd->priv; |
89 | uint32_t reg; |
90 | |
91 | if (ctrl & NAND_CTRL_CHANGE) { |
92 | BUG_ON((ctrl & NAND_ALE) && (ctrl & NAND_CLE)); |
93 | |
94 | if (ctrl & NAND_ALE) |
95 | chip->IO_ADDR_W = nand->bank_base + JZ_NAND_MEM_ADDR_OFFSET; |
96 | else if (ctrl & NAND_CLE) |
97 | chip->IO_ADDR_W = nand->bank_base + JZ_NAND_MEM_CMD_OFFSET; |
98 | else |
99 | chip->IO_ADDR_W = nand->bank_base; |
100 | |
101 | reg = readl(nand->base + JZ_REG_NAND_CTRL); |
102 | if (ctrl & NAND_NCE) |
103 | reg |= JZ_NAND_CTRL_ASSERT_CHIP(0); |
104 | else |
105 | reg &= ~JZ_NAND_CTRL_ASSERT_CHIP(0); |
106 | writel(reg, nand->base + JZ_REG_NAND_CTRL); |
107 | } |
108 | |
109 | if (dat != NAND_CMD_NONE) |
110 | writeb(dat, chip->IO_ADDR_W); |
111 | } |
112 | |
113 | static int jz_nand_dev_ready(struct mtd_info *mtd) |
114 | { |
115 | struct jz_nand *nand = mtd_to_jz_nand(mtd); |
116 | return gpio_get_value_cansleep(nand->pdata->busy_gpio); |
117 | } |
118 | |
119 | static void jz_nand_hwctl(struct mtd_info *mtd, int mode) |
120 | { |
121 | struct jz_nand *nand = mtd_to_jz_nand(mtd); |
122 | |
123 | /* While BCH_CR register is defined as RW in manual, it is |
124 | * not true. It can only be modified through BCH_CR_SET and |
125 | * BCH_CR_CLR registers. |
126 | */ |
127 | if (nand->pdata->bch_8bit) |
128 | writel(JZ_BCH_8BIT, nand->bch_base + JZ_REG_BCH_CR_SET); |
129 | else |
130 | writel(JZ_BCH_8BIT, nand->bch_base + JZ_REG_BCH_CR_CLR); |
131 | |
132 | switch (mode) { |
133 | case NAND_ECC_READ: |
134 | writel(JZ_BCH_ENCODE, nand->bch_base + JZ_REG_BCH_CR_CLR); |
135 | nand->is_reading = true; |
136 | break; |
137 | case NAND_ECC_WRITE: |
138 | writel(JZ_BCH_ENCODE, nand->bch_base + JZ_REG_BCH_CR_SET); |
139 | nand->is_reading = false; |
140 | break; |
141 | default: |
142 | break; |
143 | } |
144 | } |
145 | |
146 | static int jz_nand_calculate_ecc_bch(struct mtd_info *mtd, const uint8_t *dat, |
147 | uint8_t *ecc_code) |
148 | { |
149 | struct jz_nand *nand = mtd_to_jz_nand(mtd); |
150 | uint32_t status; |
151 | int i; |
152 | unsigned int timeout = 1000; |
153 | |
154 | int size = nand->chip.ecc.size, |
155 | bytes = nand->chip.ecc.bytes; |
156 | |
157 | if (nand->is_reading) |
158 | return 0; |
159 | |
160 | writel(JZ_BCH_ENABLE, nand->bch_base + JZ_REG_BCH_CR_SET); |
161 | writel(JZ_BCH_RESET, nand->bch_base + JZ_REG_BCH_CR_SET); |
162 | |
163 | writel(size & 0xffff, nand->bch_base + JZ_REG_BCH_CNT); |
164 | |
165 | for(i = 0; i < size; i++) |
166 | writeb(dat[i], nand->bch_base + JZ_REG_BCH_DR); |
167 | |
168 | do { |
169 | status = readl(nand->bch_base + JZ_REG_BCH_STATUS); |
170 | } while (!(status & JZ_BCH_STATUS_ENC_FINISH) && --timeout); |
171 | |
172 | writel(JZ_BCH_ENABLE, nand->bch_base + JZ_REG_BCH_CR_CLR); |
173 | |
174 | if (timeout == 0) |
175 | return -1; |
176 | |
177 | for(i = 0; i < bytes; i++) |
178 | ecc_code[i] = readb(nand->bch_base + JZ_REG_BCH_PAR(i)); |
179 | |
180 | return 0; |
181 | } |
182 | |
183 | static int jz_nand_correct_ecc_bch(struct mtd_info *mtd, uint8_t *dat, |
184 | uint8_t *read_ecc, uint8_t *calc_ecc) |
185 | { |
186 | struct jz_nand *nand = mtd_to_jz_nand(mtd); |
187 | int i, error_count; |
188 | uint32_t status, error_regs[4]; |
189 | uint16_t *error_indexes, index; |
190 | unsigned int timeout = 1000; |
191 | |
192 | int size = nand->chip.ecc.size, |
193 | bytes = nand->chip.ecc.bytes; |
194 | |
195 | writel(JZ_BCH_ENABLE, nand->bch_base + JZ_REG_BCH_CR_SET); |
196 | writel(JZ_BCH_RESET, nand->bch_base + JZ_REG_BCH_CR_SET); |
197 | |
198 | /* Clear status bits */ |
199 | writel(0xff, nand->bch_base + JZ_REG_BCH_STATUS); |
200 | |
201 | writel((size + bytes) << 16, nand->bch_base + JZ_REG_BCH_CNT); |
202 | |
203 | for (i = 0; i < size; i++) |
204 | writeb(dat[i], nand->bch_base + JZ_REG_BCH_DR); |
205 | |
206 | for (i = 0; i < bytes; i++) |
207 | writeb(read_ecc[i], nand->bch_base + JZ_REG_BCH_DR); |
208 | |
209 | do { |
210 | status = readl(nand->bch_base + JZ_REG_BCH_STATUS); |
211 | } while (!(status & JZ_BCH_STATUS_DEC_FINISH) && --timeout); |
212 | |
213 | writel(JZ_BCH_ENABLE, nand->bch_base + JZ_REG_BCH_CR_CLR); |
214 | |
215 | if (timeout == 0) |
216 | return -1; |
217 | |
218 | if (status & JZ_BCH_STATUS_ERROR) { |
219 | if (status & JZ_BCH_STATUS_UNCOR) |
220 | return -1; |
221 | |
222 | for(i = 0; i < 4; i++) |
223 | error_regs[i] = readl(nand->bch_base + JZ_REG_BCH_ERR(i)); |
224 | |
225 | /* All Ingenic processors are little-endian */ |
226 | error_indexes = (uint16_t*) error_regs; |
227 | |
228 | error_count = (status & JZ_BCH_STATUS_ERR_COUNT) >> 28; |
229 | |
230 | for (i = 0; i < error_count; i++) { |
231 | index = error_indexes[i]; |
232 | |
233 | if (index < size) |
234 | dat[index >> 3] ^= (1 << (index & 0x7)); |
235 | } |
236 | |
237 | return error_count; |
238 | } |
239 | |
240 | return 0; |
241 | } |
242 | |
243 | #ifdef CONFIG_MTD_CMDLINE_PARTS |
244 | static const char *part_probes[] = {"cmdline", NULL}; |
245 | #endif |
246 | |
247 | static int jz_nand_ioremap_resource(struct platform_device *pdev, |
248 | const char *name, struct resource **res, void __iomem **base) |
249 | { |
250 | int ret; |
251 | |
252 | *res = platform_get_resource_byname(pdev, IORESOURCE_MEM, name); |
253 | if (!*res) { |
254 | dev_err(&pdev->dev, "Failed to get platform %s memory\n", name); |
255 | ret = -ENXIO; |
256 | goto err; |
257 | } |
258 | |
259 | *res = request_mem_region((*res)->start, resource_size(*res), |
260 | pdev->name); |
261 | if (!*res) { |
262 | dev_err(&pdev->dev, "Failed to request %s memory region\n", name); |
263 | ret = -EBUSY; |
264 | goto err; |
265 | } |
266 | |
267 | *base = ioremap((*res)->start, resource_size(*res)); |
268 | if (!*base) { |
269 | dev_err(&pdev->dev, "Failed to ioremap %s memory region\n", name); |
270 | ret = -EBUSY; |
271 | goto err_release_mem; |
272 | } |
273 | |
274 | return 0; |
275 | |
276 | err_release_mem: |
277 | release_mem_region((*res)->start, resource_size(*res)); |
278 | err: |
279 | *res = NULL; |
280 | *base = NULL; |
281 | return ret; |
282 | } |
283 | |
284 | static int __devinit jz_nand_probe(struct platform_device *pdev) |
285 | { |
286 | int ret; |
287 | struct jz_nand *nand; |
288 | struct nand_chip *chip; |
289 | struct mtd_info *mtd; |
290 | struct jz_nand_platform_data *pdata = pdev->dev.platform_data; |
291 | #ifdef CONFIG_MTD_PARTITIONS |
292 | struct mtd_partition *partition_info; |
293 | int num_partitions = 0; |
294 | #endif |
295 | |
296 | nand = kzalloc(sizeof(*nand), GFP_KERNEL); |
297 | if (!nand) { |
298 | dev_err(&pdev->dev, "Failed to allocate device structure.\n"); |
299 | return -ENOMEM; |
300 | } |
301 | |
302 | ret = jz_nand_ioremap_resource(pdev, "mmio", &nand->mem, &nand->base); |
303 | if (ret) |
304 | goto err_free; |
305 | |
306 | ret = jz_nand_ioremap_resource(pdev, "bank", &nand->bank_mem, |
307 | &nand->bank_base); |
308 | if (ret) |
309 | goto err_iounmap_mmio; |
310 | |
311 | ret = jz_nand_ioremap_resource(pdev, "bch", &nand->bch_mem, |
312 | &nand->bch_base); |
313 | if (ret) |
314 | goto err_iounmap_bank; |
315 | |
316 | if (pdata && gpio_is_valid(pdata->busy_gpio)) { |
317 | ret = gpio_request(pdata->busy_gpio, "NAND busy pin"); |
318 | if (ret) { |
319 | dev_err(&pdev->dev, |
320 | "Failed to request busy gpio %d: %d\n", |
321 | pdata->busy_gpio, ret); |
322 | goto err_iounmap_bch; |
323 | } |
324 | } |
325 | |
326 | mtd = &nand->mtd; |
327 | chip = &nand->chip; |
328 | mtd->priv = chip; |
329 | mtd->owner = THIS_MODULE; |
330 | mtd->name = "jz4750-nand"; |
331 | |
332 | chip->ecc.hwctl = jz_nand_hwctl; |
333 | chip->ecc.calculate = jz_nand_calculate_ecc_bch; |
334 | chip->ecc.correct = jz_nand_correct_ecc_bch; |
335 | chip->ecc.mode = NAND_ECC_HW; |
336 | chip->ecc.size = 512; |
337 | chip->ecc.bytes = 13; |
338 | |
339 | if (pdata) |
340 | chip->ecc.layout = pdata->ecc_layout; |
341 | |
342 | chip->chip_delay = 50; |
343 | chip->cmd_ctrl = jz_nand_cmd_ctrl; |
344 | |
345 | if (pdata && gpio_is_valid(pdata->busy_gpio)) |
346 | chip->dev_ready = jz_nand_dev_ready; |
347 | |
348 | chip->IO_ADDR_R = nand->bank_base; |
349 | chip->IO_ADDR_W = nand->bank_base; |
350 | |
351 | nand->pdata = pdata; |
352 | platform_set_drvdata(pdev, nand); |
353 | |
354 | writel(JZ_NAND_CTRL_ENABLE_CHIP(0), nand->base + JZ_REG_NAND_CTRL); |
355 | |
356 | ret = nand_scan_ident(mtd, 1, NULL); |
357 | if (ret) { |
358 | dev_err(&pdev->dev, "Failed to scan nand\n"); |
359 | goto err_gpio_free; |
360 | } |
361 | |
362 | if (pdata && pdata->ident_callback) { |
363 | pdata->ident_callback(pdev, chip, &pdata->partitions, |
364 | &pdata->num_partitions); |
365 | } |
366 | |
367 | ret = nand_scan_tail(mtd); |
368 | if (ret) { |
369 | dev_err(&pdev->dev, "Failed to scan nand\n"); |
370 | goto err_gpio_free; |
371 | } |
372 | |
373 | #ifdef CONFIG_MTD_PARTITIONS |
374 | #ifdef CONFIG_MTD_CMDLINE_PARTS |
375 | num_partitions = parse_mtd_partitions(mtd, part_probes, |
376 | &partition_info, 0); |
377 | #endif |
378 | if (num_partitions <= 0 && pdata) { |
379 | num_partitions = pdata->num_partitions; |
380 | partition_info = pdata->partitions; |
381 | } |
382 | |
383 | if (num_partitions > 0) |
384 | ret = add_mtd_partitions(mtd, partition_info, num_partitions); |
385 | else |
386 | #endif |
387 | ret = add_mtd_device(mtd); |
388 | |
389 | if (ret) { |
390 | dev_err(&pdev->dev, "Failed to add mtd device\n"); |
391 | goto err_nand_release; |
392 | } |
393 | |
394 | dev_info(&pdev->dev, "Successfully registered JZ4750 NAND driver\n"); |
395 | |
396 | return 0; |
397 | |
398 | err_nand_release: |
399 | nand_release(&nand->mtd); |
400 | err_gpio_free: |
401 | platform_set_drvdata(pdev, NULL); |
402 | gpio_free(pdata->busy_gpio); |
403 | err_iounmap_bch: |
404 | iounmap(nand->bch_base); |
405 | release_mem_region(nand->bch_mem->start, resource_size(nand->bch_mem)); |
406 | err_iounmap_bank: |
407 | iounmap(nand->bank_base); |
408 | release_mem_region(nand->bank_mem->start, resource_size(nand->bank_mem)); |
409 | err_iounmap_mmio: |
410 | iounmap(nand->base); |
411 | release_mem_region(nand->mem->start, resource_size(nand->mem)); |
412 | err_free: |
413 | kfree(nand); |
414 | return ret; |
415 | } |
416 | |
417 | static int __devexit jz_nand_remove(struct platform_device *pdev) |
418 | { |
419 | struct jz_nand *nand = platform_get_drvdata(pdev); |
420 | |
421 | nand_release(&nand->mtd); |
422 | |
423 | /* Deassert and disable all chips */ |
424 | writel(0, nand->base + JZ_REG_NAND_CTRL); |
425 | |
426 | iounmap(nand->bch_base); |
427 | release_mem_region(nand->bch_mem->start, resource_size(nand->bch_mem)); |
428 | iounmap(nand->bank_base); |
429 | release_mem_region(nand->bank_mem->start, resource_size(nand->bank_mem)); |
430 | iounmap(nand->base); |
431 | release_mem_region(nand->mem->start, resource_size(nand->mem)); |
432 | |
433 | platform_set_drvdata(pdev, NULL); |
434 | kfree(nand); |
435 | |
436 | return 0; |
437 | } |
438 | |
439 | static struct platform_driver jz_nand_driver = { |
440 | .probe = jz_nand_probe, |
441 | .remove = __devexit_p(jz_nand_remove), |
442 | .driver = { |
443 | .name = "jz4750-nand", |
444 | .owner = THIS_MODULE, |
445 | }, |
446 | }; |
447 | |
448 | static int __init jz_nand_init(void) |
449 | { |
450 | return platform_driver_register(&jz_nand_driver); |
451 | } |
452 | module_init(jz_nand_init); |
453 | |
454 | static void __exit jz_nand_exit(void) |
455 | { |
456 | platform_driver_unregister(&jz_nand_driver); |
457 | } |
458 | module_exit(jz_nand_exit); |
459 | |
460 | MODULE_LICENSE("GPL"); |
461 | MODULE_AUTHOR("Peter Zotov <whitequark@whitequark.org>"); |
462 | MODULE_DESCRIPTION("NAND controller driver for JZ4750 SoC"); |
463 | MODULE_ALIAS("platform:jz4750-nand"); |
464 |
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