summaryrefslogtreecommitdiff
path: root/drivers/cpufreq
diff options
context:
space:
mode:
authorAnson Huang <b20788@freescale.com>2013-09-02 17:18:31 -0400
committerJason Liu <r64343@freescale.com>2013-10-30 09:55:23 +0800
commita5ea689faad2963c4fa0cfd5a7dc31337a8448c6 (patch)
treed41a7ae6c7bfb7d27efb62b436820dad38f90fd6 /drivers/cpufreq
parent05c28df5349be38a464852b441f19eaf9a8f9536 (diff)
ENGR00277697 cpufreq: imx: increase cpufreq during suspend/resume
During suspend/resume, when cpufreq driver try to increase voltage/freq, it needs to control I2C/SPI to communicate with external PMIC to adjust voltage, but these I2C/SPI devices may be already suspended, to avoid such scenario, we just increase cpufreq to highest setpoint before suspend. As this pm notification's updating cpu policy may work together with cpufreq governor, both of them may call set_target at same time, so we need to add mutex lock to prevent this scenario, otherwise, the clock use count will be wrong. Signed-off-by: Anson Huang <b20788@freescale.com>
Diffstat (limited to 'drivers/cpufreq')
-rw-r--r--drivers/cpufreq/cpufreq-imx6.c50
1 files changed, 49 insertions, 1 deletions
diff --git a/drivers/cpufreq/cpufreq-imx6.c b/drivers/cpufreq/cpufreq-imx6.c
index d301984bfa41..24dfa7a4b1f7 100644
--- a/drivers/cpufreq/cpufreq-imx6.c
+++ b/drivers/cpufreq/cpufreq-imx6.c
@@ -17,6 +17,7 @@
#include <linux/platform_device.h>
#include <linux/regulator/consumer.h>
#include <linux/slab.h>
+#include <linux/suspend.h>
static struct regulator *arm_reg;
static struct regulator *pu_reg;
@@ -31,6 +32,7 @@ static struct clk *pll2_pfd2_396m_clk;
static struct device *cpu_dev;
static struct cpufreq_frequency_table *freq_table;
static unsigned int transition_latency;
+static struct mutex set_cpufreq_lock;
struct soc_opp {
u32 arm_freq;
@@ -59,11 +61,14 @@ static int imx6_set_target(struct cpufreq_policy *policy,
unsigned int index, soc_opp_index = 0;
int ret;
+ mutex_lock(&set_cpufreq_lock);
+
ret = cpufreq_frequency_table_target(policy, freq_table, target_freq,
relation, &index);
if (ret) {
dev_err(cpu_dev, "failed to match target frequency %d: %d\n",
target_freq, ret);
+ mutex_unlock(&set_cpufreq_lock);
return ret;
}
@@ -71,8 +76,10 @@ static int imx6_set_target(struct cpufreq_policy *policy,
freq_hz = freqs.new * 1000;
freqs.old = clk_get_rate(arm_clk) / 1000;
- if (freqs.old == freqs.new)
+ if (freqs.old == freqs.new) {
+ mutex_unlock(&set_cpufreq_lock);
return 0;
+ }
cpufreq_notify_transition(policy, &freqs, CPUFREQ_PRECHANGE);
@@ -81,6 +88,7 @@ static int imx6_set_target(struct cpufreq_policy *policy,
if (IS_ERR(opp)) {
rcu_read_unlock();
dev_err(cpu_dev, "failed to find OPP for %ld\n", freq_hz);
+ mutex_unlock(&set_cpufreq_lock);
return PTR_ERR(opp);
}
@@ -97,6 +105,7 @@ static int imx6_set_target(struct cpufreq_policy *policy,
if (soc_opp_index >= soc_opp_count) {
dev_err(cpu_dev,
"Cannot find matching imx6_soc_opp voltage\n");
+ mutex_unlock(&set_cpufreq_lock);
return -EINVAL;
}
@@ -215,8 +224,10 @@ static int imx6_set_target(struct cpufreq_policy *policy,
cpufreq_notify_transition(policy, &freqs, CPUFREQ_POSTCHANGE);
+ mutex_unlock(&set_cpufreq_lock);
return 0;
err1:
+ mutex_unlock(&set_cpufreq_lock);
return -1;
}
@@ -262,6 +273,40 @@ static struct cpufreq_driver imx6_cpufreq_driver = {
.attr = imx6_cpufreq_attr,
};
+static int imx6_cpufreq_pm_notify(struct notifier_block *nb,
+ unsigned long event, void *dummy)
+{
+ struct cpufreq_policy *data = cpufreq_cpu_get(0);
+ static u32 cpufreq_policy_min_pre_suspend;
+
+ /*
+ * During suspend/resume, When cpufreq driver try to increase
+ * voltage/freq, it needs to control I2C/SPI to communicate
+ * with external PMIC to adjust voltage, but these I2C/SPI
+ * devices may be already suspended, to avoid such scenario,
+ * we just increase cpufreq to highest setpoint before suspend.
+ */
+ switch (event) {
+ case PM_SUSPEND_PREPARE:
+ cpufreq_policy_min_pre_suspend = data->user_policy.min;
+ data->user_policy.min = data->user_policy.max;
+ break;
+ case PM_POST_SUSPEND:
+ data->user_policy.min = cpufreq_policy_min_pre_suspend;
+ break;
+ default:
+ break;
+ }
+
+ cpufreq_update_policy(0);
+
+ return NOTIFY_OK;
+}
+
+static struct notifier_block imx6_cpufreq_pm_notifier = {
+ .notifier_call = imx6_cpufreq_pm_notify,
+};
+
static int imx6_cpufreq_probe(struct platform_device *pdev)
{
struct device_node *np;
@@ -419,6 +464,9 @@ static int imx6_cpufreq_probe(struct platform_device *pdev)
goto free_freq_table;
}
+ mutex_init(&set_cpufreq_lock);
+ register_pm_notifier(&imx6_cpufreq_pm_notifier);
+
of_node_put(np);
return 0;