diff --git a/drivers/mmc/host/msm_sdcc.c b/drivers/mmc/host/msm_sdcc.c index 23cb03d1025..b22e2f03341 100644 --- a/drivers/mmc/host/msm_sdcc.c +++ b/drivers/mmc/host/msm_sdcc.c @@ -2385,8 +2385,10 @@ static int msmsdcc_setup_vreg(struct msmsdcc_host *host, bool enable, struct msm_mmc_reg_data *vreg_table[2]; curr_slot = host->plat->vreg_data; - if (!curr_slot) + if (!curr_slot) { + rc = -EINVAL; goto out; + } vreg_table[0] = curr_slot->vdd_data; vreg_table[1] = curr_slot->vdd_io_data; @@ -3988,6 +3990,51 @@ exit: return rc; } +/* + * Work around of the unavailability of a power_reset functionality in SD cards + * by turning the OFF & back ON the regulators supplying the SD card. + */ +void msmsdcc_hw_reset(struct mmc_host *mmc) +{ + struct mmc_card *card = mmc->card; + struct msmsdcc_host *host = mmc_priv(mmc); + int rc; + + /* Write-protection bits would be lost on a hardware reset in emmc */ + if (!card || !mmc_card_sd(card)) + return; + + /* + * Continuing on failing to disable regulator would lead to a panic + * anyway, since the commands would fail and console would be flooded + * with prints, eventually leading to a watchdog bark + */ + rc = msmsdcc_setup_vreg(host, false, false); + if (rc) { + pr_err("%s: %s disable regulator: failed: %d\n", + mmc_hostname(mmc), __func__, rc); + BUG_ON(rc); + } + + /* 10ms delay for the supply to reach the desired voltage level */ + usleep_range(10000, 12000); + + /* + * Continuing on failing to enable regulator would lead to a panic + * anyway, since the commands would fail and console would be flooded + * with prints, eventually leading to a watchdog bark + */ + rc = msmsdcc_setup_vreg(host, true, false); + if (rc) { + pr_err("%s: %s enable regulator: failed: %d\n", + mmc_hostname(mmc), __func__, rc); + BUG_ON(rc); + } + + /* 10ms delay for the supply to reach the desired voltage level */ + usleep_range(10000, 12000); +} + static const struct mmc_host_ops msmsdcc_ops = { .enable = msmsdcc_enable, .disable = msmsdcc_disable, @@ -3998,7 +4045,8 @@ static const struct mmc_host_ops msmsdcc_ops = { .get_ro = msmsdcc_get_ro, .enable_sdio_irq = msmsdcc_enable_sdio_irq, .start_signal_voltage_switch = msmsdcc_switch_io_voltage, - .execute_tuning = msmsdcc_execute_tuning + .execute_tuning = msmsdcc_execute_tuning, + .hw_reset = msmsdcc_hw_reset, }; static unsigned int @@ -5595,7 +5643,7 @@ msmsdcc_probe(struct platform_device *pdev) mmc->caps |= plat->mmc_bus_width; mmc->caps |= MMC_CAP_MMC_HIGHSPEED | MMC_CAP_SD_HIGHSPEED; mmc->caps |= MMC_CAP_WAIT_WHILE_BUSY | MMC_CAP_ERASE; - + mmc->caps |= MMC_CAP_HW_RESET; /* * If we send the CMD23 before multi block write/read command * then we need not to send CMD12 at the end of the transfer.