Let the upper layers handle the details of the UHS signal voltage switch, such as timings and detection and handling of failure states. Implement the card_busy host_ops function to let the upper layers check if the card is signaling busy. Signed-off-by: Johan Rudholm <johan.rudholm@xxxxxxxxxxxxxx> --- This is a humble attempt to adapt the sdhci driver so it will work with [RFC/PATCH] mmc: core: Fixup signal voltage switch The patch is untested as I don't have the proper hardware. Furthermore, I don't have any particular insight into how the sdhci controller works, so quite possibly mistakes have been made, but the idea should hopefully be clear. I hope I can get some help at testing this, and I'm grateful for any comments. --- drivers/mmc/host/sdhci.c | 102 ++++++++++++++++------------------------------ 1 file changed, 36 insertions(+), 66 deletions(-) diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c index 0e15c79..01eed72 100644 --- a/drivers/mmc/host/sdhci.c +++ b/drivers/mmc/host/sdhci.c @@ -1639,80 +1639,32 @@ static int sdhci_do_3_3v_signal_voltage_switch(struct sdhci_host *host, static int sdhci_do_1_8v_signal_voltage_switch(struct sdhci_host *host, u16 ctrl) { - u8 pwr; - u16 clk; - u32 present_state; int ret; - /* Stop SDCLK */ - clk = sdhci_readw(host, SDHCI_CLOCK_CONTROL); - clk &= ~SDHCI_CLOCK_CARD_EN; - sdhci_writew(host, clk, SDHCI_CLOCK_CONTROL); - - /* Check whether DAT[3:0] is 0000 */ - present_state = sdhci_readl(host, SDHCI_PRESENT_STATE); - if (!((present_state & SDHCI_DATA_LVL_MASK) >> - SDHCI_DATA_LVL_SHIFT)) { - /* - * Enable 1.8V Signal Enable in the Host Control2 - * register - */ - if (host->vqmmc) - ret = regulator_set_voltage(host->vqmmc, - 1800000, 1800000); - else - ret = 0; - - if (!ret) { - ctrl |= SDHCI_CTRL_VDD_180; - sdhci_writew(host, ctrl, SDHCI_HOST_CONTROL2); - - /* Wait for 5ms */ - usleep_range(5000, 5500); - - ctrl = sdhci_readw(host, SDHCI_HOST_CONTROL2); - if (ctrl & SDHCI_CTRL_VDD_180) { - /* Provide SDCLK again and wait for 1ms */ - clk = sdhci_readw(host, SDHCI_CLOCK_CONTROL); - clk |= SDHCI_CLOCK_CARD_EN; - sdhci_writew(host, clk, SDHCI_CLOCK_CONTROL); - usleep_range(1000, 1500); - - /* - * If DAT[3:0] level is 1111b, then the card - * was successfully switched to 1.8V signaling. - */ - present_state = sdhci_readl(host, - SDHCI_PRESENT_STATE); - if ((present_state & SDHCI_DATA_LVL_MASK) == - SDHCI_DATA_LVL_MASK) - return 0; - } + if (host->vqmmc) { + ret = regulator_set_voltage(host->vqmmc, 1800000, 1800000); + if (ret) { + pr_warning("%s: Switching to 1.8V signalling voltage " + " failed\n", mmc_hostname(host->mmc)); + return -EIO; } } - /* - * If we are here, that means the switch to 1.8V signaling - * failed. We power cycle the card, and retry initialization - * sequence by setting S18R to 0. - */ - pwr = sdhci_readb(host, SDHCI_POWER_CONTROL); - pwr &= ~SDHCI_POWER_ON; - sdhci_writeb(host, pwr, SDHCI_POWER_CONTROL); - if (host->vmmc) - regulator_disable(host->vmmc); + /* Enable 1.8V Signal Enable in the Host Control2 register */ + ctrl |= SDHCI_CTRL_VDD_180; + sdhci_writew(host, ctrl, SDHCI_HOST_CONTROL2); - /* Wait for 1ms as per the spec */ - usleep_range(1000, 1500); - pwr |= SDHCI_POWER_ON; - sdhci_writeb(host, pwr, SDHCI_POWER_CONTROL); - if (host->vmmc) - regulator_enable(host->vmmc); + /* Wait for 5ms */ + usleep_range(5000, 5500); - pr_warning("%s: Switching to 1.8V signalling voltage failed, " - "retrying with S18R set to 0\n", mmc_hostname(host->mmc)); + ctrl = sdhci_readw(host, SDHCI_HOST_CONTROL2); + if (ctrl & SDHCI_CTRL_VDD_180) + return 0; + + pr_warning("%s: 1.8V regulator output did not become stable\n", + mmc_hostname(host->mmc)); - return -EAGAIN; + return -EIO; } static int sdhci_do_start_signal_voltage_switch(struct sdhci_host *host, @@ -1991,6 +1943,23 @@ static void sdhci_enable_preset_value(struct mmc_host *mmc, bool enable) sdhci_runtime_pm_put(host); } +static void sdhci_card_busy(struct mmc_host *mmc, bool keep_busy) +{ + u32 present_state; + struct sdhci_host *host = mmc_priv(mmc); + + /* Check whether DAT[3:0] is 0000 */ + sdhci_runtime_pm_get(host); + present_state = sdhci_readl(host, SDHCI_PRESENT_STATE); + sdhci_runtime_pm_put(host); + + if (!((present_state & SDHCI_DATA_LVL_MASK) >> SDHCI_DATA_LVL_SHIFT)) + return 1; + else + return 0; + +} + static const struct mmc_host_ops sdhci_ops = { .request = sdhci_request, .set_ios = sdhci_set_ios, @@ -2000,6 +1969,7 @@ static const struct mmc_host_ops sdhci_ops = { .start_signal_voltage_switch = sdhci_start_signal_voltage_switch, .execute_tuning = sdhci_execute_tuning, .enable_preset_value = sdhci_enable_preset_value, + .card_busy = sdhci_card_busy, }; /*****************************************************************************\ -- 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