]> www.pilppa.org Git - linux-2.6-omap-h63xx.git/blobdiff - drivers/mmc/host/omap_hsmmc.c
hsmmc: Fix setting the voltage back to 3V
[linux-2.6-omap-h63xx.git] / drivers / mmc / host / omap_hsmmc.c
index a134f76ede2354c71c8bd6beb4907ca3aa47bdc7..a8914801fe824aa1c0eafdcec649c90cc35d7c13 100644 (file)
 #define OMAP_MMC_DATADIR_WRITE 2
 #define MMC_TIMEOUT_MS         20
 #define OMAP_MMC_MASTER_CLOCK  96000000
-#define DRIVER_NAME            "mmci-omap"
+#define DRIVER_NAME            "mmci-omap-hs"
 
 /*
  * One controller can have multiple slots, like on some omap boards using
@@ -192,6 +192,11 @@ static void mmc_omap_idle_timer(unsigned long data)
        mmc_omap_fclk_state(host, OFF);
 }
 
+static void mmc_omap_fclk_lazy_disable(struct mmc_omap_host *host)
+{
+       mod_timer(&host->idle_timer, jiffies + IDLE_TIMEOUT);
+}
+
 /*
  * Stop clock to the card
  */
@@ -289,9 +294,10 @@ mmc_omap_start_command(struct mmc_omap_host *host, struct mmc_command *cmd,
 
        /*
         * Unlike OMAP1 controller, the cmdtype does not seem to be based on
-        * ac, bc, adtc, bcr. Only CMD12 needs a val of 0x3, rest 0x0.
+        * ac, bc, adtc, bcr. Only commands ending an open ended transfer need
+        * a val of 0x3, rest 0x0.
         */
-       if (cmd->opcode == 12)
+       if (cmd == host->mrq->stop)
                cmdtype = 0x3;
 
        cmdreg = (cmd->opcode << 24) | (resptype << 16) | (cmdtype << 22);
@@ -332,7 +338,7 @@ mmc_omap_xfer_done(struct mmc_omap_host *host, struct mmc_data *data)
 
        if (!data->stop) {
                host->mrq = NULL;
-               mod_timer(&host->idle_timer, jiffies + IDLE_TIMEOUT);
+               mmc_omap_fclk_lazy_disable(host);
                mmc_request_done(host->mmc, data->mrq);
                return;
        }
@@ -361,7 +367,7 @@ mmc_omap_cmd_done(struct mmc_omap_host *host, struct mmc_command *cmd)
        }
        if (host->data == NULL || cmd->error) {
                host->mrq = NULL;
-               mod_timer(&host->idle_timer, jiffies + IDLE_TIMEOUT);
+               mmc_omap_fclk_lazy_disable(host);
                mmc_request_done(host->mmc, cmd->mrq);
        }
 }
@@ -524,15 +530,18 @@ static int omap_mmc_switch_opcond(struct mmc_omap_host *host, int vdd)
        /*
         * If a MMC dual voltage card is detected, the set_ios fn calls
         * this fn with VDD bit set for 1.8V. Upon card removal from the
-        * slot, mmc_omap_detect fn sets the VDD back to 3V.
+        * slot, omap_mmc_set_ios sets the VDD back to 3V on MMC_POWER_OFF.
         *
         * Only MMC1 supports 3.0V.  MMC2 will not function if SDVS30 is
         * set in HCTL.
         */
-       if (host->id == OMAP_MMC1_DEVID && (((1 << vdd) == MMC_VDD_32_33) ||
-                               ((1 << vdd) == MMC_VDD_33_34)))
-               reg_val |= SDVS30;
-       if ((1 << vdd) == MMC_VDD_165_195)
+       if (host->id == OMAP_MMC1_DEVID) {
+               if (((1 << vdd) == MMC_VDD_32_33) ||
+                   ((1 << vdd) == MMC_VDD_33_34))
+                       reg_val |= SDVS30;
+               else if ((1 << vdd) == MMC_VDD_165_195)
+                       reg_val |= SDVS18;
+       } else
                reg_val |= SDVS18;
 
        OMAP_HSMMC_WRITE(host->base, HCTL, reg_val);
