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 <[email protected]>
---
 drivers/mmc/host/sdhci.c  | 35 ++++++++++++++++++++++++++++++++++-
 include/linux/mmc/sdhci.h |  2 ++
 2 files changed, 36 insertions(+), 1 deletion(-)

diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c
index 2ea429c..c81c2a2 100644
--- a/drivers/mmc/host/sdhci.c
+++ b/drivers/mmc/host/sdhci.c
@@ -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;
diff --git a/include/linux/mmc/sdhci.h b/include/linux/mmc/sdhci.h
index b838ffc..ba35bdb 100644
--- a/include/linux/mmc/sdhci.h
+++ b/include/linux/mmc/sdhci.h
@@ -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 */
-- 
1.7.11.7

--
To unsubscribe from this list: send the line "unsubscribe linux-mmc" in
the body of a message to [email protected]
More majordomo info at  http://vger.kernel.org/majordomo-info.html

Reply via email to