ipq: more v4.9 fixes
[openwrt/staging/blogic.git] / target / linux / ipq806x / patches-4.9 / 0054-cpufreq-dt-Handle-OPP-voltage-adjust-events.patch
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
5
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.
12
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
17 updates the OPP.
18
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>
22 ---
23 drivers/cpufreq/cpufreq-dt.c | 68 ++++++++++++++++++++++++++++++++++++++++++--
24 1 file changed, 65 insertions(+), 3 deletions(-)
25
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;
33 const char *reg_name;
34 + struct notifier_block opp_nb;
35 + struct mutex lock;
36 + unsigned long opp_freq;
37 };
38
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)
42 {
43 struct private_data *priv = policy->driver_data;
44 + int ret;
45 + unsigned long target_freq = policy->freq_table[index].frequency * 1000;
46 +
47 + mutex_lock(&priv->lock);
48 + ret = dev_pm_opp_set_rate(priv->cpu_dev, target_freq);
49 + if (!ret)
50 + priv->opp_freq = target_freq;
51 + mutex_unlock(&priv->lock);
52
53 - return dev_pm_opp_set_rate(priv->cpu_dev,
54 - policy->freq_table[index].frequency * 1000);
55 + return ret;
56 }
57
58 /*
59 @@ -86,6 +96,39 @@ static const char *find_supply_name(struct device *dev)
60 return name;
61 }
62
63 +static int opp_notifier(struct notifier_block *nb, unsigned long event,
64 + void *data)
65 +{
66 + struct dev_pm_opp *opp = data;
67 + struct private_data *priv = container_of(nb, struct private_data,
68 + opp_nb);
69 + struct device *cpu_dev = priv->cpu_dev;
70 + struct regulator *cpu_reg;
71 + unsigned long volt, freq;
72 + int ret = 0;
73 +
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);
78 + goto out;
79 + }
80 + volt = dev_pm_opp_get_voltage(opp);
81 + freq = dev_pm_opp_get_freq(opp);
82 +
83 + mutex_lock(&priv->lock);
84 + if (freq == priv->opp_freq) {
85 + ret = regulator_set_voltage_triplet(cpu_reg, volt, volt, volt);
86 + }
87 + mutex_unlock(&priv->lock);
88 + if (ret)
89 + dev_err(cpu_dev, "failed to scale voltage: %d\n", ret);
90 + }
91 +
92 +out:
93 + return notifier_from_errno(ret);
94 +}
95 +
96 static int resources_available(void)
97 {
98 struct device *cpu_dev;
99 @@ -153,6 +196,7 @@ static int cpufreq_init(struct cpufreq_policy *policy)
100 bool fallback = false;
101 const char *name;
102 int ret;
103 + struct srcu_notifier_head *opp_srcu_head;
104
105 cpu_dev = get_cpu_device(policy->cpu);
106 if (!cpu_dev) {
107 @@ -239,13 +283,29 @@ static int cpufreq_init(struct cpufreq_policy *policy)
108 goto out_free_opp;
109 }
110
111 + mutex_init(&priv->lock);
112 +
113 + rcu_read_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);
117 + rcu_read_unlock();
118 + goto out_free_priv;
119 + }
120 +
121 + priv->opp_nb.notifier_call = opp_notifier;
122 + ret = srcu_notifier_chain_register(opp_srcu_head, &priv->opp_nb);
123 + rcu_read_unlock();
124 + if (ret)
125 + goto out_free_priv;
126 +
127 priv->reg_name = name;
128 priv->opp_table = opp_table;
129
130 ret = dev_pm_opp_init_cpufreq_table(cpu_dev, &freq_table);
131 if (ret) {
132 dev_err(cpu_dev, "failed to init cpufreq table: %d\n", ret);
133 - goto out_free_priv;
134 + goto out_unregister_nb;
135 }
136
137 priv->cpu_dev = cpu_dev;
138 @@ -284,6 +344,8 @@ static int cpufreq_init(struct cpufreq_policy *policy)
139
140 out_free_cpufreq_table:
141 dev_pm_opp_free_cpufreq_table(cpu_dev, &freq_table);
142 +out_unregister_nb:
143 + srcu_notifier_chain_unregister(opp_srcu_head, &priv->opp_nb);
144 out_free_priv:
145 kfree(priv);
146 out_free_opp:
147 --
148 2.11.0
149