@@ -558,15 +567,6 @@ static void mmc_omap_detect(struct work_struct *work)
        sysfs_notify(&host->mmc->class_dev.kobj, NULL, "cover_switch");
        mmc_omap_fclk_state(host, ON);
        if (host->carddetect) {
-               if (!(OMAP_HSMMC_READ(host->base, HCTL) & SDVSDET)) {
-                       /*
-                        * Set the VDD back to 3V when the card is removed
-                        * before the set_ios fn turns off the power.
-                        */
-                       vdd = fls(host->mmc->ocr_avail) - 1;
-                       if (omap_mmc_switch_opcond(host, vdd) != 0)
-                               host->mmc->ios.vdd = vdd;
-               }
                mmc_detect_change(host->mmc, (HZ * 200) / 1000);
        } else {
                OMAP_HSMMC_WRITE(host->base, SYSCTL,
@@ -574,7 +574,7 @@ static void mmc_omap_detect(struct work_struct *work)
                while (OMAP_HSMMC_READ(host->base, SYSCTL) & SRD) ;
                mmc_detect_change(host->mmc, (HZ * 50) / 1000);
        }
-       mod_timer(&host->idle_timer, jiffies + IDLE_TIMEOUT);
+       mmc_omap_fclk_lazy_disable(host);
 }
 
 /*
@@ -804,6 +804,17 @@ static void omap_mmc_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
        switch (ios->power_mode) {
        case MMC_POWER_OFF:
                mmc_slot(host).set_power(host->dev, host->slot_id, 0, 0);
+
+               /*
+                * Reset bus voltage to 3V if it got set to 1.8V earlier.
+                * REVISIT: If we are able to detect cards after unplugging
+                * a 1.8V card, this code should not be needed.
+                */
+               regval = OMAP_HSMMC_READ(host->base, HCTL);
+               if (regval & SDVSDET) {
+                       regval &= ~SDVSDET;
+                       OMAP_HSMMC_WRITE(host->base, HCTL, regval);
+               }
                break;
        case MMC_POWER_UP:
                mmc_slot(host).set_power(host->dev, host->slot_id, 1, ios->vdd);
@@ -872,10 +883,7 @@ static void omap_mmc_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
                OMAP_HSMMC_WRITE(host->base, CON,
                                OMAP_HSMMC_READ(host->base, CON) | OD);
 
-       if (ios->power_mode == MMC_POWER_OFF)
-               mmc_omap_fclk_state(host, OFF);
-       else
-               mod_timer(&host->idle_timer, jiffies + IDLE_TIMEOUT);
+       mmc_omap_fclk_lazy_disable(host);
 }
 
 static int omap_hsmmc_get_cd(struct mmc_host *mmc)
@@ -998,7 +1006,7 @@ static int __init omap_mmc_probe(struct platform_device *pdev)
         * MMC can still work without debounce clock.
         */
        if (IS_ERR(host->dbclk))
-               dev_dbg(mmc_dev(host->mmc), "Failed to get debounce clock\n");
+               dev_warn(mmc_dev(host->mmc), "Failed to get debounce clock\n");
        else
                if (clk_enable(host->dbclk) != 0)
                        dev_dbg(mmc_dev(host->mmc), "Enabling debounce"
@@ -1090,7 +1098,7 @@ static int __init omap_mmc_probe(struct platform_device *pdev)
                if (ret < 0)
                        goto err_cover_switch;
        }
-       mod_timer(&host->idle_timer, jiffies + IDLE_TIMEOUT);
+       mmc_omap_fclk_lazy_disable(host);
 
        return 0;
 
