When switching SD and SDIO cards from 3.3V to 1.8V signal levels, the clock should be gated for 5 ms during the step. Failure by the card to switch is indicated by dat0 being pulled low. The host should check for this condition and power-cycle the card if failure is indicated. Signed-off-by: Johan Rudholm <johan.rudholm@xxxxxxxxxxxxxx> --- Changelog v2: - Keep calls to mmc_host_clk_hold / mmc_host_clk_release --- drivers/mmc/core/core.c | 22 ++++++++++++++++++++++ include/linux/mmc/host.h | 3 +++ 2 files changed, 25 insertions(+) diff --git a/drivers/mmc/core/core.c b/drivers/mmc/core/core.c index 6612163..dae6744 100644 --- a/drivers/mmc/core/core.c +++ b/drivers/mmc/core/core.c @@ -1220,6 +1220,7 @@ int mmc_set_signal_voltage(struct mmc_host *host, int signal_voltage, bool cmd11 { struct mmc_command cmd = {0}; int err = 0; + unsigned char old_voltage = host->ios.signal_voltage; BUG_ON(!host); @@ -1243,8 +1244,29 @@ int mmc_set_signal_voltage(struct mmc_host *host, int signal_voltage, bool cmd11 host->ios.signal_voltage = signal_voltage; if (host->ops->start_signal_voltage_switch) { + u32 clock = host->ios.clock; + mmc_host_clk_hold(host); + host->ios.clock = 0; + mmc_set_ios(host); err = host->ops->start_signal_voltage_switch(host, &host->ios); + + /* Hold clock for at least 5 ms according to spec */ + mmc_delay(5); + host->ios.clock = clock; + mmc_set_ios(host); + + /* Wait for at least 1 ms until we check if card is ready */ + mmc_delay(1); + + /* Check busy */ + if (cmd11 && host->ops->card_busy && + host->ops->card_busy(host)) { + host->ios.signal_voltage = old_voltage; + host->ops->start_signal_voltage_switch(host, + &host->ios); + err = -EAGAIN; + } mmc_host_clk_release(host); } diff --git a/include/linux/mmc/host.h b/include/linux/mmc/host.h index d5d9bd4..b58641a 100644 --- a/include/linux/mmc/host.h +++ b/include/linux/mmc/host.h @@ -131,6 +131,9 @@ struct mmc_host_ops { int (*start_signal_voltage_switch)(struct mmc_host *host, struct mmc_ios *ios); + /* Check if the card is pulling dat0 low */ + int (*card_busy)(struct mmc_host *host); + /* The tuning command opcode value is different for SD and eMMC cards */ int (*execute_tuning)(struct mmc_host *host, u32 opcode); void (*enable_preset_value)(struct mmc_host *host, bool enable); -- 1.7.10 -- To unsubscribe from this list: send the line "unsubscribe linux-mmc" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html