1 | // SPDX-License-Identifier: GPL-2.0-or-later |
2 | /* |
3 | * processor_perflib.c - ACPI Processor P-States Library ($Revision: 71 $) |
4 | * |
5 | * Copyright (C) 2001, 2002 Andy Grover <andrew.grover@intel.com> |
6 | * Copyright (C) 2001, 2002 Paul Diefenbaugh <paul.s.diefenbaugh@intel.com> |
7 | * Copyright (C) 2004 Dominik Brodowski <linux@brodo.de> |
8 | * Copyright (C) 2004 Anil S Keshavamurthy <anil.s.keshavamurthy@intel.com> |
9 | * - Added processor hotplug support |
10 | */ |
11 | |
12 | #define pr_fmt(fmt) "ACPI: " fmt |
13 | |
14 | #include <linux/kernel.h> |
15 | #include <linux/module.h> |
16 | #include <linux/init.h> |
17 | #include <linux/cpufreq.h> |
18 | #include <linux/slab.h> |
19 | #include <linux/acpi.h> |
20 | #include <acpi/processor.h> |
21 | #ifdef CONFIG_X86 |
22 | #include <asm/cpufeature.h> |
23 | #endif |
24 | |
25 | #define ACPI_PROCESSOR_FILE_PERFORMANCE "performance" |
26 | |
27 | static DEFINE_MUTEX(performance_mutex); |
28 | |
29 | /* |
30 | * _PPC support is implemented as a CPUfreq policy notifier: |
31 | * This means each time a CPUfreq driver registered also with |
32 | * the ACPI core is asked to change the speed policy, the maximum |
33 | * value is adjusted so that it is within the platform limit. |
34 | * |
35 | * Also, when a new platform limit value is detected, the CPUfreq |
36 | * policy is adjusted accordingly. |
37 | */ |
38 | |
39 | /* ignore_ppc: |
40 | * -1 -> cpufreq low level drivers not initialized -> _PSS, etc. not called yet |
41 | * ignore _PPC |
42 | * 0 -> cpufreq low level drivers initialized -> consider _PPC values |
43 | * 1 -> ignore _PPC totally -> forced by user through boot param |
44 | */ |
45 | static int ignore_ppc = -1; |
46 | module_param(ignore_ppc, int, 0644); |
47 | MODULE_PARM_DESC(ignore_ppc, "If the frequency of your machine gets wrongly" \ |
48 | "limited by BIOS, this should help" ); |
49 | |
50 | static bool acpi_processor_ppc_in_use; |
51 | |
52 | static int acpi_processor_get_platform_limit(struct acpi_processor *pr) |
53 | { |
54 | acpi_status status = 0; |
55 | unsigned long long ppc = 0; |
56 | s32 qos_value; |
57 | int index; |
58 | int ret; |
59 | |
60 | if (!pr) |
61 | return -EINVAL; |
62 | |
63 | /* |
64 | * _PPC indicates the maximum state currently supported by the platform |
65 | * (e.g. 0 = states 0..n; 1 = states 1..n; etc. |
66 | */ |
67 | status = acpi_evaluate_integer(handle: pr->handle, pathname: "_PPC" , NULL, data: &ppc); |
68 | if (status != AE_NOT_FOUND) { |
69 | acpi_processor_ppc_in_use = true; |
70 | |
71 | if (ACPI_FAILURE(status)) { |
72 | acpi_evaluation_failure_warn(handle: pr->handle, name: "_PPC" , status); |
73 | return -ENODEV; |
74 | } |
75 | } |
76 | |
77 | index = ppc; |
78 | |
79 | if (pr->performance_platform_limit == index || |
80 | ppc >= pr->performance->state_count) |
81 | return 0; |
82 | |
83 | pr_debug("CPU %d: _PPC is %d - frequency %s limited\n" , pr->id, |
84 | index, index ? "is" : "is not" ); |
85 | |
86 | pr->performance_platform_limit = index; |
87 | |
88 | if (unlikely(!freq_qos_request_active(&pr->perflib_req))) |
89 | return 0; |
90 | |
91 | /* |
92 | * If _PPC returns 0, it means that all of the available states can be |
93 | * used ("no limit"). |
94 | */ |
95 | if (index == 0) |
96 | qos_value = FREQ_QOS_MAX_DEFAULT_VALUE; |
97 | else |
98 | qos_value = pr->performance->states[index].core_frequency * 1000; |
99 | |
100 | ret = freq_qos_update_request(req: &pr->perflib_req, new_value: qos_value); |
101 | if (ret < 0) { |
102 | pr_warn("Failed to update perflib freq constraint: CPU%d (%d)\n" , |
103 | pr->id, ret); |
104 | } |
105 | |
106 | return 0; |
107 | } |
108 | |
109 | #define ACPI_PROCESSOR_NOTIFY_PERFORMANCE 0x80 |
110 | /* |
111 | * acpi_processor_ppc_ost: Notify firmware the _PPC evaluation status |
112 | * @handle: ACPI processor handle |
113 | * @status: the status code of _PPC evaluation |
114 | * 0: success. OSPM is now using the performance state specified. |
115 | * 1: failure. OSPM has not changed the number of P-states in use |
116 | */ |
117 | static void acpi_processor_ppc_ost(acpi_handle handle, int status) |
118 | { |
119 | if (acpi_has_method(handle, name: "_OST" )) |
120 | acpi_evaluate_ost(handle, ACPI_PROCESSOR_NOTIFY_PERFORMANCE, |
121 | status_code: status, NULL); |
122 | } |
123 | |
124 | void acpi_processor_ppc_has_changed(struct acpi_processor *pr, int event_flag) |
125 | { |
126 | int ret; |
127 | |
128 | if (ignore_ppc || !pr->performance) { |
129 | /* |
130 | * Only when it is notification event, the _OST object |
131 | * will be evaluated. Otherwise it is skipped. |
132 | */ |
133 | if (event_flag) |
134 | acpi_processor_ppc_ost(handle: pr->handle, status: 1); |
135 | return; |
136 | } |
137 | |
138 | ret = acpi_processor_get_platform_limit(pr); |
139 | /* |
140 | * Only when it is notification event, the _OST object |
141 | * will be evaluated. Otherwise it is skipped. |
142 | */ |
143 | if (event_flag) { |
144 | if (ret < 0) |
145 | acpi_processor_ppc_ost(handle: pr->handle, status: 1); |
146 | else |
147 | acpi_processor_ppc_ost(handle: pr->handle, status: 0); |
148 | } |
149 | if (ret >= 0) |
150 | cpufreq_update_limits(cpu: pr->id); |
151 | } |
152 | |
153 | int acpi_processor_get_bios_limit(int cpu, unsigned int *limit) |
154 | { |
155 | struct acpi_processor *pr; |
156 | |
157 | pr = per_cpu(processors, cpu); |
158 | if (!pr || !pr->performance || !pr->performance->state_count) |
159 | return -ENODEV; |
160 | |
161 | *limit = pr->performance->states[pr->performance_platform_limit]. |
162 | core_frequency * 1000; |
163 | return 0; |
164 | } |
165 | EXPORT_SYMBOL(acpi_processor_get_bios_limit); |
166 | |
167 | void acpi_processor_ignore_ppc_init(void) |
168 | { |
169 | if (ignore_ppc < 0) |
170 | ignore_ppc = 0; |
171 | } |
172 | |
173 | void acpi_processor_ppc_init(struct cpufreq_policy *policy) |
174 | { |
175 | unsigned int cpu; |
176 | |
177 | for_each_cpu(cpu, policy->related_cpus) { |
178 | struct acpi_processor *pr = per_cpu(processors, cpu); |
179 | int ret; |
180 | |
181 | if (!pr) |
182 | continue; |
183 | |
184 | /* |
185 | * Reset performance_platform_limit in case there is a stale |
186 | * value in it, so as to make it match the "no limit" QoS value |
187 | * below. |
188 | */ |
189 | pr->performance_platform_limit = 0; |
190 | |
191 | ret = freq_qos_add_request(qos: &policy->constraints, |
192 | req: &pr->perflib_req, type: FREQ_QOS_MAX, |
193 | FREQ_QOS_MAX_DEFAULT_VALUE); |
194 | if (ret < 0) |
195 | pr_err("Failed to add freq constraint for CPU%d (%d)\n" , |
196 | cpu, ret); |
197 | } |
198 | } |
199 | |
200 | void acpi_processor_ppc_exit(struct cpufreq_policy *policy) |
201 | { |
202 | unsigned int cpu; |
203 | |
204 | for_each_cpu(cpu, policy->related_cpus) { |
205 | struct acpi_processor *pr = per_cpu(processors, cpu); |
206 | |
207 | if (pr) |
208 | freq_qos_remove_request(req: &pr->perflib_req); |
209 | } |
210 | } |
211 | |
212 | static int acpi_processor_get_performance_control(struct acpi_processor *pr) |
213 | { |
214 | int result = 0; |
215 | acpi_status status = 0; |
216 | struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL }; |
217 | union acpi_object *pct = NULL; |
218 | union acpi_object obj = { 0 }; |
219 | |
220 | status = acpi_evaluate_object(object: pr->handle, pathname: "_PCT" , NULL, return_object_buffer: &buffer); |
221 | if (ACPI_FAILURE(status)) { |
222 | acpi_evaluation_failure_warn(handle: pr->handle, name: "_PCT" , status); |
223 | return -ENODEV; |
224 | } |
225 | |
226 | pct = (union acpi_object *)buffer.pointer; |
227 | if (!pct || pct->type != ACPI_TYPE_PACKAGE || pct->package.count != 2) { |
228 | pr_err("Invalid _PCT data\n" ); |
229 | result = -EFAULT; |
230 | goto end; |
231 | } |
232 | |
233 | /* |
234 | * control_register |
235 | */ |
236 | |
237 | obj = pct->package.elements[0]; |
238 | |
239 | if (!obj.buffer.pointer || obj.type != ACPI_TYPE_BUFFER || |
240 | obj.buffer.length < sizeof(struct acpi_pct_register)) { |
241 | pr_err("Invalid _PCT data (control_register)\n" ); |
242 | result = -EFAULT; |
243 | goto end; |
244 | } |
245 | memcpy(&pr->performance->control_register, obj.buffer.pointer, |
246 | sizeof(struct acpi_pct_register)); |
247 | |
248 | /* |
249 | * status_register |
250 | */ |
251 | |
252 | obj = pct->package.elements[1]; |
253 | |
254 | if (!obj.buffer.pointer || obj.type != ACPI_TYPE_BUFFER || |
255 | obj.buffer.length < sizeof(struct acpi_pct_register)) { |
256 | pr_err("Invalid _PCT data (status_register)\n" ); |
257 | result = -EFAULT; |
258 | goto end; |
259 | } |
260 | |
261 | memcpy(&pr->performance->status_register, obj.buffer.pointer, |
262 | sizeof(struct acpi_pct_register)); |
263 | |
264 | end: |
265 | kfree(objp: buffer.pointer); |
266 | |
267 | return result; |
268 | } |
269 | |
270 | #ifdef CONFIG_X86 |
271 | /* |
272 | * Some AMDs have 50MHz frequency multiples, but only provide 100MHz rounding |
273 | * in their ACPI data. Calculate the real values and fix up the _PSS data. |
274 | */ |
275 | static void amd_fixup_frequency(struct acpi_processor_px *px, int i) |
276 | { |
277 | u32 hi, lo, fid, did; |
278 | int index = px->control & 0x00000007; |
279 | |
280 | if (boot_cpu_data.x86_vendor != X86_VENDOR_AMD) |
281 | return; |
282 | |
283 | if ((boot_cpu_data.x86 == 0x10 && boot_cpu_data.x86_model < 10) || |
284 | boot_cpu_data.x86 == 0x11) { |
285 | rdmsr(MSR_AMD_PSTATE_DEF_BASE + index, lo, hi); |
286 | /* |
287 | * MSR C001_0064+: |
288 | * Bit 63: PstateEn. Read-write. If set, the P-state is valid. |
289 | */ |
290 | if (!(hi & BIT(31))) |
291 | return; |
292 | |
293 | fid = lo & 0x3f; |
294 | did = (lo >> 6) & 7; |
295 | if (boot_cpu_data.x86 == 0x10) |
296 | px->core_frequency = (100 * (fid + 0x10)) >> did; |
297 | else |
298 | px->core_frequency = (100 * (fid + 8)) >> did; |
299 | } |
300 | } |
301 | #else |
302 | static void amd_fixup_frequency(struct acpi_processor_px *px, int i) {}; |
303 | #endif |
304 | |
305 | static int acpi_processor_get_performance_states(struct acpi_processor *pr) |
306 | { |
307 | int result = 0; |
308 | acpi_status status = AE_OK; |
309 | struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL }; |
310 | struct acpi_buffer format = { sizeof("NNNNNN" ), "NNNNNN" }; |
311 | struct acpi_buffer state = { 0, NULL }; |
312 | union acpi_object *pss = NULL; |
313 | int i; |
314 | int last_invalid = -1; |
315 | |
316 | status = acpi_evaluate_object(object: pr->handle, pathname: "_PSS" , NULL, return_object_buffer: &buffer); |
317 | if (ACPI_FAILURE(status)) { |
318 | acpi_evaluation_failure_warn(handle: pr->handle, name: "_PSS" , status); |
319 | return -ENODEV; |
320 | } |
321 | |
322 | pss = buffer.pointer; |
323 | if (!pss || pss->type != ACPI_TYPE_PACKAGE) { |
324 | pr_err("Invalid _PSS data\n" ); |
325 | result = -EFAULT; |
326 | goto end; |
327 | } |
328 | |
329 | acpi_handle_debug(pr->handle, "Found %d performance states\n" , |
330 | pss->package.count); |
331 | |
332 | pr->performance->state_count = pss->package.count; |
333 | pr->performance->states = |
334 | kmalloc_array(n: pss->package.count, |
335 | size: sizeof(struct acpi_processor_px), |
336 | GFP_KERNEL); |
337 | if (!pr->performance->states) { |
338 | result = -ENOMEM; |
339 | goto end; |
340 | } |
341 | |
342 | for (i = 0; i < pr->performance->state_count; i++) { |
343 | |
344 | struct acpi_processor_px *px = &(pr->performance->states[i]); |
345 | |
346 | state.length = sizeof(struct acpi_processor_px); |
347 | state.pointer = px; |
348 | |
349 | acpi_handle_debug(pr->handle, "Extracting state %d\n" , i); |
350 | |
351 | status = acpi_extract_package(package: &(pss->package.elements[i]), |
352 | format: &format, buffer: &state); |
353 | if (ACPI_FAILURE(status)) { |
354 | acpi_handle_warn(pr->handle, "Invalid _PSS data: %s\n" , |
355 | acpi_format_exception(status)); |
356 | result = -EFAULT; |
357 | kfree(objp: pr->performance->states); |
358 | goto end; |
359 | } |
360 | |
361 | amd_fixup_frequency(px, i); |
362 | |
363 | acpi_handle_debug(pr->handle, |
364 | "State [%d]: core_frequency[%d] power[%d] transition_latency[%d] bus_master_latency[%d] control[0x%x] status[0x%x]\n" , |
365 | i, |
366 | (u32) px->core_frequency, |
367 | (u32) px->power, |
368 | (u32) px->transition_latency, |
369 | (u32) px->bus_master_latency, |
370 | (u32) px->control, (u32) px->status); |
371 | |
372 | /* |
373 | * Check that ACPI's u64 MHz will be valid as u32 KHz in cpufreq |
374 | */ |
375 | if (!px->core_frequency || |
376 | (u32)(px->core_frequency * 1000) != px->core_frequency * 1000) { |
377 | pr_err(FW_BUG |
378 | "Invalid BIOS _PSS frequency found for processor %d: 0x%llx MHz\n" , |
379 | pr->id, px->core_frequency); |
380 | if (last_invalid == -1) |
381 | last_invalid = i; |
382 | } else { |
383 | if (last_invalid != -1) { |
384 | /* |
385 | * Copy this valid entry over last_invalid entry |
386 | */ |
387 | memcpy(&(pr->performance->states[last_invalid]), |
388 | px, sizeof(struct acpi_processor_px)); |
389 | ++last_invalid; |
390 | } |
391 | } |
392 | } |
393 | |
394 | if (last_invalid == 0) { |
395 | pr_err(FW_BUG |
396 | "No valid BIOS _PSS frequency found for processor %d\n" , pr->id); |
397 | result = -EFAULT; |
398 | kfree(objp: pr->performance->states); |
399 | pr->performance->states = NULL; |
400 | } |
401 | |
402 | if (last_invalid > 0) |
403 | pr->performance->state_count = last_invalid; |
404 | |
405 | end: |
406 | kfree(objp: buffer.pointer); |
407 | |
408 | return result; |
409 | } |
410 | |
411 | int acpi_processor_get_performance_info(struct acpi_processor *pr) |
412 | { |
413 | int result = 0; |
414 | |
415 | if (!pr || !pr->performance || !pr->handle) |
416 | return -EINVAL; |
417 | |
418 | if (!acpi_has_method(handle: pr->handle, name: "_PCT" )) { |
419 | acpi_handle_debug(pr->handle, |
420 | "ACPI-based processor performance control unavailable\n" ); |
421 | return -ENODEV; |
422 | } |
423 | |
424 | result = acpi_processor_get_performance_control(pr); |
425 | if (result) |
426 | goto update_bios; |
427 | |
428 | result = acpi_processor_get_performance_states(pr); |
429 | if (result) |
430 | goto update_bios; |
431 | |
432 | /* We need to call _PPC once when cpufreq starts */ |
433 | if (ignore_ppc != 1) |
434 | result = acpi_processor_get_platform_limit(pr); |
435 | |
436 | return result; |
437 | |
438 | /* |
439 | * Having _PPC but missing frequencies (_PSS, _PCT) is a very good hint that |
440 | * the BIOS is older than the CPU and does not know its frequencies |
441 | */ |
442 | update_bios: |
443 | #ifdef CONFIG_X86 |
444 | if (acpi_has_method(handle: pr->handle, name: "_PPC" )) { |
445 | if(boot_cpu_has(X86_FEATURE_EST)) |
446 | pr_warn(FW_BUG "BIOS needs update for CPU " |
447 | "frequency support\n" ); |
448 | } |
449 | #endif |
450 | return result; |
451 | } |
452 | EXPORT_SYMBOL_GPL(acpi_processor_get_performance_info); |
453 | |
454 | int acpi_processor_pstate_control(void) |
455 | { |
456 | acpi_status status; |
457 | |
458 | if (!acpi_gbl_FADT.smi_command || !acpi_gbl_FADT.pstate_control) |
459 | return 0; |
460 | |
461 | pr_debug("Writing pstate_control [0x%x] to smi_command [0x%x]\n" , |
462 | acpi_gbl_FADT.pstate_control, acpi_gbl_FADT.smi_command); |
463 | |
464 | status = acpi_os_write_port(address: acpi_gbl_FADT.smi_command, |
465 | value: (u32)acpi_gbl_FADT.pstate_control, width: 8); |
466 | if (ACPI_SUCCESS(status)) |
467 | return 1; |
468 | |
469 | pr_warn("Failed to write pstate_control [0x%x] to smi_command [0x%x]: %s\n" , |
470 | acpi_gbl_FADT.pstate_control, acpi_gbl_FADT.smi_command, |
471 | acpi_format_exception(status)); |
472 | return -EIO; |
473 | } |
474 | |
475 | int acpi_processor_notify_smm(struct module *calling_module) |
476 | { |
477 | static int is_done; |
478 | int result = 0; |
479 | |
480 | if (!acpi_processor_cpufreq_init) |
481 | return -EBUSY; |
482 | |
483 | if (!try_module_get(module: calling_module)) |
484 | return -EINVAL; |
485 | |
486 | /* |
487 | * is_done is set to negative if an error occurs and to 1 if no error |
488 | * occurrs, but SMM has been notified already. This avoids repeated |
489 | * notification which might lead to unexpected results. |
490 | */ |
491 | if (is_done != 0) { |
492 | if (is_done < 0) |
493 | result = is_done; |
494 | |
495 | goto out_put; |
496 | } |
497 | |
498 | result = acpi_processor_pstate_control(); |
499 | if (result <= 0) { |
500 | if (result) { |
501 | is_done = result; |
502 | } else { |
503 | pr_debug("No SMI port or pstate_control\n" ); |
504 | is_done = 1; |
505 | } |
506 | goto out_put; |
507 | } |
508 | |
509 | is_done = 1; |
510 | /* |
511 | * Success. If there _PPC, unloading the cpufreq driver would be risky, |
512 | * so disallow it in that case. |
513 | */ |
514 | if (acpi_processor_ppc_in_use) |
515 | return 0; |
516 | |
517 | out_put: |
518 | module_put(module: calling_module); |
519 | return result; |
520 | } |
521 | EXPORT_SYMBOL(acpi_processor_notify_smm); |
522 | |
523 | int acpi_processor_get_psd(acpi_handle handle, struct acpi_psd_package *pdomain) |
524 | { |
525 | int result = 0; |
526 | acpi_status status = AE_OK; |
527 | struct acpi_buffer buffer = {ACPI_ALLOCATE_BUFFER, NULL}; |
528 | struct acpi_buffer format = {sizeof("NNNNN" ), "NNNNN" }; |
529 | struct acpi_buffer state = {0, NULL}; |
530 | union acpi_object *psd = NULL; |
531 | |
532 | status = acpi_evaluate_object(object: handle, pathname: "_PSD" , NULL, return_object_buffer: &buffer); |
533 | if (ACPI_FAILURE(status)) { |
534 | return -ENODEV; |
535 | } |
536 | |
537 | psd = buffer.pointer; |
538 | if (!psd || psd->type != ACPI_TYPE_PACKAGE) { |
539 | pr_err("Invalid _PSD data\n" ); |
540 | result = -EFAULT; |
541 | goto end; |
542 | } |
543 | |
544 | if (psd->package.count != 1) { |
545 | pr_err("Invalid _PSD data\n" ); |
546 | result = -EFAULT; |
547 | goto end; |
548 | } |
549 | |
550 | state.length = sizeof(struct acpi_psd_package); |
551 | state.pointer = pdomain; |
552 | |
553 | status = acpi_extract_package(package: &(psd->package.elements[0]), format: &format, buffer: &state); |
554 | if (ACPI_FAILURE(status)) { |
555 | pr_err("Invalid _PSD data\n" ); |
556 | result = -EFAULT; |
557 | goto end; |
558 | } |
559 | |
560 | if (pdomain->num_entries != ACPI_PSD_REV0_ENTRIES) { |
561 | pr_err("Unknown _PSD:num_entries\n" ); |
562 | result = -EFAULT; |
563 | goto end; |
564 | } |
565 | |
566 | if (pdomain->revision != ACPI_PSD_REV0_REVISION) { |
567 | pr_err("Unknown _PSD:revision\n" ); |
568 | result = -EFAULT; |
569 | goto end; |
570 | } |
571 | |
572 | if (pdomain->coord_type != DOMAIN_COORD_TYPE_SW_ALL && |
573 | pdomain->coord_type != DOMAIN_COORD_TYPE_SW_ANY && |
574 | pdomain->coord_type != DOMAIN_COORD_TYPE_HW_ALL) { |
575 | pr_err("Invalid _PSD:coord_type\n" ); |
576 | result = -EFAULT; |
577 | goto end; |
578 | } |
579 | end: |
580 | kfree(objp: buffer.pointer); |
581 | return result; |
582 | } |
583 | EXPORT_SYMBOL(acpi_processor_get_psd); |
584 | |
585 | int acpi_processor_preregister_performance( |
586 | struct acpi_processor_performance __percpu *performance) |
587 | { |
588 | int count_target; |
589 | int retval = 0; |
590 | unsigned int i, j; |
591 | cpumask_var_t covered_cpus; |
592 | struct acpi_processor *pr; |
593 | struct acpi_psd_package *pdomain; |
594 | struct acpi_processor *match_pr; |
595 | struct acpi_psd_package *match_pdomain; |
596 | |
597 | if (!zalloc_cpumask_var(mask: &covered_cpus, GFP_KERNEL)) |
598 | return -ENOMEM; |
599 | |
600 | mutex_lock(&performance_mutex); |
601 | |
602 | /* |
603 | * Check if another driver has already registered, and abort before |
604 | * changing pr->performance if it has. Check input data as well. |
605 | */ |
606 | for_each_possible_cpu(i) { |
607 | pr = per_cpu(processors, i); |
608 | if (!pr) { |
609 | /* Look only at processors in ACPI namespace */ |
610 | continue; |
611 | } |
612 | |
613 | if (pr->performance) { |
614 | retval = -EBUSY; |
615 | goto err_out; |
616 | } |
617 | |
618 | if (!performance || !per_cpu_ptr(performance, i)) { |
619 | retval = -EINVAL; |
620 | goto err_out; |
621 | } |
622 | } |
623 | |
624 | /* Call _PSD for all CPUs */ |
625 | for_each_possible_cpu(i) { |
626 | pr = per_cpu(processors, i); |
627 | if (!pr) |
628 | continue; |
629 | |
630 | pr->performance = per_cpu_ptr(performance, i); |
631 | pdomain = &(pr->performance->domain_info); |
632 | if (acpi_processor_get_psd(pr->handle, pdomain)) { |
633 | retval = -EINVAL; |
634 | continue; |
635 | } |
636 | } |
637 | if (retval) |
638 | goto err_ret; |
639 | |
640 | /* |
641 | * Now that we have _PSD data from all CPUs, lets setup P-state |
642 | * domain info. |
643 | */ |
644 | for_each_possible_cpu(i) { |
645 | pr = per_cpu(processors, i); |
646 | if (!pr) |
647 | continue; |
648 | |
649 | if (cpumask_test_cpu(cpu: i, cpumask: covered_cpus)) |
650 | continue; |
651 | |
652 | pdomain = &(pr->performance->domain_info); |
653 | cpumask_set_cpu(cpu: i, dstp: pr->performance->shared_cpu_map); |
654 | cpumask_set_cpu(cpu: i, dstp: covered_cpus); |
655 | if (pdomain->num_processors <= 1) |
656 | continue; |
657 | |
658 | /* Validate the Domain info */ |
659 | count_target = pdomain->num_processors; |
660 | if (pdomain->coord_type == DOMAIN_COORD_TYPE_SW_ALL) |
661 | pr->performance->shared_type = CPUFREQ_SHARED_TYPE_ALL; |
662 | else if (pdomain->coord_type == DOMAIN_COORD_TYPE_HW_ALL) |
663 | pr->performance->shared_type = CPUFREQ_SHARED_TYPE_HW; |
664 | else if (pdomain->coord_type == DOMAIN_COORD_TYPE_SW_ANY) |
665 | pr->performance->shared_type = CPUFREQ_SHARED_TYPE_ANY; |
666 | |
667 | for_each_possible_cpu(j) { |
668 | if (i == j) |
669 | continue; |
670 | |
671 | match_pr = per_cpu(processors, j); |
672 | if (!match_pr) |
673 | continue; |
674 | |
675 | match_pdomain = &(match_pr->performance->domain_info); |
676 | if (match_pdomain->domain != pdomain->domain) |
677 | continue; |
678 | |
679 | /* Here i and j are in the same domain */ |
680 | |
681 | if (match_pdomain->num_processors != count_target) { |
682 | retval = -EINVAL; |
683 | goto err_ret; |
684 | } |
685 | |
686 | if (pdomain->coord_type != match_pdomain->coord_type) { |
687 | retval = -EINVAL; |
688 | goto err_ret; |
689 | } |
690 | |
691 | cpumask_set_cpu(cpu: j, dstp: covered_cpus); |
692 | cpumask_set_cpu(cpu: j, dstp: pr->performance->shared_cpu_map); |
693 | } |
694 | |
695 | for_each_possible_cpu(j) { |
696 | if (i == j) |
697 | continue; |
698 | |
699 | match_pr = per_cpu(processors, j); |
700 | if (!match_pr) |
701 | continue; |
702 | |
703 | match_pdomain = &(match_pr->performance->domain_info); |
704 | if (match_pdomain->domain != pdomain->domain) |
705 | continue; |
706 | |
707 | match_pr->performance->shared_type = |
708 | pr->performance->shared_type; |
709 | cpumask_copy(dstp: match_pr->performance->shared_cpu_map, |
710 | srcp: pr->performance->shared_cpu_map); |
711 | } |
712 | } |
713 | |
714 | err_ret: |
715 | for_each_possible_cpu(i) { |
716 | pr = per_cpu(processors, i); |
717 | if (!pr || !pr->performance) |
718 | continue; |
719 | |
720 | /* Assume no coordination on any error parsing domain info */ |
721 | if (retval) { |
722 | cpumask_clear(dstp: pr->performance->shared_cpu_map); |
723 | cpumask_set_cpu(cpu: i, dstp: pr->performance->shared_cpu_map); |
724 | pr->performance->shared_type = CPUFREQ_SHARED_TYPE_NONE; |
725 | } |
726 | pr->performance = NULL; /* Will be set for real in register */ |
727 | } |
728 | |
729 | err_out: |
730 | mutex_unlock(lock: &performance_mutex); |
731 | free_cpumask_var(mask: covered_cpus); |
732 | return retval; |
733 | } |
734 | EXPORT_SYMBOL(acpi_processor_preregister_performance); |
735 | |
736 | int acpi_processor_register_performance(struct acpi_processor_performance |
737 | *performance, unsigned int cpu) |
738 | { |
739 | struct acpi_processor *pr; |
740 | |
741 | if (!acpi_processor_cpufreq_init) |
742 | return -EINVAL; |
743 | |
744 | mutex_lock(&performance_mutex); |
745 | |
746 | pr = per_cpu(processors, cpu); |
747 | if (!pr) { |
748 | mutex_unlock(lock: &performance_mutex); |
749 | return -ENODEV; |
750 | } |
751 | |
752 | if (pr->performance) { |
753 | mutex_unlock(lock: &performance_mutex); |
754 | return -EBUSY; |
755 | } |
756 | |
757 | WARN_ON(!performance); |
758 | |
759 | pr->performance = performance; |
760 | |
761 | if (acpi_processor_get_performance_info(pr)) { |
762 | pr->performance = NULL; |
763 | mutex_unlock(lock: &performance_mutex); |
764 | return -EIO; |
765 | } |
766 | |
767 | mutex_unlock(lock: &performance_mutex); |
768 | return 0; |
769 | } |
770 | EXPORT_SYMBOL(acpi_processor_register_performance); |
771 | |
772 | void acpi_processor_unregister_performance(unsigned int cpu) |
773 | { |
774 | struct acpi_processor *pr; |
775 | |
776 | mutex_lock(&performance_mutex); |
777 | |
778 | pr = per_cpu(processors, cpu); |
779 | if (!pr) |
780 | goto unlock; |
781 | |
782 | if (pr->performance) |
783 | kfree(objp: pr->performance->states); |
784 | |
785 | pr->performance = NULL; |
786 | |
787 | unlock: |
788 | mutex_unlock(lock: &performance_mutex); |
789 | } |
790 | EXPORT_SYMBOL(acpi_processor_unregister_performance); |
791 | |