diff options
Diffstat (limited to 'drivers/mmc')
| -rw-r--r-- | drivers/mmc/card/block.c | 9 | ||||
| -rw-r--r-- | drivers/mmc/core/core.c | 74 | ||||
| -rw-r--r-- | drivers/mmc/core/mmc.c | 17 |
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, }; /* |
