Merge git://git.kernel.org/pub/scm/linux/kernel/git/davem/net
[pandora-kernel.git] / drivers / mmc / host / sdhci-esdhc-imx.c
index 710b706..9ebfb4b 100644 (file)
@@ -20,7 +20,9 @@
 #include <linux/mmc/host.h>
 #include <linux/mmc/mmc.h>
 #include <linux/mmc/sdio.h>
-#include <mach/hardware.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/of_gpio.h>
 #include <mach/esdhc.h>
 #include "sdhci-pltfm.h"
 #include "sdhci-esdhc.h"
@@ -29,7 +31,6 @@
 #define SDHCI_VENDOR_SPEC              0xC0
 #define  SDHCI_VENDOR_SPEC_SDIO_QUIRK  0x00000002
 
-#define ESDHC_FLAG_GPIO_FOR_CD         (1 << 0)
 /*
  * The CMDTYPE of the CMD register (offset 0xE) should be set to
  * "11" when the STOP CMD12 is issued on imx53 to abort one
  */
 #define ESDHC_FLAG_MULTIBLK_NO_INT     (1 << 1)
 
+enum imx_esdhc_type {
+       IMX25_ESDHC,
+       IMX35_ESDHC,
+       IMX51_ESDHC,
+       IMX53_ESDHC,
+};
+
 struct pltfm_imx_data {
        int flags;
        u32 scratchpad;
+       enum imx_esdhc_type devtype;
+       struct esdhc_platform_data boarddata;
+};
+
+static struct platform_device_id imx_esdhc_devtype[] = {
+       {
+               .name = "sdhci-esdhc-imx25",
+               .driver_data = IMX25_ESDHC,
+       }, {
+               .name = "sdhci-esdhc-imx35",
+               .driver_data = IMX35_ESDHC,
+       }, {
+               .name = "sdhci-esdhc-imx51",
+               .driver_data = IMX51_ESDHC,
+       }, {
+               .name = "sdhci-esdhc-imx53",
+               .driver_data = IMX53_ESDHC,
+       }, {
+               /* sentinel */
+       }
 };
+MODULE_DEVICE_TABLE(platform, imx_esdhc_devtype);
+
+static const struct of_device_id imx_esdhc_dt_ids[] = {
+       { .compatible = "fsl,imx25-esdhc", .data = &imx_esdhc_devtype[IMX25_ESDHC], },
+       { .compatible = "fsl,imx35-esdhc", .data = &imx_esdhc_devtype[IMX35_ESDHC], },
+       { .compatible = "fsl,imx51-esdhc", .data = &imx_esdhc_devtype[IMX51_ESDHC], },
+       { .compatible = "fsl,imx53-esdhc", .data = &imx_esdhc_devtype[IMX53_ESDHC], },
+       { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, imx_esdhc_dt_ids);
+
+static inline int is_imx25_esdhc(struct pltfm_imx_data *data)
+{
+       return data->devtype == IMX25_ESDHC;
+}
+
+static inline int is_imx35_esdhc(struct pltfm_imx_data *data)
+{
+       return data->devtype == IMX35_ESDHC;
+}
+
+static inline int is_imx51_esdhc(struct pltfm_imx_data *data)
+{
+       return data->devtype == IMX51_ESDHC;
+}
+
+static inline int is_imx53_esdhc(struct pltfm_imx_data *data)
+{
+       return data->devtype == IMX53_ESDHC;
+}
 
 static inline void esdhc_clrset_le(struct sdhci_host *host, u32 mask, u32 val, int reg)
 {
@@ -60,17 +118,14 @@ static u32 esdhc_readl_le(struct sdhci_host *host, int reg)
 {
        struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
        struct pltfm_imx_data *imx_data = pltfm_host->priv;
+       struct esdhc_platform_data *boarddata = &imx_data->boarddata;
 
-       /* fake CARD_PRESENT flag on mx25/35 */
+       /* fake CARD_PRESENT flag */
        u32 val = readl(host->ioaddr + reg);
 
        if (unlikely((reg == SDHCI_PRESENT_STATE)
-                       && (imx_data->flags & ESDHC_FLAG_GPIO_FOR_CD))) {
-               struct esdhc_platform_data *boarddata =
-                               host->mmc->parent->platform_data;
-
-               if (boarddata && gpio_is_valid(boarddata->cd_gpio)
-                               && gpio_get_value(boarddata->cd_gpio))
+                       && gpio_is_valid(boarddata->cd_gpio))) {
+               if (gpio_get_value(boarddata->cd_gpio))
                        /* no card, if a valid gpio says so... */
                        val &= ~SDHCI_CARD_PRESENT;
                else
@@ -85,12 +140,12 @@ static void esdhc_writel_le(struct sdhci_host *host, u32 val, int reg)
 {
        struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
        struct pltfm_imx_data *imx_data = pltfm_host->priv;
+       struct esdhc_platform_data *boarddata = &imx_data->boarddata;
 
        if (unlikely((reg == SDHCI_INT_ENABLE || reg == SDHCI_SIGNAL_ENABLE)
-                       && (imx_data->flags & ESDHC_FLAG_GPIO_FOR_CD)))
+                       && (boarddata->cd_type == ESDHC_CD_GPIO)))
                /*
                 * these interrupts won't work with a custom card_detect gpio
-                * (only applied to mx25/35)
                 */
                val &= ~(SDHCI_INT_CARD_REMOVE | SDHCI_INT_CARD_INSERT);
 
@@ -173,6 +228,17 @@ static void esdhc_writeb_le(struct sdhci_host *host, u8 val, int reg)
                return;
        }
        esdhc_clrset_le(host, 0xff, val, reg);
+
+       /*
+        * The esdhc has a design violation to SDHC spec which tells
+        * that software reset should not affect card detection circuit.
+        * But esdhc clears its SYSCTL register bits [0..2] during the
+        * software reset.  This will stop those clocks that card detection
+        * circuit relies on.  To work around it, we turn the clocks on back
+        * to keep card detection circuit functional.
+        */
+       if ((reg == SDHCI_SOFTWARE_RESET) && (val & 1))
+               esdhc_clrset_le(host, 0x7, 0x7, ESDHC_SYSTEM_CONTROL);
 }
 
 static unsigned int esdhc_pltfm_get_max_clock(struct sdhci_host *host)
