diff --git a/arch/arm64/configs/vendor/lahaina_QGKI.config b/arch/arm64/configs/vendor/lahaina_QGKI.config index 57117493601d..abd9ebf53a45 100644 --- a/arch/arm64/configs/vendor/lahaina_QGKI.config +++ b/arch/arm64/configs/vendor/lahaina_QGKI.config @@ -165,3 +165,4 @@ CONFIG_NEURON_APP_BLOCK_SERVER=y CONFIG_ADSPRPC_QGKI=y CONFIG_COMMON_CLK_QCOM_DEBUG=y CONFIG_QMP_DEBUGFS_CLIENT=y +CONFIG_SDC_QTI=y diff --git a/drivers/mmc/core/Kconfig b/drivers/mmc/core/Kconfig index c12fe13e4b14..79b0084a97cd 100644 --- a/drivers/mmc/core/Kconfig +++ b/drivers/mmc/core/Kconfig @@ -81,3 +81,10 @@ config MMC_TEST This driver is only of interest to those developing or testing a host driver. Most people should say N here. +config SDC_QTI + tristate "Enable QTI specific code in MMC upstream driver" + depends on QGKI && MMC_SDHCI_MSM + default n + help + This configuration flag allows adding QTI code in + MMC upstream driver. diff --git a/drivers/mmc/core/block.c b/drivers/mmc/core/block.c index 95b41c0891d0..b6506c6ae177 100644 --- a/drivers/mmc/core/block.c +++ b/drivers/mmc/core/block.c @@ -892,7 +892,9 @@ static inline int mmc_blk_part_switch(struct mmc_card *card, } card->ext_csd.part_config = part_config; - +#if defined(CONFIG_SDC_QTI) + card->part_curr = part_type; +#endif ret = mmc_blk_part_switch_post(card, main_md->part_curr); } @@ -1949,13 +1951,18 @@ static void mmc_blk_mq_poll_completion(struct mmc_queue *mq, static void mmc_blk_mq_dec_in_flight(struct mmc_queue *mq, struct request *req) { +#if defined(CONFIG_SDC_QTI) + struct mmc_host *host = mq->card->host; +#endif unsigned long flags; bool put_card; spin_lock_irqsave(&mq->lock, flags); mq->in_flight[mmc_issue_type(mq, req)] -= 1; - +#if defined(CONFIG_SDC_QTI) + atomic_dec(&host->active_reqs); +#endif put_card = (mmc_tot_in_flight(mq) == 0); spin_unlock_irqrestore(&mq->lock, flags); diff --git a/drivers/mmc/core/core.c b/drivers/mmc/core/core.c index abf8f5eb0a1c..cb6bedcba435 100644 --- a/drivers/mmc/core/core.c +++ b/drivers/mmc/core/core.c @@ -31,6 +31,9 @@ #include #include #include +#if defined(CONFIG_SDC_QTI) +#include +#endif #define CREATE_TRACE_POINTS #include @@ -41,7 +44,9 @@ #include "host.h" #include "sdio_bus.h" #include "pwrseq.h" - +#if defined(CONFIG_SDC_QTI) +#include "queue.h" +#endif #include "mmc_ops.h" #include "sd_ops.h" #include "sdio_ops.h" @@ -109,6 +114,683 @@ static inline void mmc_should_fail_request(struct mmc_host *host, #endif /* CONFIG_FAIL_MMC_REQUEST */ +#if defined(CONFIG_SDC_QTI) +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; + unsigned long flags; + + 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_irqsave(&host->clk_scaling.lock, flags); + + /* 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_irqrestore(&host->clk_scaling.lock, flags); + + 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))) + 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: High freq 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: Low freq 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->cqe_on) { + err = host->cqe_ops->cqe_wait_for_idle(host); + if (err) { + pr_err("%s: %s: CQE went in recovery path\n", + mmc_hostname(host), __func__); + goto out; + } + host->cqe_ops->cqe_off(host); + } + + if (!mmc_is_valid_state_for_clk_scaling(host)) { + pr_debug("%s: invalid state for clock scaling - skipping\n", + mmc_hostname(host)); + goto out; + } + + 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); + /* + * CQE would be enabled as part of CQE issueing path + * So no need to unhalt it explicitly + */ + +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; + unsigned long 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); + + spin_lock_irqsave(&clk_scaling->lock, flags); + if (clk_scaling->target_freq == *freq || + clk_scaling->skip_clk_scale_freq_update) { + spin_unlock_irqrestore(&clk_scaling->lock, flags); + 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_irqrestore(&clk_scaling->lock, flags); + + if (!clk_scaling->is_suspended && host->ios.clock) + abort = __mmc_claim_host(host, NULL, + &clk_scaling->devfreq_abort); + else + goto out; + + 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_restore_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; + struct mmc_devfeq_clk_scaling clk_scaling; + unsigned long flags; + + if (!host->clk_scaling.enable) + return; + + spin_lock_irqsave(&host->clk_scaling.lock, flags); + + if (!host->clk_scaling.need_freq_change) { + spin_unlock_irqrestore(&host->clk_scaling.lock, flags); + return; + } + + atomic_inc(&host->clk_scaling.devfreq_abort); + target_freq = host->clk_scaling.target_freq; + /* + * Store the clock scaling state while the lock is acquired so that + * if devfreq context modifies clk_scaling, it will get reflected only + * in the next deferred scaling check. + */ + clk_scaling = host->clk_scaling; + host->clk_scaling.need_freq_change = false; + spin_unlock_irqrestore(&host->clk_scaling.lock, flags); + + 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, + 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); + 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 = kcalloc( + 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 = kcalloc( + clk_scaling->freq_table_sz, + sizeof(*(clk_scaling->devfreq_profile.freq_table)), + GFP_KERNEL); + if (!clk_scaling->devfreq_profile.freq_table) { + kfree(clk_scaling->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; + struct devfreq *devfreq; + + 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 (%pK) to devfreq\n", + mmc_hostname(host), + mmc_classdev(host)); + + if (host->clk_scaling.devfreq) { + pr_err("%s: dev is already registered for dev %pK\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.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.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; + } + + dev_pm_opp_add(mmc_classdev(host), + host->clk_scaling.devfreq_profile.freq_table[0], 0); + dev_pm_opp_add(mmc_classdev(host), + host->clk_scaling.devfreq_profile.freq_table[1], 0); + + 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); + + devfreq = devfreq_add_device( + mmc_classdev(host), + &host->clk_scaling.devfreq_profile, + "simple_ondemand", + &host->clk_scaling.ondemand_gov_data); + + if (IS_ERR(devfreq)) { + pr_err("%s: unable to register with devfreq\n", + mmc_hostname(host)); + dev_pm_opp_remove(mmc_classdev(host), + host->clk_scaling.devfreq_profile.freq_table[0]); + dev_pm_opp_remove(mmc_classdev(host), + host->clk_scaling.devfreq_profile.freq_table[1]); + return PTR_ERR(devfreq); + } + + host->clk_scaling.devfreq = devfreq; + pr_debug("%s: clk scaling is enabled for device %s (%pK) with devfreq %pK (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_suspend_clk_scaling() - suspend clock scaling + * @host: pointer to mmc host structure + * + * This API will suspend devfreq feature for the specific host. + * The statistics collected by mmc will be cleared. + * This function is intended to be called by the pm callbacks + * (e.g. runtime_suspend, suspend) of the mmc device + */ +int mmc_suspend_clk_scaling(struct mmc_host *host) +{ + int err; + + if (!host) { + WARN(1, "bad host parameter\n"); + return -EINVAL; + } + + if (!mmc_can_scale_clk(host) || !host->clk_scaling.enable || + host->clk_scaling.is_suspended) + 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; + } + + atomic_inc(&host->clk_scaling.devfreq_abort); + wake_up(&host->wq); + err = devfreq_suspend_device(host->clk_scaling.devfreq); + if (err) { + pr_err("%s: %s: failed to suspend devfreq\n", + mmc_hostname(host), __func__); + return err; + } + host->clk_scaling.is_suspended = true; + + host->clk_scaling.total_busy_time_us = 0; + + pr_debug("%s: devfreq was removed\n", mmc_hostname(host)); + + return 0; +} +EXPORT_SYMBOL(mmc_suspend_clk_scaling); + +/** + * mmc_resume_clk_scaling() - resume clock scaling + * @host: pointer to mmc host structure + * + * This API will resume devfreq feature for the specific host. + * This API is intended to be called by the pm callbacks + * (e.g. runtime_suspend, suspend) of the mmc device + */ +int mmc_resume_clk_scaling(struct mmc_host *host) +{ + int err = 0; + u32 max_clk_idx = 0; + u32 devfreq_max_clk = 0; + u32 devfreq_min_clk = 0; + + if (!host) { + WARN(1, "bad host parameter\n"); + return -EINVAL; + } + + 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_warn("%s: %s: no devfreq is assosiated with this device\n", + mmc_hostname(host), __func__); + return 0; + } + + atomic_set(&host->clk_scaling.devfreq_abort, 0); + + max_clk_idx = host->clk_scaling.freq_table_sz - 1; + devfreq_max_clk = host->clk_scaling.freq_table[max_clk_idx]; + devfreq_min_clk = host->clk_scaling.freq_table[0]; + + host->clk_scaling.curr_freq = devfreq_max_clk; + if (host->ios.clock < host->clk_scaling.freq_table[max_clk_idx]) + host->clk_scaling.curr_freq = devfreq_min_clk; + host->clk_scaling.target_freq = host->clk_scaling.curr_freq; + + err = devfreq_resume_device(host->clk_scaling.devfreq); + if (err) { + pr_err("%s: %s: failed to resume devfreq (%d)\n", + mmc_hostname(host), __func__, err); + } else { + host->clk_scaling.is_suspended = false; + pr_debug("%s: devfreq resumed\n", mmc_hostname(host)); + } + + return err; +} +EXPORT_SYMBOL(mmc_resume_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 = mmc_suspend_clk_scaling(host); + if (err) { + pr_err("%s: %s: fail to suspend clock scaling (%d)\n", + mmc_hostname(host), __func__, err); + return err; + } + + err = devfreq_remove_device(host->clk_scaling.devfreq); + if (err) { + pr_err("%s: remove devfreq failed (%d)\n", + mmc_hostname(host), err); + return err; + } + + dev_pm_opp_remove(mmc_classdev(host), + host->clk_scaling.devfreq_profile.freq_table[0]); + dev_pm_opp_remove(mmc_classdev(host), + host->clk_scaling.devfreq_profile.freq_table[1]); + + 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); +#endif + static inline void mmc_complete_cmd(struct mmc_request *mrq) { if (mrq->cap_cmd_during_tfr && !completion_done(&mrq->cmd_completion)) @@ -139,7 +821,10 @@ void mmc_request_done(struct mmc_host *host, struct mmc_request *mrq) { struct mmc_command *cmd = mrq->cmd; int err = cmd->error; - +#if defined(CONFIG_SDC_QTI) + if (host->clk_scaling.is_busy_started) + mmc_clk_scaling_stop_busy(host, true); +#endif /* Flag re-tuning needed on CRC errors */ if (cmd->opcode != MMC_SEND_TUNING_BLOCK && cmd->opcode != MMC_SEND_TUNING_BLOCK_HS200 && @@ -350,7 +1035,12 @@ int mmc_start_request(struct mmc_host *host, struct mmc_request *mrq) err = mmc_mrq_prep(host, mrq); if (err) return err; - +#if defined(CONFIG_SDC_QTI) + if (mmc_is_data_request(mrq)) { + mmc_deferred_scaling(host); + mmc_clk_scaling_start_busy(host, true); + } +#endif led_trigger_event(host->led, LED_FULL); __mmc_start_request(host, mrq); diff --git a/drivers/mmc/core/core.h b/drivers/mmc/core/core.h index 575ac0257af2..5de5730ee44e 100644 --- a/drivers/mmc/core/core.h +++ b/drivers/mmc/core/core.h @@ -14,6 +14,9 @@ struct mmc_host; struct mmc_card; struct mmc_request; +#if defined(CONFIG_SDC_QTI) +struct mmc_queue; +#endif #define MMC_CMD_RETRIES 3 @@ -29,6 +32,9 @@ struct mmc_bus_ops { int (*shutdown)(struct mmc_host *); int (*hw_reset)(struct mmc_host *); int (*sw_reset)(struct mmc_host *); +#if defined(CONFIG_SDC_QTI) + int (*change_bus_speed)(struct mmc_host *host, unsigned long *freq); +#endif }; void mmc_attach_bus(struct mmc_host *host, const struct mmc_bus_ops *ops); @@ -57,7 +63,10 @@ void mmc_power_off(struct mmc_host *host); void mmc_power_cycle(struct mmc_host *host, u32 ocr); void mmc_set_initial_state(struct mmc_host *host); u32 mmc_vddrange_to_ocrmask(int vdd_min, int vdd_max); - +#if defined(CONFIG_SDC_QTI) +int mmc_clk_update_freq(struct mmc_host *host, + unsigned long freq, enum mmc_load state); +#endif static inline void mmc_delay(unsigned int ms) { if (ms <= 20) @@ -89,6 +98,15 @@ void mmc_remove_host_debugfs(struct mmc_host *host); void mmc_add_card_debugfs(struct mmc_card *card); void mmc_remove_card_debugfs(struct mmc_card *card); +#if defined(CONFIG_SDC_QTI) +extern bool mmc_can_scale_clk(struct mmc_host *host); +extern int mmc_init_clk_scaling(struct mmc_host *host); +extern int mmc_suspend_clk_scaling(struct mmc_host *host); +extern int mmc_resume_clk_scaling(struct mmc_host *host); +extern int mmc_exit_clk_scaling(struct mmc_host *host); +extern void mmc_deferred_scaling(struct mmc_host *host); +extern unsigned long mmc_get_max_frequency(struct mmc_host *host); +#endif 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/host.c b/drivers/mmc/core/host.c index f6244ae50660..1af5f71a1545 100644 --- a/drivers/mmc/core/host.c +++ b/drivers/mmc/core/host.c @@ -31,6 +31,11 @@ #define cls_dev_to_mmc_host(d) container_of(d, struct mmc_host, class_dev) +#if defined(CONFIG_SDC_QTI) +#define MMC_DEVFRQ_DEFAULT_UP_THRESHOLD 35 +#define MMC_DEVFRQ_DEFAULT_DOWN_THRESHOLD 5 +#define MMC_DEVFRQ_DEFAULT_POLLING_MSEC 100 +#endif static DEFINE_IDA(mmc_host_ida); static void mmc_host_classdev_release(struct device *dev) @@ -161,6 +166,46 @@ static void mmc_retune_timer(struct timer_list *t) mmc_retune_needed(host); } +#if defined(CONFIG_SDC_QTI) +static int mmc_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_kcalloc(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; + +} +#endif + /** * mmc_of_parse() - parse host's device-tree node * @host: host whose node should be parsed. @@ -176,6 +221,10 @@ int mmc_of_parse(struct mmc_host *host) u32 bus_width, drv_type, cd_debounce_delay_ms; int ret; bool cd_cap_invert, cd_gpio_invert = false; +#if defined(CONFIG_SDC_QTI) + const char *lower_bus_speed = NULL; + struct device_node *np = dev->of_node; +#endif if (!dev || !dev_fwnode(dev)) return 0; @@ -337,6 +386,30 @@ int mmc_of_parse(struct mmc_host *host) device_property_read_u32(dev, "post-power-on-delay-ms", &host->ios.power_delay_ms); +#if defined(CONFIG_SDC_QTI) + if (mmc_dt_get_array(dev, "qcom,devfreq,freq-table", + &host->clk_scaling.pltfm_freq_table, + &host->clk_scaling.pltfm_freq_table_sz, 0)) + pr_debug("%s: no clock scaling frequencies were supplied\n", + dev_name(dev)); + else if (!host->clk_scaling.pltfm_freq_table || + !host->clk_scaling.pltfm_freq_table_sz) + dev_info(dev, "bad dts clock scaling frequencies\n"); + + /* + * Few hosts can support DDR52 mode at the same lower + * system voltage corner as high-speed mode. In such + * cases, it is always better to put it in DDR + * mode which will improve the performance + * without any power impact. + */ + if (!of_property_read_string(np, "qcom,scaling-lower-bus-speed-mode", + &lower_bus_speed)) { + if (!strcmp(lower_bus_speed, "DDR52")) + host->clk_scaling.lower_bus_speed_mode |= + MMC_SCALING_LOWER_DDR52_MODE; + } +#endif return mmc_pwrseq_alloc(host); } @@ -427,6 +500,9 @@ struct mmc_host *mmc_alloc_host(int extra, struct device *dev) } spin_lock_init(&host->lock); +#if defined(CONFIG_SDC_QTI) + atomic_set(&host->active_reqs, 0); +#endif init_waitqueue_head(&host->wq); INIT_DELAYED_WORK(&host->detect, mmc_rescan); INIT_DELAYED_WORK(&host->sdio_irq_work, sdio_irq_work); @@ -451,6 +527,155 @@ struct mmc_host *mmc_alloc_host(int extra, struct device *dev) EXPORT_SYMBOL(mmc_alloc_host); +#if defined(CONFIG_SDC_QTI) +static ssize_t enable_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct mmc_host *host = cls_dev_to_mmc_host(dev); + + if (!host) + return -EINVAL; + + return scnprintf(buf, PAGE_SIZE, "%d\n", mmc_can_scale_clk(host)); +} + +static ssize_t enable_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + struct mmc_host *host = cls_dev_to_mmc_host(dev); + unsigned long value; + + if (!host || !host->card || kstrtoul(buf, 0, &value)) + return -EINVAL; + + mmc_get_card(host->card, NULL); + + if (!value) { + /* Suspend the clock scaling and mask host capability */ + if (host->clk_scaling.enable) + mmc_suspend_clk_scaling(host); + host->clk_scaling.enable = false; + host->caps2 &= ~MMC_CAP2_CLK_SCALE; + host->clk_scaling.state = MMC_LOAD_HIGH; + /* Set to max. frequency when disabling */ + mmc_clk_update_freq(host, host->card->clk_scaling_highest, + host->clk_scaling.state); + } else if (value) { + /* Unmask host capability and resume scaling */ + host->caps2 |= MMC_CAP2_CLK_SCALE; + if (!host->clk_scaling.enable) { + host->clk_scaling.enable = true; + mmc_resume_clk_scaling(host); + } + } + + mmc_put_card(host->card, NULL); + + return count; +} + +static ssize_t up_threshold_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct mmc_host *host = cls_dev_to_mmc_host(dev); + + if (!host) + return -EINVAL; + + return scnprintf(buf, PAGE_SIZE, "%d\n", host->clk_scaling.upthreshold); +} + +#define MAX_PERCENTAGE 100 +static ssize_t up_threshold_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + struct mmc_host *host = cls_dev_to_mmc_host(dev); + unsigned long value; + + if (!host || kstrtoul(buf, 0, &value) || (value > MAX_PERCENTAGE)) + return -EINVAL; + + host->clk_scaling.upthreshold = value; + + pr_debug("%s: clkscale_up_thresh set to %lu\n", + mmc_hostname(host), value); + return count; +} + +static ssize_t down_threshold_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct mmc_host *host = cls_dev_to_mmc_host(dev); + + if (!host) + return -EINVAL; + + return scnprintf(buf, PAGE_SIZE, "%d\n", + host->clk_scaling.downthreshold); +} + +static ssize_t down_threshold_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + struct mmc_host *host = cls_dev_to_mmc_host(dev); + unsigned long value; + + if (!host || kstrtoul(buf, 0, &value) || (value > MAX_PERCENTAGE)) + return -EINVAL; + + host->clk_scaling.downthreshold = value; + + pr_debug("%s: clkscale_down_thresh set to %lu\n", + mmc_hostname(host), value); + return count; +} + +static ssize_t polling_interval_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct mmc_host *host = cls_dev_to_mmc_host(dev); + + if (!host) + return -EINVAL; + + return scnprintf(buf, PAGE_SIZE, "%lu milliseconds\n", + host->clk_scaling.polling_delay_ms); +} + +static ssize_t polling_interval_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + struct mmc_host *host = cls_dev_to_mmc_host(dev); + unsigned long value; + + if (!host || kstrtoul(buf, 0, &value)) + return -EINVAL; + + host->clk_scaling.polling_delay_ms = value; + + pr_debug("%s: clkscale_polling_delay_ms set to %lu\n", + mmc_hostname(host), value); + return count; +} + +DEVICE_ATTR_RW(enable); +DEVICE_ATTR_RW(polling_interval); +DEVICE_ATTR_RW(up_threshold); +DEVICE_ATTR_RW(down_threshold); + +static struct attribute *clk_scaling_attrs[] = { + &dev_attr_enable.attr, + &dev_attr_up_threshold.attr, + &dev_attr_down_threshold.attr, + &dev_attr_polling_interval.attr, + NULL, +}; + +static struct attribute_group clk_scaling_attr_grp = { + .name = "clk_scaling", + .attrs = clk_scaling_attrs, +}; +#endif /** * mmc_add_host - initialise host hardware * @host: mmc host @@ -471,11 +696,23 @@ int mmc_add_host(struct mmc_host *host) return err; led_trigger_register_simple(dev_name(&host->class_dev), &host->led); +#if defined(CONFIG_SDC_QTI) + 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; +#endif #ifdef CONFIG_DEBUG_FS mmc_add_host_debugfs(host); #endif +#if defined(CONFIG_SDC_QTI) + err = sysfs_create_group(&host->class_dev.kobj, &clk_scaling_attr_grp); + if (err) + pr_err("%s: failed to create clk scale sysfs group with err %d\n", + __func__, err); +#endif mmc_start_host(host); if (!(host->pm_flags & MMC_PM_IGNORE_PM_NOTIFY)) mmc_register_pm_notifier(host); @@ -503,6 +740,9 @@ void mmc_remove_host(struct mmc_host *host) mmc_remove_host_debugfs(host); #endif +#if defined(CONFIG_SDC_QTI) + sysfs_remove_group(&host->class_dev.kobj, &clk_scaling_attr_grp); +#endif device_del(&host->class_dev); led_trigger_unregister_simple(host->led); diff --git a/drivers/mmc/core/queue.c b/drivers/mmc/core/queue.c index 9edc08685e86..c2a1118276c9 100644 --- a/drivers/mmc/core/queue.c +++ b/drivers/mmc/core/queue.c @@ -296,6 +296,9 @@ static blk_status_t mmc_mq_queue_rq(struct blk_mq_hw_ctx *hctx, mq->busy = true; mq->in_flight[issue_type] += 1; +#if defined(CONFIG_SDC_QTI) + atomic_inc(&host->active_reqs); +#endif get_card = (mmc_tot_in_flight(mq) == 1); cqe_retune_ok = (mmc_cqe_qcnt(mq) == 1); @@ -335,6 +338,9 @@ static blk_status_t mmc_mq_queue_rq(struct blk_mq_hw_ctx *hctx, spin_lock_irq(&mq->lock); mq->in_flight[issue_type] -= 1; +#if defined(CONFIG_SDC_QTI) + atomic_dec(&host->active_reqs); +#endif if (mmc_tot_in_flight(mq) == 0) put_card = true; mq->busy = false; diff --git a/drivers/mmc/core/sd.c b/drivers/mmc/core/sd.c index fe914ff5f5d6..0ac29310524a 100644 --- a/drivers/mmc/core/sd.c +++ b/drivers/mmc/core/sd.c @@ -592,6 +592,60 @@ static int sd_set_current_limit(struct mmc_card *card, u8 *status) return 0; } +#if defined(CONFIG_SDC_QTI) +/** + * 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; + + /* + * 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: + return err; +} +#endif /* * UHS-I specific initialization procedure */ @@ -912,13 +966,24 @@ unsigned mmc_sd_get_max_clock(struct mmc_card *card) { unsigned max_dtr = (unsigned int)-1; - if (mmc_card_hs(card)) { +#if defined(CONFIG_SDC_QTI) + 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) { max_dtr = card->csd.max_dtr; } - +#else + if (mmc_card_hs(card)) { + if (max_dtr > card->sw_caps.uhs_max_dtr) + max_dtr = card->sw_caps.uhs_max_dtr; + } else if (max_dtr > card->csd.max_dtr) { + max_dtr = card->csd.max_dtr; + } +#endif return max_dtr; } @@ -1089,6 +1154,11 @@ retry: err = -EINVAL; goto free_card; } + +#if defined(CONFIG_SDC_QTI) + card->clk_scaling_highest = mmc_sd_get_max_clock(card); + card->clk_scaling_lowest = host->f_min; +#endif done: host->card = card; return 0; @@ -1105,6 +1175,9 @@ free_card: */ static void mmc_sd_remove(struct mmc_host *host) { +#if defined(CONFIG_SDC_QTI) + mmc_exit_clk_scaling(host); +#endif mmc_remove_card(host->card); host->card = NULL; } @@ -1146,7 +1219,14 @@ static void mmc_sd_detect(struct mmc_host *host) static int _mmc_sd_suspend(struct mmc_host *host) { int err = 0; - +#if defined(CONFIG_SDC_QTI) + err = mmc_suspend_clk_scaling(host); + if (err) { + pr_err("%s: %s: fail to suspend clock scaling (%d)\n", + mmc_hostname(host), __func__, err); + return err; + } +#endif mmc_claim_host(host); if (mmc_card_suspended(host->card)) @@ -1198,6 +1278,14 @@ static int _mmc_sd_resume(struct mmc_host *host) err = mmc_sd_init_card(host, host->card->ocr, host->card); mmc_card_clr_suspended(host->card); +#if defined(CONFIG_SDC_QTI) + err = mmc_resume_clk_scaling(host); + if (err) { + pr_err("%s: %s: fail to resume clock scaling (%d)\n", + mmc_hostname(host), __func__, err); + goto out; + } +#endif out: mmc_release_host(host); return err; @@ -1261,6 +1349,9 @@ static const struct mmc_bus_ops mmc_sd_ops = { .alive = mmc_sd_alive, .shutdown = mmc_sd_suspend, .hw_reset = mmc_sd_hw_reset, +#if defined(CONFIG_SDC_QTI) + .change_bus_speed = mmc_sd_change_bus_speed, +#endif }; /* @@ -1321,6 +1412,13 @@ int mmc_attach_sd(struct mmc_host *host) goto remove_card; mmc_claim_host(host); +#if defined(CONFIG_SDC_QTI) + err = mmc_init_clk_scaling(host); + if (err) { + mmc_release_host(host); + goto remove_card; + } +#endif return 0; remove_card: diff --git a/drivers/mmc/host/Kconfig b/drivers/mmc/host/Kconfig index 49ea02c467bf..028d92f11a54 100644 --- a/drivers/mmc/host/Kconfig +++ b/drivers/mmc/host/Kconfig @@ -1019,3 +1019,11 @@ config MMC_SDHCI_AM654 If you have a controller with this interface, say Y or M here. If unsure, say N. + +config SDC_QTI + tristate "Enable QTI specific code in MMC upstream driver" + depends on QGKI && MMC_SDHCI_MSM + default n + help + This configuration flag allows adding QTI code in + MMC upstream driver. diff --git a/drivers/mmc/host/sdhci-msm.c b/drivers/mmc/host/sdhci-msm.c index e8ccfe414ab7..8f29d6d15a94 100644 --- a/drivers/mmc/host/sdhci-msm.c +++ b/drivers/mmc/host/sdhci-msm.c @@ -3217,6 +3217,9 @@ static int sdhci_msm_probe(struct platform_device *pdev) msm_host->pltfm_init_done = true; +#if defined(CONFIG_SDC_QTI) + msm_host->mmc->caps2 |= MMC_CAP2_CLK_SCALE; +#endif pm_runtime_get_noresume(&pdev->dev); pm_runtime_set_active(&pdev->dev); pm_runtime_enable(&pdev->dev); diff --git a/include/linux/mmc/card.h b/include/linux/mmc/card.h index e459b38ef33c..2d457c3d49e4 100644 --- a/include/linux/mmc/card.h +++ b/include/linux/mmc/card.h @@ -244,6 +244,14 @@ struct mmc_card { struct mmc_host *host; /* the host this device belongs to */ struct device dev; /* the device */ u32 ocr; /* the current OCR setting */ +#if defined(CONFIG_SDC_QTI) + unsigned long clk_scaling_lowest; /* lowest scaleable + * frequency + */ + unsigned long clk_scaling_highest; /* highest scaleable + * frequency + */ +#endif unsigned int rca; /* relative card address of device */ unsigned int type; /* card type */ #define MMC_TYPE_MMC 0 /* MMC card */ @@ -307,7 +315,9 @@ struct mmc_card { struct dentry *debugfs_root; struct mmc_part part[MMC_NUM_PHY_PARTITION]; /* physical partitions */ unsigned int nr_parts; - +#if defined(CONFIG_SDC_QTI) + unsigned int part_curr; +#endif unsigned int bouncesz; /* Bounce buffer size */ struct workqueue_struct *complete_wq; /* Private workqueue */ }; diff --git a/include/linux/mmc/host.h b/include/linux/mmc/host.h index ba703384bea0..6736c032c3af 100644 --- a/include/linux/mmc/host.h +++ b/include/linux/mmc/host.h @@ -9,6 +9,9 @@ #include #include +#if defined(CONFIG_SDC_QTI) +#include +#endif #include #include @@ -268,9 +271,76 @@ struct mmc_ctx { struct task_struct *task; }; +#if defined(CONFIG_SDC_QTI) +enum dev_state { + DEV_SUSPENDING = 1, + DEV_SUSPENDED, + DEV_RESUMED, +}; + +enum mmc_load { + MMC_LOAD_HIGH, + MMC_LOAD_LOW, +}; + +/** + * 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 + * @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 + * @is_suspended: to make devfreq request queued when mmc is suspened + */ +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; + unsigned int lower_bus_speed_mode; +#define MMC_SCALING_LOWER_DDR52_MODE 1 + bool need_freq_change; + bool is_busy_started; + bool enable; + bool is_suspended; +}; +#endif + struct mmc_host { struct device *parent; struct device class_dev; +#if defined(CONFIG_SDC_QTI) + struct mmc_devfeq_clk_scaling clk_scaling; +#endif int index; const struct mmc_host_ops *ops; struct mmc_pwrseq *pwrseq; @@ -369,7 +439,9 @@ struct mmc_host { #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_MERGE_CAPABLE (1 << 26) /* Host can merge a segment over the segment size */ - +#if defined(CONFIG_SDC_QTI) +#define MMC_CAP2_CLK_SCALE (1 << 27) /* Allow dynamic clk scaling */ +#endif int fixed_drv_type; /* fixed driver type for non-removable media */ mmc_pm_flag_t pm_caps; /* supported pm features */ @@ -461,7 +533,9 @@ struct mmc_host { int cqe_qdepth; bool cqe_enabled; bool cqe_on; - +#if defined(CONFIG_SDC_QTI) + atomic_t active_reqs; +#endif unsigned long private[0] ____cacheline_aligned; };