mmc: mxcmmc: Allow binding a regulator to manage the MMC card voltage
authorAlberto Panizzo <maramaopercheseimorto@gmail.com>
Tue, 2 Nov 2010 00:05:37 +0000 (01:05 +0100)
committerChris Ball <cjb@laptop.org>
Sun, 9 Jan 2011 02:49:06 +0000 (21:49 -0500)
This implementation is based on the pxamci.c driver and it will
be used to support the mx31_3ds machine.

Signed-off-by: Alberto Panizzo <maramaopercheseimorto@gmail.com>
Acked-by: Sascha Hauer <s.hauer@pengutronix.de>
Signed-off-by: Chris Ball <cjb@laptop.org>
drivers/mmc/host/mxcmmc.c

index bdd2cbb87cba7892c068129a5a4d15a09e110757..1a7f48c3c554151841b152a9c5cad0b8efb365d9 100644 (file)
@@ -31,6 +31,7 @@
 #include <linux/clk.h>
 #include <linux/io.h>
 #include <linux/gpio.h>
+#include <linux/regulator/consumer.h>
 
 #include <asm/dma.h>
 #include <asm/irq.h>
@@ -141,10 +142,45 @@ struct mxcmci_host {
 
        struct work_struct      datawork;
        spinlock_t              lock;
+
+       struct regulator        *vcc;
 };
 
 static void mxcmci_set_clk_rate(struct mxcmci_host *host, unsigned int clk_ios);
 
+static inline void mxcmci_init_ocr(struct mxcmci_host *host)
+{
+#ifdef CONFIG_REGULATOR
+       host->vcc = regulator_get(mmc_dev(host->mmc), "vmmc");
+
+       if (IS_ERR(host->vcc)) {
+               host->vcc = NULL;
+       } else {
+               host->mmc->ocr_avail = mmc_regulator_get_ocrmask(host->vcc);
+               if (host->pdata && host->pdata->ocr_avail)
+                       dev_warn(mmc_dev(host->mmc),
+                               "pdata->ocr_avail will not be used\n");
+       }
+#endif
+       if (host->vcc == NULL) {
+               /* fall-back to platform data */
+               if (host->pdata && host->pdata->ocr_avail)
+                       host->mmc->ocr_avail = host->pdata->ocr_avail;
+               else
+                       host->mmc->ocr_avail = MMC_VDD_32_33 | MMC_VDD_33_34;
+       }
+}
+
+static inline void mxcmci_set_power(struct mxcmci_host *host, unsigned int vdd)
+{
+#ifdef CONFIG_REGULATOR
+       if (host->vcc)
+               mmc_regulator_set_ocr(host->vcc, vdd);
+#endif
+       if (host->pdata && host->pdata->setpower)
+               host->pdata->setpower(mmc_dev(host->mmc), vdd);
+}
+
 static inline int mxcmci_use_dma(struct mxcmci_host *host)
 {
        return host->do_dma;
@@ -680,9 +716,9 @@ static void mxcmci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
                host->cmdat &= ~CMD_DAT_CONT_BUS_WIDTH_4;
 
        if (host->power_mode != ios->power_mode) {
-               if (host->pdata && host->pdata->setpower)
-                       host->pdata->setpower(mmc_dev(mmc), ios->vdd);
+               mxcmci_set_power(host, ios->vdd);
                host->power_mode = ios->power_mode;
+
                if (ios->power_mode == MMC_POWER_ON)
                        host->cmdat |= CMD_DAT_CONT_INIT;
        }
@@ -807,10 +843,7 @@ static int mxcmci_probe(struct platform_device *pdev)
        host->pdata = pdev->dev.platform_data;
        spin_lock_init(&host->lock);
 
-       if (host->pdata && host->pdata->ocr_avail)
-               mmc->ocr_avail = host->pdata->ocr_avail;
-       else
-               mmc->ocr_avail = MMC_VDD_32_33 | MMC_VDD_33_34;
+       mxcmci_init_ocr(host);
 
        if (host->pdata && host->pdata->dat3_card_detect)
                host->default_irq_mask =
@@ -915,6 +948,9 @@ static int mxcmci_remove(struct platform_device *pdev)
 
        mmc_remove_host(mmc);
 
+       if (host->vcc)
+               regulator_put(host->vcc);
+
        if (host->pdata && host->pdata->exit)
                host->pdata->exit(&pdev->dev, mmc);