diff options
Diffstat (limited to 'drivers/mmc')
| -rw-r--r-- | drivers/mmc/card/block.c | 11 | ||||
| -rw-r--r-- | drivers/mmc/core/core.c | 116 | ||||
| -rw-r--r-- | drivers/mmc/core/debugfs.c | 11 | ||||
| -rw-r--r-- | drivers/mmc/core/mmc.c | 17 | ||||
| -rw-r--r-- | drivers/mmc/host/sdhci-msm.c | 10 |
5 files changed, 131 insertions, 34 deletions
diff --git a/drivers/mmc/card/block.c b/drivers/mmc/card/block.c index e9f1a19dfe3f..29c57d13744e 100644 --- a/drivers/mmc/card/block.c +++ b/drivers/mmc/card/block.c @@ -1017,7 +1017,7 @@ static int mmc_blk_ioctl_rpmb_cmd(struct block_device *bdev, { struct mmc_blk_ioc_rpmb_data *idata; struct mmc_blk_data *md; - struct mmc_card *card; + struct mmc_card *card = NULL; struct mmc_command cmd = {0}; struct mmc_data data = {0}; struct mmc_request mrq = {NULL}; @@ -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..2cb0ea03a338 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); } @@ -589,17 +616,39 @@ static int mmc_devfreq_create_freq_table(struct mmc_host *host) host->card->clk_scaling_lowest, host->card->clk_scaling_highest); + /* + * Create the frequency table and initialize it with default values. + * Initialize it with platform specific frequencies if the frequency + * table supplied by platform driver is present, otherwise initialize + * it with min and max frequencies supported by the card. + */ if (!clk_scaling->freq_table) { - pr_debug("%s: no frequency table defined - setting default\n", - mmc_hostname(host)); + if (clk_scaling->pltfm_freq_table_sz) + clk_scaling->freq_table_sz = + clk_scaling->pltfm_freq_table_sz; + else + clk_scaling->freq_table_sz = 2; + clk_scaling->freq_table = kzalloc( - 2*sizeof(*(clk_scaling->freq_table)), GFP_KERNEL); + (clk_scaling->freq_table_sz * + sizeof(*(clk_scaling->freq_table))), GFP_KERNEL); if (!clk_scaling->freq_table) return -ENOMEM; - clk_scaling->freq_table[0] = host->card->clk_scaling_lowest; - clk_scaling->freq_table[1] = host->card->clk_scaling_highest; - clk_scaling->freq_table_sz = 2; - goto out; + + if (clk_scaling->pltfm_freq_table) { + memcpy(clk_scaling->freq_table, + clk_scaling->pltfm_freq_table, + (clk_scaling->pltfm_freq_table_sz * + sizeof(*(clk_scaling->pltfm_freq_table)))); + } else { + pr_debug("%s: no frequency table defined - setting default\n", + mmc_hostname(host)); + clk_scaling->freq_table[0] = + host->card->clk_scaling_lowest; + clk_scaling->freq_table[1] = + host->card->clk_scaling_highest; + goto out; + } } if (host->card->clk_scaling_lowest > @@ -790,10 +839,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); @@ -803,7 +857,7 @@ int mmc_resume_clk_scaling(struct mmc_host *host) devfreq_min_clk = host->clk_scaling.freq_table[0]; host->clk_scaling.curr_freq = devfreq_max_clk; - if (host->ios.clock < host->card->clk_scaling_highest) + if (host->ios.clock < host->clk_scaling.freq_table[max_clk_idx]) host->clk_scaling.curr_freq = devfreq_min_clk; host->clk_scaling.clk_scaling_in_progress = false; @@ -863,6 +917,10 @@ int mmc_exit_clk_scaling(struct mmc_host *host) host->clk_scaling.devfreq = NULL; atomic_set(&host->clk_scaling.devfreq_abort, 1); + + kfree(host->clk_scaling.freq_table); + host->clk_scaling.freq_table = NULL; + pr_debug("%s: devfreq was removed\n", mmc_hostname(host)); return 0; @@ -1469,8 +1527,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 +2375,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 +4108,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/debugfs.c b/drivers/mmc/core/debugfs.c index 594fba08e623..72bfdd835178 100644 --- a/drivers/mmc/core/debugfs.c +++ b/drivers/mmc/core/debugfs.c @@ -337,10 +337,15 @@ static int mmc_force_err_set(void *data, u64 val) { struct mmc_host *host = data; - if (host && host->ops && host->ops->force_err_irq) { - mmc_host_clk_hold(host); + if (host && host->card && host->ops && + host->ops->force_err_irq) { + /* + * To access the force error irq reg, we need to make + * sure the host is powered up and host clock is ticking. + */ + mmc_get_card(host->card); host->ops->force_err_irq(host, val); - mmc_host_clk_release(host); + mmc_put_card(host->card); } return 0; 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, }; /* diff --git a/drivers/mmc/host/sdhci-msm.c b/drivers/mmc/host/sdhci-msm.c index ca72ebfd55a3..1eeab7db9722 100644 --- a/drivers/mmc/host/sdhci-msm.c +++ b/drivers/mmc/host/sdhci-msm.c @@ -1837,13 +1837,13 @@ struct sdhci_msm_pltfm_data *sdhci_msm_populate_pdata(struct device *dev, } if (sdhci_msm_dt_get_array(dev, "qcom,devfreq,freq-table", - &msm_host->mmc->clk_scaling.freq_table, - &msm_host->mmc->clk_scaling.freq_table_sz, 0)) + &msm_host->mmc->clk_scaling.pltfm_freq_table, + &msm_host->mmc->clk_scaling.pltfm_freq_table_sz, 0)) pr_debug("%s: no clock scaling frequencies were supplied\n", dev_name(dev)); - else if (!msm_host->mmc->clk_scaling.freq_table || - !msm_host->mmc->clk_scaling.freq_table_sz) - dev_err(dev, "bad dts clock scaling frequencies\n"); + else if (!msm_host->mmc->clk_scaling.pltfm_freq_table || + !msm_host->mmc->clk_scaling.pltfm_freq_table_sz) + dev_err(dev, "bad dts clock scaling frequencies\n"); /* * Few hosts can support DDR52 mode at the same lower |
