Re: [PATCH RFC 2/7] mmc: core: devfreq: Add devfreq based clock scaling support

From: Sayali Lokhande
Date: Mon Oct 01 2018 - 10:16:49 EST



On 7/23/2018 3:31 PM, Vijay Viswanath wrote:
Hi Sayali,

On 7/13/2018 3:22 PM, Sayali Lokhande wrote:
This change adds the use of devfreq to MMC.
Both eMMC and SD card will use it.
For some workloads, such as video playback, it isn't
necessary for these cards to run at high speed.
Running at lower frequency, for example 52MHz, in such
cases can still meet the deadlines for data transfers.
Scaling down the clock frequency dynamically has power
savings not only because the bus is running at lower frequency
but also has an advantage of scaling down the system core
voltage, if supported.
Provide an ondemand clock scaling support similar to the
cpufreq ondemand governor having two thresholds,
up_threshold and down_threshold to decide whether to
increase the frequency or scale it down respectively.
The sampling interval is in the order of milliseconds.
If sampling interval is too low, frequent switching of
frequencies can lead to high power consumption and if
sampling interval is too high, the clock scaling logic
would take long time to realize that the underlying
hardware (controller and card) is busy and scale up
the clocks.

Signed-off-by: Talel Shenhar <tatias@xxxxxxxxxxxxxx>
Signed-off-by: Sayali Lokhande <sayalil@xxxxxxxxxxxxxx>
---
 .../devicetree/bindings/mmc/sdhci-msm.txt | 10 +
 drivers/mmc/core/core.c | 560 +++++++++++++++++++++
 drivers/mmc/core/core.h | 7 +
 drivers/mmc/core/debugfs.c | 46 ++
 drivers/mmc/core/host.c | 8 +
 drivers/mmc/core/mmc.c | 200 +++++++-
 drivers/mmc/core/sd.c | 72 ++-
 drivers/mmc/host/sdhci-msm.c | 37 ++
 drivers/mmc/host/sdhci-pltfm.c | 11 +
 drivers/mmc/host/sdhci.c | 27 +
 drivers/mmc/host/sdhci.h | 8 +
 include/linux/mmc/card.h | 5 +
 include/linux/mmc/host.h | 70 +++
 13 files changed, 1059 insertions(+), 2 deletions(-)

diff --git a/Documentation/devicetree/bindings/mmc/sdhci-msm.txt b/Documentation/devicetree/bindings/mmc/sdhci-msm.txt
index 502b3b8..bd8470a 100644
--- a/Documentation/devicetree/bindings/mmc/sdhci-msm.txt
+++ b/Documentation/devicetree/bindings/mmc/sdhci-msm.txt
@@ -26,6 +26,15 @@ Required properties:
ÂÂÂÂÂ "cal"ÂÂÂ - reference clock for RCLK delay calibration (optional)
ÂÂÂÂÂ "sleep"ÂÂÂ - sleep clock for RCLK delay calibration (optional)
 +Optional Properties:
