mmc: sdhci: add ability to stay runtime-resumed if the card is powered up
authorAdrian Hunter <adrian.hunter@intel.com>
Mon, 6 May 2013 09:17:32 +0000 (12:17 +0300)
committerChris Ball <cjb@laptop.org>
Sun, 26 May 2013 18:23:23 +0000 (14:23 -0400)
If card power is dependent on SD bus power then the host controller
must not be runtime suspended while the card is powered up.  Add
the ability to stay runtime-resumed in that case and enable it with a new
quirk SDHCI_QUIRK2_CARD_ON_NEEDS_BUS_ON.

Signed-off-by: Adrian Hunter <adrian.hunter@intel.com>
Signed-off-by: Chris Ball <cjb@laptop.org>
drivers/mmc/host/sdhci.c
include/linux/mmc/sdhci.h

index 2ea429c..c81c2a2 100644 (file)
@@ -58,6 +58,8 @@ static void sdhci_enable_preset_value(struct sdhci_host *host, bool enable);
 #ifdef CONFIG_PM_RUNTIME
 static int sdhci_runtime_pm_get(struct sdhci_host *host);
 static int sdhci_runtime_pm_put(struct sdhci_host *host);
+static void sdhci_runtime_pm_bus_on(struct sdhci_host *host);
+static void sdhci_runtime_pm_bus_off(struct sdhci_host *host);
 #else
 static inline int sdhci_runtime_pm_get(struct sdhci_host *host)
 {
@@ -67,6 +69,12 @@ static inline int sdhci_runtime_pm_put(struct sdhci_host *host)
 {
        return 0;
 }
+static void sdhci_runtime_pm_bus_on(struct sdhci_host *host)
+{
+}
+static void sdhci_runtime_pm_bus_off(struct sdhci_host *host)
+{
+}
 #endif
 
 static void sdhci_dumpregs(struct sdhci_host *host)
@@ -192,8 +200,12 @@ static void sdhci_reset(struct sdhci_host *host, u8 mask)
 
        sdhci_writeb(host, mask, SDHCI_SOFTWARE_RESET);
 
-       if (mask & SDHCI_RESET_ALL)
+       if (mask & SDHCI_RESET_ALL) {
                host->clock = 0;
+               /* Reset-all turns off SD Bus Power */
+               if (host->quirks2 & SDHCI_QUIRK2_CARD_ON_NEEDS_BUS_ON)
+                       sdhci_runtime_pm_bus_off(host);
+       }
 
        /* Wait max 100 ms */
        timeout = 100;
@@ -1268,6 +1280,8 @@ static int sdhci_set_power(struct sdhci_host *host, unsigned short power)
 
        if (pwr == 0) {
                sdhci_writeb(host, 0, SDHCI_POWER_CONTROL);
+               if (host->quirks2 & SDHCI_QUIRK2_CARD_ON_NEEDS_BUS_ON)
+                       sdhci_runtime_pm_bus_off(host);
                return 0;
        }
 
@@ -1289,6 +1303,9 @@ static int sdhci_set_power(struct sdhci_host *host, unsigned short power)
 
        sdhci_writeb(host, pwr, SDHCI_POWER_CONTROL);
 
+       if (host->quirks2 & SDHCI_QUIRK2_CARD_ON_NEEDS_BUS_ON)
+               sdhci_runtime_pm_bus_on(host);
+
        /*
         * Some controllers need an extra 10ms delay of 10ms before they
         * can apply clock after applying power
@@ -2625,6 +2642,22 @@ static int sdhci_runtime_pm_put(struct sdhci_host *host)
        return pm_runtime_put_autosuspend(host->mmc->parent);
 }
 
+static void sdhci_runtime_pm_bus_on(struct sdhci_host *host)
+{
+       if (host->runtime_suspended || host->bus_on)
+               return;
+       host->bus_on = true;
+       pm_runtime_get_noresume(host->mmc->parent);
+}
+
+static void sdhci_runtime_pm_bus_off(struct sdhci_host *host)
+{
+       if (host->runtime_suspended || !host->bus_on)
+               return;
+       host->bus_on = false;
+       pm_runtime_put_noidle(host->mmc->parent);
+}
+
 int sdhci_runtime_suspend_host(struct sdhci_host *host)
 {
        unsigned long flags;
index b838ffc..ba35bdb 100644 (file)
@@ -95,6 +95,7 @@ struct sdhci_host {
 /* The system physically doesn't support 1.8v, even if the host does */
 #define SDHCI_QUIRK2_NO_1_8_V                          (1<<2)
 #define SDHCI_QUIRK2_PRESET_VALUE_BROKEN               (1<<3)
+#define SDHCI_QUIRK2_CARD_ON_NEEDS_BUS_ON              (1<<4)
 
        int irq;                /* Device IRQ */
        void __iomem *ioaddr;   /* Mapped address */
@@ -139,6 +140,7 @@ struct sdhci_host {
        u8 pwr;                 /* Current voltage */
 
        bool runtime_suspended; /* Host is runtime suspended */
+       bool bus_on;            /* Bus power prevents runtime suspend */
 
        struct mmc_request *mrq;        /* Current request */
        struct mmc_command *cmd;        /* Current command */