[PATCH 1/3] mmc: sdhci: add ability to stay runtime-resumed if the card is powered up

Adrian Hunter
2013-05-06T09:12:40Z

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>
---
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