summaryrefslogtreecommitdiff
path: root/drivers/mmc
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/mmc')
-rw-r--r--drivers/mmc/card/block.c9
-rw-r--r--drivers/mmc/core/core.c74
-rw-r--r--drivers/mmc/core/mmc.c17
3 files changed, 83 insertions, 17 deletions
diff --git a/drivers/mmc/card/block.c b/drivers/mmc/card/block.c
index e9f1a19dfe3f..69e51cc96303 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;
/*
@@ -4695,10 +4698,6 @@ static int _mmc_blk_suspend(struct mmc_card *card, bool wait)
static void mmc_blk_shutdown(struct mmc_card *card)
{
_mmc_blk_suspend(card, 1);
-
- /* send power off notification */
- if (mmc_card_mmc(card))
- mmc_send_pon(card);
}
#ifdef CONFIG_PM_SLEEP
diff --git a/drivers/mmc/core/core.c b/drivers/mmc/core/core.c
index c462eee4a5f7..63f7bf87843f 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);
}
@@ -790,10 +817,15 @@ int mmc_resume_clk_scaling(struct mmc_host *host)
if (!mmc_can_scale_clk(host))
return 0;
+ /*
+ * If clock scaling is already exited when resume is called, like
+ * during mmc shutdown, it is not an error and should not fail the
+ * API calling this.
+ */
if (!host->clk_scaling.devfreq) {
- pr_err("%s: %s: no devfreq is assosiated with this device\n",
+ pr_warn("%s: %s: no devfreq is assosiated with this device\n",
mmc_hostname(host), __func__);
- return -EPERM;
+ return 0;
}
atomic_set(&host->clk_scaling.devfreq_abort, 0);
@@ -1469,8 +1501,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);
@@ -2312,6 +2349,13 @@ void mmc_ungate_clock(struct mmc_host *host)
WARN_ON(host->ios.clock);
/* This call will also set host->clk_gated to false */
__mmc_set_clock(host, host->clk_old);
+ /*
+ * We have seen that host controller's clock tuning circuit may
+ * go out of sync if controller clocks are gated.
+ * To workaround this issue, we are triggering retuning of the
+ * tuning circuit after ungating the controller clocks.
+ */
+ mmc_retune_needed(host);
}
}
@@ -4038,12 +4082,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/drivers/mmc/core/mmc.c b/drivers/mmc/core/mmc.c
index 5eda4f4fb0fe..df60774b02af 100644
--- a/drivers/mmc/core/mmc.c
+++ b/drivers/mmc/core/mmc.c
@@ -2933,6 +2933,22 @@ static int mmc_reset(struct mmc_host *host)
return ret;
}
+static int mmc_shutdown(struct mmc_host *host)
+{
+ struct mmc_card *card = host->card;
+
+ /*
+ * Exit clock scaling so that it doesn't kick in after
+ * power off notification is sent
+ */
+ if (host->caps2 & MMC_CAP2_CLK_SCALE)
+ mmc_exit_clk_scaling(card->host);
+ /* send power off notification */
+ if (mmc_card_mmc(card))
+ mmc_send_pon(card);
+ return 0;
+}
+
static const struct mmc_bus_ops mmc_ops = {
.remove = mmc_remove,
.detect = mmc_detect,
@@ -2943,6 +2959,7 @@ static const struct mmc_bus_ops mmc_ops = {
.alive = mmc_alive,
.change_bus_speed = mmc_change_bus_speed,
.reset = mmc_reset,
+ .shutdown = mmc_shutdown,
};
/*