]> err.no Git - linux-2.6/blobdiff - drivers/mtd/nand/atmel_nand.c
Merge branch 'master' of git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux-2.6
[linux-2.6] / drivers / mtd / nand / atmel_nand.c
index 675a82ca77f587b8f85a841898f46f7c607aa9b9..3387e0d5076b06bad1af8506d73f673db99a1d2a 100644 (file)
 #include <linux/mtd/nand.h>
 #include <linux/mtd/partitions.h>
 
-#include <asm/gpio.h>
-#include <asm/io.h>
+#include <linux/gpio.h>
+#include <linux/io.h>
 
-#include <asm/arch/board.h>
+#include <mach/board.h>
+#include <mach/cpu.h>
 
 #ifdef CONFIG_MTD_NAND_ATMEL_ECC_HW
 #define hard_ecc       1
@@ -141,6 +142,37 @@ static int atmel_nand_device_ready(struct mtd_info *mtd)
        return gpio_get_value(host->board->rdy_pin);
 }
 
+/*
+ * Minimal-overhead PIO for data access.
+ */
+static void atmel_read_buf(struct mtd_info *mtd, u8 *buf, int len)
+{
+       struct nand_chip        *nand_chip = mtd->priv;
+
+       __raw_readsb(nand_chip->IO_ADDR_R, buf, len);
+}
+
+static void atmel_read_buf16(struct mtd_info *mtd, u8 *buf, int len)
+{
+       struct nand_chip        *nand_chip = mtd->priv;
+
+       __raw_readsw(nand_chip->IO_ADDR_R, buf, len / 2);
+}
+
+static void atmel_write_buf(struct mtd_info *mtd, const u8 *buf, int len)
+{
+       struct nand_chip        *nand_chip = mtd->priv;
+
+       __raw_writesb(nand_chip->IO_ADDR_W, buf, len);
+}
+
+static void atmel_write_buf16(struct mtd_info *mtd, const u8 *buf, int len)
+{
+       struct nand_chip        *nand_chip = mtd->priv;
+
+       __raw_writesw(nand_chip->IO_ADDR_W, buf, len / 2);
+}
+
 /*
  * write oob for small pages
  */
