diff --git a/drivers/mmc/card/block.c b/drivers/mmc/card/block.c index f3004355053f..c6f34964d839 100644 --- a/drivers/mmc/card/block.c +++ b/drivers/mmc/card/block.c @@ -1718,6 +1718,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; @@ -2208,7 +2210,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 c19aa0ca4cdf..093f28a5dcbe 100644 --- a/drivers/mmc/core/core.c +++ b/drivers/mmc/core/core.c @@ -456,6 +456,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) { @@ -507,6 +523,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 @@ -515,15 +534,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; @@ -544,6 +566,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 || @@ -564,13 +589,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); } @@ -1571,8 +1598,13 @@ void mmc_wait_for_req_done(struct mmc_host *host, struct mmc_request *mrq) } } 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); @@ -4264,12 +4296,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/slot-gpio.c b/drivers/mmc/core/slot-gpio.c index b5c81e444c80..91ad9465476a 100644 --- a/drivers/mmc/core/slot-gpio.c +++ b/drivers/mmc/core/slot-gpio.c @@ -77,6 +77,15 @@ EXPORT_SYMBOL(mmc_gpio_get_ro); int mmc_gpio_get_cd(struct mmc_host *host) { struct mmc_gpio *ctx = host->slot.handler_priv; + int ret; + + if (host->extcon) { + ret = extcon_get_state(host->extcon, EXTCON_MECHANICAL); + if (ret < 0) + dev_err(mmc_dev(host), "%s: Extcon failed to check card state, ret=%d\n", + __func__, ret); + return ret; + } if (!ctx || !ctx->cd_gpio) return -ENOSYS; diff --git a/include/linux/mmc/core.h b/include/linux/mmc/core.h index 0f2e65101b7c..b71810518922 100644 --- a/include/linux/mmc/core.h +++ b/include/linux/mmc/core.h @@ -233,6 +233,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