summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSahitya Tummala <stummala@codeaurora.org>2017-05-09 11:04:01 +0530
committerVeerabhadrarao Badiganti <vbadigan@codeaurora.org>2017-05-24 15:51:09 +0530
commitb8189b170793ca0bce98a1b49a673d63d028b805 (patch)
treede81ceb90ecbf40ac2d5e4a14fae929bbabf2a2a
parent60be71604a84d2e047215cb702d6324379a353bb (diff)
mmc: core: extend SDR104 workaround for other paths
UHS-I SD cards support SDR104 mode which runs the SD card interface clock upto 208 MHz. But we may see repeated CRC errors in SDR104 with some SDCC controllers. If this happens, commit <aafc430b36f3> ("mmc: sd: reduce the bus speed in case of multiple CRC errors") would reinit the card to lower speed (SDR50) hoping that CRC error rate would reduce at lower clock speed (100MHz for SDR50). As the error may happen for any cmd, this change tries to fix several other paths - clock scaling, mmc_rescan, non-data commands error path. Change-Id: I8ccbbf7e330cde3862d9660d3be4d67fb00d97ca Signed-off-by: Sahitya Tummala <stummala@codeaurora.org> Signed-off-by: Subhash Jadavani <subhashj@codeaurora.org> Signed-off-by: Veerabhadrarao Badiganti <vbadigan@codeaurora.org>
-rw-r--r--drivers/mmc/card/block.c5
-rw-r--r--drivers/mmc/core/core.c58
-rw-r--r--include/linux/mmc/core.h1
3 files changed, 53 insertions, 11 deletions
diff --git a/drivers/mmc/card/block.c b/drivers/mmc/card/block.c
index e9f1a19dfe3f..ee99c565e842 100644
--- a/drivers/mmc/card/block.c
+++ b/drivers/mmc/card/block.c
@@ -1707,6 +1707,8 @@ static int mmc_blk_cmd_recovery(struct mmc_card *card, struct request *req,
/* We couldn't get a response from the card. Give up. */
if (err) {
+ if (card->err_in_sdr104)
+ return ERR_RETRY;
/* Check if the card is removed */
if (mmc_detect_card_removed(card->host))
return ERR_NOMEDIUM;
@@ -2197,7 +2199,8 @@ static int mmc_blk_err_check(struct mmc_card *card,
brq->data.error == -ETIMEDOUT ||
brq->cmd.error == -EILSEQ ||
brq->cmd.error == -EIO ||
- brq->cmd.error == -ETIMEDOUT))
+ brq->cmd.error == -ETIMEDOUT ||
+ brq->sbc.error))
card->err_in_sdr104 = true;
/*
diff --git a/drivers/mmc/core/core.c b/drivers/mmc/core/core.c
index c462eee4a5f7..23bc5ba3b700 100644
--- a/drivers/mmc/core/core.c
+++ b/drivers/mmc/core/core.c
@@ -464,6 +464,22 @@ out:
}
EXPORT_SYMBOL(mmc_clk_update_freq);
+void mmc_recovery_fallback_lower_speed(struct mmc_host *host)
+{
+ if (!host->card)
+ return;
+
+ if (host->sdr104_wa && mmc_card_sd(host->card) &&
+ (host->ios.timing == MMC_TIMING_UHS_SDR104) &&
+ !host->card->sdr104_blocked) {
+ pr_err("%s: %s: blocked SDR104, lower the bus-speed (SDR50 / DDR50)\n",
+ mmc_hostname(host), __func__);
+ mmc_host_clear_sdr104(host);
+ mmc_hw_reset(host);
+ host->card->sdr104_blocked = true;
+ }
+}
+
static int mmc_devfreq_set_target(struct device *dev,
unsigned long *freq, u32 devfreq_flags)
{
@@ -510,6 +526,9 @@ static int mmc_devfreq_set_target(struct device *dev,
if (abort)
goto out;
+ if (mmc_card_sd(host->card) && host->card->sdr104_blocked)
+ goto rel_host;
+
/*
* In case we were able to claim host there is no need to
* defer the frequency change. It will be done now
@@ -518,15 +537,18 @@ static int mmc_devfreq_set_target(struct device *dev,
mmc_host_clk_hold(host);
err = mmc_clk_update_freq(host, *freq, clk_scaling->state);
- if (err && err != -EAGAIN)
+ if (err && err != -EAGAIN) {
pr_err("%s: clock scale to %lu failed with error %d\n",
mmc_hostname(host), *freq, err);
- else
+ mmc_recovery_fallback_lower_speed(host);
+ } else {
pr_debug("%s: clock change to %lu finished successfully (%s)\n",
mmc_hostname(host), *freq, current->comm);
+ }
mmc_host_clk_release(host);
+rel_host:
mmc_release_host(host);
out:
return err;
@@ -547,6 +569,9 @@ void mmc_deferred_scaling(struct mmc_host *host)
if (!host->clk_scaling.enable)
return;
+ if (mmc_card_sd(host->card) && host->card->sdr104_blocked)
+ return;
+
spin_lock_bh(&host->clk_scaling.lock);
if (host->clk_scaling.clk_scaling_in_progress ||
@@ -567,13 +592,15 @@ void mmc_deferred_scaling(struct mmc_host *host)
err = mmc_clk_update_freq(host, target_freq,
host->clk_scaling.state);
- if (err && err != -EAGAIN)
+ if (err && err != -EAGAIN) {
pr_err("%s: failed on deferred scale clocks (%d)\n",
mmc_hostname(host), err);
- else
+ mmc_recovery_fallback_lower_speed(host);
+ } else {
pr_debug("%s: clocks were successfully scaled to %lu (%s)\n",
mmc_hostname(host),
target_freq, current->comm);
+ }
host->clk_scaling.clk_scaling_in_progress = false;
atomic_dec(&host->clk_scaling.devfreq_abort);
}
@@ -1469,8 +1496,13 @@ static void mmc_wait_for_req_done(struct mmc_host *host,
}
}
if (!cmd->error || !cmd->retries ||
- mmc_card_removed(host->card))
+ mmc_card_removed(host->card)) {
+ if (cmd->error && !cmd->retries &&
+ cmd->opcode != MMC_SEND_STATUS &&
+ cmd->opcode != MMC_SEND_TUNING_BLOCK)
+ mmc_recovery_fallback_lower_speed(host);
break;
+ }
mmc_retune_recheck(host);
@@ -4038,12 +4070,18 @@ int _mmc_detect_card_removed(struct mmc_host *host)
}
if (ret) {
- mmc_card_set_removed(host->card);
- if (host->card->sdr104_blocked) {
- mmc_host_set_sdr104(host);
- host->card->sdr104_blocked = false;
+ if (host->ops->get_cd && host->ops->get_cd(host)) {
+ mmc_recovery_fallback_lower_speed(host);
+ ret = 0;
+ } else {
+ mmc_card_set_removed(host->card);
+ if (host->card->sdr104_blocked) {
+ mmc_host_set_sdr104(host);
+ host->card->sdr104_blocked = false;
+ }
+ pr_debug("%s: card remove detected\n",
+ mmc_hostname(host));
}
- pr_debug("%s: card remove detected\n", mmc_hostname(host));
}
return ret;
diff --git a/include/linux/mmc/core.h b/include/linux/mmc/core.h
index 2a1a6fec179f..0065ffc9322b 100644
--- a/include/linux/mmc/core.h
+++ b/include/linux/mmc/core.h
@@ -223,6 +223,7 @@ extern void mmc_cmdq_clk_scaling_start_busy(struct mmc_host *host,
bool lock_needed);
extern void mmc_cmdq_clk_scaling_stop_busy(struct mmc_host *host,
bool lock_needed, bool is_cmdq_dcmd);
+extern void mmc_recovery_fallback_lower_speed(struct mmc_host *host);
/**
* mmc_claim_host - exclusively claim a host