@@ -1126,24 +1134,7 @@ static int omap_mmc_remove(struct platform_device *pdev)
 {
        struct mmc_omap_host *host = platform_get_drvdata(pdev);
        struct resource *res;
-       u16 vdd = 0;
-
-       mmc_omap_fclk_state(host, ON);
-       if (!(OMAP_HSMMC_READ(host->base, HCTL) & SDVSDET)) {
-       /*
-        * Set the vdd back to 3V,
-        * applicable for dual volt support.
-        */
-               vdd = fls(host->mmc->ocr_avail) - 1;
-               if (omap_mmc_switch_opcond(host, vdd) != 0)
-                       host->mmc->ios.vdd = vdd;
-       }
-
-       res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
-       if (res)
-               release_mem_region(res->start, res->end - res->start + 1);
 
-       platform_set_drvdata(pdev, NULL);
        if (host) {
                mmc_remove_host(host->mmc);
                if (host->pdata->cleanup)
@@ -1166,6 +1157,11 @@ static int omap_mmc_remove(struct platform_device *pdev)
                iounmap(host->base);
        }
 
+       res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+       if (res)
+               release_mem_region(res->start, res->end - res->start + 1);
+       platform_set_drvdata(pdev, NULL);
+
        return 0;
 }
 
@@ -1179,12 +1175,11 @@ static int omap_mmc_suspend(struct platform_device *pdev, pm_message_t state)
                return 0;
 
        if (host) {
-               mmc_omap_fclk_state(host, ON);
-
                ret = mmc_suspend_host(host->mmc, state);
                if (ret == 0) {
                        host->suspended = 1;
 
+                       mmc_omap_fclk_state(host, ON);
                        OMAP_HSMMC_WRITE(host->base, ISE, 0);
                        OMAP_HSMMC_WRITE(host->base, IE, 0);
 
@@ -1196,18 +1191,8 @@ static int omap_mmc_suspend(struct platform_device *pdev, pm_message_t state)
                                                " level suspend\n");
                        }
 
-                       if (!(OMAP_HSMMC_READ(host->base, HCTL) & SDVSDET)) {
-                               OMAP_HSMMC_WRITE(host->base, HCTL,
-                                       OMAP_HSMMC_READ(host->base, HCTL)
-                                       & SDVSCLR);
-                               OMAP_HSMMC_WRITE(host->base, HCTL,
-                                       OMAP_HSMMC_READ(host->base, HCTL)
-                                       | SDVS30);
-                               OMAP_HSMMC_WRITE(host->base, HCTL,
-                                       OMAP_HSMMC_READ(host->base, HCTL)
-                                       | SDBP);
-                       }
-
+                       OMAP_HSMMC_WRITE(host->base, HCTL,
+                                        OMAP_HSMMC_READ(host->base, HCTL) & ~SDBP);
                        mmc_omap_fclk_state(host, OFF);
                        clk_disable(host->iclk);
                        clk_disable(host->dbclk);
@@ -1227,7 +1212,7 @@ static int omap_mmc_resume(struct platform_device *pdev)
                return 0;
 
        if (host) {
-
+               int i;
                if (mmc_omap_fclk_state(host, ON) != 0)
                        goto clk_en_err;
 
@@ -1242,6 +1227,13 @@ static int omap_mmc_resume(struct platform_device *pdev)
                        dev_dbg(mmc_dev(host->mmc),
                                        "Enabling debounce clk failed\n");
 
+               OMAP_HSMMC_WRITE(host->base, HCTL,
+                                OMAP_HSMMC_READ(host->base, HCTL) | SDBP);
+
+               for (i = 0; i < 100; i++)
+                       if (OMAP_HSMMC_READ(host->base, HCTL) & SDBP)
+                               break;
+
                if (host->pdata->resume) {
                        ret = host->pdata->resume(&pdev->dev, host->slot_id);
                        if (ret)
@@ -1254,7 +1246,7 @@ static int omap_mmc_resume(struct platform_device *pdev)
                if (ret == 0)
                        host->suspended = 0;
 
-               mod_timer(&host->idle_timer, jiffies + IDLE_TIMEOUT);
+               mmc_omap_fclk_lazy_disable(host);
        }
 
        return ret;