ipq806x: switch to generic cpufreq driver cpufreq-dt
[openwrt/openwrt.git] / target / linux / ipq806x / patches-4.4 / 172-cpufreq-dt-Handle-OPP-voltage-adjust-events.patch
1 From 175329015c8a0b480240da222822d2f8316f074d Mon Sep 17 00:00:00 2001
2 From: Stephen Boyd <sboyd@codeaurora.org>
3 Date: Mon, 1 Jun 2015 18:47:58 -0700
4 Subject: 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 ---
21 drivers/cpufreq/cpufreq-dt.c | 72 ++++++++++++++++++++++++++++++++++++++++----
22 1 file changed, 66 insertions(+), 6 deletions(-)
23
24 --- a/drivers/cpufreq/cpufreq-dt.c
25 +++ b/drivers/cpufreq/cpufreq-dt.c
26 @@ -34,6 +34,9 @@ struct private_data {
27 struct regulator *cpu_reg;
28 struct thermal_cooling_device *cdev;
29 unsigned int voltage_tolerance; /* in percentage */
30 + struct notifier_block opp_nb;
31 + struct mutex lock;
32 + unsigned long opp_freq;
33 };
34
35 static struct freq_attr *cpufreq_dt_attr[] = {
36 @@ -42,6 +45,42 @@ static struct freq_attr *cpufreq_dt_attr
37 NULL,
38 };
39
40 +static int opp_notifier(struct notifier_block *nb, unsigned long event,
41 + void *data)
42 +{
43 + struct dev_pm_opp *opp = data;
44 + struct private_data *priv = container_of(nb, struct private_data,
45 + opp_nb);
46 + struct device *cpu_dev = priv->cpu_dev;
47 + struct regulator *cpu_reg = priv->cpu_reg;
48 + unsigned long volt, tol, freq;
49 + int ret = 0;
50 +
51 + switch (event) {
52 + case OPP_EVENT_ADJUST_VOLTAGE:
53 + volt = dev_pm_opp_get_voltage(opp);
54 + freq = dev_pm_opp_get_freq(opp);
55 + tol = volt * priv->voltage_tolerance / 100;
56 +
57 + mutex_lock(&priv->lock);
58 + if (freq == priv->opp_freq)
59 + ret = regulator_set_voltage_tol(cpu_reg, volt,
60 + tol);
61 + mutex_unlock(&priv->lock);
62 + if (ret) {
63 + dev_err(cpu_dev,
64 + "failed to scale voltage up: %d\n",
65 + ret);
66 + return ret;
67 + }
68 + break;
69 + default:
70 + break;
71 + }
72 +
73 + return 0;
74 +}
75 +
76 static int set_target(struct cpufreq_policy *policy, unsigned int index)
77 {
78 struct dev_pm_opp *opp;
79 @@ -53,6 +92,7 @@ static int set_target(struct cpufreq_pol
80 unsigned long volt = 0, volt_old = 0, tol = 0;
81 unsigned int old_freq, new_freq;
82 long freq_Hz, freq_exact;
83 + unsigned long opp_freq = 0;
84 int ret;
85
86 freq_Hz = clk_round_rate(cpu_clk, freq_table[index].frequency * 1000);
87 @@ -63,8 +103,8 @@ static int set_target(struct cpufreq_pol
88 new_freq = freq_Hz / 1000;
89 old_freq = clk_get_rate(cpu_clk) / 1000;
90
91 + mutex_lock(&priv->lock);
92 if (!IS_ERR(cpu_reg)) {
93 - unsigned long opp_freq;
94
95 rcu_read_lock();
96 opp = dev_pm_opp_find_freq_ceil(cpu_dev, &freq_Hz);
97 @@ -72,7 +112,8 @@ static int set_target(struct cpufreq_pol
98 rcu_read_unlock();
99 dev_err(cpu_dev, "failed to find OPP for %ld\n",
100 freq_Hz);
101 - return PTR_ERR(opp);
102 + ret = PTR_ERR(opp);
103 + goto out;
104 }
105 volt = dev_pm_opp_get_voltage(opp);
106 opp_freq = dev_pm_opp_get_freq(opp);
107 @@ -93,7 +134,7 @@ static int set_target(struct cpufreq_pol
108 if (ret) {
109 dev_err(cpu_dev, "failed to scale voltage up: %d\n",
110 ret);
111 - return ret;
112 + goto out;
113 }
114 }
115
116 @@ -102,7 +143,7 @@ static int set_target(struct cpufreq_pol
117 dev_err(cpu_dev, "failed to set clock rate: %d\n", ret);
118 if (!IS_ERR(cpu_reg) && volt_old > 0)
119 regulator_set_voltage_tol(cpu_reg, volt_old, tol);
120 - return ret;
121 + goto out;
122 }
123
124 /* scaling down? scale voltage after frequency */
125 @@ -112,9 +153,12 @@ static int set_target(struct cpufreq_pol
126 dev_err(cpu_dev, "failed to scale voltage down: %d\n",
127 ret);
128 clk_set_rate(cpu_clk, old_freq * 1000);
129 + goto out;
130 }
131 }
132 -
133 + priv->opp_freq = opp_freq;
134 +out:
135 + mutex_unlock(&priv->lock);
136 return ret;
137 }
138
139 @@ -201,6 +245,7 @@ static int cpufreq_init(struct cpufreq_p
140 unsigned int transition_latency;
141 bool need_update = false;
142 int ret;
143 + struct srcu_notifier_head *opp_srcu_head;
144
145 ret = allocate_resources(policy->cpu, &cpu_dev, &cpu_reg, &cpu_clk);
146 if (ret) {
147 @@ -277,6 +322,19 @@ static int cpufreq_init(struct cpufreq_p
148 goto out_free_opp;
149 }
150
151 + mutex_init(&priv->lock);
152 +
153 + opp_srcu_head = dev_pm_opp_get_notifier(cpu_dev);
154 + if (IS_ERR(opp_srcu_head)) {
155 + ret = PTR_ERR(opp_srcu_head);
156 + goto out_free_priv;
157 + }
158 +
159 + priv->opp_nb.notifier_call = opp_notifier;
160 + ret = srcu_notifier_chain_register(opp_srcu_head, &priv->opp_nb);
161 + if (ret)
162 + goto out_free_priv;
163 +
164 of_property_read_u32(np, "voltage-tolerance", &priv->voltage_tolerance);
165
166 if (!transition_latency)
167 @@ -326,7 +384,7 @@ static int cpufreq_init(struct cpufreq_p
168 ret = dev_pm_opp_init_cpufreq_table(cpu_dev, &freq_table);
169 if (ret) {
170 pr_err("failed to init cpufreq table: %d\n", ret);
171 - goto out_free_priv;
172 + goto out_unregister_nb;
173 }
174
175 priv->cpu_dev = cpu_dev;
176 @@ -365,6 +423,8 @@ static int cpufreq_init(struct cpufreq_p
177
178 out_free_cpufreq_table:
179 dev_pm_opp_free_cpufreq_table(cpu_dev, &freq_table);
180 +out_unregister_nb:
181 + srcu_notifier_chain_unregister(opp_srcu_head, &priv->opp_nb);
182 out_free_priv:
183 kfree(priv);
184 out_free_opp: