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 <asutoshd@codeaurora.org>
diff --git a/drivers/mmc/host/msm_sdcc.c b/drivers/mmc/host/msm_sdcc.c
index 23cb03d..b22e2f0 100644
--- a/drivers/mmc/host/msm_sdcc.c
+++ b/drivers/mmc/host/msm_sdcc.c
@@ -2385,8 +2385,10 @@
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 @@
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 @@
.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 @@
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.