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