diff options
author | Maarten ter Huurne <maarten@treewalker.org> | 2012-03-29 13:17:01 -0400 |
---|---|---|
committer | Ralf Baechle <ralf@linux-mips.org> | 2012-07-23 08:56:20 -0400 |
commit | 1471d41a5bdfdf83ed1e5c2148a9763e64b1f53b (patch) | |
tree | e3e9ffb84311be6af7ba2dccaad0d0f287a8cb32 /drivers/mtd | |
parent | 28a33cbc24e4256c143dce96c7d93bf423229f92 (diff) |
MTD: NAND: JZ4740: Multi-bank support with autodetection
The platform data can now specify which external memory banks to probe
for NAND chips, and in which order. Banks that contain a NAND are used
and the other banks are freed.
Squashed version of development done in jz-2.6.38 branch.
Original patch by Lars-Peter Clausen with some bug fixes from me.
Thanks to Paul Cercueil for the initial autodetection patch.
Signed-off-by: Maarten ter Huurne <maarten@treewalker.org>
Cc: linux-mips@linux-mips.org
Patchwork: https://patchwork.linux-mips.org/patch/3560/
Acked-By: David Woodhouse <David.Woodhouse@intel.com>
Signed-off-by: Ralf Baechle <ralf@linux-mips.org>
Diffstat (limited to 'drivers/mtd')
-rw-r--r-- | drivers/mtd/nand/jz4740_nand.c | 228 |
1 files changed, 192 insertions, 36 deletions
diff --git a/drivers/mtd/nand/jz4740_nand.c b/drivers/mtd/nand/jz4740_nand.c index a6fa884ae49b..100b6775e175 100644 --- a/drivers/mtd/nand/jz4740_nand.c +++ b/drivers/mtd/nand/jz4740_nand.c | |||
@@ -52,9 +52,10 @@ | |||
52 | 52 | ||
53 | #define JZ_NAND_CTRL_ENABLE_CHIP(x) BIT((x) << 1) | 53 | #define JZ_NAND_CTRL_ENABLE_CHIP(x) BIT((x) << 1) |
54 | #define JZ_NAND_CTRL_ASSERT_CHIP(x) BIT(((x) << 1) + 1) | 54 | #define JZ_NAND_CTRL_ASSERT_CHIP(x) BIT(((x) << 1) + 1) |
55 | #define JZ_NAND_CTRL_ASSERT_CHIP_MASK 0xaa | ||
55 | 56 | ||
56 | #define JZ_NAND_MEM_ADDR_OFFSET 0x10000 | ||
57 | #define JZ_NAND_MEM_CMD_OFFSET 0x08000 | 57 | #define JZ_NAND_MEM_CMD_OFFSET 0x08000 |
58 | #define JZ_NAND_MEM_ADDR_OFFSET 0x10000 | ||
58 | 59 | ||
59 | struct jz_nand { | 60 | struct jz_nand { |
60 | struct mtd_info mtd; | 61 | struct mtd_info mtd; |
@@ -62,8 +63,11 @@ struct jz_nand { | |||
62 | void __iomem *base; | 63 | void __iomem *base; |
63 | struct resource *mem; | 64 | struct resource *mem; |
64 | 65 | ||
65 | void __iomem *bank_base; | 66 | unsigned char banks[JZ_NAND_NUM_BANKS]; |
66 | struct resource *bank_mem; | 67 | void __iomem *bank_base[JZ_NAND_NUM_BANKS]; |
68 | struct resource *bank_mem[JZ_NAND_NUM_BANKS]; | ||
69 | |||
70 | int selected_bank; | ||
67 | 71 | ||
68 | struct jz_nand_platform_data *pdata; | 72 | struct jz_nand_platform_data *pdata; |
69 | bool is_reading; | 73 | bool is_reading; |
@@ -74,26 +78,50 @@ static inline struct jz_nand *mtd_to_jz_nand(struct mtd_info *mtd) | |||
74 | return container_of(mtd, struct jz_nand, mtd); | 78 | return container_of(mtd, struct jz_nand, mtd); |
75 | } | 79 | } |
76 | 80 | ||
81 | static void jz_nand_select_chip(struct mtd_info *mtd, int chipnr) | ||
82 | { | ||
83 | struct jz_nand *nand = mtd_to_jz_nand(mtd); | ||
84 | struct nand_chip *chip = mtd->priv; | ||
85 | uint32_t ctrl; | ||
86 | int banknr; | ||
87 | |||
88 | ctrl = readl(nand->base + JZ_REG_NAND_CTRL); | ||
89 | ctrl &= ~JZ_NAND_CTRL_ASSERT_CHIP_MASK; | ||
90 | |||
91 | if (chipnr == -1) { | ||
92 | banknr = -1; | ||
93 | } else { | ||
94 | banknr = nand->banks[chipnr] - 1; | ||
95 | chip->IO_ADDR_R = nand->bank_base[banknr]; | ||
96 | chip->IO_ADDR_W = nand->bank_base[banknr]; | ||
97 | } | ||
98 | writel(ctrl, nand->base + JZ_REG_NAND_CTRL); | ||
99 | |||
100 | nand->selected_bank = banknr; | ||
101 | } | ||
102 | |||
77 | static void jz_nand_cmd_ctrl(struct mtd_info *mtd, int dat, unsigned int ctrl) | 103 | static void jz_nand_cmd_ctrl(struct mtd_info *mtd, int dat, unsigned int ctrl) |
78 | { | 104 | { |
79 | struct jz_nand *nand = mtd_to_jz_nand(mtd); | 105 | struct jz_nand *nand = mtd_to_jz_nand(mtd); |
80 | struct nand_chip *chip = mtd->priv; | 106 | struct nand_chip *chip = mtd->priv; |
81 | uint32_t reg; | 107 | uint32_t reg; |
108 | void __iomem *bank_base = nand->bank_base[nand->selected_bank]; | ||
109 | |||
110 | BUG_ON(nand->selected_bank < 0); | ||
82 | 111 | ||
83 | if (ctrl & NAND_CTRL_CHANGE) { | 112 | if (ctrl & NAND_CTRL_CHANGE) { |
84 | BUG_ON((ctrl & NAND_ALE) && (ctrl & NAND_CLE)); | 113 | BUG_ON((ctrl & NAND_ALE) && (ctrl & NAND_CLE)); |
85 | if (ctrl & NAND_ALE) | 114 | if (ctrl & NAND_ALE) |
86 | chip->IO_ADDR_W = nand->bank_base + JZ_NAND_MEM_ADDR_OFFSET; | 115 | bank_base += JZ_NAND_MEM_ADDR_OFFSET; |
87 | else if (ctrl & NAND_CLE) | 116 | else if (ctrl & NAND_CLE) |
88 | chip->IO_ADDR_W = nand->bank_base + JZ_NAND_MEM_CMD_OFFSET; | 117 | bank_base += JZ_NAND_MEM_CMD_OFFSET; |
89 | else | 118 | chip->IO_ADDR_W = bank_base; |
90 | chip->IO_ADDR_W = nand->bank_base; | ||
91 | 119 | ||
92 | reg = readl(nand->base + JZ_REG_NAND_CTRL); | 120 | reg = readl(nand->base + JZ_REG_NAND_CTRL); |
93 | if (ctrl & NAND_NCE) | 121 | if (ctrl & NAND_NCE) |
94 | reg |= JZ_NAND_CTRL_ASSERT_CHIP(0); | 122 | reg |= JZ_NAND_CTRL_ASSERT_CHIP(nand->selected_bank); |
95 | else | 123 | else |
96 | reg &= ~JZ_NAND_CTRL_ASSERT_CHIP(0); | 124 | reg &= ~JZ_NAND_CTRL_ASSERT_CHIP(nand->selected_bank); |
97 | writel(reg, nand->base + JZ_REG_NAND_CTRL); | 125 | writel(reg, nand->base + JZ_REG_NAND_CTRL); |
98 | } | 126 | } |
99 | if (dat != NAND_CMD_NONE) | 127 | if (dat != NAND_CMD_NONE) |
@@ -252,7 +280,7 @@ static int jz_nand_correct_ecc_rs(struct mtd_info *mtd, uint8_t *dat, | |||
252 | } | 280 | } |
253 | 281 | ||
254 | static int jz_nand_ioremap_resource(struct platform_device *pdev, | 282 | static int jz_nand_ioremap_resource(struct platform_device *pdev, |
255 | const char *name, struct resource **res, void __iomem **base) | 283 | const char *name, struct resource **res, void *__iomem *base) |
256 | { | 284 | { |
257 | int ret; | 285 | int ret; |
258 | 286 | ||
@@ -288,6 +316,90 @@ err: | |||
288 | return ret; | 316 | return ret; |
289 | } | 317 | } |
290 | 318 | ||
319 | static inline void jz_nand_iounmap_resource(struct resource *res, void __iomem *base) | ||
320 | { | ||
321 | iounmap(base); | ||
322 | release_mem_region(res->start, resource_size(res)); | ||
323 | } | ||
324 | |||
325 | static int __devinit jz_nand_detect_bank(struct platform_device *pdev, struct jz_nand *nand, unsigned char bank, size_t chipnr, uint8_t *nand_maf_id, uint8_t *nand_dev_id) { | ||
326 | int ret; | ||
327 | int gpio; | ||
328 | char gpio_name[9]; | ||
329 | char res_name[6]; | ||
330 | uint32_t ctrl; | ||
331 | struct mtd_info *mtd = &nand->mtd; | ||
332 | struct nand_chip *chip = &nand->chip; | ||
333 | |||
334 | /* Request GPIO port. */ | ||
335 | gpio = JZ_GPIO_MEM_CS0 + bank - 1; | ||
336 | sprintf(gpio_name, "NAND CS%d", bank); | ||
337 | ret = gpio_request(gpio, gpio_name); | ||
338 | if (ret) { | ||
339 | dev_warn(&pdev->dev, | ||
340 | "Failed to request %s gpio %d: %d\n", | ||
341 | gpio_name, gpio, ret); | ||
342 | goto notfound_gpio; | ||
343 | } | ||
344 | |||
345 | /* Request I/O resource. */ | ||
346 | sprintf(res_name, "bank%d", bank); | ||
347 | ret = jz_nand_ioremap_resource(pdev, res_name, | ||
348 | &nand->bank_mem[bank - 1], | ||
349 | &nand->bank_base[bank - 1]); | ||
350 | if (ret) | ||
351 | goto notfound_resource; | ||
352 | |||
353 | /* Enable chip in bank. */ | ||
354 | jz_gpio_set_function(gpio, JZ_GPIO_FUNC_MEM_CS0); | ||
355 | ctrl = readl(nand->base + JZ_REG_NAND_CTRL); | ||
356 | ctrl |= JZ_NAND_CTRL_ENABLE_CHIP(bank - 1); | ||
357 | writel(ctrl, nand->base + JZ_REG_NAND_CTRL); | ||
358 | |||
359 | if (chipnr == 0) { | ||
360 | /* Detect first chip. */ | ||
361 | ret = nand_scan_ident(mtd, 1, NULL); | ||
362 | if (ret) | ||
363 | goto notfound_id; | ||
364 | |||
365 | /* Retrieve the IDs from the first chip. */ | ||
366 | chip->select_chip(mtd, 0); | ||
367 | chip->cmdfunc(mtd, NAND_CMD_RESET, -1, -1); | ||
368 | chip->cmdfunc(mtd, NAND_CMD_READID, 0x00, -1); | ||
369 | *nand_maf_id = chip->read_byte(mtd); | ||
370 | *nand_dev_id = chip->read_byte(mtd); | ||
371 | } else { | ||
372 | /* Detect additional chip. */ | ||
373 | chip->select_chip(mtd, chipnr); | ||
374 | chip->cmdfunc(mtd, NAND_CMD_RESET, -1, -1); | ||
375 | chip->cmdfunc(mtd, NAND_CMD_READID, 0x00, -1); | ||
376 | if (*nand_maf_id != chip->read_byte(mtd) | ||
377 | || *nand_dev_id != chip->read_byte(mtd)) { | ||
378 | ret = -ENODEV; | ||
379 | goto notfound_id; | ||
380 | } | ||
381 | |||
382 | /* Update size of the MTD. */ | ||
383 | chip->numchips++; | ||
384 | mtd->size += chip->chipsize; | ||
385 | } | ||
386 | |||
387 | dev_info(&pdev->dev, "Found chip %i on bank %i\n", chipnr, bank); | ||
388 | return 0; | ||
389 | |||
390 | notfound_id: | ||
391 | dev_info(&pdev->dev, "No chip found on bank %i\n", bank); | ||
392 | ctrl &= ~(JZ_NAND_CTRL_ENABLE_CHIP(bank - 1)); | ||
393 | writel(ctrl, nand->base + JZ_REG_NAND_CTRL); | ||
394 | jz_gpio_set_function(gpio, JZ_GPIO_FUNC_NONE); | ||
395 | jz_nand_iounmap_resource(nand->bank_mem[bank - 1], | ||
396 | nand->bank_base[bank - 1]); | ||
397 | notfound_resource: | ||
398 | gpio_free(gpio); | ||
399 | notfound_gpio: | ||
400 | return ret; | ||
401 | } | ||
402 | |||
291 | static int __devinit jz_nand_probe(struct platform_device *pdev) | 403 | static int __devinit jz_nand_probe(struct platform_device *pdev) |
292 | { | 404 | { |
293 | int ret; | 405 | int ret; |
@@ -295,6 +407,8 @@ static int __devinit jz_nand_probe(struct platform_device *pdev) | |||
295 | struct nand_chip *chip; | 407 | struct nand_chip *chip; |
296 | struct mtd_info *mtd; | 408 | struct mtd_info *mtd; |
297 | struct jz_nand_platform_data *pdata = pdev->dev.platform_data; | 409 | struct jz_nand_platform_data *pdata = pdev->dev.platform_data; |
410 | size_t chipnr, bank_idx; | ||
411 | uint8_t nand_maf_id = 0, nand_dev_id = 0; | ||
298 | 412 | ||
299 | nand = kzalloc(sizeof(*nand), GFP_KERNEL); | 413 | nand = kzalloc(sizeof(*nand), GFP_KERNEL); |
300 | if (!nand) { | 414 | if (!nand) { |
@@ -305,10 +419,6 @@ static int __devinit jz_nand_probe(struct platform_device *pdev) | |||
305 | ret = jz_nand_ioremap_resource(pdev, "mmio", &nand->mem, &nand->base); | 419 | ret = jz_nand_ioremap_resource(pdev, "mmio", &nand->mem, &nand->base); |
306 | if (ret) | 420 | if (ret) |
307 | goto err_free; | 421 | goto err_free; |
308 | ret = jz_nand_ioremap_resource(pdev, "bank", &nand->bank_mem, | ||
309 | &nand->bank_base); | ||
310 | if (ret) | ||
311 | goto err_iounmap_mmio; | ||
312 | 422 | ||
313 | if (pdata && gpio_is_valid(pdata->busy_gpio)) { | 423 | if (pdata && gpio_is_valid(pdata->busy_gpio)) { |
314 | ret = gpio_request(pdata->busy_gpio, "NAND busy pin"); | 424 | ret = gpio_request(pdata->busy_gpio, "NAND busy pin"); |
@@ -316,7 +426,7 @@ static int __devinit jz_nand_probe(struct platform_device *pdev) | |||
316 | dev_err(&pdev->dev, | 426 | dev_err(&pdev->dev, |
317 | "Failed to request busy gpio %d: %d\n", | 427 | "Failed to request busy gpio %d: %d\n", |
318 | pdata->busy_gpio, ret); | 428 | pdata->busy_gpio, ret); |
319 | goto err_iounmap_mem; | 429 | goto err_iounmap_mmio; |
320 | } | 430 | } |
321 | } | 431 | } |
322 | 432 | ||
@@ -339,22 +449,51 @@ static int __devinit jz_nand_probe(struct platform_device *pdev) | |||
339 | 449 | ||
340 | chip->chip_delay = 50; | 450 | chip->chip_delay = 50; |
341 | chip->cmd_ctrl = jz_nand_cmd_ctrl; | 451 | chip->cmd_ctrl = jz_nand_cmd_ctrl; |
452 | chip->select_chip = jz_nand_select_chip; | ||
342 | 453 | ||
343 | if (pdata && gpio_is_valid(pdata->busy_gpio)) | 454 | if (pdata && gpio_is_valid(pdata->busy_gpio)) |
344 | chip->dev_ready = jz_nand_dev_ready; | 455 | chip->dev_ready = jz_nand_dev_ready; |
345 | 456 | ||
346 | chip->IO_ADDR_R = nand->bank_base; | ||
347 | chip->IO_ADDR_W = nand->bank_base; | ||
348 | |||
349 | nand->pdata = pdata; | 457 | nand->pdata = pdata; |
350 | platform_set_drvdata(pdev, nand); | 458 | platform_set_drvdata(pdev, nand); |
351 | 459 | ||
352 | writel(JZ_NAND_CTRL_ENABLE_CHIP(0), nand->base + JZ_REG_NAND_CTRL); | 460 | /* We are going to autodetect NAND chips in the banks specified in the |
353 | 461 | * platform data. Although nand_scan_ident() can detect multiple chips, | |
354 | ret = nand_scan_ident(mtd, 1, NULL); | 462 | * it requires those chips to be numbered consecuitively, which is not |
355 | if (ret) { | 463 | * always the case for external memory banks. And a fixed chip-to-bank |
356 | dev_err(&pdev->dev, "Failed to scan nand\n"); | 464 | * mapping is not practical either, since for example Dingoo units |
357 | goto err_gpio_free; | 465 | * produced at different times have NAND chips in different banks. |
466 | */ | ||
467 | chipnr = 0; | ||
468 | for (bank_idx = 0; bank_idx < JZ_NAND_NUM_BANKS; bank_idx++) { | ||
469 | unsigned char bank; | ||
470 | |||
471 | /* If there is no platform data, look for NAND in bank 1, | ||
472 | * which is the most likely bank since it is the only one | ||
473 | * that can be booted from. | ||
474 | */ | ||
475 | bank = pdata ? pdata->banks[bank_idx] : bank_idx ^ 1; | ||
476 | if (bank == 0) | ||
477 | break; | ||
478 | if (bank > JZ_NAND_NUM_BANKS) { | ||
479 | dev_warn(&pdev->dev, | ||
480 | "Skipping non-existing bank: %d\n", bank); | ||
481 | continue; | ||
482 | } | ||
483 | /* The detection routine will directly or indirectly call | ||
484 | * jz_nand_select_chip(), so nand->banks has to contain the | ||
485 | * bank we're checking. | ||
486 | */ | ||
487 | nand->banks[chipnr] = bank; | ||
488 | if (jz_nand_detect_bank(pdev, nand, bank, chipnr, | ||
489 | &nand_maf_id, &nand_dev_id) == 0) | ||
490 | chipnr++; | ||
491 | else | ||
492 | nand->banks[chipnr] = 0; | ||
493 | } | ||
494 | if (chipnr == 0) { | ||
495 | dev_err(&pdev->dev, "No NAND chips found\n"); | ||
496 | goto err_gpio_busy; | ||
358 | } | 497 | } |
359 | 498 | ||
360 | if (pdata && pdata->ident_callback) { | 499 | if (pdata && pdata->ident_callback) { |
@@ -364,8 +503,8 @@ static int __devinit jz_nand_probe(struct platform_device *pdev) | |||
364 | 503 | ||
365 | ret = nand_scan_tail(mtd); | 504 | ret = nand_scan_tail(mtd); |
366 | if (ret) { | 505 | if (ret) { |
367 | dev_err(&pdev->dev, "Failed to scan nand\n"); | 506 | dev_err(&pdev->dev, "Failed to scan NAND\n"); |
368 | goto err_gpio_free; | 507 | goto err_unclaim_banks; |
369 | } | 508 | } |
370 | 509 | ||
371 | ret = mtd_device_parse_register(mtd, NULL, NULL, | 510 | ret = mtd_device_parse_register(mtd, NULL, NULL, |
@@ -382,14 +521,21 @@ static int __devinit jz_nand_probe(struct platform_device *pdev) | |||
382 | return 0; | 521 | return 0; |
383 | 522 | ||
384 | err_nand_release: | 523 | err_nand_release: |
385 | nand_release(&nand->mtd); | 524 | nand_release(mtd); |
386 | err_gpio_free: | 525 | err_unclaim_banks: |
526 | while (chipnr--) { | ||
527 | unsigned char bank = nand->banks[chipnr]; | ||
528 | gpio_free(JZ_GPIO_MEM_CS0 + bank - 1); | ||
529 | jz_nand_iounmap_resource(nand->bank_mem[bank - 1], | ||
530 | nand->bank_base[bank - 1]); | ||
531 | } | ||
532 | writel(0, nand->base + JZ_REG_NAND_CTRL); | ||
533 | err_gpio_busy: | ||
534 | if (pdata && gpio_is_valid(pdata->busy_gpio)) | ||
535 | gpio_free(pdata->busy_gpio); | ||
387 | platform_set_drvdata(pdev, NULL); | 536 | platform_set_drvdata(pdev, NULL); |
388 | gpio_free(pdata->busy_gpio); | ||
389 | err_iounmap_mem: | ||
390 | iounmap(nand->bank_base); | ||
391 | err_iounmap_mmio: | 537 | err_iounmap_mmio: |
392 | iounmap(nand->base); | 538 | jz_nand_iounmap_resource(nand->mem, nand->base); |
393 | err_free: | 539 | err_free: |
394 | kfree(nand); | 540 | kfree(nand); |
395 | return ret; | 541 | return ret; |
@@ -398,16 +544,26 @@ err_free: | |||
398 | static int __devexit jz_nand_remove(struct platform_device *pdev) | 544 | static int __devexit jz_nand_remove(struct platform_device *pdev) |
399 | { | 545 | { |
400 | struct jz_nand *nand = platform_get_drvdata(pdev); | 546 | struct jz_nand *nand = platform_get_drvdata(pdev); |
547 | struct jz_nand_platform_data *pdata = pdev->dev.platform_data; | ||
548 | size_t i; | ||
401 | 549 | ||
402 | nand_release(&nand->mtd); | 550 | nand_release(&nand->mtd); |
403 | 551 | ||
404 | /* Deassert and disable all chips */ | 552 | /* Deassert and disable all chips */ |
405 | writel(0, nand->base + JZ_REG_NAND_CTRL); | 553 | writel(0, nand->base + JZ_REG_NAND_CTRL); |
406 | 554 | ||
407 | iounmap(nand->bank_base); | 555 | for (i = 0; i < JZ_NAND_NUM_BANKS; ++i) { |
408 | release_mem_region(nand->bank_mem->start, resource_size(nand->bank_mem)); | 556 | unsigned char bank = nand->banks[i]; |
409 | iounmap(nand->base); | 557 | if (bank != 0) { |
410 | release_mem_region(nand->mem->start, resource_size(nand->mem)); | 558 | jz_nand_iounmap_resource(nand->bank_mem[bank - 1], |
559 | nand->bank_base[bank - 1]); | ||
560 | gpio_free(JZ_GPIO_MEM_CS0 + bank - 1); | ||
561 | } | ||
562 | } | ||
563 | if (pdata && gpio_is_valid(pdata->busy_gpio)) | ||
564 | gpio_free(pdata->busy_gpio); | ||
565 | |||
566 | jz_nand_iounmap_resource(nand->mem, nand->base); | ||
411 | 567 | ||
412 | platform_set_drvdata(pdev, NULL); | 568 | platform_set_drvdata(pdev, NULL); |
413 | kfree(nand); | 569 | kfree(nand); |