@@ -233,6 +265,19 @@ static int atmel_nand_read_page(struct mtd_info *mtd,
        uint8_t *ecc_pos;
        int stat;
 
+       /*
+        * Errata: ALE is incorrectly wired up to the ECC controller
+        * on the AP7000, so it will include the address cycles in the
+        * ECC calculation.
+        *
+        * Workaround: Reset the parity registers before reading the
+        * actual data.
+        */
+       if (cpu_is_at32ap7000()) {
+               struct atmel_nand_host *host = chip->priv;
+               ecc_writel(host->ecc, CR, ATMEL_ECC_RST);
+       }
+
        /* read the page */
        chip->read_buf(mtd, p, eccsize);
 
@@ -346,9 +391,16 @@ static int atmel_nand_correct(struct mtd_info *mtd, u_char *dat,
 }
 
 /*
- * Enable HW ECC : unsused
+ * Enable HW ECC : unused on most chips
  */
-static void atmel_nand_hwctl(struct mtd_info *mtd, int mode) { ; }
+static void atmel_nand_hwctl(struct mtd_info *mtd, int mode)
+{
+       if (cpu_is_at32ap7000()) {
+               struct nand_chip *nand_chip = mtd->priv;
+               struct atmel_nand_host *host = nand_chip->priv;
+               ecc_writel(host->ecc, CR, ATMEL_ECC_RST);
+       }
+}
 
 #ifdef CONFIG_MTD_PARTITIONS
 static const char *part_probes[] = { "cmdlinepart", NULL };
@@ -371,6 +423,12 @@ static int __init atmel_nand_probe(struct platform_device *pdev)
        int num_partitions = 0;
 #endif
 
+       mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+       if (!mem) {
+               printk(KERN_ERR "atmel_nand: can't get I/O resource mem\n");
+               return -ENXIO;
+       }
+
        /* Allocate memory for the device structure (and zero it) */
        host = kzalloc(sizeof(struct atmel_nand_host), GFP_KERNEL);
        if (!host) {
@@ -378,17 +436,11 @@ static int __init atmel_nand_probe(struct platform_device *pdev)
                return -ENOMEM;
        }
 
-       mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
-       if (!mem) {
-               printk(KERN_ERR "atmel_nand: can't get I/O resource mem\n");
-               return -ENXIO;
-       }
-
        host->io_base = ioremap(mem->start, mem->end - mem->start + 1);
        if (host->io_base == NULL) {
                printk(KERN_ERR "atmel_nand: ioremap failed\n");
-               kfree(host);
-               return -EIO;
+               res = -EIO;
+               goto err_nand_ioremap;
        }
 
        mtd = &host->mtd;
@@ -436,24 +488,30 @@ static int __init atmel_nand_probe(struct platform_device *pdev)
 
        nand_chip->chip_delay = 20;             /* 20us command delay time */
 
-       if (host->board->bus_width_16)          /* 16-bit bus width */
+       if (host->board->bus_width_16) {        /* 16-bit bus width */
                nand_chip->options |= NAND_BUSWIDTH_16;
+               nand_chip->read_buf = atmel_read_buf16;
+               nand_chip->write_buf = atmel_write_buf16;
+       } else {
+               nand_chip->read_buf = atmel_read_buf;
+               nand_chip->write_buf = atmel_write_buf;
+       }
 
        platform_set_drvdata(pdev, host);
        atmel_nand_enable(host);
 
        if (host->board->det_pin) {
                if (gpio_get_value(host->board->det_pin)) {
-                       printk ("No SmartMedia card inserted.\n");
+                       printk("No SmartMedia card inserted.\n");
                        res = ENXIO;
-                       goto out;
+                       goto err_no_card;
                }
        }
 
        /* first scan to find the device and get the page size */
        if (nand_scan_ident(mtd, 1)) {
                res = -ENXIO;
-               goto out;
+               goto err_scan_ident;
        }
 
        if (nand_chip->ecc.mode == NAND_ECC_HW_SYNDROME) {
@@ -498,7 +556,7 @@ static int __init atmel_nand_probe(struct platform_device *pdev)
        /* second phase scan */
        if (nand_scan_tail(mtd)) {
                res = -ENXIO;
-               goto out;
+               goto err_scan_tail;
        }
 
 #ifdef CONFIG_MTD_PARTITIONS
@@ -514,7 +572,7 @@ static int __init atmel_nand_probe(struct platform_device *pdev)
        if ((!partitions) || (num_partitions == 0)) {
                printk(KERN_ERR "atmel_nand: No parititions defined, or unsupported device.\n");
                res = ENXIO;
-               goto release;
+               goto err_no_partitions;
        }
 
        res = add_mtd_partitions(mtd, partitions, num_partitions);
@@ -526,17 +584,19 @@ static int __init atmel_nand_probe(struct platform_device *pdev)
                return res;
 
 #ifdef CONFIG_MTD_PARTITIONS
-release:
+err_no_partitions:
 #endif
        nand_release(mtd);
-
-out:
-       iounmap(host->ecc);
-
-err_ecc_ioremap:
+err_scan_tail:
+err_scan_ident:
+err_no_card:
        atmel_nand_disable(host);
        platform_set_drvdata(pdev, NULL);
+       if (host->ecc)
+               iounmap(host->ecc);
+err_ecc_ioremap:
        iounmap(host->io_base);
+err_nand_ioremap:
        kfree(host);
        return res;
 }
@@ -544,7 +604,7 @@ err_ecc_ioremap:
 /*
  * Remove a NAND device.
  */
-static int __devexit atmel_nand_remove(struct platform_device *pdev)
+static int __exit atmel_nand_remove(struct platform_device *pdev)
 {
        struct atmel_nand_host *host = platform_get_drvdata(pdev);
        struct mtd_info *mtd = &host->mtd;
@@ -553,16 +613,16 @@ static int __devexit atmel_nand_remove(struct platform_device *pdev)
 
        atmel_nand_disable(host);
 
+       if (host->ecc)
+               iounmap(host->ecc);
        iounmap(host->io_base);
-       iounmap(host->ecc);
        kfree(host);
 
        return 0;
 }
 
 static struct platform_driver atmel_nand_driver = {
-       .probe          = atmel_nand_probe,
-       .remove         = atmel_nand_remove,
+       .remove         = __exit_p(atmel_nand_remove),
        .driver         = {
                .name   = "atmel_nand",
                .owner  = THIS_MODULE,
@@ -571,7 +631,7 @@ static struct platform_driver atmel_nand_driver = {
 
 static int __init atmel_nand_init(void)
 {
-       return platform_driver_register(&atmel_nand_driver);
+       return platform_driver_probe(&atmel_nand_driver, atmel_nand_probe);
 }