@@ -189,6 +255,26 @@ static unsigned int esdhc_pltfm_get_min_clock(struct sdhci_host *host)
        return clk_get_rate(pltfm_host->clk) / 256 / 16;
 }
 
+static unsigned int esdhc_pltfm_get_ro(struct sdhci_host *host)
+{
+       struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
+       struct pltfm_imx_data *imx_data = pltfm_host->priv;
+       struct esdhc_platform_data *boarddata = &imx_data->boarddata;
+
+       switch (boarddata->wp_type) {
+       case ESDHC_WP_GPIO:
+               if (gpio_is_valid(boarddata->wp_gpio))
+                       return gpio_get_value(boarddata->wp_gpio);
+       case ESDHC_WP_CONTROLLER:
+               return !(readl(host->ioaddr + SDHCI_PRESENT_STATE) &
+                              SDHCI_WRITE_PROTECT);
+       case ESDHC_WP_NONE:
+               break;
+       }
+
+       return -ENOSYS;
+}
+
 static struct sdhci_ops sdhci_esdhc_ops = {
        .read_l = esdhc_readl_le,
        .read_w = esdhc_readw_le,
@@ -198,6 +284,7 @@ static struct sdhci_ops sdhci_esdhc_ops = {
        .set_clock = esdhc_set_clock,
        .get_max_clock = esdhc_pltfm_get_max_clock,
        .get_min_clock = esdhc_pltfm_get_min_clock,
+       .get_ro = esdhc_pltfm_get_ro,
 };
 
 static struct sdhci_pltfm_data sdhci_esdhc_imx_pdata = {
@@ -207,17 +294,6 @@ static struct sdhci_pltfm_data sdhci_esdhc_imx_pdata = {
        .ops = &sdhci_esdhc_ops,
 };
 
-static unsigned int esdhc_pltfm_get_ro(struct sdhci_host *host)
-{
-       struct esdhc_platform_data *boarddata =
-                       host->mmc->parent->platform_data;
-
-       if (boarddata && gpio_is_valid(boarddata->wp_gpio))
-               return gpio_get_value(boarddata->wp_gpio);
-       else
-               return -ENOSYS;
-}
-
 static irqreturn_t cd_irq(int irq, void *data)
 {
        struct sdhci_host *sdhost = (struct sdhci_host *)data;
@@ -226,8 +302,48 @@ static irqreturn_t cd_irq(int irq, void *data)
        return IRQ_HANDLED;
 };
 
+#ifdef CONFIG_OF
+static int __devinit
+sdhci_esdhc_imx_probe_dt(struct platform_device *pdev,
+                        struct esdhc_platform_data *boarddata)
+{
+       struct device_node *np = pdev->dev.of_node;
+
+       if (!np)
+               return -ENODEV;
+
+       if (of_get_property(np, "fsl,card-wired", NULL))
+               boarddata->cd_type = ESDHC_CD_PERMANENT;
+
+       if (of_get_property(np, "fsl,cd-controller", NULL))
+               boarddata->cd_type = ESDHC_CD_CONTROLLER;
+
+       if (of_get_property(np, "fsl,wp-controller", NULL))
+               boarddata->wp_type = ESDHC_WP_CONTROLLER;
+
+       boarddata->cd_gpio = of_get_named_gpio(np, "cd-gpios", 0);
+       if (gpio_is_valid(boarddata->cd_gpio))
+               boarddata->cd_type = ESDHC_CD_GPIO;
+
+       boarddata->wp_gpio = of_get_named_gpio(np, "wp-gpios", 0);
+       if (gpio_is_valid(boarddata->wp_gpio))
+               boarddata->wp_type = ESDHC_WP_GPIO;
+
+       return 0;
+}
+#else
+static inline int
+sdhci_esdhc_imx_probe_dt(struct platform_device *pdev,
+                        struct esdhc_platform_data *boarddata)
+{
+       return -ENODEV;
+}
+#endif
+
 static int __devinit sdhci_esdhc_imx_probe(struct platform_device *pdev)
 {
+       const struct of_device_id *of_id =
+                       of_match_device(imx_esdhc_dt_ids, &pdev->dev);
        struct sdhci_pltfm_host *pltfm_host;
        struct sdhci_host *host;
        struct esdhc_platform_data *boarddata;
@@ -242,8 +358,14 @@ static int __devinit sdhci_esdhc_imx_probe(struct platform_device *pdev)
        pltfm_host = sdhci_priv(host);
 
        imx_data = kzalloc(sizeof(struct pltfm_imx_data), GFP_KERNEL);
-       if (!imx_data)
-               return -ENOMEM;
+       if (!imx_data) {
+               err = -ENOMEM;
+               goto err_imx_data;
+       }
+
+       if (of_id)
+               pdev->id_entry = of_id->data;
+       imx_data->devtype = pdev->id_entry->driver_data;
        pltfm_host->priv = imx_data;
 
        clk = clk_get(mmc_dev(host->mmc), NULL);
@@ -255,50 +377,72 @@ static int __devinit sdhci_esdhc_imx_probe(struct platform_device *pdev)
        clk_enable(clk);
        pltfm_host->clk = clk;
 
-       if (!cpu_is_mx25())
+       if (!is_imx25_esdhc(imx_data))
                host->quirks |= SDHCI_QUIRK_BROKEN_TIMEOUT_VAL;
 
-       if (cpu_is_mx25() || cpu_is_mx35()) {
+       if (is_imx25_esdhc(imx_data) || is_imx35_esdhc(imx_data))
                /* Fix errata ENGcm07207 present on i.MX25 and i.MX35 */
                host->quirks |= SDHCI_QUIRK_NO_MULTIBLOCK;
-               /* write_protect can't be routed to controller, use gpio */
-               sdhci_esdhc_ops.get_ro = esdhc_pltfm_get_ro;
-       }
 
-       if (!(cpu_is_mx25() || cpu_is_mx35() || cpu_is_mx51()))
+       if (is_imx53_esdhc(imx_data))
                imx_data->flags |= ESDHC_FLAG_MULTIBLK_NO_INT;
 
-       boarddata = host->mmc->parent->platform_data;
-       if (boarddata) {
+       boarddata = &imx_data->boarddata;
+       if (sdhci_esdhc_imx_probe_dt(pdev, boarddata) < 0) {
+               if (!host->mmc->parent->platform_data) {
+                       dev_err(mmc_dev(host->mmc), "no board data!\n");
+                       err = -EINVAL;
+                       goto no_board_data;
+               }
+               imx_data->boarddata = *((struct esdhc_platform_data *)
+                                       host->mmc->parent->platform_data);
+       }
+
+       /* write_protect */
+       if (boarddata->wp_type == ESDHC_WP_GPIO) {
                err = gpio_request_one(boarddata->wp_gpio, GPIOF_IN, "ESDHC_WP");
                if (err) {
                        dev_warn(mmc_dev(host->mmc),
-                               "no write-protect pin available!\n");
-                       boarddata->wp_gpio = err;
+                                "no write-protect pin available!\n");
+                       boarddata->wp_gpio = -EINVAL;
                }
+       } else {
+               boarddata->wp_gpio = -EINVAL;
+       }
+
+       /* card_detect */
+       if (boarddata->cd_type != ESDHC_CD_GPIO)
+               boarddata->cd_gpio = -EINVAL;
 
+       switch (boarddata->cd_type) {
+       case ESDHC_CD_GPIO:
                err = gpio_request_one(boarddata->cd_gpio, GPIOF_IN, "ESDHC_CD");
                if (err) {
-                       dev_warn(mmc_dev(host->mmc),
+                       dev_err(mmc_dev(host->mmc),
                                "no card-detect pin available!\n");
                        goto no_card_detect_pin;
                }
 
-               /* i.MX5x has issues to be researched */
-               if (!cpu_is_mx25() && !cpu_is_mx35())
-                       goto not_supported;
-
                err = request_irq(gpio_to_irq(boarddata->cd_gpio), cd_irq,
                                 IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING,
                                 mmc_hostname(host->mmc), host);
                if (err) {
-                       dev_warn(mmc_dev(host->mmc), "request irq error\n");
+                       dev_err(mmc_dev(host->mmc), "request irq error\n");
                        goto no_card_detect_irq;
                }
+               /* fall through */
 
-               imx_data->flags |= ESDHC_FLAG_GPIO_FOR_CD;
-               /* Now we have a working card_detect again */
+       case ESDHC_CD_CONTROLLER:
+               /* we have a working card_detect back */
                host->quirks &= ~SDHCI_QUIRK_BROKEN_CARD_DETECTION;
+               break;
+
+       case ESDHC_CD_PERMANENT:
+               host->mmc->caps = MMC_CAP_NONREMOVABLE;
+               break;
+
+       case ESDHC_CD_NONE:
+               break;
        }
 
        err = sdhci_add_host(host);
@@ -307,16 +451,21 @@ static int __devinit sdhci_esdhc_imx_probe(struct platform_device *pdev)
 
        return 0;
 
- no_card_detect_irq:
-       gpio_free(boarddata->cd_gpio);
- no_card_detect_pin:
-       boarddata->cd_gpio = err;
- not_supported:
-       kfree(imx_data);
- err_add_host:
+err_add_host:
+       if (gpio_is_valid(boarddata->cd_gpio))
+               free_irq(gpio_to_irq(boarddata->cd_gpio), host);
+no_card_detect_irq:
+       if (gpio_is_valid(boarddata->cd_gpio))
+               gpio_free(boarddata->cd_gpio);
+       if (gpio_is_valid(boarddata->wp_gpio))
+               gpio_free(boarddata->wp_gpio);
+no_card_detect_pin:
+no_board_data:
        clk_disable(pltfm_host->clk);
        clk_put(pltfm_host->clk);
- err_clk_get:
+err_clk_get:
+       kfree(imx_data);
+err_imx_data:
        sdhci_pltfm_free(pdev);
        return err;
 }
@@ -325,20 +474,18 @@ static int __devexit sdhci_esdhc_imx_remove(struct platform_device *pdev)
 {
        struct sdhci_host *host = platform_get_drvdata(pdev);
        struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
-       struct esdhc_platform_data *boarddata = host->mmc->parent->platform_data;
        struct pltfm_imx_data *imx_data = pltfm_host->priv;
+       struct esdhc_platform_data *boarddata = &imx_data->boarddata;
        int dead = (readl(host->ioaddr + SDHCI_INT_STATUS) == 0xffffffff);
 
        sdhci_remove_host(host, dead);
 
-       if (boarddata && gpio_is_valid(boarddata->wp_gpio))
+       if (gpio_is_valid(boarddata->wp_gpio))
                gpio_free(boarddata->wp_gpio);
 
-       if (boarddata && gpio_is_valid(boarddata->cd_gpio)) {
+       if (gpio_is_valid(boarddata->cd_gpio)) {
+               free_irq(gpio_to_irq(boarddata->cd_gpio), host);
                gpio_free(boarddata->cd_gpio);
-
-               if (!(host->quirks & SDHCI_QUIRK_BROKEN_CARD_DETECTION))
-                       free_irq(gpio_to_irq(boarddata->cd_gpio), host);
        }
 
        clk_disable(pltfm_host->clk);
@@ -354,7 +501,9 @@ static struct platform_driver sdhci_esdhc_imx_driver = {
        .driver         = {
                .name   = "sdhci-esdhc-imx",
                .owner  = THIS_MODULE,
+               .of_match_table = imx_esdhc_dt_ids,
        },
+       .id_table       = imx_esdhc_devtype,
        .probe          = sdhci_esdhc_imx_probe,
        .remove         = __devexit_p(sdhci_esdhc_imx_remove),
 #ifdef CONFIG_PM