diff options
Diffstat (limited to 'drivers/mtd/nand/atmel_nand.c')
-rw-r--r-- | drivers/mtd/nand/atmel_nand.c | 136 |
1 files changed, 98 insertions, 38 deletions
diff --git a/drivers/mtd/nand/atmel_nand.c b/drivers/mtd/nand/atmel_nand.c index 35b4fb55dbd6..ae7e37d9ac17 100644 --- a/drivers/mtd/nand/atmel_nand.c +++ b/drivers/mtd/nand/atmel_nand.c | |||
@@ -27,6 +27,10 @@ | |||
27 | #include <linux/module.h> | 27 | #include <linux/module.h> |
28 | #include <linux/moduleparam.h> | 28 | #include <linux/moduleparam.h> |
29 | #include <linux/platform_device.h> | 29 | #include <linux/platform_device.h> |
30 | #include <linux/of.h> | ||
31 | #include <linux/of_device.h> | ||
32 | #include <linux/of_gpio.h> | ||
33 | #include <linux/of_mtd.h> | ||
30 | #include <linux/mtd/mtd.h> | 34 | #include <linux/mtd/mtd.h> |
31 | #include <linux/mtd/nand.h> | 35 | #include <linux/mtd/nand.h> |
32 | #include <linux/mtd/partitions.h> | 36 | #include <linux/mtd/partitions.h> |
@@ -34,22 +38,10 @@ | |||
34 | #include <linux/dmaengine.h> | 38 | #include <linux/dmaengine.h> |
35 | #include <linux/gpio.h> | 39 | #include <linux/gpio.h> |
36 | #include <linux/io.h> | 40 | #include <linux/io.h> |
41 | #include <linux/platform_data/atmel.h> | ||
37 | 42 | ||
38 | #include <mach/board.h> | ||
39 | #include <mach/cpu.h> | 43 | #include <mach/cpu.h> |
40 | 44 | ||
41 | #ifdef CONFIG_MTD_NAND_ATMEL_ECC_HW | ||
42 | #define hard_ecc 1 | ||
43 | #else | ||
44 | #define hard_ecc 0 | ||
45 | #endif | ||
46 | |||
47 | #ifdef CONFIG_MTD_NAND_ATMEL_ECC_NONE | ||
48 | #define no_ecc 1 | ||
49 | #else | ||
50 | #define no_ecc 0 | ||
51 | #endif | ||
52 | |||
53 | static int use_dma = 1; | 45 | static int use_dma = 1; |
54 | module_param(use_dma, int, 0); | 46 | module_param(use_dma, int, 0); |
55 | 47 | ||
@@ -95,7 +87,7 @@ struct atmel_nand_host { | |||
95 | struct mtd_info mtd; | 87 | struct mtd_info mtd; |
96 | void __iomem *io_base; | 88 | void __iomem *io_base; |
97 | dma_addr_t io_phys; | 89 | dma_addr_t io_phys; |
98 | struct atmel_nand_data *board; | 90 | struct atmel_nand_data board; |
99 | struct device *dev; | 91 | struct device *dev; |
100 | void __iomem *ecc; | 92 | void __iomem *ecc; |
101 | 93 | ||
@@ -113,8 +105,8 @@ static int cpu_has_dma(void) | |||
113 | */ | 105 | */ |
114 | static void atmel_nand_enable(struct atmel_nand_host *host) | 106 | static void atmel_nand_enable(struct atmel_nand_host *host) |
115 | { | 107 | { |
116 | if (gpio_is_valid(host->board->enable_pin)) | 108 | if (gpio_is_valid(host->board.enable_pin)) |
117 | gpio_set_value(host->board->enable_pin, 0); | 109 | gpio_set_value(host->board.enable_pin, 0); |
118 | } | 110 | } |
119 | 111 | ||
120 | /* | 112 | /* |
@@ -122,8 +114,8 @@ static void atmel_nand_enable(struct atmel_nand_host *host) | |||
122 | */ | 114 | */ |
123 | static void atmel_nand_disable(struct atmel_nand_host *host) | 115 | static void atmel_nand_disable(struct atmel_nand_host *host) |
124 | { | 116 | { |
125 | if (gpio_is_valid(host->board->enable_pin)) | 117 | if (gpio_is_valid(host->board.enable_pin)) |
126 | gpio_set_value(host->board->enable_pin, 1); | 118 | gpio_set_value(host->board.enable_pin, 1); |
127 | } | 119 | } |
128 | 120 | ||
129 | /* | 121 | /* |
@@ -144,9 +136,9 @@ static void atmel_nand_cmd_ctrl(struct mtd_info *mtd, int cmd, unsigned int ctrl | |||
144 | return; | 136 | return; |
145 | 137 | ||
146 | if (ctrl & NAND_CLE) | 138 | if (ctrl & NAND_CLE) |
147 | writeb(cmd, host->io_base + (1 << host->board->cle)); | 139 | writeb(cmd, host->io_base + (1 << host->board.cle)); |
148 | else | 140 | else |
149 | writeb(cmd, host->io_base + (1 << host->board->ale)); | 141 | writeb(cmd, host->io_base + (1 << host->board.ale)); |
150 | } | 142 | } |
151 | 143 | ||
152 | /* | 144 | /* |
@@ -157,8 +149,8 @@ static int atmel_nand_device_ready(struct mtd_info *mtd) | |||
157 | struct nand_chip *nand_chip = mtd->priv; | 149 | struct nand_chip *nand_chip = mtd->priv; |
158 | struct atmel_nand_host *host = nand_chip->priv; | 150 | struct atmel_nand_host *host = nand_chip->priv; |
159 | 151 | ||
160 | return gpio_get_value(host->board->rdy_pin) ^ | 152 | return gpio_get_value(host->board.rdy_pin) ^ |
161 | !!host->board->rdy_pin_active_low; | 153 | !!host->board.rdy_pin_active_low; |
162 | } | 154 | } |
163 | 155 | ||
164 | /* | 156 | /* |
@@ -273,7 +265,7 @@ static void atmel_read_buf(struct mtd_info *mtd, u8 *buf, int len) | |||
273 | if (atmel_nand_dma_op(mtd, buf, len, 1) == 0) | 265 | if (atmel_nand_dma_op(mtd, buf, len, 1) == 0) |
274 | return; | 266 | return; |
275 | 267 | ||
276 | if (host->board->bus_width_16) | 268 | if (host->board.bus_width_16) |
277 | atmel_read_buf16(mtd, buf, len); | 269 | atmel_read_buf16(mtd, buf, len); |
278 | else | 270 | else |
279 | atmel_read_buf8(mtd, buf, len); | 271 | atmel_read_buf8(mtd, buf, len); |
@@ -289,7 +281,7 @@ static void atmel_write_buf(struct mtd_info *mtd, const u8 *buf, int len) | |||
289 | if (atmel_nand_dma_op(mtd, (void *)buf, len, 0) == 0) | 281 | if (atmel_nand_dma_op(mtd, (void *)buf, len, 0) == 0) |
290 | return; | 282 | return; |
291 | 283 | ||
292 | if (host->board->bus_width_16) | 284 | if (host->board.bus_width_16) |
293 | atmel_write_buf16(mtd, buf, len); | 285 | atmel_write_buf16(mtd, buf, len); |
294 | else | 286 | else |
295 | atmel_write_buf8(mtd, buf, len); | 287 | atmel_write_buf8(mtd, buf, len); |
@@ -481,6 +473,56 @@ static void atmel_nand_hwctl(struct mtd_info *mtd, int mode) | |||
481 | } | 473 | } |
482 | } | 474 | } |
483 | 475 | ||
476 | #if defined(CONFIG_OF) | ||
477 | static int __devinit atmel_of_init_port(struct atmel_nand_host *host, | ||
478 | struct device_node *np) | ||
479 | { | ||
480 | u32 val; | ||
481 | int ecc_mode; | ||
482 | struct atmel_nand_data *board = &host->board; | ||
483 | enum of_gpio_flags flags; | ||
484 | |||
485 | if (of_property_read_u32(np, "atmel,nand-addr-offset", &val) == 0) { | ||
486 | if (val >= 32) { | ||
487 | dev_err(host->dev, "invalid addr-offset %u\n", val); | ||
488 | return -EINVAL; | ||
489 | } | ||
490 | board->ale = val; | ||
491 | } | ||
492 | |||
493 | if (of_property_read_u32(np, "atmel,nand-cmd-offset", &val) == 0) { | ||
494 | if (val >= 32) { | ||
495 | dev_err(host->dev, "invalid cmd-offset %u\n", val); | ||
496 | return -EINVAL; | ||
497 | } | ||
498 | board->cle = val; | ||
499 | } | ||
500 | |||
501 | ecc_mode = of_get_nand_ecc_mode(np); | ||
502 | |||
503 | board->ecc_mode = ecc_mode < 0 ? NAND_ECC_SOFT : ecc_mode; | ||
504 | |||
505 | board->on_flash_bbt = of_get_nand_on_flash_bbt(np); | ||
506 | |||
507 | if (of_get_nand_bus_width(np) == 16) | ||
508 | board->bus_width_16 = 1; | ||
509 | |||
510 | board->rdy_pin = of_get_gpio_flags(np, 0, &flags); | ||
511 | board->rdy_pin_active_low = (flags == OF_GPIO_ACTIVE_LOW); | ||
512 | |||
513 | board->enable_pin = of_get_gpio(np, 1); | ||
514 | board->det_pin = of_get_gpio(np, 2); | ||
515 | |||
516 | return 0; | ||
517 | } | ||
518 | #else | ||
519 | static int __devinit atmel_of_init_port(struct atmel_nand_host *host, | ||
520 | struct device_node *np) | ||
521 | { | ||
522 | return -EINVAL; | ||
523 | } | ||
524 | #endif | ||
525 | |||
484 | /* | 526 | /* |
485 | * Probe for the NAND device. | 527 | * Probe for the NAND device. |
486 | */ | 528 | */ |
@@ -491,6 +533,7 @@ static int __init atmel_nand_probe(struct platform_device *pdev) | |||
491 | struct nand_chip *nand_chip; | 533 | struct nand_chip *nand_chip; |
492 | struct resource *regs; | 534 | struct resource *regs; |
493 | struct resource *mem; | 535 | struct resource *mem; |
536 | struct mtd_part_parser_data ppdata = {}; | ||
494 | int res; | 537 | int res; |
495 | 538 | ||
496 | mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); | 539 | mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); |
@@ -517,8 +560,15 @@ static int __init atmel_nand_probe(struct platform_device *pdev) | |||
517 | 560 | ||
518 | mtd = &host->mtd; | 561 | mtd = &host->mtd; |
519 | nand_chip = &host->nand_chip; | 562 | nand_chip = &host->nand_chip; |
520 | host->board = pdev->dev.platform_data; | ||
521 | host->dev = &pdev->dev; | 563 | host->dev = &pdev->dev; |
564 | if (pdev->dev.of_node) { | ||
565 | res = atmel_of_init_port(host, pdev->dev.of_node); | ||
566 | if (res) | ||
567 | goto err_nand_ioremap; | ||
568 | } else { | ||
569 | memcpy(&host->board, pdev->dev.platform_data, | ||
570 | sizeof(struct atmel_nand_data)); | ||
571 | } | ||
522 | 572 | ||
523 | nand_chip->priv = host; /* link the private data structures */ | 573 | nand_chip->priv = host; /* link the private data structures */ |
524 | mtd->priv = nand_chip; | 574 | mtd->priv = nand_chip; |
@@ -529,26 +579,25 @@ static int __init atmel_nand_probe(struct platform_device *pdev) | |||
529 | nand_chip->IO_ADDR_W = host->io_base; | 579 | nand_chip->IO_ADDR_W = host->io_base; |
530 | nand_chip->cmd_ctrl = atmel_nand_cmd_ctrl; | 580 | nand_chip->cmd_ctrl = atmel_nand_cmd_ctrl; |
531 | 581 | ||
532 | if (gpio_is_valid(host->board->rdy_pin)) | 582 | if (gpio_is_valid(host->board.rdy_pin)) |
533 | nand_chip->dev_ready = atmel_nand_device_ready; | 583 | nand_chip->dev_ready = atmel_nand_device_ready; |
534 | 584 | ||
585 | nand_chip->ecc.mode = host->board.ecc_mode; | ||
586 | |||
535 | regs = platform_get_resource(pdev, IORESOURCE_MEM, 1); | 587 | regs = platform_get_resource(pdev, IORESOURCE_MEM, 1); |
536 | if (!regs && hard_ecc) { | 588 | if (!regs && nand_chip->ecc.mode == NAND_ECC_HW) { |
537 | printk(KERN_ERR "atmel_nand: can't get I/O resource " | 589 | printk(KERN_ERR "atmel_nand: can't get I/O resource " |
538 | "regs\nFalling back on software ECC\n"); | 590 | "regs\nFalling back on software ECC\n"); |
591 | nand_chip->ecc.mode = NAND_ECC_SOFT; | ||
539 | } | 592 | } |
540 | 593 | ||
541 | nand_chip->ecc.mode = NAND_ECC_SOFT; /* enable ECC */ | 594 | if (nand_chip->ecc.mode == NAND_ECC_HW) { |
542 | if (no_ecc) | ||
543 | nand_chip->ecc.mode = NAND_ECC_NONE; | ||
544 | if (hard_ecc && regs) { | ||
545 | host->ecc = ioremap(regs->start, resource_size(regs)); | 595 | host->ecc = ioremap(regs->start, resource_size(regs)); |
546 | if (host->ecc == NULL) { | 596 | if (host->ecc == NULL) { |
547 | printk(KERN_ERR "atmel_nand: ioremap failed\n"); | 597 | printk(KERN_ERR "atmel_nand: ioremap failed\n"); |
548 | res = -EIO; | 598 | res = -EIO; |
549 | goto err_ecc_ioremap; | 599 | goto err_ecc_ioremap; |
550 | } | 600 | } |
551 | nand_chip->ecc.mode = NAND_ECC_HW; | ||
552 | nand_chip->ecc.calculate = atmel_nand_calculate; | 601 | nand_chip->ecc.calculate = atmel_nand_calculate; |
553 | nand_chip->ecc.correct = atmel_nand_correct; | 602 | nand_chip->ecc.correct = atmel_nand_correct; |
554 | nand_chip->ecc.hwctl = atmel_nand_hwctl; | 603 | nand_chip->ecc.hwctl = atmel_nand_hwctl; |
@@ -558,7 +607,7 @@ static int __init atmel_nand_probe(struct platform_device *pdev) | |||
558 | 607 | ||
559 | nand_chip->chip_delay = 20; /* 20us command delay time */ | 608 | nand_chip->chip_delay = 20; /* 20us command delay time */ |
560 | 609 | ||
561 | if (host->board->bus_width_16) /* 16-bit bus width */ | 610 | if (host->board.bus_width_16) /* 16-bit bus width */ |
562 | nand_chip->options |= NAND_BUSWIDTH_16; | 611 | nand_chip->options |= NAND_BUSWIDTH_16; |
563 | 612 | ||
564 | nand_chip->read_buf = atmel_read_buf; | 613 | nand_chip->read_buf = atmel_read_buf; |
@@ -567,15 +616,15 @@ static int __init atmel_nand_probe(struct platform_device *pdev) | |||
567 | platform_set_drvdata(pdev, host); | 616 | platform_set_drvdata(pdev, host); |
568 | atmel_nand_enable(host); | 617 | atmel_nand_enable(host); |
569 | 618 | ||
570 | if (gpio_is_valid(host->board->det_pin)) { | 619 | if (gpio_is_valid(host->board.det_pin)) { |
571 | if (gpio_get_value(host->board->det_pin)) { | 620 | if (gpio_get_value(host->board.det_pin)) { |
572 | printk(KERN_INFO "No SmartMedia card inserted.\n"); | 621 | printk(KERN_INFO "No SmartMedia card inserted.\n"); |
573 | res = -ENXIO; | 622 | res = -ENXIO; |
574 | goto err_no_card; | 623 | goto err_no_card; |
575 | } | 624 | } |
576 | } | 625 | } |
577 | 626 | ||
578 | if (on_flash_bbt) { | 627 | if (host->board.on_flash_bbt || on_flash_bbt) { |
579 | printk(KERN_INFO "atmel_nand: Use On Flash BBT\n"); | 628 | printk(KERN_INFO "atmel_nand: Use On Flash BBT\n"); |
580 | nand_chip->bbt_options |= NAND_BBT_USE_FLASH; | 629 | nand_chip->bbt_options |= NAND_BBT_USE_FLASH; |
581 | } | 630 | } |
@@ -650,8 +699,9 @@ static int __init atmel_nand_probe(struct platform_device *pdev) | |||
650 | } | 699 | } |
651 | 700 | ||
652 | mtd->name = "atmel_nand"; | 701 | mtd->name = "atmel_nand"; |
653 | res = mtd_device_parse_register(mtd, NULL, 0, | 702 | ppdata.of_node = pdev->dev.of_node; |
654 | host->board->parts, host->board->num_parts); | 703 | res = mtd_device_parse_register(mtd, NULL, &ppdata, |
704 | host->board.parts, host->board.num_parts); | ||
655 | if (!res) | 705 | if (!res) |
656 | return res; | 706 | return res; |
657 | 707 | ||
@@ -695,11 +745,21 @@ static int __exit atmel_nand_remove(struct platform_device *pdev) | |||
695 | return 0; | 745 | return 0; |
696 | } | 746 | } |
697 | 747 | ||
748 | #if defined(CONFIG_OF) | ||
749 | static const struct of_device_id atmel_nand_dt_ids[] = { | ||
750 | { .compatible = "atmel,at91rm9200-nand" }, | ||
751 | { /* sentinel */ } | ||
752 | }; | ||
753 | |||
754 | MODULE_DEVICE_TABLE(of, atmel_nand_dt_ids); | ||
755 | #endif | ||
756 | |||
698 | static struct platform_driver atmel_nand_driver = { | 757 | static struct platform_driver atmel_nand_driver = { |
699 | .remove = __exit_p(atmel_nand_remove), | 758 | .remove = __exit_p(atmel_nand_remove), |
700 | .driver = { | 759 | .driver = { |
701 | .name = "atmel_nand", | 760 | .name = "atmel_nand", |
702 | .owner = THIS_MODULE, | 761 | .owner = THIS_MODULE, |
762 | .of_match_table = of_match_ptr(atmel_nand_dt_ids), | ||
703 | }, | 763 | }, |
704 | }; | 764 | }; |
705 | 765 | ||