Merge git://git.infradead.org/mtd-2.6
[cascardo/linux.git] / drivers / mtd / onenand / omap2.c
index d0894ca..ac31f46 100644 (file)
@@ -35,6 +35,7 @@
 #include <linux/dma-mapping.h>
 #include <linux/io.h>
 #include <linux/slab.h>
+#include <linux/regulator/consumer.h>
 
 #include <asm/mach/flash.h>
 #include <plat/gpmc.h>
@@ -63,8 +64,13 @@ struct omap2_onenand {
        int dma_channel;
        int freq;
        int (*setup)(void __iomem *base, int freq);
+       struct regulator *regulator;
 };
 
+#ifdef CONFIG_MTD_PARTITIONS
+static const char *part_probes[] = { "cmdlinepart", NULL,  };
+#endif
+
 static void omap2_onenand_dma_cb(int lch, u16 ch_status, void *data)
 {
        struct omap2_onenand *c = data;
@@ -108,8 +114,9 @@ static void wait_warn(char *msg, int state, unsigned int ctrl,
 static int omap2_onenand_wait(struct mtd_info *mtd, int state)
 {
        struct omap2_onenand *c = container_of(mtd, struct omap2_onenand, mtd);
+       struct onenand_chip *this = mtd->priv;
        unsigned int intr = 0;
-       unsigned int ctrl;
+       unsigned int ctrl, ctrl_mask;
        unsigned long timeout;
        u32 syscfg;
 
@@ -180,7 +187,8 @@ retry:
                        if (result == 0) {
                                /* Timeout after 20ms */
                                ctrl = read_reg(c, ONENAND_REG_CTRL_STATUS);
-                               if (ctrl & ONENAND_CTRL_ONGO) {
+                               if (ctrl & ONENAND_CTRL_ONGO &&
+                                   !this->ongoing) {
                                        /*
                                         * The operation seems to be still going
                                         * so give it some more time.
@@ -269,7 +277,11 @@ retry:
                return -EIO;
        }
 
-       if (ctrl & 0xFE9F)
+       ctrl_mask = 0xFE9F;
+       if (this->ongoing)
+               ctrl_mask &= ~0x8000;
+
+       if (ctrl & ctrl_mask)
                wait_warn("unexpected controller status", state, ctrl, intr);
 
        return 0;
@@ -591,6 +603,30 @@ static void omap2_onenand_shutdown(struct platform_device *pdev)
        memset((__force void *)c->onenand.base, 0, ONENAND_BUFRAM_SIZE);
 }
 
+static int omap2_onenand_enable(struct mtd_info *mtd)
+{
+       int ret;
+       struct omap2_onenand *c = container_of(mtd, struct omap2_onenand, mtd);
+
+       ret = regulator_enable(c->regulator);
+       if (ret != 0)
+               dev_err(&c->pdev->dev, "cant enable regulator\n");
+
+       return ret;
+}
+
+static int omap2_onenand_disable(struct mtd_info *mtd)
+{
+       int ret;
+       struct omap2_onenand *c = container_of(mtd, struct omap2_onenand, mtd);
+
+       ret = regulator_disable(c->regulator);
+       if (ret != 0)
+               dev_err(&c->pdev->dev, "cant disable regulator\n");
+
+       return ret;
+}
+
 static int __devinit omap2_onenand_probe(struct platform_device *pdev)
 {
        struct omap_onenand_platform_data *pdata;
@@ -705,8 +741,18 @@ static int __devinit omap2_onenand_probe(struct platform_device *pdev)
                }
        }
 
+       if (pdata->regulator_can_sleep) {
+               c->regulator = regulator_get(&pdev->dev, "vonenand");
+               if (IS_ERR(c->regulator)) {
+                       dev_err(&pdev->dev,  "Failed to get regulator\n");
+                       goto err_release_dma;
+               }
+               c->onenand.enable = omap2_onenand_enable;
+               c->onenand.disable = omap2_onenand_disable;
+       }
+
        if ((r = onenand_scan(&c->mtd, 1)) < 0)
-               goto err_release_dma;
+               goto err_release_regulator;
 
        switch ((c->onenand.version_id >> 4) & 0xf) {
        case 0:
@@ -727,13 +773,15 @@ static int __devinit omap2_onenand_probe(struct platform_device *pdev)
        }
 
 #ifdef CONFIG_MTD_PARTITIONS
-       if (pdata->parts != NULL)
-               r = add_mtd_partitions(&c->mtd, pdata->parts,
-                                      pdata->nr_parts);
+       r = parse_mtd_partitions(&c->mtd, part_probes, &c->parts, 0);
+       if (r > 0)
+               r = add_mtd_partitions(&c->mtd, c->parts, r);
+       else if (pdata->parts != NULL)
+               r = add_mtd_partitions(&c->mtd, pdata->parts, pdata->nr_parts);
        else
 #endif
                r = add_mtd_device(&c->mtd);
-       if (r < 0)
+       if (r)
                goto err_release_onenand;
 
        platform_set_drvdata(pdev, c);
@@ -742,6 +790,8 @@ static int __devinit omap2_onenand_probe(struct platform_device *pdev)
 
 err_release_onenand:
        onenand_release(&c->mtd);
+err_release_regulator:
+       regulator_put(c->regulator);
 err_release_dma:
        if (c->dma_channel != -1)
                omap_free_dma(c->dma_channel);
@@ -757,6 +807,7 @@ err_release_mem_region:
 err_free_cs:
        gpmc_cs_free(c->gpmc_cs);
 err_kfree:
+       kfree(c->parts);
        kfree(c);
 
        return r;
@@ -766,18 +817,8 @@ static int __devexit omap2_onenand_remove(struct platform_device *pdev)
 {
        struct omap2_onenand *c = dev_get_drvdata(&pdev->dev);
 
-       BUG_ON(c == NULL);
-
-#ifdef CONFIG_MTD_PARTITIONS
-       if (c->parts)
-               del_mtd_partitions(&c->mtd);
-       else
-               del_mtd_device(&c->mtd);
-#else
-       del_mtd_device(&c->mtd);
-#endif
-
        onenand_release(&c->mtd);
+       regulator_put(c->regulator);
        if (c->dma_channel != -1)
                omap_free_dma(c->dma_channel);
        omap2_onenand_shutdown(pdev);
@@ -789,6 +830,7 @@ static int __devexit omap2_onenand_remove(struct platform_device *pdev)
        iounmap(c->onenand.base);
        release_mem_region(c->phys_base, ONENAND_IO_SIZE);
        gpmc_cs_free(c->gpmc_cs);
+       kfree(c->parts);
        kfree(c);
 
        return 0;