]>
Commit | Line | Data |
---|---|---|
b886d83c | 1 | // SPDX-License-Identifier: GPL-2.0-only |
5477fb3b AC |
2 | /* |
3 | * CPPC (Collaborative Processor Performance Control) driver for | |
4 | * interfacing with the CPUfreq layer and governors. See | |
5 | * cppc_acpi.c for CPPC specific methods. | |
6 | * | |
7 | * (C) Copyright 2014, 2015 Linaro Ltd. | |
8 | * Author: Ashwin Chaugule <ashwin.chaugule@linaro.org> | |
5477fb3b AC |
9 | */ |
10 | ||
11 | #define pr_fmt(fmt) "CPPC Cpufreq:" fmt | |
12 | ||
13 | #include <linux/kernel.h> | |
14 | #include <linux/module.h> | |
15 | #include <linux/delay.h> | |
16 | #include <linux/cpu.h> | |
17 | #include <linux/cpufreq.h> | |
ad38677d | 18 | #include <linux/dmi.h> |
3d41386d | 19 | #include <linux/time.h> |
5477fb3b AC |
20 | #include <linux/vmalloc.h> |
21 | ||
ad38677d AS |
22 | #include <asm/unaligned.h> |
23 | ||
5477fb3b AC |
24 | #include <acpi/cppc_acpi.h> |
25 | ||
ad38677d AS |
26 | /* Minimum struct length needed for the DMI processor entry we want */ |
27 | #define DMI_ENTRY_PROCESSOR_MIN_LENGTH 48 | |
28 | ||
29 | /* Offest in the DMI processor structure for the max frequency */ | |
30 | #define DMI_PROCESSOR_MAX_SPEED 0x14 | |
31 | ||
5477fb3b AC |
32 | /* |
33 | * These structs contain information parsed from per CPU | |
34 | * ACPI _CPC structures. | |
35 | * e.g. For each CPU the highest, lowest supported | |
36 | * performance capabilities, desired performance level | |
37 | * requested etc. | |
38 | */ | |
41dd6403 | 39 | static struct cppc_cpudata **all_cpu_data; |
5477fb3b | 40 | |
6c8d750f | 41 | struct cppc_workaround_oem_info { |
c7402379 | 42 | char oem_id[ACPI_OEM_ID_SIZE + 1]; |
6c8d750f XW |
43 | char oem_table_id[ACPI_OEM_TABLE_ID_SIZE + 1]; |
44 | u32 oem_revision; | |
45 | }; | |
46 | ||
47 | static bool apply_hisi_workaround; | |
48 | ||
49 | static struct cppc_workaround_oem_info wa_info[] = { | |
50 | { | |
51 | .oem_id = "HISI ", | |
52 | .oem_table_id = "HIP07 ", | |
53 | .oem_revision = 0, | |
54 | }, { | |
55 | .oem_id = "HISI ", | |
56 | .oem_table_id = "HIP08 ", | |
57 | .oem_revision = 0, | |
58 | } | |
59 | }; | |
60 | ||
61 | static unsigned int cppc_cpufreq_perf_to_khz(struct cppc_cpudata *cpu, | |
62 | unsigned int perf); | |
63 | ||
64 | /* | |
65 | * HISI platform does not support delivered performance counter and | |
66 | * reference performance counter. It can calculate the performance using the | |
67 | * platform specific mechanism. We reuse the desired performance register to | |
68 | * store the real performance calculated by the platform. | |
69 | */ | |
70 | static unsigned int hisi_cppc_cpufreq_get_rate(unsigned int cpunum) | |
71 | { | |
72 | struct cppc_cpudata *cpudata = all_cpu_data[cpunum]; | |
73 | u64 desired_perf; | |
74 | int ret; | |
75 | ||
76 | ret = cppc_get_desired_perf(cpunum, &desired_perf); | |
77 | if (ret < 0) | |
78 | return -EIO; | |
79 | ||
80 | return cppc_cpufreq_perf_to_khz(cpudata, desired_perf); | |
81 | } | |
82 | ||
83 | static void cppc_check_hisi_workaround(void) | |
84 | { | |
85 | struct acpi_table_header *tbl; | |
86 | acpi_status status = AE_OK; | |
87 | int i; | |
88 | ||
89 | status = acpi_get_table(ACPI_SIG_PCCT, 0, &tbl); | |
90 | if (ACPI_FAILURE(status) || !tbl) | |
91 | return; | |
92 | ||
93 | for (i = 0; i < ARRAY_SIZE(wa_info); i++) { | |
94 | if (!memcmp(wa_info[i].oem_id, tbl->oem_id, ACPI_OEM_ID_SIZE) && | |
95 | !memcmp(wa_info[i].oem_table_id, tbl->oem_table_id, ACPI_OEM_TABLE_ID_SIZE) && | |
c7402379 | 96 | wa_info[i].oem_revision == tbl->oem_revision) { |
6c8d750f | 97 | apply_hisi_workaround = true; |
c7402379 HG |
98 | break; |
99 | } | |
6c8d750f | 100 | } |
80e8b1e5 HG |
101 | |
102 | acpi_put_table(tbl); | |
6c8d750f XW |
103 | } |
104 | ||
ad38677d AS |
105 | /* Callback function used to retrieve the max frequency from DMI */ |
106 | static void cppc_find_dmi_mhz(const struct dmi_header *dm, void *private) | |
107 | { | |
108 | const u8 *dmi_data = (const u8 *)dm; | |
109 | u16 *mhz = (u16 *)private; | |
110 | ||
111 | if (dm->type == DMI_ENTRY_PROCESSOR && | |
112 | dm->length >= DMI_ENTRY_PROCESSOR_MIN_LENGTH) { | |
113 | u16 val = (u16)get_unaligned((const u16 *) | |
114 | (dmi_data + DMI_PROCESSOR_MAX_SPEED)); | |
115 | *mhz = val > *mhz ? val : *mhz; | |
116 | } | |
117 | } | |
118 | ||
119 | /* Look up the max frequency in DMI */ | |
120 | static u64 cppc_get_dmi_max_khz(void) | |
121 | { | |
122 | u16 mhz = 0; | |
123 | ||
124 | dmi_walk(cppc_find_dmi_mhz, &mhz); | |
125 | ||
126 | /* | |
127 | * Real stupid fallback value, just in case there is no | |
128 | * actual value set. | |
129 | */ | |
130 | mhz = mhz ? mhz : 1; | |
131 | ||
132 | return (1000 * mhz); | |
133 | } | |
134 | ||
256f19d2 PP |
135 | /* |
136 | * If CPPC lowest_freq and nominal_freq registers are exposed then we can | |
137 | * use them to convert perf to freq and vice versa | |
138 | * | |
139 | * If the perf/freq point lies between Nominal and Lowest, we can treat | |
140 | * (Low perf, Low freq) and (Nom Perf, Nom freq) as 2D co-ordinates of a line | |
141 | * and extrapolate the rest | |
142 | * For perf/freq > Nominal, we use the ratio perf:freq at Nominal for conversion | |
143 | */ | |
144 | static unsigned int cppc_cpufreq_perf_to_khz(struct cppc_cpudata *cpu, | |
145 | unsigned int perf) | |
146 | { | |
147 | static u64 max_khz; | |
148 | struct cppc_perf_caps *caps = &cpu->perf_caps; | |
149 | u64 mul, div; | |
150 | ||
151 | if (caps->lowest_freq && caps->nominal_freq) { | |
152 | if (perf >= caps->nominal_perf) { | |
153 | mul = caps->nominal_freq; | |
154 | div = caps->nominal_perf; | |
155 | } else { | |
156 | mul = caps->nominal_freq - caps->lowest_freq; | |
157 | div = caps->nominal_perf - caps->lowest_perf; | |
158 | } | |
159 | } else { | |
160 | if (!max_khz) | |
161 | max_khz = cppc_get_dmi_max_khz(); | |
162 | mul = max_khz; | |
163 | div = cpu->perf_caps.highest_perf; | |
164 | } | |
165 | return (u64)perf * mul / div; | |
166 | } | |
167 | ||
168 | static unsigned int cppc_cpufreq_khz_to_perf(struct cppc_cpudata *cpu, | |
169 | unsigned int freq) | |
170 | { | |
171 | static u64 max_khz; | |
172 | struct cppc_perf_caps *caps = &cpu->perf_caps; | |
173 | u64 mul, div; | |
174 | ||
175 | if (caps->lowest_freq && caps->nominal_freq) { | |
176 | if (freq >= caps->nominal_freq) { | |
177 | mul = caps->nominal_perf; | |
178 | div = caps->nominal_freq; | |
179 | } else { | |
180 | mul = caps->lowest_perf; | |
181 | div = caps->lowest_freq; | |
182 | } | |
183 | } else { | |
184 | if (!max_khz) | |
185 | max_khz = cppc_get_dmi_max_khz(); | |
186 | mul = cpu->perf_caps.highest_perf; | |
187 | div = max_khz; | |
188 | } | |
189 | ||
190 | return (u64)freq * mul / div; | |
191 | } | |
192 | ||
5477fb3b AC |
193 | static int cppc_cpufreq_set_target(struct cpufreq_policy *policy, |
194 | unsigned int target_freq, | |
195 | unsigned int relation) | |
196 | { | |
41dd6403 | 197 | struct cppc_cpudata *cpu; |
5477fb3b | 198 | struct cpufreq_freqs freqs; |
c197d758 | 199 | u32 desired_perf; |
5477fb3b AC |
200 | int ret = 0; |
201 | ||
202 | cpu = all_cpu_data[policy->cpu]; | |
203 | ||
256f19d2 | 204 | desired_perf = cppc_cpufreq_khz_to_perf(cpu, target_freq); |
c197d758 HT |
205 | /* Return if it is exactly the same perf */ |
206 | if (desired_perf == cpu->perf_ctrls.desired_perf) | |
207 | return ret; | |
208 | ||
209 | cpu->perf_ctrls.desired_perf = desired_perf; | |
5477fb3b AC |
210 | freqs.old = policy->cur; |
211 | freqs.new = target_freq; | |
212 | ||
213 | cpufreq_freq_transition_begin(policy, &freqs); | |
214 | ret = cppc_set_perf(cpu->cpu, &cpu->perf_ctrls); | |
215 | cpufreq_freq_transition_end(policy, &freqs, ret != 0); | |
216 | ||
217 | if (ret) | |
218 | pr_debug("Failed to set target on CPU:%d. ret:%d\n", | |
219 | cpu->cpu, ret); | |
220 | ||
221 | return ret; | |
222 | } | |
223 | ||
1e4f63ae | 224 | static int cppc_verify_policy(struct cpufreq_policy_data *policy) |
5477fb3b AC |
225 | { |
226 | cpufreq_verify_within_cpu_limits(policy); | |
227 | return 0; | |
228 | } | |
229 | ||
230 | static void cppc_cpufreq_stop_cpu(struct cpufreq_policy *policy) | |
231 | { | |
232 | int cpu_num = policy->cpu; | |
41dd6403 | 233 | struct cppc_cpudata *cpu = all_cpu_data[cpu_num]; |
5477fb3b AC |
234 | int ret; |
235 | ||
236 | cpu->perf_ctrls.desired_perf = cpu->perf_caps.lowest_perf; | |
237 | ||
238 | ret = cppc_set_perf(cpu_num, &cpu->perf_ctrls); | |
239 | if (ret) | |
240 | pr_debug("Err setting perf value:%d on CPU:%d. ret:%d\n", | |
241 | cpu->perf_caps.lowest_perf, cpu_num, ret); | |
242 | } | |
243 | ||
d4f3388a PP |
244 | /* |
245 | * The PCC subspace describes the rate at which platform can accept commands | |
246 | * on the shared PCC channel (including READs which do not count towards freq | |
247 | * trasition requests), so ideally we need to use the PCC values as a fallback | |
248 | * if we don't have a platform specific transition_delay_us | |
249 | */ | |
250 | #ifdef CONFIG_ARM64 | |
251 | #include <asm/cputype.h> | |
252 | ||
253 | static unsigned int cppc_cpufreq_get_transition_delay_us(int cpu) | |
254 | { | |
255 | unsigned long implementor = read_cpuid_implementor(); | |
256 | unsigned long part_num = read_cpuid_part_number(); | |
257 | unsigned int delay_us = 0; | |
258 | ||
259 | switch (implementor) { | |
260 | case ARM_CPU_IMP_QCOM: | |
261 | switch (part_num) { | |
262 | case QCOM_CPU_PART_FALKOR_V1: | |
263 | case QCOM_CPU_PART_FALKOR: | |
264 | delay_us = 10000; | |
265 | break; | |
266 | default: | |
267 | delay_us = cppc_get_transition_latency(cpu) / NSEC_PER_USEC; | |
268 | break; | |
269 | } | |
270 | break; | |
271 | default: | |
272 | delay_us = cppc_get_transition_latency(cpu) / NSEC_PER_USEC; | |
273 | break; | |
274 | } | |
275 | ||
276 | return delay_us; | |
277 | } | |
278 | ||
279 | #else | |
280 | ||
281 | static unsigned int cppc_cpufreq_get_transition_delay_us(int cpu) | |
282 | { | |
283 | return cppc_get_transition_latency(cpu) / NSEC_PER_USEC; | |
284 | } | |
285 | #endif | |
286 | ||
5477fb3b AC |
287 | static int cppc_cpufreq_cpu_init(struct cpufreq_policy *policy) |
288 | { | |
41dd6403 | 289 | struct cppc_cpudata *cpu; |
5477fb3b AC |
290 | unsigned int cpu_num = policy->cpu; |
291 | int ret = 0; | |
292 | ||
293 | cpu = all_cpu_data[policy->cpu]; | |
294 | ||
295 | cpu->cpu = cpu_num; | |
296 | ret = cppc_get_perf_caps(policy->cpu, &cpu->perf_caps); | |
297 | ||
298 | if (ret) { | |
299 | pr_debug("Err reading CPU%d perf capabilities. ret:%d\n", | |
300 | cpu_num, ret); | |
301 | return ret; | |
302 | } | |
303 | ||
256f19d2 PP |
304 | /* Convert the lowest and nominal freq from MHz to KHz */ |
305 | cpu->perf_caps.lowest_freq *= 1000; | |
306 | cpu->perf_caps.nominal_freq *= 1000; | |
ad38677d | 307 | |
73808d0f PP |
308 | /* |
309 | * Set min to lowest nonlinear perf to avoid any efficiency penalty (see | |
310 | * Section 8.4.7.1.1.5 of ACPI 6.1 spec) | |
311 | */ | |
256f19d2 PP |
312 | policy->min = cppc_cpufreq_perf_to_khz(cpu, cpu->perf_caps.lowest_nonlinear_perf); |
313 | policy->max = cppc_cpufreq_perf_to_khz(cpu, cpu->perf_caps.highest_perf); | |
73808d0f PP |
314 | |
315 | /* | |
316 | * Set cpuinfo.min_freq to Lowest to make the full range of performance | |
317 | * available if userspace wants to use any perf between lowest & lowest | |
318 | * nonlinear perf | |
319 | */ | |
256f19d2 PP |
320 | policy->cpuinfo.min_freq = cppc_cpufreq_perf_to_khz(cpu, cpu->perf_caps.lowest_perf); |
321 | policy->cpuinfo.max_freq = cppc_cpufreq_perf_to_khz(cpu, cpu->perf_caps.highest_perf); | |
73808d0f | 322 | |
d4f3388a | 323 | policy->transition_delay_us = cppc_cpufreq_get_transition_delay_us(cpu_num); |
9dc17917 | 324 | policy->shared_type = cpu->shared_type; |
5477fb3b | 325 | |
8913315e SY |
326 | if (policy->shared_type == CPUFREQ_SHARED_TYPE_ANY) { |
327 | int i; | |
328 | ||
5477fb3b | 329 | cpumask_copy(policy->cpus, cpu->shared_cpu_map); |
8913315e SY |
330 | |
331 | for_each_cpu(i, policy->cpus) { | |
332 | if (unlikely(i == policy->cpu)) | |
333 | continue; | |
334 | ||
335 | memcpy(&all_cpu_data[i]->perf_caps, &cpu->perf_caps, | |
336 | sizeof(cpu->perf_caps)); | |
337 | } | |
338 | } else if (policy->shared_type == CPUFREQ_SHARED_TYPE_ALL) { | |
5477fb3b AC |
339 | /* Support only SW_ANY for now. */ |
340 | pr_debug("Unsupported CPU co-ord type\n"); | |
341 | return -EFAULT; | |
342 | } | |
343 | ||
5477fb3b AC |
344 | cpu->cur_policy = policy; |
345 | ||
346 | /* Set policy->cur to max now. The governors will adjust later. */ | |
256f19d2 PP |
347 | policy->cur = cppc_cpufreq_perf_to_khz(cpu, |
348 | cpu->perf_caps.highest_perf); | |
ad38677d | 349 | cpu->perf_ctrls.desired_perf = cpu->perf_caps.highest_perf; |
5477fb3b AC |
350 | |
351 | ret = cppc_set_perf(cpu_num, &cpu->perf_ctrls); | |
352 | if (ret) | |
353 | pr_debug("Err setting perf value:%d on CPU:%d. ret:%d\n", | |
354 | cpu->perf_caps.highest_perf, cpu_num, ret); | |
355 | ||
356 | return ret; | |
357 | } | |
358 | ||
33477d84 GC |
359 | static inline u64 get_delta(u64 t1, u64 t0) |
360 | { | |
361 | if (t1 > t0 || t0 > ~(u32)0) | |
362 | return t1 - t0; | |
363 | ||
364 | return (u32)t1 - (u32)t0; | |
365 | } | |
366 | ||
367 | static int cppc_get_rate_from_fbctrs(struct cppc_cpudata *cpu, | |
368 | struct cppc_perf_fb_ctrs fb_ctrs_t0, | |
369 | struct cppc_perf_fb_ctrs fb_ctrs_t1) | |
370 | { | |
371 | u64 delta_reference, delta_delivered; | |
372 | u64 reference_perf, delivered_perf; | |
373 | ||
374 | reference_perf = fb_ctrs_t0.reference_perf; | |
375 | ||
376 | delta_reference = get_delta(fb_ctrs_t1.reference, | |
377 | fb_ctrs_t0.reference); | |
378 | delta_delivered = get_delta(fb_ctrs_t1.delivered, | |
379 | fb_ctrs_t0.delivered); | |
380 | ||
381 | /* Check to avoid divide-by zero */ | |
382 | if (delta_reference || delta_delivered) | |
383 | delivered_perf = (reference_perf * delta_delivered) / | |
384 | delta_reference; | |
385 | else | |
386 | delivered_perf = cpu->perf_ctrls.desired_perf; | |
387 | ||
388 | return cppc_cpufreq_perf_to_khz(cpu, delivered_perf); | |
389 | } | |
390 | ||
391 | static unsigned int cppc_cpufreq_get_rate(unsigned int cpunum) | |
392 | { | |
393 | struct cppc_perf_fb_ctrs fb_ctrs_t0 = {0}, fb_ctrs_t1 = {0}; | |
394 | struct cppc_cpudata *cpu = all_cpu_data[cpunum]; | |
395 | int ret; | |
396 | ||
6c8d750f XW |
397 | if (apply_hisi_workaround) |
398 | return hisi_cppc_cpufreq_get_rate(cpunum); | |
399 | ||
33477d84 GC |
400 | ret = cppc_get_perf_ctrs(cpunum, &fb_ctrs_t0); |
401 | if (ret) | |
402 | return ret; | |
403 | ||
404 | udelay(2); /* 2usec delay between sampling */ | |
405 | ||
406 | ret = cppc_get_perf_ctrs(cpunum, &fb_ctrs_t1); | |
407 | if (ret) | |
408 | return ret; | |
409 | ||
410 | return cppc_get_rate_from_fbctrs(cpu, fb_ctrs_t0, fb_ctrs_t1); | |
411 | } | |
412 | ||
5477fb3b AC |
413 | static struct cpufreq_driver cppc_cpufreq_driver = { |
414 | .flags = CPUFREQ_CONST_LOOPS, | |
415 | .verify = cppc_verify_policy, | |
416 | .target = cppc_cpufreq_set_target, | |
33477d84 | 417 | .get = cppc_cpufreq_get_rate, |
5477fb3b AC |
418 | .init = cppc_cpufreq_cpu_init, |
419 | .stop_cpu = cppc_cpufreq_stop_cpu, | |
420 | .name = "cppc_cpufreq", | |
421 | }; | |
422 | ||
423 | static int __init cppc_cpufreq_init(void) | |
424 | { | |
425 | int i, ret = 0; | |
41dd6403 | 426 | struct cppc_cpudata *cpu; |
5477fb3b AC |
427 | |
428 | if (acpi_disabled) | |
429 | return -ENODEV; | |
430 | ||
6396bb22 KC |
431 | all_cpu_data = kcalloc(num_possible_cpus(), sizeof(void *), |
432 | GFP_KERNEL); | |
5477fb3b AC |
433 | if (!all_cpu_data) |
434 | return -ENOMEM; | |
435 | ||
436 | for_each_possible_cpu(i) { | |
41dd6403 | 437 | all_cpu_data[i] = kzalloc(sizeof(struct cppc_cpudata), GFP_KERNEL); |
5477fb3b AC |
438 | if (!all_cpu_data[i]) |
439 | goto out; | |
440 | ||
441 | cpu = all_cpu_data[i]; | |
442 | if (!zalloc_cpumask_var(&cpu->shared_cpu_map, GFP_KERNEL)) | |
443 | goto out; | |
444 | } | |
445 | ||
446 | ret = acpi_get_psd_map(all_cpu_data); | |
447 | if (ret) { | |
448 | pr_debug("Error parsing PSD data. Aborting cpufreq registration.\n"); | |
449 | goto out; | |
450 | } | |
451 | ||
6c8d750f XW |
452 | cppc_check_hisi_workaround(); |
453 | ||
5477fb3b AC |
454 | ret = cpufreq_register_driver(&cppc_cpufreq_driver); |
455 | if (ret) | |
456 | goto out; | |
457 | ||
458 | return ret; | |
459 | ||
460 | out: | |
55b55abc CH |
461 | for_each_possible_cpu(i) { |
462 | cpu = all_cpu_data[i]; | |
463 | if (!cpu) | |
464 | break; | |
465 | free_cpumask_var(cpu->shared_cpu_map); | |
466 | kfree(cpu); | |
467 | } | |
5477fb3b AC |
468 | |
469 | kfree(all_cpu_data); | |
470 | return -ENODEV; | |
471 | } | |
472 | ||
a29a1e76 AC |
473 | static void __exit cppc_cpufreq_exit(void) |
474 | { | |
41dd6403 | 475 | struct cppc_cpudata *cpu; |
a29a1e76 AC |
476 | int i; |
477 | ||
478 | cpufreq_unregister_driver(&cppc_cpufreq_driver); | |
479 | ||
480 | for_each_possible_cpu(i) { | |
481 | cpu = all_cpu_data[i]; | |
482 | free_cpumask_var(cpu->shared_cpu_map); | |
483 | kfree(cpu); | |
484 | } | |
485 | ||
486 | kfree(all_cpu_data); | |
487 | } | |
488 | ||
489 | module_exit(cppc_cpufreq_exit); | |
490 | MODULE_AUTHOR("Ashwin Chaugule"); | |
491 | MODULE_DESCRIPTION("CPUFreq driver based on the ACPI CPPC v5.0+ spec"); | |
492 | MODULE_LICENSE("GPL"); | |
493 | ||
5477fb3b | 494 | late_initcall(cppc_cpufreq_init); |
974f8649 | 495 | |
8ff3c226 | 496 | static const struct acpi_device_id cppc_acpi_ids[] __used = { |
974f8649 PP |
497 | {ACPI_PROCESSOR_DEVICE_HID, }, |
498 | {} | |
499 | }; | |
500 | ||
501 | MODULE_DEVICE_TABLE(acpi, cppc_acpi_ids); |