+- qcom,devfreq,freq-table - specifies supported frequencies for clock scaling.
+ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂ Clock scaling logic shall toggle between these frequencies based
+ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂ on card load. In case the defined frequencies are over or below
+ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂ the supported card frequencies, they will be overridden
+ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂ during card init. In case this entry is not supplied,
+ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂ the driver will construct one based on the card
+ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂ supported max and min frequencies.
+ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂ The frequencies must be ordered from lowest to highest.
 Example:
 Â sdhc_1: sdhci@f9824900 {
@@ -43,6 +52,7 @@ Example:
 Â clocks = <&gcc GCC_SDCC1_APPS_CLK>, <&gcc GCC_SDCC1_AHB_CLK>;
ÂÂÂÂÂÂÂÂÂ clock-names = "core", "iface";
+ÂÂÂÂÂÂÂ qcom,devfreq,freq-table = <52000000 200000000>;
ÂÂÂÂÂ };
 Â sdhc_2: sdhci@f98a4900 {
diff --git a/drivers/mmc/core/core.c b/drivers/mmc/core/core.c
index 281826d..0eaee42 100644
--- a/drivers/mmc/core/core.c
+++ b/drivers/mmc/core/core.c
@@ -14,6 +14,7 @@
 #include <linux/init.h>
 #include <linux/interrupt.h>
 #include <linux/completion.h>
+#include <linux/devfreq.h>
 #include <linux/device.h>
 #include <linux/delay.h>
 #include <linux/pagemap.h>
@@ -112,6 +113,556 @@ static inline void mmc_should_fail_request(struct mmc_host *host,
  #endif /* CONFIG_FAIL_MMC_REQUEST */
 +static bool mmc_is_data_request(struct mmc_request *mmc_request)
+{
+ÂÂÂ switch (mmc_request->cmd->opcode) {
+ÂÂÂ case MMC_READ_SINGLE_BLOCK:
+ÂÂÂ case MMC_READ_MULTIPLE_BLOCK:
+ÂÂÂ case MMC_WRITE_BLOCK:
+ÂÂÂ case MMC_WRITE_MULTIPLE_BLOCK:
+ÂÂÂÂÂÂÂ return true;
+ÂÂÂ default:
+ÂÂÂÂÂÂÂ return false;
+ÂÂÂ }
+}
+
+static void mmc_clk_scaling_start_busy(struct mmc_host *host, bool lock_needed)
+{
+ÂÂÂ struct mmc_devfeq_clk_scaling *clk_scaling = &host->clk_scaling;
+
+ÂÂÂ if (!clk_scaling->enable)
+ÂÂÂÂÂÂÂ return;
+
+ÂÂÂ if (lock_needed)
+ÂÂÂÂÂÂÂ spin_lock_bh(&clk_scaling->lock);
+
+ÂÂÂ clk_scaling->start_busy = ktime_get();
+ÂÂÂ clk_scaling->is_busy_started = true;
+
+ÂÂÂ if (lock_needed)
+ÂÂÂÂÂÂÂ spin_unlock_bh(&clk_scaling->lock);
+}
+
+static void mmc_clk_scaling_stop_busy(struct mmc_host *host, bool lock_needed)
+{
+ÂÂÂ struct mmc_devfeq_clk_scaling *clk_scaling = &host->clk_scaling;
+
+ÂÂÂ if (!clk_scaling->enable)
+ÂÂÂÂÂÂÂ return;
+
+ÂÂÂ if (lock_needed)
+ÂÂÂÂÂÂÂ spin_lock_bh(&clk_scaling->lock);
+
+ÂÂÂ if (!clk_scaling->is_busy_started) {
+ÂÂÂÂÂÂÂ WARN_ON(1);
+ÂÂÂÂÂÂÂ goto out;
+ÂÂÂ }
+
+ÂÂÂ clk_scaling->total_busy_time_us +=
+ÂÂÂÂÂÂÂ ktime_to_us(ktime_sub(ktime_get(),
+ÂÂÂÂÂÂÂÂÂÂÂ clk_scaling->start_busy));
+ÂÂÂ pr_debug("%s: accumulated busy time is %lu usec\n",
+ÂÂÂÂÂÂÂ mmc_hostname(host), clk_scaling->total_busy_time_us);
+ÂÂÂ clk_scaling->is_busy_started = false;
+
+out:
+ÂÂÂ if (lock_needed)
+ÂÂÂÂÂÂÂ spin_unlock_bh(&clk_scaling->lock);
+}
+
+/**
+ * mmc_can_scale_clk() - Check clock scaling capability
+ * @host: pointer to mmc host structure
+ */
+bool mmc_can_scale_clk(struct mmc_host *host)
+{
+ÂÂÂ if (!host) {
+ÂÂÂÂÂÂÂ pr_err("bad host parameter\n");
+ÂÂÂÂÂÂÂ WARN_ON(1);
+ÂÂÂÂÂÂÂ return false;
+ÂÂÂ }
+
+ÂÂÂ return host->caps2 & MMC_CAP2_CLK_SCALE;
+}
+EXPORT_SYMBOL(mmc_can_scale_clk);
+
+static int mmc_devfreq_get_dev_status(struct device *dev,
+ÂÂÂÂÂÂÂ struct devfreq_dev_status *status)
+{
+ÂÂÂ struct mmc_host *host = container_of(dev, struct mmc_host, class_dev);
+ÂÂÂ struct mmc_devfeq_clk_scaling *clk_scaling;
+
+ÂÂÂ if (!host) {
+ÂÂÂÂÂÂÂ pr_err("bad host parameter\n");
+ÂÂÂÂÂÂÂ WARN_ON(1);
+ÂÂÂÂÂÂÂ return -EINVAL;
+ÂÂÂ }
+
+ÂÂÂ clk_scaling = &host->clk_scaling;
+
+ÂÂÂ if (!clk_scaling->enable)
+ÂÂÂÂÂÂÂ return 0;
+
+ÂÂÂ spin_lock_bh(&clk_scaling->lock);
+
+ÂÂÂ /* accumulate the busy time of ongoing work */
+ÂÂÂ memset(status, 0, sizeof(*status));
+ÂÂÂ if (clk_scaling->is_busy_started) {
+ÂÂÂÂÂÂÂÂÂÂÂ mmc_clk_scaling_stop_busy(host, false);
+ÂÂÂÂÂÂÂÂÂÂÂ mmc_clk_scaling_start_busy(host, false);
+ÂÂÂ }
+
+ÂÂÂ status->busy_time = clk_scaling->total_busy_time_us;
+ÂÂÂ status->total_time = ktime_to_us(ktime_sub(ktime_get(),
+ÂÂÂÂÂÂÂ clk_scaling->measure_interval_start));
+ÂÂÂ clk_scaling->total_busy_time_us = 0;
+ÂÂÂ status->current_frequency = clk_scaling->curr_freq;
+ÂÂÂ clk_scaling->measure_interval_start = ktime_get();
+
+ÂÂÂ pr_debug("%s: status: load = %lu%% - total_time=%lu busy_time = %lu, clk=%lu\n",
+ÂÂÂÂÂÂÂ mmc_hostname(host),
+ÂÂÂÂÂÂÂ (status->busy_time*100)/status->total_time,
+ÂÂÂÂÂÂÂ status->total_time, status->busy_time,
+ÂÂÂÂÂÂÂ status->current_frequency);
+
+ÂÂÂ spin_unlock_bh(&clk_scaling->lock);
+
+ÂÂÂ return 0;
+}
+
+static bool mmc_is_valid_state_for_clk_scaling(struct mmc_host *host)
+{
+ÂÂÂ struct mmc_card *card = host->card;
+ÂÂÂ u32 status;
+
+ÂÂÂ /*
+ÂÂÂÂ * If the current partition type is RPMB, clock switching may not
+ÂÂÂÂ * work properly as sending tuning command (CMD21) is illegal in
+ÂÂÂÂ * this mode.
+ÂÂÂÂ */
+ÂÂÂ if (!card || (mmc_card_mmc(card) &&
+ÂÂÂÂÂÂÂÂÂÂÂ (card->part_curr == EXT_CSD_PART_CONFIG_ACC_RPMB ||
+ÂÂÂÂÂÂÂÂÂÂÂ mmc_card_doing_bkops(card))))
+ÂÂÂÂÂÂÂ return false;
+
+ÂÂÂ if (mmc_send_status(card, &status)) {
+ÂÂÂÂÂÂÂ pr_err("%s: Get card status fail\n", mmc_hostname(card->host));
+ÂÂÂÂÂÂÂ return false;
+ÂÂÂ }
+
+ÂÂÂ return R1_CURRENT_STATE(status) == R1_STATE_TRAN;
+}
+
+int mmc_clk_update_freq(struct mmc_host *host,
+ÂÂÂÂÂÂÂ unsigned long freq, enum mmc_load state)
+{
+ÂÂÂ int err = 0;
+
+ÂÂÂ if (!host) {
+ÂÂÂÂÂÂÂ pr_err("bad host parameter\n");
+ÂÂÂÂÂÂÂ WARN_ON(1);
+ÂÂÂÂÂÂÂ return -EINVAL;
+ÂÂÂ }
+
+ÂÂÂ /* make sure the card supports the frequency we want */
+ÂÂÂ if (unlikely(freq > host->card->clk_scaling_highest)) {
+ÂÂÂÂÂÂÂ freq = host->card->clk_scaling_highest;
+ÂÂÂÂÂÂÂ pr_warn("%s: %s: frequency was overridden to %lu\n",
+ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂ mmc_hostname(host), __func__,
+ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂ host->card->clk_scaling_highest);
+ÂÂÂ }
+
+ÂÂÂ if (unlikely(freq < host->card->clk_scaling_lowest)) {
+ÂÂÂÂÂÂÂ freq = host->card->clk_scaling_lowest;
+ÂÂÂÂÂÂÂ pr_warn("%s: %s: frequency was overridden to %lu\n",
+ÂÂÂÂÂÂÂÂÂÂÂ mmc_hostname(host), __func__,
+ÂÂÂÂÂÂÂÂÂÂÂ host->card->clk_scaling_lowest);
+ÂÂÂ }
+
+ÂÂÂ if (freq == host->clk_scaling.curr_freq)
+ÂÂÂÂÂÂÂ goto out;
+
+ÂÂÂ if (host->ops->notify_load) {
+ÂÂÂÂÂÂÂ err = host->ops->notify_load(host, state);
+ÂÂÂÂÂÂÂ if (err) {
+ÂÂÂÂÂÂÂÂÂÂÂ pr_err("%s: %s: fail on notify_load\n",
+ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂ mmc_hostname(host), __func__);
+ÂÂÂÂÂÂÂÂÂÂÂ goto out;
+ÂÂÂÂÂÂÂ }
+ÂÂÂ }
+
+ÂÂÂ if (!mmc_is_valid_state_for_clk_scaling(host)) {
+ÂÂÂÂÂÂÂ pr_debug("%s: invalid state for clock scaling - skipping",
+ÂÂÂÂÂÂÂÂÂÂÂ mmc_hostname(host));
+ÂÂÂÂÂÂÂ goto invalid_state;
+ÂÂÂ }
+
+ÂÂÂ err = host->bus_ops->change_bus_speed(host, &freq);
+ÂÂÂ if (!err)
+ÂÂÂÂÂÂÂ host->clk_scaling.curr_freq = freq;
+ÂÂÂ else
+ÂÂÂÂÂÂÂ pr_err("%s: %s: failed (%d) at freq=%lu\n",
+ÂÂÂÂÂÂÂÂÂÂÂ mmc_hostname(host), __func__, err, freq);
+
+invalid_state:
+ÂÂÂ if (err) {
+ÂÂÂÂÂÂÂ /* restore previous state */
+ÂÂÂÂÂÂÂ if (host->ops->notify_load)
+ÂÂÂÂÂÂÂÂÂÂÂ if (host->ops->notify_load(host,
+ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂ host->clk_scaling.state))
+ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂ pr_err("%s: %s: fail on notify_load restore\n",
+ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂ mmc_hostname(host), __func__);
+ÂÂÂ }
+out:
+ÂÂÂ return err;
+}
+EXPORT_SYMBOL(mmc_clk_update_freq);
+
+static int mmc_devfreq_set_target(struct device *dev,
+ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂ unsigned long *freq, u32 devfreq_flags)
+{
+ÂÂÂ struct mmc_host *host = container_of(dev, struct mmc_host, class_dev);
+ÂÂÂ struct mmc_devfeq_clk_scaling *clk_scaling;
+ÂÂÂ int err = 0;
+ÂÂÂ int abort;
+ÂÂÂ unsigned long pflags = current->flags;
+
+ÂÂÂ /* Ensure scaling would happen even in memory pressure conditions */
+ÂÂÂ current->flags |= PF_MEMALLOC;
+
+ÂÂÂ if (!(host && freq)) {
+ÂÂÂÂÂÂÂ pr_err("%s: unexpected host/freq parameter\n", __func__);
+ÂÂÂÂÂÂÂ err = -EINVAL;
+ÂÂÂÂÂÂÂ goto out;
+ÂÂÂ }
+
+ÂÂÂ clk_scaling = &host->clk_scaling;
+
+ÂÂÂ if (!clk_scaling->enable)
+ÂÂÂÂÂÂÂ goto out;
+
+ÂÂÂ pr_debug("%s: target freq = %lu (%s)\n", mmc_hostname(host),
+ÂÂÂÂÂÂÂ *freq, current->comm);
+
+ÂÂÂ if ((clk_scaling->curr_freq == *freq) ||
+ÂÂÂÂÂÂÂ clk_scaling->skip_clk_scale_freq_update)
+ÂÂÂÂÂÂÂ goto out;
+

Suppose devfreq issued a low_freq request while mmc is handling a request already issued to card (so mmc host is claimed by issuing context). So we will set target freq to LOW_FREQ and quit from here.
Now, if devfreq comes again and issues a HIGH_FREQ request (before mmc thread had a chance to change the freq to LOW in mmc_deferred_scaling), we will skip this devfreq request (since current freq is high already). Then, when mmc thread comes, it will execute deferred scaling to LOW_FREQ.

How about comparing *freq with clk_scaling->target_freq ?
Agree. Will update this in next patchset.

+ÂÂÂ /* No need to scale the clocks if they are gated */
+ÂÂÂ if (!host->ios.clock)
+ÂÂÂÂÂÂÂ goto out;
+
+ÂÂÂ spin_lock_bh(&clk_scaling->lock);
+ÂÂÂ if (clk_scaling->clk_scaling_in_progress) {
+ÂÂÂÂÂÂÂ pr_debug("%s: clocks scaling is already in-progress by mmc thread\n",
+ÂÂÂÂÂÂÂÂÂÂÂ mmc_hostname(host));
+ÂÂÂÂÂÂÂ spin_unlock_bh(&clk_scaling->lock);
+ÂÂÂÂÂÂÂ goto out;
+ÂÂÂ }
+ÂÂÂ clk_scaling->need_freq_change = true;
+ÂÂÂ clk_scaling->target_freq = *freq;
+ÂÂÂ clk_scaling->state = *freq < clk_scaling->curr_freq ?
+ÂÂÂÂÂÂÂ MMC_LOAD_LOW : MMC_LOAD_HIGH;
+ÂÂÂ spin_unlock_bh(&clk_scaling->lock);
+
+ÂÂÂ abort = __mmc_claim_host(host, NULL, &clk_scaling->devfreq_abort);
+ÂÂÂ if (abort)
+ÂÂÂÂÂÂÂ goto out;
+
+ÂÂÂ /*
+ÂÂÂÂ * In case we were able to claim host there is no need to
+ÂÂÂÂ * defer the frequency change. It will be done now
+ÂÂÂÂ */
+ÂÂÂ clk_scaling->need_freq_change = false;
+
+ÂÂÂ err = mmc_clk_update_freq(host, *freq, clk_scaling->state);
+ÂÂÂ if (err && err != -EAGAIN) {
+ÂÂÂÂÂÂÂ pr_err("%s: clock scale to %lu failed with error %d\n",
+ÂÂÂÂÂÂÂÂÂÂÂ mmc_hostname(host), *freq, err);
+ÂÂÂ } else {
+ÂÂÂÂÂÂÂ pr_debug("%s: clock change to %lu finished successfully (%s)\n",
+ÂÂÂÂÂÂÂÂÂÂÂ mmc_hostname(host), *freq, current->comm);
+ÂÂÂ }
+
+ÂÂÂ mmc_release_host(host);
+out:
+ÂÂÂ current->flags &= ~PF_MEMALLOC;
+ÂÂÂ current->flags |= pflags & PF_MEMALLOC;
+ÂÂÂ return err;
+}
+
+/**
+ * mmc_deferred_scaling() - scale clocks from data path (mmc thread context)
+ * @host: pointer to mmc host structure
+ *
+ * This function does clock scaling in case "need_freq_change" flag was set
+ * by the clock scaling logic.
+ */
+void mmc_deferred_scaling(struct mmc_host *host)
+{
+ÂÂÂ unsigned long target_freq;
+ÂÂÂ int err;
+
+ÂÂÂ if (!host->clk_scaling.enable)
+ÂÂÂÂÂÂÂ return;
+
+ÂÂÂ spin_lock_bh(&host->clk_scaling.lock);
+
+ÂÂÂ if (host->clk_scaling.clk_scaling_in_progress ||
+ÂÂÂÂÂÂÂ !(host->clk_scaling.need_freq_change)) {
+ÂÂÂÂÂÂÂ spin_unlock_bh(&host->clk_scaling.lock);
+ÂÂÂÂÂÂÂ return;
+ÂÂÂ }
+
+
+ÂÂÂ atomic_inc(&host->clk_scaling.devfreq_abort);
+ÂÂÂ target_freq = host->clk_scaling.target_freq;
+ÂÂÂ host->clk_scaling.clk_scaling_in_progress = true;
+ÂÂÂ host->clk_scaling.need_freq_change = false;
+ÂÂÂ spin_unlock_bh(&host->clk_scaling.lock);
+ÂÂÂ pr_debug("%s: doing deferred frequency change (%lu) (%s)\n",
+ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂ mmc_hostname(host),
+ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂ target_freq, current->comm);
+
+ÂÂÂ err = mmc_clk_update_freq(host, target_freq,
+ÂÂÂÂÂÂÂ host->clk_scaling.state);
+ÂÂÂ if (err && err != -EAGAIN) {
+ÂÂÂÂÂÂÂ pr_err("%s: failed on deferred scale clocks (%d)\n",
+ÂÂÂÂÂÂÂÂÂÂÂ mmc_hostname(host), err);
+ÂÂÂ } 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);
+}
+EXPORT_SYMBOL(mmc_deferred_scaling);
+
+static int mmc_devfreq_create_freq_table(struct mmc_host *host)
+{
+ÂÂÂ int i;
+ÂÂÂ struct mmc_devfeq_clk_scaling *clk_scaling = &host->clk_scaling;
+
+ÂÂÂ pr_debug("%s: supported: lowest=%lu, highest=%lu\n",
+ÂÂÂÂÂÂÂ mmc_hostname(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) {
+ÂÂÂÂÂÂÂ 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(
+ÂÂÂÂÂÂÂÂÂÂÂ (clk_scaling->freq_table_sz *
+ÂÂÂÂÂÂÂÂÂÂÂ sizeof(*(clk_scaling->freq_table))), GFP_KERNEL);
+ÂÂÂÂÂÂÂ if (!clk_scaling->freq_table)
+ÂÂÂÂÂÂÂÂÂÂÂ return -ENOMEM;
+
+ÂÂÂÂÂÂÂ 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 >
+ÂÂÂÂÂÂÂ clk_scaling->freq_table[0])
+ÂÂÂÂÂÂÂ pr_debug("%s: frequency table undershot possible freq\n",
+ÂÂÂÂÂÂÂÂÂÂÂ mmc_hostname(host));
+
+ÂÂÂ for (i = 0; i < clk_scaling->freq_table_sz; i++) {
+ÂÂÂÂÂÂÂ if (clk_scaling->freq_table[i] <=
+ÂÂÂÂÂÂÂÂÂÂÂ host->card->clk_scaling_highest)
+ÂÂÂÂÂÂÂÂÂÂÂ continue;
+ÂÂÂÂÂÂÂ clk_scaling->freq_table[i] =
+ÂÂÂÂÂÂÂÂÂÂÂ host->card->clk_scaling_highest;
+ÂÂÂÂÂÂÂ clk_scaling->freq_table_sz = i + 1;
+ÂÂÂÂÂÂÂ pr_debug("%s: frequency table overshot possible freq (%d)\n",
+ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂ mmc_hostname(host), clk_scaling->freq_table[i]);
+ÂÂÂÂÂÂÂ break;
+ÂÂÂ }
+
+out:
+ÂÂÂ /**
+ÂÂÂÂ * devfreq requires unsigned long type freq_table while the
+ÂÂÂÂ * freq_table in clk_scaling is un32. Here allocates an individual
+ÂÂÂÂ * memory space for it and release it when exit clock scaling.
+ÂÂÂÂ */
+ÂÂÂ clk_scaling->devfreq_profile.freq_table =Â kzalloc(
+ÂÂÂÂÂÂÂÂÂÂÂ clk_scaling->freq_table_sz *
+ sizeof(*(clk_scaling->devfreq_profile.freq_table)),
+ÂÂÂÂÂÂÂÂÂÂÂ GFP_KERNEL);
+ÂÂÂ if (!clk_scaling->devfreq_profile.freq_table)
+ÂÂÂÂÂÂÂ return -ENOMEM;
+ÂÂÂ clk_scaling->devfreq_profile.max_state = clk_scaling->freq_table_sz;
+
+ÂÂÂ for (i = 0; i < clk_scaling->freq_table_sz; i++) {
+ÂÂÂÂÂÂÂ clk_scaling->devfreq_profile.freq_table[i] =
+ÂÂÂÂÂÂÂÂÂÂÂ clk_scaling->freq_table[i];
+ÂÂÂÂÂÂÂ pr_debug("%s: freq[%d] = %u\n",
+ÂÂÂÂÂÂÂÂÂÂÂ mmc_hostname(host), i, clk_scaling->freq_table[i]);
+ÂÂÂ }
+
+ÂÂÂ return 0;
+}
+
+/**
+ * mmc_init_devfreq_clk_scaling() - Initialize clock scaling
+ * @host: pointer to mmc host structure
+ *
+ * Initialize clock scaling for supported hosts. It is assumed that the caller
+ * ensure clock is running at maximum possible frequency before calling this
+ * function. Shall use struct devfreq_simple_ondemand_data to configure
+ * governor.
+ */
+int mmc_init_clk_scaling(struct mmc_host *host)
+{
+ÂÂÂ int err;
+
+ÂÂÂ if (!host || !host->card) {
+ÂÂÂÂÂÂÂ pr_err("%s: unexpected host/card parameters\n",
+ÂÂÂÂÂÂÂÂÂÂÂ __func__);
+ÂÂÂÂÂÂÂ return -EINVAL;
+ÂÂÂ }
+
+ÂÂÂ if (!mmc_can_scale_clk(host) ||
+ÂÂÂÂÂÂÂ !host->bus_ops->change_bus_speed) {
+ÂÂÂÂÂÂÂ pr_debug("%s: clock scaling is not supported\n",
+ÂÂÂÂÂÂÂÂÂÂÂ mmc_hostname(host));
+ÂÂÂÂÂÂÂ return 0;
+ÂÂÂ }
+
+ÂÂÂ pr_debug("registering %s dev (%p) to devfreq",
+ÂÂÂÂÂÂÂ mmc_hostname(host),
+ÂÂÂÂÂÂÂ mmc_classdev(host));
+
+ÂÂÂ if (host->clk_scaling.devfreq) {
+ÂÂÂÂÂÂÂ pr_err("%s: dev is already registered for dev %p\n",
+ÂÂÂÂÂÂÂÂÂÂÂ mmc_hostname(host),
+ÂÂÂÂÂÂÂÂÂÂÂ mmc_dev(host));
+ÂÂÂÂÂÂÂ return -EPERM;
+ÂÂÂ }
+ÂÂÂ spin_lock_init(&host->clk_scaling.lock);
+ÂÂÂ atomic_set(&host->clk_scaling.devfreq_abort, 0);
+ÂÂÂ host->clk_scaling.curr_freq = host->ios.clock;
+ÂÂÂ host->clk_scaling.clk_scaling_in_progress = false;
+ÂÂÂ host->clk_scaling.need_freq_change = false;
+ÂÂÂ host->clk_scaling.is_busy_started = false;
+
+ÂÂÂ host->clk_scaling.devfreq_profile.polling_ms =
+ÂÂÂÂÂÂÂ host->clk_scaling.polling_delay_ms;
+ÂÂÂ host->clk_scaling.devfreq_profile.get_dev_status =
+ÂÂÂÂÂÂÂ mmc_devfreq_get_dev_status;
+ÂÂÂ host->clk_scaling.devfreq_profile.target = mmc_devfreq_set_target;
+ÂÂÂ host->clk_scaling.devfreq_profile.initial_freq = host->ios.clock;
+
+ÂÂÂ host->clk_scaling.ondemand_gov_data.simple_scaling = true;
+ÂÂÂ host->clk_scaling.ondemand_gov_data.upthreshold =
+ÂÂÂÂÂÂÂ host->clk_scaling.upthreshold;
+ÂÂÂ host->clk_scaling.ondemand_gov_data.downdifferential =
+ÂÂÂÂÂÂÂ host->clk_scaling.upthreshold - host->clk_scaling.downthreshold;
+
+ÂÂÂ err = mmc_devfreq_create_freq_table(host);
+ÂÂÂ if (err) {
+ÂÂÂÂÂÂÂ pr_err("%s: fail to create devfreq frequency table\n",
+ÂÂÂÂÂÂÂÂÂÂÂ mmc_hostname(host));
+ÂÂÂÂÂÂÂ return err;
+ÂÂÂ }
+
+ÂÂÂ pr_debug("%s: adding devfreq with: upthreshold=%u downthreshold=%u polling=%u\n",
+ÂÂÂÂÂÂÂ mmc_hostname(host),
+ÂÂÂÂÂÂÂ host->clk_scaling.ondemand_gov_data.upthreshold,
+ host->clk_scaling.ondemand_gov_data.downdifferential,
+ÂÂÂÂÂÂÂ host->clk_scaling.devfreq_profile.polling_ms);
+ÂÂÂ host->clk_scaling.devfreq = devfreq_add_device(
+ÂÂÂÂÂÂÂ mmc_classdev(host),
+ÂÂÂÂÂÂÂ &host->clk_scaling.devfreq_profile,
+ÂÂÂÂÂÂÂ "simple_ondemand",
+ÂÂÂÂÂÂÂ &host->clk_scaling.ondemand_gov_data);
+ÂÂÂ if (!host->clk_scaling.devfreq) {
+ÂÂÂÂÂÂÂ pr_err("%s: unable to register with devfreq\n",
+ÂÂÂÂÂÂÂÂÂÂÂ mmc_hostname(host));
+ÂÂÂÂÂÂÂ return -EPERM;
+ÂÂÂ }
+
+ÂÂÂ pr_debug("%s: clk scaling is enabled for device %s (%p) with devfreq %p (clock = %uHz)\n",
+ÂÂÂÂÂÂÂ mmc_hostname(host),
+ÂÂÂÂÂÂÂ dev_name(mmc_classdev(host)),
+ÂÂÂÂÂÂÂ mmc_classdev(host),
+ÂÂÂÂÂÂÂ host->clk_scaling.devfreq,
+ÂÂÂÂÂÂÂ host->ios.clock);
+
+ÂÂÂ host->clk_scaling.enable = true;
+
+ÂÂÂ return err;
+}
+EXPORT_SYMBOL(mmc_init_clk_scaling);
+
+/**
+ * mmc_exit_devfreq_clk_scaling() - Disable clock scaling
+ * @host: pointer to mmc host structure
+ *
+ * Disable clock scaling permanently.
+ */
+int mmc_exit_clk_scaling(struct mmc_host *host)
+{
+ÂÂÂ int err;
+
+ÂÂÂ if (!host) {
+ÂÂÂÂÂÂÂ pr_err("%s: bad host parameter\n", __func__);
+ÂÂÂÂÂÂÂ WARN_ON(1);
+ÂÂÂÂÂÂÂ return -EINVAL;
+ÂÂÂ }
+
+ÂÂÂ if (!mmc_can_scale_clk(host))
+ÂÂÂÂÂÂÂ return 0;
+
+ÂÂÂ if (!host->clk_scaling.devfreq) {
+ÂÂÂÂÂÂÂ pr_err("%s: %s: no devfreq is assosiated with this device\n",
+ÂÂÂÂÂÂÂÂÂÂÂ mmc_hostname(host), __func__);
+ÂÂÂÂÂÂÂ return -EPERM;
+ÂÂÂ }
+
+ÂÂÂ err = devfreq_remove_device(host->clk_scaling.devfreq);
+ÂÂÂ if (err) {
+ÂÂÂÂÂÂÂ pr_err("%s: remove devfreq failed (%d)\n",
+ÂÂÂÂÂÂÂÂÂÂÂ mmc_hostname(host), err);
+ÂÂÂÂÂÂÂ return err;
+ÂÂÂ }
+
+ÂÂÂ kfree(host->clk_scaling.devfreq_profile.freq_table);
+
+ÂÂÂ 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;
+}
+EXPORT_SYMBOL(mmc_exit_clk_scaling);
+
 static inline void mmc_complete_cmd(struct mmc_request *mrq)
 {
ÂÂÂÂÂ if (mrq->cap_cmd_during_tfr && !completion_done(&mrq->cmd_completion))
@@ -143,6 +694,9 @@ void mmc_request_done(struct mmc_host *host, struct mmc_request *mrq)
ÂÂÂÂÂ struct mmc_command *cmd = mrq->cmd;
ÂÂÂÂÂ int err = cmd->error;
 + if (host->clk_scaling.is_busy_started)
+ÂÂÂÂÂÂÂ mmc_clk_scaling_stop_busy(host, true);
+
ÂÂÂÂÂ /* Flag re-tuning needed on CRC errors */
ÂÂÂÂÂ if ((cmd->opcode != MMC_SEND_TUNING_BLOCK &&
ÂÂÂÂÂÂÂÂÂ cmd->opcode != MMC_SEND_TUNING_BLOCK_HS200) &&
@@ -354,6 +908,12 @@ int mmc_start_request(struct mmc_host *host, struct mmc_request *mrq)
ÂÂÂÂÂÂÂÂÂ return err;
 Â led_trigger_event(host->led, LED_FULL);
+
+ÂÂÂ if (mmc_is_data_request(mrq)) {
+ÂÂÂÂÂÂÂ mmc_deferred_scaling(host);
+ÂÂÂÂÂÂÂ mmc_clk_scaling_start_busy(host, true);
+ÂÂÂ }
+
ÂÂÂÂÂ __mmc_start_request(host, mrq);
 Â return 0;
diff --git a/drivers/mmc/core/core.h b/drivers/mmc/core/core.h
index 9d8f09a..fc0a9b7 100644
--- a/drivers/mmc/core/core.h
+++ b/drivers/mmc/core/core.h
@@ -34,6 +34,7 @@ struct mmc_bus_ops {
ÂÂÂÂÂ int (*shutdown)(struct mmc_host *);
ÂÂÂÂÂ int (*hw_reset)(struct mmc_host *);
ÂÂÂÂÂ int (*sw_reset)(struct mmc_host *);
+ÂÂÂ int (*change_bus_speed)(struct mmc_host *, unsigned long *);
 };
  void mmc_attach_bus(struct mmc_host *host, const struct mmc_bus_ops *ops);
@@ -46,6 +47,8 @@ struct device_node *mmc_of_find_child_device(struct mmc_host *host,
  void mmc_set_chip_select(struct mmc_host *host, int mode);
 void mmc_set_clock(struct mmc_host *host, unsigned int hz);
+int mmc_clk_update_freq(struct mmc_host *host,
+ÂÂÂÂÂÂÂ unsigned long freq, enum mmc_load state);
 void mmc_set_bus_mode(struct mmc_host *host, unsigned int mode);
 void mmc_set_bus_width(struct mmc_host *host, unsigned int width);
 u32 mmc_select_voltage(struct mmc_host *host, u32 ocr);
@@ -91,6 +94,10 @@ static inline void mmc_delay(unsigned int ms)
 void mmc_add_card_debugfs(struct mmc_card *card);
 void mmc_remove_card_debugfs(struct mmc_card *card);
 +extern bool mmc_can_scale_clk(struct mmc_host *host);
+extern int mmc_init_clk_scaling(struct mmc_host *host);
+extern int mmc_exit_clk_scaling(struct mmc_host *host);
+
 int mmc_execute_tuning(struct mmc_card *card);
 int mmc_hs200_to_hs400(struct mmc_card *card);
 int mmc_hs400_to_hs200(struct mmc_card *card);
diff --git a/drivers/mmc/core/debugfs.c b/drivers/mmc/core/debugfs.c
index d2275c5..630ca8e 100644
--- a/drivers/mmc/core/debugfs.c
+++ b/drivers/mmc/core/debugfs.c
@@ -225,6 +225,43 @@ static int mmc_clock_opt_set(void *data, u64 val)
 DEFINE_SIMPLE_ATTRIBUTE(mmc_clock_fops, mmc_clock_opt_get, mmc_clock_opt_set,
ÂÂÂÂÂ "%llu\n");
 +#include <linux/delay.h>
+
+static int mmc_scale_get(void *data, u64 *val)
+{
+ÂÂÂ struct mmc_host *host = data;
+
+ÂÂÂ *val = host->clk_scaling.curr_freq;
+
+ÂÂÂ return 0;
+}
+
+static int mmc_scale_set(void *data, u64 val)
+{
+ÂÂÂ int err = 0;
+ÂÂÂ struct mmc_host *host = data;
+
+ÂÂÂ mmc_claim_host(host);
+
+ÂÂÂ /* change frequency from sysfs manually */
+ÂÂÂ err = mmc_clk_update_freq(host, val, host->clk_scaling.state);
+ÂÂÂ if (err == -EAGAIN)
+ÂÂÂÂÂÂÂ err = 0;
+ÂÂÂ else if (err)
+ÂÂÂÂÂÂÂ pr_err("%s: clock scale to %llu failed with error %d\n",
+ÂÂÂÂÂÂÂÂÂÂÂ mmc_hostname(host), val, err);
+ÂÂÂ else
+ÂÂÂÂÂÂÂ pr_debug("%s: clock change to %llu finished successfully (%s)\n",
+ÂÂÂÂÂÂÂÂÂÂÂ mmc_hostname(host), val, current->comm);
+
+ÂÂÂ mmc_release_host(host);
+
+ÂÂÂ return err;
+}
+
+DEFINE_SIMPLE_ATTRIBUTE(mmc_scale_fops, mmc_scale_get, mmc_scale_set,
+ÂÂÂ "%llu\n");
+
 void mmc_add_host_debugfs(struct mmc_host *host)
 {
ÂÂÂÂÂ struct dentry *root;
@@ -253,6 +290,15 @@ void mmc_add_host_debugfs(struct mmc_host *host)
ÂÂÂÂÂÂÂÂÂÂÂÂÂ &mmc_clock_fops))
ÂÂÂÂÂÂÂÂÂ goto err_node;
 + if (!debugfs_create_file("scale", 0600, root, host,
+ÂÂÂÂÂÂÂ &mmc_scale_fops))
+ÂÂÂÂÂÂÂ goto err_node;
+
+ÂÂÂ if (!debugfs_create_bool("skip_clk_scale_freq_update",
+ÂÂÂÂÂÂÂ 0600, root,
+ÂÂÂÂÂÂÂ &host->clk_scaling.skip_clk_scale_freq_update))
+ÂÂÂÂÂÂÂ goto err_node;
+
 #ifdef CONFIG_FAIL_MMC_REQUEST
ÂÂÂÂÂ if (fail_request)
ÂÂÂÂÂÂÂÂÂ setup_fault_attr(&fail_default_attr, fail_request);
diff --git a/drivers/mmc/core/host.c b/drivers/mmc/core/host.c
index abf9e88..1e46aa4 100644
--- a/drivers/mmc/core/host.c
+++ b/drivers/mmc/core/host.c
@@ -32,6 +32,10 @@
 #include "pwrseq.h"
 #include "sdio_ops.h"
 +#define MMC_DEVFRQ_DEFAULT_UP_THRESHOLD 35
+#define MMC_DEVFRQ_DEFAULT_DOWN_THRESHOLD 5
+#define MMC_DEVFRQ_DEFAULT_POLLING_MSEC 100
+
 #define cls_dev_to_mmc_host(d) container_of(d, struct mmc_host, class_dev)
  static DEFINE_IDA(mmc_host_ida);
@@ -435,6 +439,10 @@ int mmc_add_host(struct mmc_host *host)
ÂÂÂÂÂÂÂÂÂ return err;
led_trigger_register_simple(dev_name(&host->class_dev), &host->led);
+ÂÂÂ host->clk_scaling.upthreshold = MMC_DEVFRQ_DEFAULT_UP_THRESHOLD;
+ÂÂÂ host->clk_scaling.downthreshold = MMC_DEVFRQ_DEFAULT_DOWN_THRESHOLD;
+ÂÂÂ host->clk_scaling.polling_delay_ms = MMC_DEVFRQ_DEFAULT_POLLING_MSEC;
+ÂÂÂ host->clk_scaling.skip_clk_scale_freq_update = false;
  #ifdef CONFIG_DEBUG_FS
ÂÂÂÂÂ mmc_add_host_debugfs(host);
diff --git a/drivers/mmc/core/mmc.c b/drivers/mmc/core/mmc.c
index 4466f5d..c8aedf3 100644
--- a/drivers/mmc/core/mmc.c
+++ b/drivers/mmc/core/mmc.c
@@ -1526,6 +1526,170 @@ static int mmc_hs200_tuning(struct mmc_card *card)
 }
  /*
+ * Scale down from HS400 to HS in order to allow frequency change.
+ * This is needed for cards that doesn't support changing frequency in HS400
+ */
+static int mmc_scale_low(struct mmc_host *host, unsigned long freq)
+{
+ÂÂÂ int err = 0;
+
+ÂÂÂ mmc_set_timing(host, MMC_TIMING_LEGACY);
+ÂÂÂ mmc_set_clock(host, MMC_HIGH_26_MAX_DTR);
+
+ÂÂÂ err = mmc_select_hs(host->card);
+ÂÂÂ if (err) {
+ÂÂÂÂÂÂÂ pr_err("%s: %s: scaling low: failed (%d)\n",
+ÂÂÂÂÂÂÂÂÂÂÂÂÂÂ mmc_hostname(host), __func__, err);
+ÂÂÂÂÂÂÂ return err;
+ÂÂÂ }
+
+ÂÂÂ err = mmc_select_bus_width(host->card);
+ÂÂÂ if (err < 0) {
+ÂÂÂÂÂÂÂ pr_err("%s: %s: select_bus_width failed(%d)\n",
+ÂÂÂÂÂÂÂÂÂÂÂ mmc_hostname(host), __func__, err);
+ÂÂÂÂÂÂÂ return err;
+ÂÂÂ }
+
+ÂÂÂ mmc_set_clock(host, freq);
+
+ÂÂÂ return 0;
+}
+
+/*
+ * Scale UP from HS to HS200/H400
+ */
+static int mmc_scale_high(struct mmc_host *host)
+{
+ÂÂÂ int err = 0;
+
+ÂÂÂ if (mmc_card_ddr52(host->card)) {
+ÂÂÂÂÂÂÂ mmc_set_timing(host, MMC_TIMING_LEGACY);
+ÂÂÂÂÂÂÂ mmc_set_clock(host, MMC_HIGH_26_MAX_DTR);
+ÂÂÂ }
+
+ÂÂÂ if (!host->card->ext_csd.strobe_support) {
+ÂÂÂÂÂÂÂ if (!(host->card->mmc_avail_type & EXT_CSD_CARD_TYPE_HS200)) {
+ÂÂÂÂÂÂÂÂÂÂÂ pr_err("%s: %s: card does not support HS200\n",
+ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂ mmc_hostname(host), __func__);
+ÂÂÂÂÂÂÂÂÂÂÂ WARN_ON(1);
+ÂÂÂÂÂÂÂÂÂÂÂ return -EPERM;
+ÂÂÂÂÂÂÂ }
+
+ÂÂÂÂÂÂÂ err = mmc_select_hs200(host->card);
+ÂÂÂÂÂÂÂ if (err) {
+ÂÂÂÂÂÂÂÂÂÂÂ pr_err("%s: %s: selecting HS200 failed (%d)\n",
+ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂ mmc_hostname(host), __func__, err);
+ÂÂÂÂÂÂÂÂÂÂÂ return err;
+ÂÂÂÂÂÂÂ }
+
+ÂÂÂÂÂÂÂ mmc_set_bus_speed(host->card);
+
+ÂÂÂÂÂÂÂ err = mmc_hs200_tuning(host->card);
+ÂÂÂÂÂÂÂ if (err) {
+ÂÂÂÂÂÂÂÂÂÂÂ pr_err("%s: %s: hs200 tuning failed (%d)\n",
+ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂ mmc_hostname(host), __func__, err);
+ÂÂÂÂÂÂÂÂÂÂÂ return err;
+ÂÂÂÂÂÂÂ }
+
+ÂÂÂÂÂÂÂ if (!(host->card->mmc_avail_type & EXT_CSD_CARD_TYPE_HS400)) {
+ÂÂÂÂÂÂÂÂÂÂÂ pr_debug("%s: card does not support HS400\n",
+ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂ mmc_hostname(host));
+ÂÂÂÂÂÂÂÂÂÂÂ return 0;
+ÂÂÂÂÂÂÂ }
+ÂÂÂ }
+
+ÂÂÂ err = mmc_select_hs400(host->card);
+ÂÂÂ if (err) {
+ÂÂÂÂÂÂÂ pr_err("%s: %s: select hs400 failed (%d)\n",
+ÂÂÂÂÂÂÂÂÂÂÂ mmc_hostname(host), __func__, err);
+ÂÂÂÂÂÂÂ return err;
+ÂÂÂ }
+
+ÂÂÂ return err;
+}
+
+static int mmc_set_clock_bus_speed(struct mmc_card *card, unsigned long freq)
+{
+ÂÂÂ int err = 0;
+
+ÂÂÂ if (freq == MMC_HS200_MAX_DTR)
+ÂÂÂÂÂÂÂ err = mmc_scale_high(card->host);
+ÂÂÂ else
+ÂÂÂÂÂÂÂ err = mmc_scale_low(card->host, freq);
+
+ÂÂÂ return err;
+}
+
+static inline unsigned long mmc_ddr_freq_accommodation(unsigned long freq)
+{
+ÂÂÂ if (freq == MMC_HIGH_DDR_MAX_DTR)
+ÂÂÂÂÂÂÂ return freq;
+
+ÂÂÂ return freq/2;
+}
+
+/**
+ * mmc_change_bus_speed() - Change MMC card bus frequency at runtime
+ * @host: pointer to mmc host structure
+ * @freq: pointer to desired frequency to be set
+ *
+ * Change the MMC card bus frequency at runtime after the card is
+ * initialized. Callers are expected to make sure of the card's
+ * state (DATA/RCV/TRANSFER) before changing the frequency at runtime.
+ *
+ * If the frequency to change is greater than max. supported by card,
+ * *freq is changed to max. supported by card. If it is less than min.
+ * supported by host, *freq is changed to min. supported by host.
+ * Host is assumed to be calimed while calling this funciton.
+ */
+static int mmc_change_bus_speed(struct mmc_host *host, unsigned long *freq)
+{
+ÂÂÂ int err = 0;
+ÂÂÂ struct mmc_card *card;
+ÂÂÂ unsigned long actual_freq;
+
+ÂÂÂ card = host->card;
+
+ÂÂÂ if (!card || !freq) {
+ÂÂÂÂÂÂÂ err = -EINVAL;
+ÂÂÂÂÂÂÂ goto out;
+ÂÂÂ }
+ÂÂÂ actual_freq = *freq;
+
+ÂÂÂ WARN_ON(!host->claimed);
+
+ÂÂÂ /*
+ÂÂÂÂ * For scaling up/down HS400 we'll need special handling,
+ÂÂÂÂ * for other timings we can simply do clock frequency change
+ÂÂÂÂ */
+ÂÂÂ if (mmc_card_hs400(card) ||
+ÂÂÂÂÂÂÂ (!mmc_card_hs200(host->card) && *freq == MMC_HS200_MAX_DTR)) {
+ÂÂÂÂÂÂÂ err = mmc_set_clock_bus_speed(card, *freq);
+ÂÂÂÂÂÂÂ if (err) {
+ÂÂÂÂÂÂÂÂÂÂÂ pr_err("%s: %s: failed (%d)to set bus and clock speed (freq=%lu)\n",
+ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂ mmc_hostname(host), __func__, err, *freq);
+ÂÂÂÂÂÂÂÂÂÂÂ goto out;
+ÂÂÂÂÂÂÂ }
+ÂÂÂ } else if (mmc_card_hs200(host->card)) {
+ÂÂÂÂÂÂÂ mmc_set_clock(host, *freq);
+ÂÂÂÂÂÂÂ err = mmc_hs200_tuning(host->card);
+ÂÂÂÂÂÂÂ if (err) {
+ÂÂÂÂÂÂÂÂÂÂÂ pr_warn("%s: %s: tuning execution failed %d\n",
+ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂ mmc_hostname(card->host),
+ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂ __func__, err);
+ÂÂÂÂÂÂÂÂÂÂÂ mmc_set_clock(host, host->clk_scaling.curr_freq);
+ÂÂÂÂÂÂÂ }
+ÂÂÂ } else {
+ÂÂÂÂÂÂÂ if (mmc_card_ddr52(host->card))
+ÂÂÂÂÂÂÂÂÂÂÂ actual_freq = mmc_ddr_freq_accommodation(*freq);
+ÂÂÂÂÂÂÂ mmc_set_clock(host, actual_freq);
+ÂÂÂ }
+
+out:
+ÂÂÂ return err;
+}
+
+/*
ÂÂ * Handle the detection and initialisation of a card.
ÂÂ *
ÂÂ * In the case of a resume, "oldcard" will contain the card
@@ -1751,6 +1915,16 @@ static int mmc_init_card(struct mmc_host *host, u32 ocr,
ÂÂÂÂÂÂÂÂÂ }
ÂÂÂÂÂ }
 + card->clk_scaling_lowest = host->f_min;
+ÂÂÂ if ((card->mmc_avail_type & EXT_CSD_CARD_TYPE_HS400) ||
+ÂÂÂÂÂÂÂÂÂÂÂ (card->mmc_avail_type & EXT_CSD_CARD_TYPE_HS200))
+ÂÂÂÂÂÂÂ card->clk_scaling_highest = card->ext_csd.hs200_max_dtr;
+ÂÂÂ else if ((card->mmc_avail_type & EXT_CSD_CARD_TYPE_HS) ||
+ÂÂÂÂÂÂÂÂÂÂÂ (card->mmc_avail_type & EXT_CSD_CARD_TYPE_DDR_52))
+ÂÂÂÂÂÂÂ card->clk_scaling_highest = card->ext_csd.hs_max_dtr;
+ÂÂÂ else
+ÂÂÂÂÂÂÂ card->clk_scaling_highest = card->csd.max_dtr;
+
ÂÂÂÂÂ /*
ÂÂÂÂÂÂ * Choose the power class with selected bus interface
ÂÂÂÂÂÂ */
@@ -1942,6 +2116,7 @@ static int mmc_poweroff_notify(struct mmc_card *card, unsigned int notify_type)
ÂÂ */
 static void mmc_remove(struct mmc_host *host)
 {
+ÂÂÂ mmc_exit_clk_scaling(host);
ÂÂÂÂÂ mmc_remove_card(host->card);
ÂÂÂÂÂ host->card = NULL;
 }
@@ -2064,6 +2239,13 @@ static int mmc_shutdown(struct mmc_host *host)
ÂÂÂÂÂ int err = 0;
 Â /*
+ÂÂÂÂ * 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(host);
+
+ÂÂÂ /*
ÂÂÂÂÂÂ * In a specific case for poweroff notify, we need to resume the card
ÂÂÂÂÂÂ * before we can shutdown it properly.
ÂÂÂÂÂÂ */
@@ -2132,6 +2314,7 @@ static int mmc_can_reset(struct mmc_card *card)
 static int _mmc_hw_reset(struct mmc_host *host)
 {
ÂÂÂÂÂ struct mmc_card *card = host->card;
+ÂÂÂ int ret;
 Â /*
ÂÂÂÂÂÂ * In the case of recovery, we can't expect flushing the cache to work
@@ -2151,7 +2334,15 @@ static int _mmc_hw_reset(struct mmc_host *host)
ÂÂÂÂÂÂÂÂÂ mmc_power_cycle(host, card->ocr);
ÂÂÂÂÂÂÂÂÂ mmc_pwrseq_reset(host);
ÂÂÂÂÂ }
-ÂÂÂ return mmc_init_card(host, card->ocr, card);
+
+ÂÂÂ ret = mmc_init_card(host, card->ocr, card);
+ÂÂÂ if (ret) {
+ÂÂÂÂÂÂÂ pr_err("%s: %s: mmc_init_card failed (%d)\n",
+ÂÂÂÂÂÂÂÂÂÂÂ mmc_hostname(host), __func__, ret);
+ÂÂÂÂÂÂÂ return ret;
+ÂÂÂ }
+
+ÂÂÂ return ret;
 }
  static const struct mmc_bus_ops mmc_ops = {
@@ -2164,6 +2355,7 @@ static int _mmc_hw_reset(struct mmc_host *host)
ÂÂÂÂÂ .alive = mmc_alive,
ÂÂÂÂÂ .shutdown = mmc_shutdown,
ÂÂÂÂÂ .hw_reset = _mmc_hw_reset,
+ÂÂÂ .change_bus_speed = mmc_change_bus_speed,
 };
  /*
@@ -2220,6 +2412,12 @@ int mmc_attach_mmc(struct mmc_host *host)
ÂÂÂÂÂÂÂÂÂ goto remove_card;
 Â mmc_claim_host(host);
+ÂÂÂ err = mmc_init_clk_scaling(host);
+ÂÂÂ if (err) {
+ÂÂÂÂÂÂÂ mmc_release_host(host);
+ÂÂÂÂÂÂÂ goto remove_card;
+ÂÂÂ }
+
ÂÂÂÂÂ return 0;
  remove_card:
diff --git a/drivers/mmc/core/sd.c b/drivers/mmc/core/sd.c
index d0d9f90..40144c1 100644
--- a/drivers/mmc/core/sd.c
+++ b/drivers/mmc/core/sd.c
@@ -892,7 +892,10 @@ unsigned mmc_sd_get_max_clock(struct mmc_card *card)
 {
ÂÂÂÂÂ unsigned max_dtr = (unsigned int)-1;
 - if (mmc_card_hs(card)) {
+ÂÂÂ if (mmc_card_uhs(card)) {
+ÂÂÂÂÂÂÂ if (max_dtr > card->sw_caps.uhs_max_dtr)
+ÂÂÂÂÂÂÂÂÂÂÂ max_dtr = card->sw_caps.uhs_max_dtr;
+ÂÂÂ } else if (mmc_card_hs(card)) {
ÂÂÂÂÂÂÂÂÂ if (max_dtr > card->sw_caps.hs_max_dtr)
ÂÂÂÂÂÂÂÂÂÂÂÂÂ max_dtr = card->sw_caps.hs_max_dtr;
ÂÂÂÂÂ } else if (max_dtr > card->csd.max_dtr) {
@@ -1059,6 +1062,9 @@ static int mmc_sd_init_card(struct mmc_host *host, u32 ocr,
ÂÂÂÂÂÂÂÂÂ }
ÂÂÂÂÂ }
 + card->clk_scaling_highest = mmc_sd_get_max_clock(card);
+ÂÂÂ card->clk_scaling_lowest = host->f_min;
+
ÂÂÂÂÂ if (host->caps2 & MMC_CAP2_AVOID_3_3V &&
ÂÂÂÂÂÂÂÂÂ host->ios.signal_voltage == MMC_SIGNAL_VOLTAGE_330) {
ÂÂÂÂÂÂÂÂÂ pr_err("%s: Host failed to negotiate down from 3.3V\n",
@@ -1082,6 +1088,7 @@ static int mmc_sd_init_card(struct mmc_host *host, u32 ocr,
ÂÂ */
 static void mmc_sd_remove(struct mmc_host *host)
 {
+ÂÂÂ mmc_exit_clk_scaling(host);
ÂÂÂÂÂ mmc_remove_card(host->card);
ÂÂÂÂÂ host->card = NULL;
 }
@@ -1228,6 +1235,62 @@ static int mmc_sd_hw_reset(struct mmc_host *host)
ÂÂÂÂÂ return mmc_sd_init_card(host, host->card->ocr, host->card);
 }
 +/**
+ * mmc_sd_change_bus_speed() - Change SD card bus frequency at runtime
+ * @host: pointer to mmc host structure
+ * @freq: pointer to desired frequency to be set
+ *
+ * Change the SD card bus frequency at runtime after the card is
+ * initialized. Callers are expected to make sure of the card's
+ * state (DATA/RCV/TRANSFER) beforing changing the frequency at runtime.
+ *
+ * If the frequency to change is greater than max. supported by card,
+ * *freq is changed to max. supported by card and if it is less than min.
+ * supported by host, *freq is changed to min. supported by host.
+ */
+static int mmc_sd_change_bus_speed(struct mmc_host *host, unsigned long *freq)
+{
+ÂÂÂ int err = 0;
+ÂÂÂ struct mmc_card *card;
+
+ÂÂÂ mmc_claim_host(host);
+ÂÂÂ /*
+ÂÂÂÂ * Assign card pointer after claiming host to avoid race
+ÂÂÂÂ * conditions that may arise during removal of the card.
+ÂÂÂÂ */
+ÂÂÂ card = host->card;
+
+ÂÂÂ /* sanity checks */
+ÂÂÂ if (!card || !freq) {
+ÂÂÂÂÂÂÂ err = -EINVAL;
+ÂÂÂÂÂÂÂ goto out;
+ÂÂÂ }
+
+ÂÂÂ mmc_set_clock(host, (unsigned int) (*freq));
+
+ÂÂÂ if (!mmc_host_is_spi(card->host) && mmc_card_uhs(card)
+ÂÂÂÂÂÂÂÂÂÂÂ && card->host->ops->execute_tuning) {
+ÂÂÂÂÂÂÂ /*
+ÂÂÂÂÂÂÂÂ * We try to probe host driver for tuning for any
+ÂÂÂÂÂÂÂÂ * frequency, it is host driver responsibility to
+ÂÂÂÂÂÂÂÂ * perform actual tuning only when required.
+ÂÂÂÂÂÂÂÂ */
+ÂÂÂÂÂÂÂ err = card->host->ops->execute_tuning(card->host,
+ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂ MMC_SEND_TUNING_BLOCK);
+
+ÂÂÂÂÂÂÂ if (err) {
+ÂÂÂÂÂÂÂÂÂÂÂ pr_warn("%s: %s: tuning execution failed %d. Restoring to previous clock %lu\n",
+ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂ mmc_hostname(card->host), __func__, err,
+ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂ host->clk_scaling.curr_freq);
+ÂÂÂÂÂÂÂÂÂÂÂ mmc_set_clock(host, host->clk_scaling.curr_freq);
+ÂÂÂÂÂÂÂ }
+ÂÂÂ }
+
+out:
+ÂÂÂ mmc_release_host(host);
+ÂÂÂ return err;
+}
+
 static const struct mmc_bus_ops mmc_sd_ops = {
ÂÂÂÂÂ .remove = mmc_sd_remove,
ÂÂÂÂÂ .detect = mmc_sd_detect,
@@ -1238,6 +1301,7 @@ static int mmc_sd_hw_reset(struct mmc_host *host)
ÂÂÂÂÂ .alive = mmc_sd_alive,
ÂÂÂÂÂ .shutdown = mmc_sd_suspend,
ÂÂÂÂÂ .hw_reset = mmc_sd_hw_reset,
+ÂÂÂ .change_bus_speed = mmc_sd_change_bus_speed,
 };
  /*
@@ -1292,6 +1356,12 @@ int mmc_attach_sd(struct mmc_host *host)
ÂÂÂÂÂÂÂÂÂ goto remove_card;
 Â mmc_claim_host(host);
+ÂÂÂ err = mmc_init_clk_scaling(host);
+ÂÂÂ if (err) {
+ÂÂÂÂÂÂÂ mmc_release_host(host);
+ÂÂÂÂÂÂÂ goto remove_card;
+ÂÂÂ }
+
ÂÂÂÂÂ return 0;
  remove_card:
diff --git a/drivers/mmc/host/sdhci-msm.c b/drivers/mmc/host/sdhci-msm.c
index b5519a5..e9fe8c6 100644
--- a/drivers/mmc/host/sdhci-msm.c
+++ b/drivers/mmc/host/sdhci-msm.c
@@ -1705,6 +1705,43 @@ static int sdhci_msm_register_vreg(struct sdhci_msm_host *msm_host)
  MODULE_DEVICE_TABLE(of, sdhci_msm_dt_match);
 +int sdhci_msm_dt_get_array(struct device *dev, const char *prop_name,
+ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂ u32 **out, int *len, u32 size)
+{
+ÂÂÂ int ret = 0;
+ÂÂÂ struct device_node *np = dev->of_node;
+ÂÂÂ size_t sz;
+ÂÂÂ u32 *arr = NULL;
+
+ÂÂÂ if (!of_get_property(np, prop_name, len)) {
+ÂÂÂÂÂÂÂ ret = -EINVAL;
+ÂÂÂÂÂÂÂ goto out;
+ÂÂÂ }
+ÂÂÂ sz = *len = *len / sizeof(*arr);
+ÂÂÂ if (sz <= 0 || (size > 0 && (sz > size))) {
+ÂÂÂÂÂÂÂ dev_err(dev, "%s invalid size\n", prop_name);
+ÂÂÂÂÂÂÂ ret = -EINVAL;
+ÂÂÂÂÂÂÂ goto out;
+ÂÂÂ }
+
+ÂÂÂ arr = devm_kzalloc(dev, sz * sizeof(*arr), GFP_KERNEL);
+ÂÂÂ if (!arr) {
+ÂÂÂÂÂÂÂ ret = -ENOMEM;
+ÂÂÂÂÂÂÂ goto out;
+ÂÂÂ }
+
+ÂÂÂ ret = of_property_read_u32_array(np, prop_name, arr, sz);
+ÂÂÂ if (ret < 0) {
+ÂÂÂÂÂÂÂ dev_err(dev, "%s failed reading array %d\n", prop_name, ret);
+ÂÂÂÂÂÂÂ goto out;
+ÂÂÂ }
+ÂÂÂ *out = arr;
+out:
+ÂÂÂ if (ret)
+ÂÂÂÂÂÂÂ *len = 0;
+ÂÂÂ return ret;
+}
+
 static const struct sdhci_ops sdhci_msm_ops = {
ÂÂÂÂÂ .reset = sdhci_reset,
ÂÂÂÂÂ .set_clock = sdhci_msm_set_clock,
diff --git a/drivers/mmc/host/sdhci-pltfm.c b/drivers/mmc/host/sdhci-pltfm.c
index 02bea61..354fc68 100644
--- a/drivers/mmc/host/sdhci-pltfm.c
+++ b/drivers/mmc/host/sdhci-pltfm.c
@@ -36,6 +36,9 @@
 #endif
 #include "sdhci-pltfm.h"
 +int sdhci_msm_dt_get_array(struct device *dev, const char *prop_name,
+ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂ u32 **out, int *len, u32 size);
+
 unsigned int sdhci_pltfm_clk_get_max_clock(struct sdhci_host *host)
 {
ÂÂÂÂÂ struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
@@ -101,6 +104,14 @@ void sdhci_get_of_property(struct platform_device *pdev)
 Â of_property_read_u32(np, "clock-frequency", &pltfm_host->clock);
 + if (sdhci_msm_dt_get_array(&pdev->dev, "qcom,devfreq,freq-table",
+ÂÂÂÂÂÂÂÂÂÂÂ &host->mmc->clk_scaling.pltfm_freq_table,
+ &host->mmc->clk_scaling.pltfm_freq_table_sz, 0))
+ÂÂÂÂÂÂÂ pr_debug("no clock scaling frequencies were supplied\n");
+ÂÂÂ else if (!host->mmc->clk_scaling.pltfm_freq_table ||
+ÂÂÂÂÂÂÂÂÂÂÂ !host->mmc->clk_scaling.pltfm_freq_table_sz)
+ÂÂÂÂÂÂÂ pr_err("bad dts clock scaling frequencies\n");
+
ÂÂÂÂÂ if (of_find_property(np, "keep-power-in-suspend", NULL))
ÂÂÂÂÂÂÂÂÂ host->mmc->pm_caps |= MMC_PM_KEEP_POWER;
 diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c
index 162b9af..f0aafab 100644
--- a/drivers/mmc/host/sdhci.c
+++ b/drivers/mmc/host/sdhci.c
@@ -2427,6 +2427,32 @@ static void sdhci_card_event(struct mmc_host *mmc)
ÂÂÂÂÂ spin_unlock_irqrestore(&host->lock, flags);
 }
 +static inline void sdhci_update_power_policy(struct sdhci_host *host,
+ÂÂÂÂÂÂÂ enum sdhci_power_policy policy)
+{
+ÂÂÂ host->power_policy = policy;
+}
+
+static int sdhci_notify_load(struct mmc_host *mmc, enum mmc_load state)
+{
+ÂÂÂ int err = 0;
+ÂÂÂ struct sdhci_host *host = mmc_priv(mmc);
+
+ÂÂÂ switch (state) {
+ÂÂÂ case MMC_LOAD_HIGH:
+ÂÂÂÂÂÂÂ sdhci_update_power_policy(host, SDHCI_PERFORMANCE_MODE);
+ÂÂÂÂÂÂÂ break;
+ÂÂÂ case MMC_LOAD_LOW:
+ÂÂÂÂÂÂÂ sdhci_update_power_policy(host, SDHCI_POWER_SAVE_MODE);
+ÂÂÂÂÂÂÂ break;
+ÂÂÂ default:
+ÂÂÂÂÂÂÂ err = -EINVAL;
+ÂÂÂÂÂÂÂ break;
+ÂÂÂ }
+
+ÂÂÂ return err;
+}
+
 static const struct mmc_host_ops sdhci_ops = {
ÂÂÂÂÂ .requestÂÂÂ = sdhci_request,
ÂÂÂÂÂ .post_reqÂÂÂ = sdhci_post_req,
@@ -2441,6 +2467,7 @@ static void sdhci_card_event(struct mmc_host *mmc)
ÂÂÂÂÂ .execute_tuningÂÂÂÂÂÂÂÂÂÂÂ = sdhci_execute_tuning,
ÂÂÂÂÂ .card_eventÂÂÂÂÂÂÂÂÂÂÂ = sdhci_card_event,
ÂÂÂÂÂ .card_busyÂÂÂ = sdhci_card_busy,
+ÂÂÂ .notify_load = sdhci_notify_load,
 };
/*****************************************************************************\
diff --git a/drivers/mmc/host/sdhci.h b/drivers/mmc/host/sdhci.h
index 3b0c97a..740471f 100644
--- a/drivers/mmc/host/sdhci.h
+++ b/drivers/mmc/host/sdhci.h
@@ -346,6 +346,12 @@ enum sdhci_cookie {
ÂÂÂÂÂ COOKIE_MAPPED,ÂÂÂÂÂÂÂ /* mapped by sdhci_prepare_data() */
 };
 +enum sdhci_power_policy {
+ÂÂÂ SDHCI_PERFORMANCE_MODE,
+ÂÂÂ SDHCI_POWER_SAVE_MODE,
+ÂÂÂ SDHCI_POWER_POLICY_NUM /* Always keep this one last */
+};
+
 struct sdhci_host {
ÂÂÂÂÂ /* Data set by hardware interface driver */
ÂÂÂÂÂ const char *hw_name;ÂÂÂ /* Hardware bus name */
@@ -562,6 +568,8 @@ struct sdhci_host {
ÂÂÂÂÂ /* Delay (ms) between tuning commands */
ÂÂÂÂÂ intÂÂÂÂÂÂÂÂÂÂÂ tuning_delay;
 + enum sdhci_power_policy power_policy;
+
ÂÂÂÂÂ /* Host SDMA buffer boundary. */
ÂÂÂÂÂ u32ÂÂÂÂÂÂÂÂÂÂÂ sdma_boundary;
 diff --git a/include/linux/mmc/card.h b/include/linux/mmc/card.h
index de73778..c713581 100644
--- a/include/linux/mmc/card.h
+++ b/include/linux/mmc/card.h
@@ -245,6 +245,10 @@ struct mmc_card {
ÂÂÂÂÂ struct mmc_hostÂÂÂÂÂÂÂ *host;ÂÂÂÂÂÂÂ /* the host this device belongs to */
ÂÂÂÂÂ struct deviceÂÂÂÂÂÂÂ dev;ÂÂÂÂÂÂÂ /* the device */
ÂÂÂÂÂ u32ÂÂÂÂÂÂÂÂÂÂÂ ocr;ÂÂÂÂÂÂÂ /* the current OCR setting */
+ÂÂÂ unsigned longÂÂÂÂÂÂÂ clk_scaling_lowest;ÂÂÂ /* lowest scaleable*/
+ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂ /* frequency */
+ÂÂÂ unsigned longÂÂÂÂÂÂÂ clk_scaling_highest;ÂÂÂ /* highest scaleable */
+
ÂÂÂÂÂ unsigned intÂÂÂÂÂÂÂ rca;ÂÂÂÂÂÂÂ /* relative card address of device */
ÂÂÂÂÂ unsigned intÂÂÂÂÂÂÂ type;ÂÂÂÂÂÂÂ /* card type */
 #define MMC_TYPE_MMC 0 /* MMC card */
@@ -308,6 +312,7 @@ struct mmc_card {
ÂÂÂÂÂ unsigned intÂÂÂ nr_parts;
 Â unsigned int bouncesz; /* Bounce buffer size */
+ÂÂÂ unsigned intÂÂÂÂÂÂÂ part_curr;
 };
  static inline bool mmc_large_sector(struct mmc_card *card)
diff --git a/include/linux/mmc/host.h b/include/linux/mmc/host.h
index 64300a4..321ab39 100644
--- a/include/linux/mmc/host.h
+++ b/include/linux/mmc/host.h
@@ -12,6 +12,7 @@
  #include <linux/sched.h>
 #include <linux/device.h>
+#include <linux/devfreq.h>
 #include <linux/fault-inject.h>
  #include <linux/mmc/core.h>
@@ -82,6 +83,12 @@ struct mmc_ios {
  struct mmc_host;
 +/* states to represent load on the host */
+enum mmc_load {
+ÂÂÂ MMC_LOAD_HIGH,
+ÂÂÂ MMC_LOAD_LOW,
+};
+
 struct mmc_host_ops {
ÂÂÂÂÂ /*
ÂÂÂÂÂÂ * It is optional for the host to implement pre_req and post_req in
@@ -161,6 +168,7 @@ struct mmc_host_ops {
ÂÂÂÂÂÂ */
ÂÂÂÂÂ intÂÂÂ (*multi_io_quirk)(struct mmc_card *card,
ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂ unsigned int direction, int blk_size);
+ÂÂÂ intÂÂÂ (*notify_load)(struct mmc_host *, enum mmc_load);
 };
  struct mmc_cqe_ops {
@@ -260,9 +268,60 @@ struct mmc_ctx {
ÂÂÂÂÂ struct task_struct *task;
 };
 +/**
+ * struct mmc_devfeq_clk_scaling - main context for MMC clock scaling logic
+ *
+ * @lock: spinlock to protect statistics
+ * @devfreq: struct that represent mmc-host as a client for devfreq
+ * @devfreq_profile: MMC device profile, mostly polling interval and callbacks
+ * @ondemand_gov_data: struct supplied to ondemmand governor (thresholds)
+ * @state: load state, can be HIGH or LOW. used to notify mmc_host_ops callback
+ * @start_busy: timestamped armed once a data request is started
+ * @measure_interval_start: timestamped armed once a measure interval started
+ * @devfreq_abort: flag to sync between different contexts relevant to devfreq
+ * @skip_clk_scale_freq_update: flag that enable/disable frequency change
+ * @freq_table_sz: table size of frequencies supplied to devfreq
+ * @freq_table: frequencies table supplied to devfreq
+ * @curr_freq: current frequency
+ * @polling_delay_ms: polling interval for status collection used by devfreq
+ * @upthreshold: up-threshold supplied to ondemand governor
+ * @downthreshold: down-threshold supplied to ondemand governor
+ * @need_freq_change: flag indicating if a frequency change is required
+ * @clk_scaling_in_progress: flag indicating if there's ongoing frequency change
+ * @is_busy_started: flag indicating if a request is handled by the HW
+ * @enable: flag indicating if the clock scaling logic is enabled for this host
+ */
+struct mmc_devfeq_clk_scaling {
+ÂÂÂ spinlock_tÂÂÂ lock;
+ÂÂÂ structÂÂÂÂÂÂÂ devfreq *devfreq;
+ÂÂÂ structÂÂÂÂÂÂÂ devfreq_dev_profile devfreq_profile;
+ÂÂÂ structÂÂÂÂÂÂÂ devfreq_simple_ondemand_data ondemand_gov_data;
+ÂÂÂ enum mmc_loadÂÂÂ state;
+ÂÂÂ ktime_tÂÂÂÂÂÂÂ start_busy;
+ÂÂÂ ktime_tÂÂÂÂÂÂÂ measure_interval_start;
+ÂÂÂ atomic_tÂÂÂ devfreq_abort;
+ÂÂÂ boolÂÂÂÂÂÂÂ skip_clk_scale_freq_update;
+ÂÂÂ intÂÂÂÂÂÂÂ freq_table_sz;
+ÂÂÂ intÂÂÂÂÂÂÂ pltfm_freq_table_sz;
+ÂÂÂ u32ÂÂÂÂÂÂÂ *freq_table;
+ÂÂÂ u32ÂÂÂÂÂÂÂ *pltfm_freq_table;
+ÂÂÂ unsigned longÂÂÂ total_busy_time_us;
+ÂÂÂ unsigned longÂÂÂ target_freq;
+ÂÂÂ unsigned longÂÂÂ curr_freq;
+ÂÂÂ unsigned longÂÂÂ polling_delay_ms;
+ÂÂÂ unsigned intÂÂÂ upthreshold;
+ÂÂÂ unsigned intÂÂÂ downthreshold;
+ÂÂÂ boolÂÂÂÂÂÂÂ need_freq_change;
+ÂÂÂ boolÂÂÂÂÂÂÂ clk_scaling_in_progress;
+ÂÂÂ boolÂÂÂÂÂÂÂ is_busy_started;
+ÂÂÂ boolÂÂÂÂÂÂÂ enable;
+};
+
+
 struct mmc_host {
ÂÂÂÂÂ struct deviceÂÂÂÂÂÂÂ *parent;
ÂÂÂÂÂ struct deviceÂÂÂÂÂÂÂ class_dev;
+ÂÂÂ struct mmc_devfeq_clk_scalingÂÂÂ clk_scaling;
ÂÂÂÂÂ intÂÂÂÂÂÂÂÂÂÂÂ index;
ÂÂÂÂÂ const struct mmc_host_ops *ops;
ÂÂÂÂÂ struct mmc_pwrseqÂÂÂ *pwrseq;
@@ -360,6 +419,7 @@ struct mmc_host {
 #define MMC_CAP2_CQE (1 << 23) /* Has eMMC command queue engine */
 #define MMC_CAP2_CQE_DCMD (1 << 24) /* CQE can issue a direct command */
 #define MMC_CAP2_AVOID_3_3V (1 << 25) /* Host must negotiate down from 3.3V */
+#define MMC_CAP2_CLK_SCALEÂÂÂÂÂ (1 << 26)ÂÂÂÂÂÂ /* Allow dynamic clk scaling */
 Â int fixed_drv_type; /* fixed driver type for non-removable media */
 @@ -523,6 +583,16 @@ static inline int mmc_regulator_set_vqmmc(struct mmc_host *mmc,
 u32 mmc_vddrange_to_ocrmask(int vdd_min, int vdd_max);
 int mmc_regulator_get_supply(struct mmc_host *mmc);
 +static inline void mmc_host_clear_sdr104(struct mmc_host *host)
+{
+ÂÂÂ host->caps &= ~MMC_CAP_UHS_SDR104;
+}
+
+static inline void mmc_host_set_sdr104(struct mmc_host *host)
+{
+ÂÂÂ host->caps |= MMC_CAP_UHS_SDR104;
+}
+
 static inline int mmc_card_is_removable(struct mmc_host *host)
 {
ÂÂÂÂÂ return !(host->caps & MMC_CAP_NONREMOVABLE);


Thanks,
Vijay