1 From 10577f74c35bd395951d1b2382c8d821089b5745 Mon Sep 17 00:00:00 2001
2 From: Stephen Boyd <sboyd@codeaurora.org>
3 Date: Fri, 18 Sep 2015 17:52:08 -0700
4 Subject: [PATCH 54/69] cpufreq-dt: Handle OPP voltage adjust events
6 On some SoCs the Adaptive Voltage Scaling (AVS) technique is
7 employed to optimize the operating voltage of a device. At a
8 given frequency, the hardware monitors dynamic factors and either
9 makes a suggestion for how much to adjust a voltage for the
10 current frequency, or it automatically adjusts the voltage
11 without software intervention.
13 In the former case, an AVS driver will call
14 dev_pm_opp_modify_voltage() and update the voltage for the
15 particular OPP the CPUs are using. Add an OPP notifier to
16 cpufreq-dt so that we can adjust the voltage of the CPU when AVS
19 Signed-off-by: Stephen Boyd <sboyd@codeaurora.org>
20 Acked-by: Viresh Kumar <viresh.kumar@linaro.org>
21 Signed-off-by: Georgi Djakov <georgi.djakov@linaro.org>
23 drivers/cpufreq/cpufreq-dt.c | 68 ++++++++++++++++++++++++++++++++++++++++++--
24 1 file changed, 65 insertions(+), 3 deletions(-)
26 diff --git a/drivers/cpufreq/cpufreq-dt.c b/drivers/cpufreq/cpufreq-dt.c
27 index 4d3ec92cbabf..058f08edf315 100644
28 --- a/drivers/cpufreq/cpufreq-dt.c
29 +++ b/drivers/cpufreq/cpufreq-dt.c
30 @@ -32,6 +32,9 @@ struct private_data {
31 struct device *cpu_dev;
32 struct thermal_cooling_device *cdev;
34 + struct notifier_block opp_nb;
36 + unsigned long opp_freq;
39 static struct freq_attr *cpufreq_dt_attr[] = {
40 @@ -43,9 +46,16 @@ static struct freq_attr *cpufreq_dt_attr[] = {
41 static int set_target(struct cpufreq_policy *policy, unsigned int index)
43 struct private_data *priv = policy->driver_data;
45 + unsigned long target_freq = policy->freq_table[index].frequency * 1000;
47 + mutex_lock(&priv->lock);
48 + ret = dev_pm_opp_set_rate(priv->cpu_dev, target_freq);
50 + priv->opp_freq = target_freq;
51 + mutex_unlock(&priv->lock);
53 - return dev_pm_opp_set_rate(priv->cpu_dev,
54 - policy->freq_table[index].frequency * 1000);
59 @@ -86,6 +96,39 @@ static const char *find_supply_name(struct device *dev)
63 +static int opp_notifier(struct notifier_block *nb, unsigned long event,
66 + struct dev_pm_opp *opp = data;
67 + struct private_data *priv = container_of(nb, struct private_data,
69 + struct device *cpu_dev = priv->cpu_dev;
70 + struct regulator *cpu_reg;
71 + unsigned long volt, freq;
74 + if (event == OPP_EVENT_ADJUST_VOLTAGE) {
75 + cpu_reg = dev_pm_opp_get_regulator(cpu_dev);
76 + if (IS_ERR(cpu_reg)) {
77 + ret = PTR_ERR(cpu_reg);
80 + volt = dev_pm_opp_get_voltage(opp);
81 + freq = dev_pm_opp_get_freq(opp);
83 + mutex_lock(&priv->lock);
84 + if (freq == priv->opp_freq) {
85 + ret = regulator_set_voltage_triplet(cpu_reg, volt, volt, volt);
87 + mutex_unlock(&priv->lock);
89 + dev_err(cpu_dev, "failed to scale voltage: %d\n", ret);
93 + return notifier_from_errno(ret);
96 static int resources_available(void)
98 struct device *cpu_dev;
99 @@ -153,6 +196,7 @@ static int cpufreq_init(struct cpufreq_policy *policy)
100 bool fallback = false;
103 + struct srcu_notifier_head *opp_srcu_head;
105 cpu_dev = get_cpu_device(policy->cpu);
107 @@ -239,13 +283,29 @@ static int cpufreq_init(struct cpufreq_policy *policy)
111 + mutex_init(&priv->lock);
114 + opp_srcu_head = dev_pm_opp_get_notifier(cpu_dev);
115 + if (IS_ERR(opp_srcu_head)) {
116 + ret = PTR_ERR(opp_srcu_head);
118 + goto out_free_priv;
121 + priv->opp_nb.notifier_call = opp_notifier;
122 + ret = srcu_notifier_chain_register(opp_srcu_head, &priv->opp_nb);
125 + goto out_free_priv;
127 priv->reg_name = name;
128 priv->opp_table = opp_table;
130 ret = dev_pm_opp_init_cpufreq_table(cpu_dev, &freq_table);
132 dev_err(cpu_dev, "failed to init cpufreq table: %d\n", ret);
133 - goto out_free_priv;
134 + goto out_unregister_nb;
137 priv->cpu_dev = cpu_dev;
138 @@ -284,6 +344,8 @@ static int cpufreq_init(struct cpufreq_policy *policy)
140 out_free_cpufreq_table:
141 dev_pm_opp_free_cpufreq_table(cpu_dev, &freq_table);
143 + srcu_notifier_chain_unregister(opp_srcu_head, &priv->opp_nb);