summaryrefslogtreecommitdiffstats
path: root/drivers/mmc/host/sdhci.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/mmc/host/sdhci.c')
-rw-r--r--drivers/mmc/host/sdhci.c63
1 files changed, 51 insertions, 12 deletions
diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c
index 2ea429c27714..a78bd4f3aecc 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
@@ -1526,16 +1543,15 @@ static void sdhci_do_set_ios(struct sdhci_host *host, struct mmc_ios *ios)
ctrl_2 = sdhci_readw(host, SDHCI_HOST_CONTROL2);
/* Select Bus Speed Mode for host */
ctrl_2 &= ~SDHCI_CTRL_UHS_MASK;
- if (ios->timing == MMC_TIMING_MMC_HS200)
- ctrl_2 |= SDHCI_CTRL_HS_SDR200;
+ if ((ios->timing == MMC_TIMING_MMC_HS200) ||
+ (ios->timing == MMC_TIMING_UHS_SDR104))
+ ctrl_2 |= SDHCI_CTRL_UHS_SDR104;
else if (ios->timing == MMC_TIMING_UHS_SDR12)
ctrl_2 |= SDHCI_CTRL_UHS_SDR12;
else if (ios->timing == MMC_TIMING_UHS_SDR25)
ctrl_2 |= SDHCI_CTRL_UHS_SDR25;
else if (ios->timing == MMC_TIMING_UHS_SDR50)
ctrl_2 |= SDHCI_CTRL_UHS_SDR50;
- else if (ios->timing == MMC_TIMING_UHS_SDR104)
- ctrl_2 |= SDHCI_CTRL_UHS_SDR104;
else if (ios->timing == MMC_TIMING_UHS_DDR50)
ctrl_2 |= SDHCI_CTRL_UHS_DDR50;
sdhci_writew(host, ctrl_2, SDHCI_HOST_CONTROL2);
@@ -1846,7 +1862,7 @@ static int sdhci_execute_tuning(struct mmc_host *mmc, u32 opcode)
*/
if (((ctrl & SDHCI_CTRL_UHS_MASK) == SDHCI_CTRL_UHS_SDR50) &&
(host->flags & SDHCI_SDR50_NEEDS_TUNING ||
- host->flags & SDHCI_HS200_NEEDS_TUNING))
+ host->flags & SDHCI_SDR104_NEEDS_TUNING))
requires_tuning_nonuhs = true;
if (((ctrl & SDHCI_CTRL_UHS_MASK) == SDHCI_CTRL_UHS_SDR104) ||
@@ -2046,11 +2062,14 @@ static void sdhci_card_event(struct mmc_host *mmc)
struct sdhci_host *host = mmc_priv(mmc);
unsigned long flags;
+ /* First check if client has provided their own card event */
+ if (host->ops->card_event)
+ host->ops->card_event(host);
+
spin_lock_irqsave(&host->lock, flags);
/* Check host->mrq first in case we are runtime suspended */
- if (host->mrq &&
- !(sdhci_readl(host, SDHCI_PRESENT_STATE) & SDHCI_CARD_PRESENT)) {
+ if (host->mrq && !sdhci_do_get_cd(host)) {
pr_err("%s: Card removed during transfer!\n",
mmc_hostname(host->mmc));
pr_err("%s: Resetting controller.\n",
@@ -2625,6 +2644,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;
@@ -2962,9 +2997,13 @@ int sdhci_add_host(struct sdhci_host *host)
mmc->caps |= MMC_CAP_UHS_SDR12 | MMC_CAP_UHS_SDR25;
/* SDR104 supports also implies SDR50 support */
- if (caps[1] & SDHCI_SUPPORT_SDR104)
+ if (caps[1] & SDHCI_SUPPORT_SDR104) {
mmc->caps |= MMC_CAP_UHS_SDR104 | MMC_CAP_UHS_SDR50;
- else if (caps[1] & SDHCI_SUPPORT_SDR50)
+ /* SD3.0: SDR104 is supported so (for eMMC) the caps2
+ * field can be promoted to support HS200.
+ */
+ mmc->caps2 |= MMC_CAP2_HS200;
+ } else if (caps[1] & SDHCI_SUPPORT_SDR50)
mmc->caps |= MMC_CAP_UHS_SDR50;
if (caps[1] & SDHCI_SUPPORT_DDR50)
@@ -2974,9 +3013,9 @@ int sdhci_add_host(struct sdhci_host *host)
if (caps[1] & SDHCI_USE_SDR50_TUNING)
host->flags |= SDHCI_SDR50_NEEDS_TUNING;
- /* Does the host need tuning for HS200? */
+ /* Does the host need tuning for SDR104 / HS200? */
if (mmc->caps2 & MMC_CAP2_HS200)
- host->flags |= SDHCI_HS200_NEEDS_TUNING;
+ host->flags |= SDHCI_SDR104_NEEDS_TUNING;
/* Driver Type(s) (A, C, D) supported by the host */
if (caps[1] & SDHCI_DRIVER_TYPE_A)