.. | .. |
---|
8 | 8 | |
---|
9 | 9 | #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt |
---|
10 | 10 | |
---|
| 11 | +#include <linux/clk-provider.h> |
---|
11 | 12 | #include <linux/cpu.h> |
---|
12 | 13 | #include <linux/cpufreq.h> |
---|
13 | 14 | #include <linux/cpumask.h> |
---|
14 | | -#include <linux/cpu_cooling.h> |
---|
15 | 15 | #include <linux/energy_model.h> |
---|
16 | 16 | #include <linux/export.h> |
---|
17 | 17 | #include <linux/module.h> |
---|
.. | .. |
---|
23 | 23 | struct scmi_data { |
---|
24 | 24 | int domain_id; |
---|
25 | 25 | struct device *cpu_dev; |
---|
26 | | - struct thermal_cooling_device *cdev; |
---|
27 | 26 | }; |
---|
28 | 27 | |
---|
29 | | -static const struct scmi_handle *handle; |
---|
| 28 | +static struct scmi_protocol_handle *ph; |
---|
| 29 | +static const struct scmi_perf_proto_ops *perf_ops; |
---|
30 | 30 | |
---|
31 | 31 | static unsigned int scmi_cpufreq_get_rate(unsigned int cpu) |
---|
32 | 32 | { |
---|
33 | 33 | struct cpufreq_policy *policy = cpufreq_cpu_get_raw(cpu); |
---|
34 | | - struct scmi_perf_ops *perf_ops = handle->perf_ops; |
---|
35 | 34 | struct scmi_data *priv = policy->driver_data; |
---|
36 | 35 | unsigned long rate; |
---|
37 | 36 | int ret; |
---|
38 | 37 | |
---|
39 | | - ret = perf_ops->freq_get(handle, priv->domain_id, &rate, false); |
---|
| 38 | + ret = perf_ops->freq_get(ph, priv->domain_id, &rate, false); |
---|
40 | 39 | if (ret) |
---|
41 | 40 | return 0; |
---|
42 | 41 | return rate / 1000; |
---|
.. | .. |
---|
50 | 49 | static int |
---|
51 | 50 | scmi_cpufreq_set_target(struct cpufreq_policy *policy, unsigned int index) |
---|
52 | 51 | { |
---|
53 | | - int ret; |
---|
54 | 52 | struct scmi_data *priv = policy->driver_data; |
---|
55 | | - struct scmi_perf_ops *perf_ops = handle->perf_ops; |
---|
56 | 53 | u64 freq = policy->freq_table[index].frequency; |
---|
57 | 54 | |
---|
58 | | - ret = perf_ops->freq_set(handle, priv->domain_id, freq * 1000, false); |
---|
59 | | - if (!ret) |
---|
60 | | - arch_set_freq_scale(policy->related_cpus, freq, |
---|
61 | | - policy->cpuinfo.max_freq); |
---|
62 | | - return ret; |
---|
| 55 | + return perf_ops->freq_set(ph, priv->domain_id, freq * 1000, false); |
---|
63 | 56 | } |
---|
64 | 57 | |
---|
65 | 58 | static unsigned int scmi_cpufreq_fast_switch(struct cpufreq_policy *policy, |
---|
66 | 59 | unsigned int target_freq) |
---|
67 | 60 | { |
---|
68 | 61 | struct scmi_data *priv = policy->driver_data; |
---|
69 | | - struct scmi_perf_ops *perf_ops = handle->perf_ops; |
---|
70 | 62 | |
---|
71 | | - if (!perf_ops->freq_set(handle, priv->domain_id, |
---|
72 | | - target_freq * 1000, true)) { |
---|
73 | | - arch_set_freq_scale(policy->related_cpus, target_freq, |
---|
74 | | - policy->cpuinfo.max_freq); |
---|
| 63 | + if (!perf_ops->freq_set(ph, priv->domain_id, |
---|
| 64 | + target_freq * 1000, true)) |
---|
75 | 65 | return target_freq; |
---|
76 | | - } |
---|
77 | 66 | |
---|
78 | 67 | return 0; |
---|
79 | 68 | } |
---|
.. | .. |
---|
84 | 73 | int cpu, domain, tdomain; |
---|
85 | 74 | struct device *tcpu_dev; |
---|
86 | 75 | |
---|
87 | | - domain = handle->perf_ops->device_domain_id(cpu_dev); |
---|
| 76 | + domain = perf_ops->device_domain_id(cpu_dev); |
---|
88 | 77 | if (domain < 0) |
---|
89 | 78 | return domain; |
---|
90 | 79 | |
---|
.. | .. |
---|
96 | 85 | if (!tcpu_dev) |
---|
97 | 86 | continue; |
---|
98 | 87 | |
---|
99 | | - tdomain = handle->perf_ops->device_domain_id(tcpu_dev); |
---|
| 88 | + tdomain = perf_ops->device_domain_id(tcpu_dev); |
---|
100 | 89 | if (tdomain == domain) |
---|
101 | 90 | cpumask_set_cpu(cpu, cpumask); |
---|
102 | 91 | } |
---|
.. | .. |
---|
105 | 94 | } |
---|
106 | 95 | |
---|
107 | 96 | static int __maybe_unused |
---|
108 | | -scmi_get_cpu_power(unsigned long *power, unsigned long *KHz, int cpu) |
---|
| 97 | +scmi_get_cpu_power(unsigned long *power, unsigned long *KHz, |
---|
| 98 | + struct device *cpu_dev) |
---|
109 | 99 | { |
---|
110 | | - struct device *cpu_dev = get_cpu_device(cpu); |
---|
111 | 100 | unsigned long Hz; |
---|
112 | 101 | int ret, domain; |
---|
113 | 102 | |
---|
114 | | - if (!cpu_dev) { |
---|
115 | | - pr_err("failed to get cpu%d device\n", cpu); |
---|
116 | | - return -ENODEV; |
---|
117 | | - } |
---|
118 | | - |
---|
119 | | - domain = handle->perf_ops->device_domain_id(cpu_dev); |
---|
| 103 | + domain = perf_ops->device_domain_id(cpu_dev); |
---|
120 | 104 | if (domain < 0) |
---|
121 | 105 | return domain; |
---|
122 | 106 | |
---|
123 | 107 | /* Get the power cost of the performance domain. */ |
---|
124 | 108 | Hz = *KHz * 1000; |
---|
125 | | - ret = handle->perf_ops->est_power_get(handle, domain, &Hz, power); |
---|
| 109 | + ret = perf_ops->est_power_get(ph, domain, &Hz, power); |
---|
126 | 110 | if (ret) |
---|
127 | 111 | return ret; |
---|
128 | 112 | |
---|
.. | .. |
---|
140 | 124 | struct scmi_data *priv; |
---|
141 | 125 | struct cpufreq_frequency_table *freq_table; |
---|
142 | 126 | struct em_data_callback em_cb = EM_DATA_CB(scmi_get_cpu_power); |
---|
| 127 | + bool power_scale_mw; |
---|
143 | 128 | |
---|
144 | 129 | cpu_dev = get_cpu_device(policy->cpu); |
---|
145 | 130 | if (!cpu_dev) { |
---|
.. | .. |
---|
147 | 132 | return -ENODEV; |
---|
148 | 133 | } |
---|
149 | 134 | |
---|
150 | | - ret = handle->perf_ops->device_opps_add(handle, cpu_dev); |
---|
| 135 | + ret = perf_ops->device_opps_add(ph, cpu_dev); |
---|
151 | 136 | if (ret) { |
---|
152 | 137 | dev_warn(cpu_dev, "failed to add opps to the device\n"); |
---|
153 | 138 | return ret; |
---|
.. | .. |
---|
166 | 151 | return ret; |
---|
167 | 152 | } |
---|
168 | 153 | |
---|
169 | | - ret = dev_pm_opp_get_opp_count(cpu_dev); |
---|
170 | | - if (ret <= 0) { |
---|
| 154 | + nr_opp = dev_pm_opp_get_opp_count(cpu_dev); |
---|
| 155 | + if (nr_opp <= 0) { |
---|
171 | 156 | dev_dbg(cpu_dev, "OPP table is not ready, deferring probe\n"); |
---|
172 | 157 | ret = -EPROBE_DEFER; |
---|
173 | 158 | goto out_free_opp; |
---|
174 | 159 | } |
---|
175 | | - nr_opp = ret; |
---|
176 | 160 | |
---|
177 | 161 | priv = kzalloc(sizeof(*priv), GFP_KERNEL); |
---|
178 | 162 | if (!priv) { |
---|
.. | .. |
---|
187 | 171 | } |
---|
188 | 172 | |
---|
189 | 173 | priv->cpu_dev = cpu_dev; |
---|
190 | | - priv->domain_id = handle->perf_ops->device_domain_id(cpu_dev); |
---|
| 174 | + priv->domain_id = perf_ops->device_domain_id(cpu_dev); |
---|
191 | 175 | |
---|
192 | 176 | policy->driver_data = priv; |
---|
193 | 177 | policy->freq_table = freq_table; |
---|
.. | .. |
---|
195 | 179 | /* SCMI allows DVFS request for any domain from any CPU */ |
---|
196 | 180 | policy->dvfs_possible_from_any_cpu = true; |
---|
197 | 181 | |
---|
198 | | - latency = handle->perf_ops->transition_latency_get(handle, cpu_dev); |
---|
| 182 | + latency = perf_ops->transition_latency_get(ph, cpu_dev); |
---|
199 | 183 | if (!latency) |
---|
200 | 184 | latency = CPUFREQ_ETERNAL; |
---|
201 | 185 | |
---|
202 | 186 | policy->cpuinfo.transition_latency = latency; |
---|
203 | 187 | |
---|
204 | | - policy->fast_switch_possible = true; |
---|
| 188 | + policy->fast_switch_possible = |
---|
| 189 | + perf_ops->fast_switch_possible(ph, cpu_dev); |
---|
205 | 190 | |
---|
206 | | - em_register_perf_domain(policy->cpus, nr_opp, &em_cb); |
---|
| 191 | + power_scale_mw = perf_ops->power_scale_mw_get(ph); |
---|
| 192 | + em_dev_register_perf_domain(cpu_dev, nr_opp, &em_cb, policy->cpus, |
---|
| 193 | + power_scale_mw); |
---|
207 | 194 | |
---|
208 | 195 | return 0; |
---|
209 | 196 | |
---|
210 | 197 | out_free_priv: |
---|
211 | 198 | kfree(priv); |
---|
212 | 199 | out_free_opp: |
---|
213 | | - dev_pm_opp_cpumask_remove_table(policy->cpus); |
---|
| 200 | + dev_pm_opp_remove_all_dynamic(cpu_dev); |
---|
214 | 201 | |
---|
215 | 202 | return ret; |
---|
216 | 203 | } |
---|
.. | .. |
---|
219 | 206 | { |
---|
220 | 207 | struct scmi_data *priv = policy->driver_data; |
---|
221 | 208 | |
---|
222 | | - cpufreq_cooling_unregister(priv->cdev); |
---|
223 | 209 | dev_pm_opp_free_cpufreq_table(priv->cpu_dev, &policy->freq_table); |
---|
| 210 | + dev_pm_opp_remove_all_dynamic(priv->cpu_dev); |
---|
224 | 211 | kfree(priv); |
---|
225 | | - dev_pm_opp_cpumask_remove_table(policy->related_cpus); |
---|
226 | 212 | |
---|
227 | 213 | return 0; |
---|
228 | | -} |
---|
229 | | - |
---|
230 | | -static void scmi_cpufreq_ready(struct cpufreq_policy *policy) |
---|
231 | | -{ |
---|
232 | | - struct scmi_data *priv = policy->driver_data; |
---|
233 | | - |
---|
234 | | - priv->cdev = of_cpufreq_cooling_register(policy); |
---|
235 | 214 | } |
---|
236 | 215 | |
---|
237 | 216 | static struct cpufreq_driver scmi_cpufreq_driver = { |
---|
238 | 217 | .name = "scmi", |
---|
239 | 218 | .flags = CPUFREQ_STICKY | CPUFREQ_HAVE_GOVERNOR_PER_POLICY | |
---|
240 | | - CPUFREQ_NEED_INITIAL_FREQ_CHECK, |
---|
| 219 | + CPUFREQ_NEED_INITIAL_FREQ_CHECK | |
---|
| 220 | + CPUFREQ_IS_COOLING_DEV, |
---|
241 | 221 | .verify = cpufreq_generic_frequency_table_verify, |
---|
242 | 222 | .attr = cpufreq_generic_attr, |
---|
243 | 223 | .target_index = scmi_cpufreq_set_target, |
---|
.. | .. |
---|
245 | 225 | .get = scmi_cpufreq_get_rate, |
---|
246 | 226 | .init = scmi_cpufreq_init, |
---|
247 | 227 | .exit = scmi_cpufreq_exit, |
---|
248 | | - .ready = scmi_cpufreq_ready, |
---|
249 | 228 | }; |
---|
250 | 229 | |
---|
251 | 230 | static int scmi_cpufreq_probe(struct scmi_device *sdev) |
---|
252 | 231 | { |
---|
253 | 232 | int ret; |
---|
| 233 | + struct device *dev = &sdev->dev; |
---|
| 234 | + const struct scmi_handle *handle; |
---|
254 | 235 | |
---|
255 | 236 | handle = sdev->handle; |
---|
256 | 237 | |
---|
257 | | - if (!handle || !handle->perf_ops) |
---|
| 238 | + if (!handle) |
---|
258 | 239 | return -ENODEV; |
---|
| 240 | + |
---|
| 241 | + perf_ops = handle->devm_get_protocol(sdev, SCMI_PROTOCOL_PERF, &ph); |
---|
| 242 | + if (IS_ERR(perf_ops)) |
---|
| 243 | + return PTR_ERR(perf_ops); |
---|
| 244 | + |
---|
| 245 | +#ifdef CONFIG_COMMON_CLK |
---|
| 246 | + /* dummy clock provider as needed by OPP if clocks property is used */ |
---|
| 247 | + if (of_find_property(dev->of_node, "#clock-cells", NULL)) |
---|
| 248 | + devm_of_clk_add_hw_provider(dev, of_clk_hw_simple_get, NULL); |
---|
| 249 | +#endif |
---|
259 | 250 | |
---|
260 | 251 | ret = cpufreq_register_driver(&scmi_cpufreq_driver); |
---|
261 | 252 | if (ret) { |
---|
262 | | - dev_err(&sdev->dev, "%s: registering cpufreq failed, err: %d\n", |
---|
| 253 | + dev_err(dev, "%s: registering cpufreq failed, err: %d\n", |
---|
263 | 254 | __func__, ret); |
---|
264 | 255 | } |
---|
265 | 256 | |
---|
.. | .. |
---|
272 | 263 | } |
---|
273 | 264 | |
---|
274 | 265 | static const struct scmi_device_id scmi_id_table[] = { |
---|
275 | | - { SCMI_PROTOCOL_PERF }, |
---|
| 266 | + { SCMI_PROTOCOL_PERF, "cpufreq" }, |
---|
276 | 267 | { }, |
---|
277 | 268 | }; |
---|
278 | 269 | MODULE_DEVICE_TABLE(scmi, scmi_id_table); |
---|