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