From 3b1258a65e7ca928fa3c7dec80db3d4c5d459afb Mon Sep 17 00:00:00 2001 From: Asutosh Das Date: Mon, 9 Jul 2012 19:08:26 +0530 Subject: [PATCH] mmc: msm_sdcc: Added hardware reset functionality for SD cards In error situations, sometimes the card doesn't respond to commands. To recover from such situations, a power-cycle is issued to the card and it is reinitialized. msmsdcc_hw_reset: Defined in mmc host operations 'hw_reset'. This function power-cycles the card. CRs-fixed: 375869 Change-Id: I2b02649ae3befe700a7a2401b93eb69c8014a4ce Signed-off-by: Asutosh Das --- drivers/mmc/host/msm_sdcc.c | 54 ++++++++++++++++++++++++++++++++++--- 1 file changed, 51 insertions(+), 3 deletions(-) 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.