mmc: core: add support for devfreq suspend/resume
This change adds use of devfreq suspend/resume API.
In the current version of the code, devfreq is fully brought
up/down on each runtime resume/suspend which causes
statistics loss and is time consuming.
This change addresses the above by adding support for
devfreq suspend/resume to be called on each system
suspend/resume, runtime suspend/resume, power restore.
Change-Id: Id209826fb9499084ae96c7d3a47e4032326f61e9
Signed-off-by: Talel Shenhar <tatias@codeaurora.org>
[subhashj@codeaurora.org: fixed merge conflicts]
Signed-off-by: Subhash Jadavani <subhashj@codeaurora.org>
[xiaonian@codeaurora.org: fixed merge conflicts]
Signed-off-by: Xiaonian Wang <xiaonian@codeaurora.org>
diff --git a/drivers/mmc/core/core.c b/drivers/mmc/core/core.c
index 57763be..bc7f5c1 100644
--- a/drivers/mmc/core/core.c
+++ b/drivers/mmc/core/core.c
@@ -258,25 +258,6 @@
EXPORT_SYMBOL(mmc_cmdq_clk_scaling_stop_busy);
/**
- * mmc_disable_devfreq_clk_scaling() - Disable clock scaling
- * @host: pointer to mmc host structure
- *
- * Disables clock scaling aggresively
- */
-void mmc_disable_clk_scaling(struct mmc_host *host)
-{
- if (!host) {
- pr_err("bad host parameter\n");
- WARN_ON(1);
- return;
- }
- pr_debug("%s: disabling clock scaling\n", mmc_hostname(host));
- mmc_exit_clk_scaling(host);
-
-}
-EXPORT_SYMBOL(mmc_disable_clk_scaling);
-
-/**
* mmc_can_scale_clk() - Check clock scaling capability
* @host: pointer to mmc host structure
*/
@@ -288,8 +269,7 @@
return false;
}
- return (host->caps2 & MMC_CAP2_CLK_SCALE) &&
- (!(host->pm_flags & MMC_PM_IGNORE_PM_NOTIFY));
+ return host->caps2 & MMC_CAP2_CLK_SCALE;
}
EXPORT_SYMBOL(mmc_can_scale_clk);
@@ -737,6 +717,94 @@
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)
+ 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);
+ 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.enable = false;
+
+ 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;
+
+ if (!host) {
+ WARN(1, "bad host parameter\n");
+ 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;
+ }
+
+ 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;
+
+ 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.enable = true;
+ 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
*
@@ -747,65 +815,42 @@
int err;
if (!host) {
- pr_err("bad host parameter\n");
+ pr_err("%s: bad host parameter\n", __func__);
WARN_ON(1);
return -EINVAL;
}
- if (!mmc_can_scale_clk(host) || !host->clk_scaling.enable)
+ if (!mmc_can_scale_clk(host))
return 0;
if (!host->clk_scaling.devfreq) {
- pr_err("%s: no devfreq is assosiated with this device\n",
- mmc_hostname(host));
+ pr_err("%s: %s: no devfreq is assosiated with this device\n",
+ mmc_hostname(host), __func__);
return -EPERM;
}
- host->clk_scaling.enable = false;
- atomic_inc(&host->clk_scaling.devfreq_abort);
- err = devfreq_suspend_device(host->clk_scaling.devfreq);
+ err = mmc_suspend_clk_scaling(host);
if (err) {
- pr_err("%s: failed to suspend devfreq\n", mmc_hostname(host));
+ pr_err("%s: %s: fail to suspend clock scaling (%d)\n",
+ mmc_hostname(host), __func__, err);
return err;
}
- pr_debug("%s: devfreq suspended\n", mmc_hostname(host));
err = devfreq_remove_device(host->clk_scaling.devfreq);
if (err) {
- pr_err("%s: remove devfreq failed\n", mmc_hostname(host));
+ pr_err("%s: remove devfreq failed (%d)\n",
+ mmc_hostname(host), err);
return err;
}
host->clk_scaling.devfreq = NULL;
atomic_set(&host->clk_scaling.devfreq_abort, 1);
- mmc_reset_clk_scale_stats(host);
pr_debug("%s: devfreq was removed\n", mmc_hostname(host));
return 0;
}
EXPORT_SYMBOL(mmc_exit_clk_scaling);
-
-/**
- * mmc_reset_clk_scale_stats() - reset clock scaling statistics
- * @host: pointer to mmc host structure
- */
-void mmc_reset_clk_scale_stats(struct mmc_host *host)
-{
- if (!host) {
- pr_err("bad host parameter\n");
- WARN_ON(1);
- return;
- }
- if (!host->clk_scaling.enable)
- return;
- spin_lock_bh(&host->clk_scaling.lock);
- host->clk_scaling.total_busy_time_us = 0;
- spin_unlock_bh(&host->clk_scaling.lock);
-
-}
-EXPORT_SYMBOL(mmc_reset_clk_scale_stats);
-
static inline void mmc_complete_cmd(struct mmc_request *mrq)
{
if (mrq->cap_cmd_during_tfr && !completion_done(&mrq->cmd_completion))
@@ -4359,7 +4404,6 @@
spin_unlock_irqrestore(&host->lock, flags);
cancel_delayed_work_sync(&host->detect);
- mmc_disable_clk_scaling(host);
if (!host->bus_ops)
break;