Commit c981cdfb authored by Adrian Hunter's avatar Adrian Hunter Committed by Ulf Hansson

mmc: sdhci: Fix voltage switch delay

Commit 20b92a30 ("mmc: sdhci: update signal voltage switch code")
removed voltage switch delays from sdhci because mmc core had been
enhanced to support them. However that assumed that sdhci_set_ios()
did a single clock change, which it did not, and so the delays in mmc
core, which should have come after the first clock change, were not
effective.

Fix by avoiding re-configuring UHS and preset settings when the clock
is turning on and the settings have not changed. That then also avoids
the associated clock changes, so that then sdhci_set_ios() does a single
clock change when voltage switching, and the mmc core delays become
effective.

To do that has meant keeping track of driver strength (host->drv_type),
and cases of reinitialization (host->reinit_uhs).

Note also, the 'turning_on_clk' restriction should not be necessary
but is done to minimize the impact of the change on stable kernels.

Fixes: 20b92a30 ("mmc: sdhci: update signal voltage switch code")
Cc: stable@vger.kernel.org
Signed-off-by: default avatarAdrian Hunter <adrian.hunter@intel.com>
Link: https://lore.kernel.org/r/20221128133259.38305-2-adrian.hunter@intel.comSigned-off-by: default avatarUlf Hansson <ulf.hansson@linaro.org>
parent c61bfb1c
...@@ -373,6 +373,7 @@ static void sdhci_init(struct sdhci_host *host, int soft) ...@@ -373,6 +373,7 @@ static void sdhci_init(struct sdhci_host *host, int soft)
if (soft) { if (soft) {
/* force clock reconfiguration */ /* force clock reconfiguration */
host->clock = 0; host->clock = 0;
host->reinit_uhs = true;
mmc->ops->set_ios(mmc, &mmc->ios); mmc->ops->set_ios(mmc, &mmc->ios);
} }
} }
...@@ -2293,11 +2294,46 @@ void sdhci_set_uhs_signaling(struct sdhci_host *host, unsigned timing) ...@@ -2293,11 +2294,46 @@ void sdhci_set_uhs_signaling(struct sdhci_host *host, unsigned timing)
} }
EXPORT_SYMBOL_GPL(sdhci_set_uhs_signaling); EXPORT_SYMBOL_GPL(sdhci_set_uhs_signaling);
static bool sdhci_timing_has_preset(unsigned char timing)
{
switch (timing) {
case MMC_TIMING_UHS_SDR12:
case MMC_TIMING_UHS_SDR25:
case MMC_TIMING_UHS_SDR50:
case MMC_TIMING_UHS_SDR104:
case MMC_TIMING_UHS_DDR50:
case MMC_TIMING_MMC_DDR52:
return true;
};
return false;
}
static bool sdhci_preset_needed(struct sdhci_host *host, unsigned char timing)
{
return !(host->quirks2 & SDHCI_QUIRK2_PRESET_VALUE_BROKEN) &&
sdhci_timing_has_preset(timing);
}
static bool sdhci_presetable_values_change(struct sdhci_host *host, struct mmc_ios *ios)
{
/*
* Preset Values are: Driver Strength, Clock Generator and SDCLK/RCLK
* Frequency. Check if preset values need to be enabled, or the Driver
* Strength needs updating. Note, clock changes are handled separately.
*/
return !host->preset_enabled &&
(sdhci_preset_needed(host, ios->timing) || host->drv_type != ios->drv_type);
}
void sdhci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) void sdhci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
{ {
struct sdhci_host *host = mmc_priv(mmc); struct sdhci_host *host = mmc_priv(mmc);
bool reinit_uhs = host->reinit_uhs;
bool turning_on_clk = false;
u8 ctrl; u8 ctrl;
host->reinit_uhs = false;
if (ios->power_mode == MMC_POWER_UNDEFINED) if (ios->power_mode == MMC_POWER_UNDEFINED)
return; return;
...@@ -2323,6 +2359,8 @@ void sdhci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) ...@@ -2323,6 +2359,8 @@ void sdhci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
sdhci_enable_preset_value(host, false); sdhci_enable_preset_value(host, false);
if (!ios->clock || ios->clock != host->clock) { if (!ios->clock || ios->clock != host->clock) {
turning_on_clk = ios->clock && !host->clock;
host->ops->set_clock(host, ios->clock); host->ops->set_clock(host, ios->clock);
host->clock = ios->clock; host->clock = ios->clock;
...@@ -2349,6 +2387,17 @@ void sdhci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) ...@@ -2349,6 +2387,17 @@ void sdhci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
host->ops->set_bus_width(host, ios->bus_width); host->ops->set_bus_width(host, ios->bus_width);
/*
* Special case to avoid multiple clock changes during voltage
* switching.
*/
if (!reinit_uhs &&
turning_on_clk &&
host->timing == ios->timing &&
host->version >= SDHCI_SPEC_300 &&
!sdhci_presetable_values_change(host, ios))
return;
ctrl = sdhci_readb(host, SDHCI_HOST_CONTROL); ctrl = sdhci_readb(host, SDHCI_HOST_CONTROL);
if (!(host->quirks & SDHCI_QUIRK_NO_HISPD_BIT)) { if (!(host->quirks & SDHCI_QUIRK_NO_HISPD_BIT)) {
...@@ -2392,6 +2441,7 @@ void sdhci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) ...@@ -2392,6 +2441,7 @@ void sdhci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
} }
sdhci_writew(host, ctrl_2, SDHCI_HOST_CONTROL2); sdhci_writew(host, ctrl_2, SDHCI_HOST_CONTROL2);
host->drv_type = ios->drv_type;
} else { } else {
/* /*
* According to SDHC Spec v3.00, if the Preset Value * According to SDHC Spec v3.00, if the Preset Value
...@@ -2419,19 +2469,14 @@ void sdhci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) ...@@ -2419,19 +2469,14 @@ void sdhci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
host->ops->set_uhs_signaling(host, ios->timing); host->ops->set_uhs_signaling(host, ios->timing);
host->timing = ios->timing; host->timing = ios->timing;
if (!(host->quirks2 & SDHCI_QUIRK2_PRESET_VALUE_BROKEN) && if (sdhci_preset_needed(host, ios->timing)) {
((ios->timing == MMC_TIMING_UHS_SDR12) ||
(ios->timing == MMC_TIMING_UHS_SDR25) ||
(ios->timing == MMC_TIMING_UHS_SDR50) ||
(ios->timing == MMC_TIMING_UHS_SDR104) ||
(ios->timing == MMC_TIMING_UHS_DDR50) ||
(ios->timing == MMC_TIMING_MMC_DDR52))) {
u16 preset; u16 preset;
sdhci_enable_preset_value(host, true); sdhci_enable_preset_value(host, true);
preset = sdhci_get_preset_value(host); preset = sdhci_get_preset_value(host);
ios->drv_type = FIELD_GET(SDHCI_PRESET_DRV_MASK, ios->drv_type = FIELD_GET(SDHCI_PRESET_DRV_MASK,
preset); preset);
host->drv_type = ios->drv_type;
} }
/* Re-enable SD Clock */ /* Re-enable SD Clock */
...@@ -3768,6 +3813,7 @@ int sdhci_resume_host(struct sdhci_host *host) ...@@ -3768,6 +3813,7 @@ int sdhci_resume_host(struct sdhci_host *host)
sdhci_init(host, 0); sdhci_init(host, 0);
host->pwr = 0; host->pwr = 0;
host->clock = 0; host->clock = 0;
host->reinit_uhs = true;
mmc->ops->set_ios(mmc, &mmc->ios); mmc->ops->set_ios(mmc, &mmc->ios);
} else { } else {
sdhci_init(host, (mmc->pm_flags & MMC_PM_KEEP_POWER)); sdhci_init(host, (mmc->pm_flags & MMC_PM_KEEP_POWER));
...@@ -3830,6 +3876,7 @@ int sdhci_runtime_resume_host(struct sdhci_host *host, int soft_reset) ...@@ -3830,6 +3876,7 @@ int sdhci_runtime_resume_host(struct sdhci_host *host, int soft_reset)
/* Force clock and power re-program */ /* Force clock and power re-program */
host->pwr = 0; host->pwr = 0;
host->clock = 0; host->clock = 0;
host->reinit_uhs = true;
mmc->ops->start_signal_voltage_switch(mmc, &mmc->ios); mmc->ops->start_signal_voltage_switch(mmc, &mmc->ios);
mmc->ops->set_ios(mmc, &mmc->ios); mmc->ops->set_ios(mmc, &mmc->ios);
......
...@@ -524,6 +524,8 @@ struct sdhci_host { ...@@ -524,6 +524,8 @@ struct sdhci_host {
unsigned int clock; /* Current clock (MHz) */ unsigned int clock; /* Current clock (MHz) */
u8 pwr; /* Current voltage */ u8 pwr; /* Current voltage */
u8 drv_type; /* Current UHS-I driver type */
bool reinit_uhs; /* Force UHS-related re-initialization */
bool runtime_suspended; /* Host is runtime suspended */ bool runtime_suspended; /* Host is runtime suspended */
bool bus_on; /* Bus power prevents runtime suspend */ bool bus_on; /* Bus power prevents runtime suspend */
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment