From 674da8208bea51ae566a1c636c9d0fa39c317621 Mon Sep 17 00:00:00 2001 From: Ram Prakash Gupta Date: Mon, 21 Sep 2020 14:27:54 +0530 Subject: [PATCH] mmc: Enable clk scaling for cqe Enable clk scaling for cqe. Change-Id: I99e87a808e4d7663114736e45614a8082dda0eb1 Signed-off-by: Ram Prakash Gupta --- drivers/mmc/core/block.c | 21 +++ drivers/mmc/core/core.c | 118 ++++++++++++- drivers/mmc/core/core.h | 4 + drivers/mmc/core/debugfs.c | 86 ++++++++++ drivers/mmc/core/mmc.c | 317 +++++++++++++++++++++++++++++++++-- drivers/mmc/host/sdhci-msm.c | 27 ++- drivers/mmc/host/sdhci.c | 15 ++ drivers/mmc/host/sdhci.h | 5 +- include/linux/mmc/host.h | 14 +- 9 files changed, 578 insertions(+), 29 deletions(-) diff --git a/drivers/mmc/core/block.c b/drivers/mmc/core/block.c index d66d005d08fc..da185cbdfc36 100644 --- a/drivers/mmc/core/block.c +++ b/drivers/mmc/core/block.c @@ -1458,6 +1458,9 @@ static void mmc_blk_cqe_complete_rq(struct mmc_queue *mq, struct request *req) spin_lock_irqsave(&mq->lock, flags); mq->in_flight[issue_type] -= 1; +#if defined(CONFIG_SDC_QTI) + atomic_dec(&host->active_reqs); +#endif put_card = (mmc_tot_in_flight(mq) == 0); @@ -1467,6 +1470,10 @@ static void mmc_blk_cqe_complete_rq(struct mmc_queue *mq, struct request *req) if (!mq->cqe_busy) blk_mq_run_hw_queues(q, true); +#if defined(CONFIG_SDC_QTI) + mmc_cqe_clk_scaling_stop_busy(host, true, + (issue_type == MMC_ISSUE_DCMD)); +#endif if (put_card) mmc_put_card(mq->card, &mq->ctx); @@ -1563,13 +1570,27 @@ static int mmc_blk_cqe_issue_rw_rq(struct mmc_queue *mq, struct request *req) { struct mmc_queue_req *mqrq = req_to_mmc_queue_req(req); struct mmc_host *host = mq->card->host; +#if defined(CONFIG_SDC_QTI) + int err; +#endif if (host->hsq_enabled) return mmc_blk_hsq_issue_rw_rq(mq, req); mmc_blk_data_prep(mq, mqrq, 0, NULL, NULL); +#if defined(CONFIG_SDC_QTI) + mmc_deferred_scaling(mq->card->host); + mmc_cqe_clk_scaling_start_busy(mq, mq->card->host, true); + err = mmc_blk_cqe_start_req(mq->card->host, &mqrq->brq.mrq); + + if (err) + mmc_cqe_clk_scaling_stop_busy(mq->card->host, true, false); + + return err; +#else return mmc_blk_cqe_start_req(mq->card->host, &mqrq->brq.mrq); +#endif } static void mmc_blk_rw_rq_prep(struct mmc_queue_req *mqrq, diff --git a/drivers/mmc/core/core.c b/drivers/mmc/core/core.c index f718426fd45d..bcba55f0414b 100644 --- a/drivers/mmc/core/core.c +++ b/drivers/mmc/core/core.c @@ -176,6 +176,89 @@ out: spin_unlock_irqrestore(&clk_scaling->lock, flags); } +/* mmc_cqe_clk_scaling_start_busy() - start busy timer for data requests + * @host: pointer to mmc host structure + * @lock_needed: flag indication if locking is needed + * + * This function starts the busy timer in case it was not already started. + */ +void mmc_cqe_clk_scaling_start_busy(struct mmc_queue *mq, + struct mmc_host *host, bool lock_needed) +{ + unsigned long flags; + + if (!host->clk_scaling.enable) + return; + + if (lock_needed) + spin_lock_irqsave(&host->clk_scaling.lock, flags); + + if (!host->clk_scaling.is_busy_started && + !(mq->cqe_busy & MMC_CQE_DCMD_BUSY)) { + host->clk_scaling.start_busy = ktime_get(); + host->clk_scaling.is_busy_started = true; + } + + if (lock_needed) + spin_unlock_irqrestore(&host->clk_scaling.lock, flags); +} +EXPORT_SYMBOL(mmc_cqe_clk_scaling_start_busy); + +/** + * mmc_cqe_clk_scaling_stop_busy() - stop busy timer for last data requests + * @host: pointer to mmc host structure + * @lock_needed: flag indication if locking is needed + * + * This function stops the busy timer in case it is the last data request. + * In case the current request is not the last one, the busy time till + * now will be accumulated and the counter will be restarted. + */ +void mmc_cqe_clk_scaling_stop_busy(struct mmc_host *host, + bool lock_needed, bool is_cqe_dcmd) +{ + unsigned int cqe_active_reqs = 0; + + if (!host->clk_scaling.enable) + return; + + cqe_active_reqs = atomic_read(&host->active_reqs); + + /* + * This gets invoked from CQE completion path which is hard IRQ context + * So use spin_lock() instread of spin_lock_irqsave() + */ + if (lock_needed) + spin_lock(&host->clk_scaling.lock); + + /* + * For CQ mode: In completion of DCMD request, start busy time in + * case of pending data requests + */ + if (is_cqe_dcmd) { + if (cqe_active_reqs && !host->clk_scaling.is_busy_started) { + host->clk_scaling.is_busy_started = true; + host->clk_scaling.start_busy = ktime_get(); + } + goto out; + } + + host->clk_scaling.total_busy_time_us += + ktime_to_us(ktime_sub(ktime_get(), + host->clk_scaling.start_busy)); + + if (cqe_active_reqs) { + host->clk_scaling.is_busy_started = true; + host->clk_scaling.start_busy = ktime_get(); + } else { + host->clk_scaling.is_busy_started = false; + } +out: + if (lock_needed) + spin_unlock(&host->clk_scaling.lock); + +} +EXPORT_SYMBOL(mmc_cqe_clk_scaling_stop_busy); + /** * mmc_can_scale_clk() - Check clock scaling capability * @host: pointer to mmc host structure @@ -215,8 +298,15 @@ static int mmc_devfreq_get_dev_status(struct device *dev, /* 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); + if (host->cqe_on) { + /* the "busy-timer" will be restarted in case there + * are pending data requests + */ + mmc_cqe_clk_scaling_stop_busy(host, false, false); + } else { + mmc_clk_scaling_stop_busy(host, false); + mmc_clk_scaling_start_busy(host, false); + } } status->busy_time = clk_scaling->total_busy_time_us; @@ -293,11 +383,20 @@ int mmc_clk_update_freq(struct mmc_host *host, if (err) { pr_err("%s: %s: CQE went in recovery path\n", mmc_hostname(host), __func__); - goto out; + goto error; } host->cqe_ops->cqe_off(host); } + 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 error; + } + } + if (!mmc_is_valid_state_for_clk_scaling(host)) { pr_debug("%s: invalid state for clock scaling - skipping\n", mmc_hostname(host)); @@ -315,6 +414,16 @@ int mmc_clk_update_freq(struct mmc_host *host, * So no need to unhalt it explicitly */ +error: + 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; } @@ -428,8 +537,7 @@ void mmc_deferred_scaling(struct mmc_host *host) mmc_hostname(host), target_freq, current->comm); - err = mmc_clk_update_freq(host, target_freq, - clk_scaling.state); + 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); diff --git a/drivers/mmc/core/core.h b/drivers/mmc/core/core.h index 5de5730ee44e..50ef5b5ecbc7 100644 --- a/drivers/mmc/core/core.h +++ b/drivers/mmc/core/core.h @@ -106,6 +106,10 @@ 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); +extern void mmc_cqe_clk_scaling_start_busy(struct mmc_queue *mq, + struct mmc_host *host, bool lock_needed); +extern void mmc_cqe_clk_scaling_stop_busy(struct mmc_host *host, + bool lock_needed, bool is_cqe_dcmd); #endif int mmc_execute_tuning(struct mmc_card *card); int mmc_hs200_to_hs400(struct mmc_card *card); diff --git a/drivers/mmc/core/debugfs.c b/drivers/mmc/core/debugfs.c index 09e0c7659469..b99ff071d256 100644 --- a/drivers/mmc/core/debugfs.c +++ b/drivers/mmc/core/debugfs.c @@ -222,6 +222,83 @@ 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"); +#if defined(CONFIG_SDC_QTI) +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_DEBUGFS_ATTRIBUTE(mmc_scale_fops, mmc_scale_get, mmc_scale_set, + "%llu\n"); + +static int mmc_max_clock_get(void *data, u64 *val) +{ + struct mmc_host *host = data; + + if (!host) + return -EINVAL; + + *val = host->f_max; + + return 0; +} + +static int mmc_max_clock_set(void *data, u64 val) +{ + struct mmc_host *host = data; + int err = -EINVAL; + unsigned long freq = val; + unsigned int old_freq; + + if (!host || (val < host->f_min)) + goto out; + + mmc_claim_host(host); + if (host->bus_ops && host->bus_ops->change_bus_speed) { + old_freq = host->f_max; + host->f_max = freq; + + err = host->bus_ops->change_bus_speed(host, &freq); + + if (err) + host->f_max = old_freq; + } + mmc_release_host(host); +out: + return err; +} + +DEFINE_DEBUGFS_ATTRIBUTE(mmc_max_clock_fops, mmc_max_clock_get, + mmc_max_clock_set, "%llu\n"); +#endif + void mmc_add_host_debugfs(struct mmc_host *host) { struct dentry *root; @@ -234,6 +311,15 @@ void mmc_add_host_debugfs(struct mmc_host *host) debugfs_create_x32("caps2", S_IRUSR, root, &host->caps2); debugfs_create_file("clock", S_IRUSR | S_IWUSR, root, host, &mmc_clock_fops); +#if defined(CONFIG_SDC_QTI) + debugfs_create_file("max_clock", 0600, root, host, + &mmc_max_clock_fops); + debugfs_create_file("scale", 0600, root, host, + &mmc_scale_fops); + debugfs_create_bool("skip_clk_scale_freq_update", + 0600, root, + &host->clk_scaling.skip_clk_scale_freq_update); +#endif #ifdef CONFIG_FAIL_MMC_REQUEST if (fail_request) diff --git a/drivers/mmc/core/mmc.c b/drivers/mmc/core/mmc.c index cd3bcf8332a1..f94ad2ca3ebe 100644 --- a/drivers/mmc/core/mmc.c +++ b/drivers/mmc/core/mmc.c @@ -1539,6 +1539,235 @@ static int mmc_hs200_tuning(struct mmc_card *card) return mmc_execute_tuning(card); } +#if defined(CONFIG_SDC_QTI) +static int mmc_select_hs_ddr52(struct mmc_host *host) +{ + int err; + + mmc_select_hs(host->card); + 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; + } + + err = mmc_select_hs_ddr(host->card); + mmc_set_clock(host, MMC_HIGH_52_MAX_DTR); + + return err; +} + +/* + * 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); + + if (host->clk_scaling.lower_bus_speed_mode & + MMC_SCALING_LOWER_DDR52_MODE) { + err = mmc_select_hs_ddr52(host); + if (err) + pr_err("%s: %s: failed to switch to DDR52: err: %d\n", + mmc_hostname(host), __func__, err); + else + return err; + } + + 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; + } + } + + mmc_set_initial_state(host); + err = mmc_select_timing(host->card); + if (err) { + pr_err("%s: %s: select hs400 failed (%d)\n", + mmc_hostname(host), __func__, err); + return err; + } + + return 0; +} + +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; +} + +static void mmc_init_setup_scaling(struct mmc_card *card, struct mmc_host *host) +{ + 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; +} +#endif + +static void mmc_initialize_cqe(struct mmc_host *host, struct mmc_card *card) +{ + int err; + + if (host->cqe_ops && !host->cqe_enabled) { + err = host->cqe_ops->cqe_enable(host, card); + if (!err) { + host->cqe_enabled = true; + + if (card->ext_csd.cmdq_en) { + pr_info("%s: Command Queue Engine enabled\n", + mmc_hostname(host)); + } else { + host->hsq_enabled = true; + pr_info("%s: Host Software Queue enabled\n", + mmc_hostname(host)); + } + } + } +} + /* * Handle the detection and initialisation of a card. * @@ -1775,6 +2004,9 @@ static int mmc_init_card(struct mmc_host *host, u32 ocr, } } +#if defined(CONFIG_SDC_QTI) + mmc_init_setup_scaling(card, host); +#endif /* * Choose the power class with selected bus interface */ @@ -1852,21 +2084,7 @@ static int mmc_init_card(struct mmc_host *host, u32 ocr, */ card->reenable_cmdq = card->ext_csd.cmdq_en; - if (host->cqe_ops && !host->cqe_enabled) { - err = host->cqe_ops->cqe_enable(host, card); - if (!err) { - host->cqe_enabled = true; - - if (card->ext_csd.cmdq_en) { - pr_info("%s: Command Queue Engine enabled\n", - mmc_hostname(host)); - } else { - host->hsq_enabled = true; - pr_info("%s: Host Software Queue enabled\n", - mmc_hostname(host)); - } - } - } + mmc_initialize_cqe(host, card); if (host->caps2 & MMC_CAP2_AVOID_3_3V && host->ios.signal_voltage == MMC_SIGNAL_VOLTAGE_330) { @@ -1979,6 +2197,9 @@ static int mmc_poweroff_notify(struct mmc_card *card, unsigned int notify_type) */ static void mmc_remove(struct mmc_host *host) { +#if defined(CONFIG_SDC_QTI) + mmc_exit_clk_scaling(host); +#endif mmc_remove_card(host->card); host->card = NULL; } @@ -2022,7 +2243,14 @@ static int _mmc_suspend(struct mmc_host *host, bool is_suspend) int err = 0; unsigned int notify_type = is_suspend ? EXT_CSD_POWER_OFF_SHORT : EXT_CSD_POWER_OFF_LONG; - +#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)) @@ -2047,6 +2275,10 @@ static int _mmc_suspend(struct mmc_host *host, bool is_suspend) } out: mmc_release_host(host); +#if defined(CONFIG_SDC_QTI) + if (err) + mmc_resume_clk_scaling(host); +#endif return err; } @@ -2076,15 +2308,33 @@ static int _mmc_resume(struct mmc_host *host) mmc_claim_host(host); +#if defined(CONFIG_SDC_QTI) + if (!mmc_card_suspended(host->card)) { + mmc_release_host(host); +#else if (!mmc_card_suspended(host->card)) +#endif goto out; +#if defined(CONFIG_SDC_QTI) + } +#endif mmc_power_up(host, host->card->ocr); err = mmc_init_card(host, host->card->ocr, host->card); mmc_card_clr_suspended(host->card); +#if !defined(CONFIG_SDC_QTI) out: +#endif mmc_release_host(host); + +#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); +out: +#endif return err; } @@ -2103,6 +2353,15 @@ static int mmc_shutdown(struct mmc_host *host) !(host->caps2 & MMC_CAP2_FULL_PWR_CYCLE)) err = _mmc_resume(host); +#if defined(CONFIG_SDC_QTI) + /* + * 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); +#endif + if (!err) err = _mmc_suspend(host, false); @@ -2164,6 +2423,9 @@ static int mmc_can_reset(struct mmc_card *card) static int _mmc_hw_reset(struct mmc_host *host) { struct mmc_card *card = host->card; +#if defined(CONFIG_SDC_QTI) + int ret; +#endif /* * In the case of recovery, we can't expect flushing the cache to work @@ -2183,7 +2445,19 @@ static int _mmc_hw_reset(struct mmc_host *host) mmc_power_cycle(host, card->ocr); mmc_pwrseq_reset(host); } + +#if defined(CONFIG_SDC_QTI) + ret = mmc_init_card(host, host->card->ocr, host->card); + if (ret) { + pr_err("%s: %s: mmc_init_card failed (%d)\n", + mmc_hostname(host), __func__, ret); + return ret; + } + + return ret; +#else return mmc_init_card(host, card->ocr, card); +#endif } static const struct mmc_bus_ops mmc_ops = { @@ -2196,6 +2470,10 @@ static const struct mmc_bus_ops mmc_ops = { .alive = mmc_alive, .shutdown = mmc_shutdown, .hw_reset = _mmc_hw_reset, +#if defined(CONFIG_SDC_QTI) + .change_bus_speed = mmc_change_bus_speed, +#endif + }; /* @@ -2252,6 +2530,13 @@ int mmc_attach_mmc(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/sdhci-msm.c b/drivers/mmc/host/sdhci-msm.c index 0f1b91162a50..87a440c5e8ff 100644 --- a/drivers/mmc/host/sdhci-msm.c +++ b/drivers/mmc/host/sdhci-msm.c @@ -3146,7 +3146,6 @@ static void sdhci_msm_reset(struct sdhci_host *host, u8 mask) sdhci_reset(host, mask); } - #define MAX_TEST_BUS 60 #define DRIVER_NAME "sdhci_msm" #define SDHCI_MSM_DUMP(f, x...) \ @@ -3268,6 +3267,31 @@ static void sdhci_msm_dump_vendor_regs(struct sdhci_host *host) debug_reg[i+2], debug_reg[i+3]); } +static int sdhci_msm_notify_load(struct sdhci_host *host, enum mmc_load state) +{ + struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); + struct sdhci_msm_host *msm_host = sdhci_pltfm_priv(pltfm_host); + int ret = 0; + u32 clk_rate = 0; + + if (!IS_ERR(msm_host->bulk_clks[2].clk)) { + clk_rate = (state == MMC_LOAD_LOW) ? + msm_host->ice_clk_min : + msm_host->ice_clk_max; + if (msm_host->ice_clk_rate == clk_rate) + return 0; + pr_debug("%s: changing ICE clk rate to %u\n", + mmc_hostname(host->mmc), clk_rate); + ret = clk_set_rate(msm_host->bulk_clks[2].clk, clk_rate); + if (ret) { + pr_err("%s: ICE_CLK rate set failed (%d) for %u\n", + mmc_hostname(host->mmc), ret, clk_rate); + return ret; + } + msm_host->ice_clk_rate = clk_rate; + } + return 0; +} #endif static const struct sdhci_msm_variant_ops mci_var_ops = { @@ -3374,6 +3398,7 @@ static const struct sdhci_ops sdhci_msm_ops = { .irq = sdhci_msm_cqe_irq, #if defined(CONFIG_SDC_QTI) .get_current_limit = sdhci_msm_get_current_limit, + .notify_load = sdhci_msm_notify_load, #endif .hw_reset = sdhci_msm_hw_reset, }; diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c index 5116f49c1531..2de42bb29c10 100644 --- a/drivers/mmc/host/sdhci.c +++ b/drivers/mmc/host/sdhci.c @@ -1884,6 +1884,18 @@ EXPORT_SYMBOL_GPL(sdhci_set_power); * MMC callbacks * * * \*****************************************************************************/ +#if defined(CONFIG_SDC_QTI) +static int sdhci_notify_load(struct mmc_host *mmc, enum mmc_load state) +{ + int err = 0; + struct sdhci_host *host = mmc_priv(mmc); + + if (host->ops->notify_load) + err = host->ops->notify_load(host, state); + + return err; +} +#endif void sdhci_request(struct mmc_host *mmc, struct mmc_request *mrq) { @@ -2747,6 +2759,9 @@ static const struct mmc_host_ops sdhci_ops = { .execute_tuning = sdhci_execute_tuning, .card_event = sdhci_card_event, .card_busy = sdhci_card_busy, +#if defined(CONFIG_SDC_QTI) + .notify_load = sdhci_notify_load, +#endif }; /*****************************************************************************\ diff --git a/drivers/mmc/host/sdhci.h b/drivers/mmc/host/sdhci.h index 5ee3819b3c6b..d9606f2ef1b1 100644 --- a/drivers/mmc/host/sdhci.h +++ b/drivers/mmc/host/sdhci.h @@ -658,10 +658,11 @@ struct sdhci_ops { dma_addr_t addr, int len, unsigned int cmd); void (*request_done)(struct sdhci_host *host, struct mmc_request *mrq); - #if defined(CONFIG_SDC_QTI) +#if defined(CONFIG_SDC_QTI) unsigned int (*get_current_limit)(struct sdhci_host *host); void (*dump_vendor_regs)(struct sdhci_host *host); - #endif + int (*notify_load)(struct sdhci_host *host, enum mmc_load state); +#endif }; #ifdef CONFIG_MMC_SDHCI_IO_ACCESSORS diff --git a/include/linux/mmc/host.h b/include/linux/mmc/host.h index c8ad4e5ffe28..68afb7a6f447 100644 --- a/include/linux/mmc/host.h +++ b/include/linux/mmc/host.h @@ -81,6 +81,12 @@ struct mmc_ios { }; struct mmc_host; +#if defined(CONFIG_SDC_QTI) +enum mmc_load { + MMC_LOAD_HIGH, + MMC_LOAD_LOW, +}; +#endif #if defined(CONFIG_SDC_QTI) enum { @@ -194,6 +200,9 @@ struct mmc_host_ops { */ int (*multi_io_quirk)(struct mmc_card *card, unsigned int direction, int blk_size); +#if defined(CONFIG_SDC_QTI) + int (*notify_load)(struct mmc_host *host, enum mmc_load); +#endif }; struct mmc_cqe_ops { @@ -300,11 +309,6 @@ enum dev_state { DEV_RESUMED, }; -enum mmc_load { - MMC_LOAD_HIGH, - MMC_LOAD_LOW, -}; - /** * struct mmc_devfeq_clk_scaling - main context for MMC clock scaling logic *