Linux 2.6.27
[GitHub/mt8127/android_kernel_alcatel_ttab.git] / drivers / acpi / processor_perflib.c
CommitLineData
1da177e4
LT
1/*
2 * processor_perflib.c - ACPI Processor P-States Library ($Revision: 71 $)
3 *
4 * Copyright (C) 2001, 2002 Andy Grover <andrew.grover@intel.com>
5 * Copyright (C) 2001, 2002 Paul Diefenbaugh <paul.s.diefenbaugh@intel.com>
6 * Copyright (C) 2004 Dominik Brodowski <linux@brodo.de>
7 * Copyright (C) 2004 Anil S Keshavamurthy <anil.s.keshavamurthy@intel.com>
8 * - Added processor hotplug support
9 *
10 *
11 * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
12 *
13 * This program is free software; you can redistribute it and/or modify
14 * it under the terms of the GNU General Public License as published by
15 * the Free Software Foundation; either version 2 of the License, or (at
16 * your option) any later version.
17 *
18 * This program is distributed in the hope that it will be useful, but
19 * WITHOUT ANY WARRANTY; without even the implied warranty of
20 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
21 * General Public License for more details.
22 *
23 * You should have received a copy of the GNU General Public License along
24 * with this program; if not, write to the Free Software Foundation, Inc.,
25 * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
26 *
27 */
28
1da177e4
LT
29#include <linux/kernel.h>
30#include <linux/module.h>
31#include <linux/init.h>
32#include <linux/cpufreq.h>
33
34#ifdef CONFIG_X86_ACPI_CPUFREQ_PROC_INTF
35#include <linux/proc_fs.h>
36#include <linux/seq_file.h>
65c19bbd 37#include <linux/mutex.h>
1da177e4
LT
38
39#include <asm/uaccess.h>
40#endif
41
42#include <acpi/acpi_bus.h>
43#include <acpi/processor.h>
44
1da177e4
LT
45#define ACPI_PROCESSOR_COMPONENT 0x01000000
46#define ACPI_PROCESSOR_CLASS "processor"
1da177e4
LT
47#define ACPI_PROCESSOR_FILE_PERFORMANCE "performance"
48#define _COMPONENT ACPI_PROCESSOR_COMPONENT
f52fd66d 49ACPI_MODULE_NAME("processor_perflib");
1da177e4 50
65c19bbd 51static DEFINE_MUTEX(performance_mutex);
1da177e4 52
919158d1
TR
53/* Use cpufreq debug layer for _PPC changes. */
54#define cpufreq_printk(msg...) cpufreq_debug_printk(CPUFREQ_DEBUG_CORE, \
55 "cpufreq-core", msg)
56
1da177e4
LT
57/*
58 * _PPC support is implemented as a CPUfreq policy notifier:
59 * This means each time a CPUfreq driver registered also with
60 * the ACPI core is asked to change the speed policy, the maximum
61 * value is adjusted so that it is within the platform limit.
62 *
63 * Also, when a new platform limit value is detected, the CPUfreq
64 * policy is adjusted accordingly.
65 */
66
a1531acd
TR
67/* ignore_ppc:
68 * -1 -> cpufreq low level drivers not initialized -> _PSS, etc. not called yet
69 * ignore _PPC
70 * 0 -> cpufreq low level drivers initialized -> consider _PPC values
71 * 1 -> ignore _PPC totally -> forced by user through boot param
72 */
9f497bcc 73static int ignore_ppc = -1;
613e5f33 74module_param(ignore_ppc, int, 0644);
623b78c3
TR
75MODULE_PARM_DESC(ignore_ppc, "If the frequency of your machine gets wrongly" \
76 "limited by BIOS, this should help");
77
1da177e4
LT
78#define PPC_REGISTERED 1
79#define PPC_IN_USE 2
80
a1531acd 81static int acpi_processor_ppc_status;
1da177e4
LT
82
83static int acpi_processor_ppc_notifier(struct notifier_block *nb,
4be44fcd 84 unsigned long event, void *data)
1da177e4
LT
85{
86 struct cpufreq_policy *policy = data;
87 struct acpi_processor *pr;
88 unsigned int ppc = 0;
89
a1531acd
TR
90 if (event == CPUFREQ_START && ignore_ppc <= 0) {
91 ignore_ppc = 0;
92 return 0;
93 }
94
623b78c3
TR
95 if (ignore_ppc)
96 return 0;
97
1da177e4 98 if (event != CPUFREQ_INCOMPATIBLE)
9b67c5d4
TR
99 return 0;
100
101 mutex_lock(&performance_mutex);
1da177e4 102
706546d0 103 pr = per_cpu(processors, policy->cpu);
1da177e4
LT
104 if (!pr || !pr->performance)
105 goto out;
106
4be44fcd 107 ppc = (unsigned int)pr->performance_platform_limit;
1da177e4 108
0916bd3e 109 if (ppc >= pr->performance->state_count)
1da177e4
LT
110 goto out;
111
112 cpufreq_verify_within_limits(policy, 0,
4be44fcd
LB
113 pr->performance->states[ppc].
114 core_frequency * 1000);
1da177e4 115
4be44fcd 116 out:
65c19bbd 117 mutex_unlock(&performance_mutex);
1da177e4
LT
118
119 return 0;
120}
121
1da177e4
LT
122static struct notifier_block acpi_ppc_notifier_block = {
123 .notifier_call = acpi_processor_ppc_notifier,
124};
125
4be44fcd 126static int acpi_processor_get_platform_limit(struct acpi_processor *pr)
1da177e4 127{
4be44fcd
LB
128 acpi_status status = 0;
129 unsigned long ppc = 0;
1da177e4 130
1da177e4
LT
131
132 if (!pr)
d550d98d 133 return -EINVAL;
1da177e4
LT
134
135 /*
136 * _PPC indicates the maximum state currently supported by the platform
137 * (e.g. 0 = states 0..n; 1 = states 1..n; etc.
138 */
139 status = acpi_evaluate_integer(pr->handle, "_PPC", NULL, &ppc);
140
141 if (status != AE_NOT_FOUND)
142 acpi_processor_ppc_status |= PPC_IN_USE;
143
4be44fcd 144 if (ACPI_FAILURE(status) && status != AE_NOT_FOUND) {
a6fc6720 145 ACPI_EXCEPTION((AE_INFO, status, "Evaluating _PPC"));
d550d98d 146 return -ENODEV;
1da177e4
LT
147 }
148
919158d1
TR
149 cpufreq_printk("CPU %d: _PPC is %d - frequency %s limited\n", pr->id,
150 (int)ppc, ppc ? "" : "not");
151
4be44fcd 152 pr->performance_platform_limit = (int)ppc;
1da177e4 153
d550d98d 154 return 0;
1da177e4
LT
155}
156
4be44fcd 157int acpi_processor_ppc_has_changed(struct acpi_processor *pr)
1da177e4 158{
623b78c3
TR
159 int ret;
160
161 if (ignore_ppc)
162 return 0;
163
164 ret = acpi_processor_get_platform_limit(pr);
165
1da177e4
LT
166 if (ret < 0)
167 return (ret);
168 else
169 return cpufreq_update_policy(pr->id);
170}
171
4be44fcd
LB
172void acpi_processor_ppc_init(void)
173{
174 if (!cpufreq_register_notifier
175 (&acpi_ppc_notifier_block, CPUFREQ_POLICY_NOTIFIER))
1da177e4
LT
176 acpi_processor_ppc_status |= PPC_REGISTERED;
177 else
4be44fcd
LB
178 printk(KERN_DEBUG
179 "Warning: Processor Platform Limit not supported.\n");
1da177e4
LT
180}
181
4be44fcd
LB
182void acpi_processor_ppc_exit(void)
183{
1da177e4 184 if (acpi_processor_ppc_status & PPC_REGISTERED)
4be44fcd
LB
185 cpufreq_unregister_notifier(&acpi_ppc_notifier_block,
186 CPUFREQ_POLICY_NOTIFIER);
1da177e4
LT
187
188 acpi_processor_ppc_status &= ~PPC_REGISTERED;
189}
190
4be44fcd 191static int acpi_processor_get_performance_control(struct acpi_processor *pr)
1da177e4 192{
4be44fcd
LB
193 int result = 0;
194 acpi_status status = 0;
195 struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL };
196 union acpi_object *pct = NULL;
197 union acpi_object obj = { 0 };
1da177e4 198
1da177e4
LT
199
200 status = acpi_evaluate_object(pr->handle, "_PCT", NULL, &buffer);
4be44fcd 201 if (ACPI_FAILURE(status)) {
a6fc6720 202 ACPI_EXCEPTION((AE_INFO, status, "Evaluating _PCT"));
d550d98d 203 return -ENODEV;
1da177e4
LT
204 }
205
4be44fcd 206 pct = (union acpi_object *)buffer.pointer;
1da177e4 207 if (!pct || (pct->type != ACPI_TYPE_PACKAGE)
4be44fcd 208 || (pct->package.count != 2)) {
6468463a 209 printk(KERN_ERR PREFIX "Invalid _PCT data\n");
1da177e4
LT
210 result = -EFAULT;
211 goto end;
212 }
213
214 /*
215 * control_register
216 */
217
218 obj = pct->package.elements[0];
219
220 if ((obj.type != ACPI_TYPE_BUFFER)
4be44fcd
LB
221 || (obj.buffer.length < sizeof(struct acpi_pct_register))
222 || (obj.buffer.pointer == NULL)) {
6468463a 223 printk(KERN_ERR PREFIX "Invalid _PCT data (control_register)\n");
1da177e4
LT
224 result = -EFAULT;
225 goto end;
226 }
4be44fcd
LB
227 memcpy(&pr->performance->control_register, obj.buffer.pointer,
228 sizeof(struct acpi_pct_register));
1da177e4
LT
229
230 /*
231 * status_register
232 */
233
234 obj = pct->package.elements[1];
235
236 if ((obj.type != ACPI_TYPE_BUFFER)
4be44fcd
LB
237 || (obj.buffer.length < sizeof(struct acpi_pct_register))
238 || (obj.buffer.pointer == NULL)) {
6468463a 239 printk(KERN_ERR PREFIX "Invalid _PCT data (status_register)\n");
1da177e4
LT
240 result = -EFAULT;
241 goto end;
242 }
243
4be44fcd
LB
244 memcpy(&pr->performance->status_register, obj.buffer.pointer,
245 sizeof(struct acpi_pct_register));
1da177e4 246
4be44fcd 247 end:
02438d87 248 kfree(buffer.pointer);
1da177e4 249
d550d98d 250 return result;
1da177e4
LT
251}
252
4be44fcd 253static int acpi_processor_get_performance_states(struct acpi_processor *pr)
1da177e4 254{
4be44fcd
LB
255 int result = 0;
256 acpi_status status = AE_OK;
257 struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL };
258 struct acpi_buffer format = { sizeof("NNNNNN"), "NNNNNN" };
259 struct acpi_buffer state = { 0, NULL };
260 union acpi_object *pss = NULL;
261 int i;
1da177e4 262
1da177e4
LT
263
264 status = acpi_evaluate_object(pr->handle, "_PSS", NULL, &buffer);
4be44fcd 265 if (ACPI_FAILURE(status)) {
a6fc6720 266 ACPI_EXCEPTION((AE_INFO, status, "Evaluating _PSS"));
d550d98d 267 return -ENODEV;
1da177e4
LT
268 }
269
50dd0969 270 pss = buffer.pointer;
1da177e4 271 if (!pss || (pss->type != ACPI_TYPE_PACKAGE)) {
6468463a 272 printk(KERN_ERR PREFIX "Invalid _PSS data\n");
1da177e4
LT
273 result = -EFAULT;
274 goto end;
275 }
276
277 ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Found %d performance states\n",
4be44fcd 278 pss->package.count));
1da177e4
LT
279
280 pr->performance->state_count = pss->package.count;
4be44fcd
LB
281 pr->performance->states =
282 kmalloc(sizeof(struct acpi_processor_px) * pss->package.count,
283 GFP_KERNEL);
1da177e4
LT
284 if (!pr->performance->states) {
285 result = -ENOMEM;
286 goto end;
287 }
288
289 for (i = 0; i < pr->performance->state_count; i++) {
290
291 struct acpi_processor_px *px = &(pr->performance->states[i]);
292
293 state.length = sizeof(struct acpi_processor_px);
294 state.pointer = px;
295
296 ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Extracting state %d\n", i));
297
298 status = acpi_extract_package(&(pss->package.elements[i]),
4be44fcd 299 &format, &state);
1da177e4 300 if (ACPI_FAILURE(status)) {
a6fc6720 301 ACPI_EXCEPTION((AE_INFO, status, "Invalid _PSS data"));
1da177e4
LT
302 result = -EFAULT;
303 kfree(pr->performance->states);
304 goto end;
305 }
306
307 ACPI_DEBUG_PRINT((ACPI_DB_INFO,
4be44fcd
LB
308 "State [%d]: core_frequency[%d] power[%d] transition_latency[%d] bus_master_latency[%d] control[0x%x] status[0x%x]\n",
309 i,
310 (u32) px->core_frequency,
311 (u32) px->power,
312 (u32) px->transition_latency,
313 (u32) px->bus_master_latency,
314 (u32) px->control, (u32) px->status));
1da177e4
LT
315
316 if (!px->core_frequency) {
6468463a
LB
317 printk(KERN_ERR PREFIX
318 "Invalid _PSS data: freq is zero\n");
1da177e4
LT
319 result = -EFAULT;
320 kfree(pr->performance->states);
321 goto end;
322 }
323 }
324
4be44fcd 325 end:
02438d87 326 kfree(buffer.pointer);
1da177e4 327
d550d98d 328 return result;
1da177e4
LT
329}
330
4be44fcd 331static int acpi_processor_get_performance_info(struct acpi_processor *pr)
1da177e4 332{
4be44fcd
LB
333 int result = 0;
334 acpi_status status = AE_OK;
335 acpi_handle handle = NULL;
1da177e4 336
1da177e4
LT
337
338 if (!pr || !pr->performance || !pr->handle)
d550d98d 339 return -EINVAL;
1da177e4 340
1da177e4
LT
341 status = acpi_get_handle(pr->handle, "_PCT", &handle);
342 if (ACPI_FAILURE(status)) {
343 ACPI_DEBUG_PRINT((ACPI_DB_INFO,
4be44fcd 344 "ACPI-based processor performance control unavailable\n"));
d550d98d 345 return -ENODEV;
1da177e4
LT
346 }
347
348 result = acpi_processor_get_performance_control(pr);
349 if (result)
d550d98d 350 return result;
1da177e4
LT
351
352 result = acpi_processor_get_performance_states(pr);
353 if (result)
d550d98d 354 return result;
1da177e4 355
d550d98d 356 return 0;
1da177e4
LT
357}
358
4be44fcd
LB
359int acpi_processor_notify_smm(struct module *calling_module)
360{
361 acpi_status status;
362 static int is_done = 0;
1da177e4 363
1da177e4
LT
364
365 if (!(acpi_processor_ppc_status & PPC_REGISTERED))
d550d98d 366 return -EBUSY;
1da177e4
LT
367
368 if (!try_module_get(calling_module))
d550d98d 369 return -EINVAL;
1da177e4
LT
370
371 /* is_done is set to negative if an error occured,
372 * and to postitive if _no_ error occured, but SMM
373 * was already notified. This avoids double notification
374 * which might lead to unexpected results...
375 */
376 if (is_done > 0) {
377 module_put(calling_module);
d550d98d 378 return 0;
4be44fcd 379 } else if (is_done < 0) {
1da177e4 380 module_put(calling_module);
d550d98d 381 return is_done;
1da177e4
LT
382 }
383
384 is_done = -EIO;
385
ad71860a 386 /* Can't write pstate_control to smi_command if either value is zero */
cee324b1 387 if ((!acpi_gbl_FADT.smi_command) || (!acpi_gbl_FADT.pstate_control)) {
ad71860a 388 ACPI_DEBUG_PRINT((ACPI_DB_INFO, "No SMI port or pstate_control\n"));
1da177e4 389 module_put(calling_module);
d550d98d 390 return 0;
1da177e4
LT
391 }
392
4be44fcd 393 ACPI_DEBUG_PRINT((ACPI_DB_INFO,
ad71860a 394 "Writing pstate_control [0x%x] to smi_command [0x%x]\n",
cee324b1 395 acpi_gbl_FADT.pstate_control, acpi_gbl_FADT.smi_command));
1da177e4 396
cee324b1
AS
397 status = acpi_os_write_port(acpi_gbl_FADT.smi_command,
398 (u32) acpi_gbl_FADT.pstate_control, 8);
4be44fcd 399 if (ACPI_FAILURE(status)) {
a6fc6720 400 ACPI_EXCEPTION((AE_INFO, status,
ad71860a 401 "Failed to write pstate_control [0x%x] to "
cee324b1
AS
402 "smi_command [0x%x]", acpi_gbl_FADT.pstate_control,
403 acpi_gbl_FADT.smi_command));
1da177e4 404 module_put(calling_module);
d550d98d 405 return status;
1da177e4
LT
406 }
407
408 /* Success. If there's no _PPC, we need to fear nothing, so
409 * we can allow the cpufreq driver to be rmmod'ed. */
410 is_done = 1;
411
412 if (!(acpi_processor_ppc_status & PPC_IN_USE))
413 module_put(calling_module);
414
d550d98d 415 return 0;
1da177e4 416}
1da177e4 417
4be44fcd 418EXPORT_SYMBOL(acpi_processor_notify_smm);
1da177e4
LT
419
420#ifdef CONFIG_X86_ACPI_CPUFREQ_PROC_INTF
421/* /proc/acpi/processor/../performance interface (DEPRECATED) */
422
423static int acpi_processor_perf_open_fs(struct inode *inode, struct file *file);
424static struct file_operations acpi_processor_perf_fops = {
cf7acfab 425 .owner = THIS_MODULE,
4be44fcd
LB
426 .open = acpi_processor_perf_open_fs,
427 .read = seq_read,
428 .llseek = seq_lseek,
429 .release = single_release,
1da177e4
LT
430};
431
432static int acpi_processor_perf_seq_show(struct seq_file *seq, void *offset)
433{
50dd0969 434 struct acpi_processor *pr = seq->private;
4be44fcd 435 int i;
1da177e4 436
1da177e4
LT
437
438 if (!pr)
439 goto end;
440
441 if (!pr->performance) {
442 seq_puts(seq, "<not supported>\n");
443 goto end;
444 }
445
446 seq_printf(seq, "state count: %d\n"
4be44fcd
LB
447 "active state: P%d\n",
448 pr->performance->state_count, pr->performance->state);
1da177e4
LT
449
450 seq_puts(seq, "states:\n");
451 for (i = 0; i < pr->performance->state_count; i++)
4be44fcd
LB
452 seq_printf(seq,
453 " %cP%d: %d MHz, %d mW, %d uS\n",
454 (i == pr->performance->state ? '*' : ' '), i,
455 (u32) pr->performance->states[i].core_frequency,
456 (u32) pr->performance->states[i].power,
457 (u32) pr->performance->states[i].transition_latency);
458
459 end:
d550d98d 460 return 0;
1da177e4
LT
461}
462
463static int acpi_processor_perf_open_fs(struct inode *inode, struct file *file)
464{
465 return single_open(file, acpi_processor_perf_seq_show,
4be44fcd 466 PDE(inode)->data);
1da177e4
LT
467}
468
4be44fcd 469static void acpi_cpufreq_add_file(struct acpi_processor *pr)
1da177e4 470{
4be44fcd 471 struct acpi_device *device = NULL;
1da177e4 472
1da177e4
LT
473
474 if (acpi_bus_get_device(pr->handle, &device))
d550d98d 475 return;
1da177e4
LT
476
477 /* add file 'performance' [R/W] */
cf7acfab
DL
478 proc_create_data(ACPI_PROCESSOR_FILE_PERFORMANCE, S_IFREG | S_IRUGO,
479 acpi_device_dir(device),
480 &acpi_processor_perf_fops, acpi_driver_data(device));
d550d98d 481 return;
1da177e4
LT
482}
483
4be44fcd 484static void acpi_cpufreq_remove_file(struct acpi_processor *pr)
1da177e4 485{
4be44fcd 486 struct acpi_device *device = NULL;
1da177e4 487
1da177e4
LT
488
489 if (acpi_bus_get_device(pr->handle, &device))
d550d98d 490 return;
1da177e4
LT
491
492 /* remove file 'performance' */
493 remove_proc_entry(ACPI_PROCESSOR_FILE_PERFORMANCE,
4be44fcd 494 acpi_device_dir(device));
1da177e4 495
d550d98d 496 return;
1da177e4
LT
497}
498
499#else
4be44fcd
LB
500static void acpi_cpufreq_add_file(struct acpi_processor *pr)
501{
502 return;
503}
504static void acpi_cpufreq_remove_file(struct acpi_processor *pr)
505{
506 return;
507}
508#endif /* CONFIG_X86_ACPI_CPUFREQ_PROC_INTF */
1da177e4 509
3b2d9942
VP
510static int acpi_processor_get_psd(struct acpi_processor *pr)
511{
512 int result = 0;
513 acpi_status status = AE_OK;
514 struct acpi_buffer buffer = {ACPI_ALLOCATE_BUFFER, NULL};
515 struct acpi_buffer format = {sizeof("NNNNN"), "NNNNN"};
516 struct acpi_buffer state = {0, NULL};
517 union acpi_object *psd = NULL;
518 struct acpi_psd_package *pdomain;
519
3b2d9942
VP
520 status = acpi_evaluate_object(pr->handle, "_PSD", NULL, &buffer);
521 if (ACPI_FAILURE(status)) {
9011bff4 522 return -ENODEV;
3b2d9942
VP
523 }
524
50dd0969 525 psd = buffer.pointer;
3b2d9942
VP
526 if (!psd || (psd->type != ACPI_TYPE_PACKAGE)) {
527 ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "Invalid _PSD data\n"));
528 result = -EFAULT;
529 goto end;
530 }
531
532 if (psd->package.count != 1) {
533 ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "Invalid _PSD data\n"));
534 result = -EFAULT;
535 goto end;
536 }
537
538 pdomain = &(pr->performance->domain_info);
539
540 state.length = sizeof(struct acpi_psd_package);
541 state.pointer = pdomain;
542
543 status = acpi_extract_package(&(psd->package.elements[0]),
544 &format, &state);
545 if (ACPI_FAILURE(status)) {
546 ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "Invalid _PSD data\n"));
547 result = -EFAULT;
548 goto end;
549 }
550
551 if (pdomain->num_entries != ACPI_PSD_REV0_ENTRIES) {
552 ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "Unknown _PSD:num_entries\n"));
553 result = -EFAULT;
554 goto end;
555 }
556
557 if (pdomain->revision != ACPI_PSD_REV0_REVISION) {
558 ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "Unknown _PSD:revision\n"));
559 result = -EFAULT;
560 goto end;
561 }
562
563end:
02438d87 564 kfree(buffer.pointer);
9011bff4 565 return result;
3b2d9942
VP
566}
567
568int acpi_processor_preregister_performance(
50109292 569 struct acpi_processor_performance *performance)
3b2d9942
VP
570{
571 int count, count_target;
572 int retval = 0;
573 unsigned int i, j;
574 cpumask_t covered_cpus;
575 struct acpi_processor *pr;
576 struct acpi_psd_package *pdomain;
577 struct acpi_processor *match_pr;
578 struct acpi_psd_package *match_pdomain;
579
785fcccd 580 mutex_lock(&performance_mutex);
3b2d9942
VP
581
582 retval = 0;
583
584 /* Call _PSD for all CPUs */
193de0c7 585 for_each_possible_cpu(i) {
706546d0 586 pr = per_cpu(processors, i);
3b2d9942
VP
587 if (!pr) {
588 /* Look only at processors in ACPI namespace */
589 continue;
590 }
591
592 if (pr->performance) {
593 retval = -EBUSY;
594 continue;
595 }
596
50109292 597 if (!performance || !percpu_ptr(performance, i)) {
3b2d9942
VP
598 retval = -EINVAL;
599 continue;
600 }
601
50109292 602 pr->performance = percpu_ptr(performance, i);
3b2d9942
VP
603 cpu_set(i, pr->performance->shared_cpu_map);
604 if (acpi_processor_get_psd(pr)) {
605 retval = -EINVAL;
606 continue;
607 }
608 }
609 if (retval)
610 goto err_ret;
611
612 /*
613 * Now that we have _PSD data from all CPUs, lets setup P-state
614 * domain info.
615 */
193de0c7 616 for_each_possible_cpu(i) {
706546d0 617 pr = per_cpu(processors, i);
3b2d9942
VP
618 if (!pr)
619 continue;
620
621 /* Basic validity check for domain info */
622 pdomain = &(pr->performance->domain_info);
623 if ((pdomain->revision != ACPI_PSD_REV0_REVISION) ||
624 (pdomain->num_entries != ACPI_PSD_REV0_ENTRIES)) {
625 retval = -EINVAL;
626 goto err_ret;
627 }
628 if (pdomain->coord_type != DOMAIN_COORD_TYPE_SW_ALL &&
629 pdomain->coord_type != DOMAIN_COORD_TYPE_SW_ANY &&
630 pdomain->coord_type != DOMAIN_COORD_TYPE_HW_ALL) {
631 retval = -EINVAL;
632 goto err_ret;
633 }
634 }
635
636 cpus_clear(covered_cpus);
193de0c7 637 for_each_possible_cpu(i) {
706546d0 638 pr = per_cpu(processors, i);
3b2d9942
VP
639 if (!pr)
640 continue;
641
642 if (cpu_isset(i, covered_cpus))
643 continue;
644
645 pdomain = &(pr->performance->domain_info);
646 cpu_set(i, pr->performance->shared_cpu_map);
647 cpu_set(i, covered_cpus);
648 if (pdomain->num_processors <= 1)
649 continue;
650
651 /* Validate the Domain info */
652 count_target = pdomain->num_processors;
653 count = 1;
46f18e3a 654 if (pdomain->coord_type == DOMAIN_COORD_TYPE_SW_ALL)
3b2d9942 655 pr->performance->shared_type = CPUFREQ_SHARED_TYPE_ALL;
46f18e3a
VP
656 else if (pdomain->coord_type == DOMAIN_COORD_TYPE_HW_ALL)
657 pr->performance->shared_type = CPUFREQ_SHARED_TYPE_HW;
658 else if (pdomain->coord_type == DOMAIN_COORD_TYPE_SW_ANY)
3b2d9942 659 pr->performance->shared_type = CPUFREQ_SHARED_TYPE_ANY;
3b2d9942 660
193de0c7 661 for_each_possible_cpu(j) {
3b2d9942
VP
662 if (i == j)
663 continue;
664
706546d0 665 match_pr = per_cpu(processors, j);
3b2d9942
VP
666 if (!match_pr)
667 continue;
668
669 match_pdomain = &(match_pr->performance->domain_info);
670 if (match_pdomain->domain != pdomain->domain)
671 continue;
672
673 /* Here i and j are in the same domain */
674
675 if (match_pdomain->num_processors != count_target) {
676 retval = -EINVAL;
677 goto err_ret;
678 }
679
680 if (pdomain->coord_type != match_pdomain->coord_type) {
681 retval = -EINVAL;
682 goto err_ret;
683 }
684
685 cpu_set(j, covered_cpus);
686 cpu_set(j, pr->performance->shared_cpu_map);
687 count++;
688 }
689
193de0c7 690 for_each_possible_cpu(j) {
3b2d9942
VP
691 if (i == j)
692 continue;
693
706546d0 694 match_pr = per_cpu(processors, j);
3b2d9942
VP
695 if (!match_pr)
696 continue;
697
698 match_pdomain = &(match_pr->performance->domain_info);
699 if (match_pdomain->domain != pdomain->domain)
700 continue;
701
702 match_pr->performance->shared_type =
703 pr->performance->shared_type;
704 match_pr->performance->shared_cpu_map =
705 pr->performance->shared_cpu_map;
706 }
707 }
708
709err_ret:
193de0c7 710 for_each_possible_cpu(i) {
706546d0 711 pr = per_cpu(processors, i);
3b2d9942
VP
712 if (!pr || !pr->performance)
713 continue;
714
715 /* Assume no coordination on any error parsing domain info */
716 if (retval) {
717 cpus_clear(pr->performance->shared_cpu_map);
718 cpu_set(i, pr->performance->shared_cpu_map);
719 pr->performance->shared_type = CPUFREQ_SHARED_TYPE_ALL;
720 }
721 pr->performance = NULL; /* Will be set for real in register */
722 }
723
785fcccd 724 mutex_unlock(&performance_mutex);
9011bff4 725 return retval;
3b2d9942
VP
726}
727EXPORT_SYMBOL(acpi_processor_preregister_performance);
728
729
1da177e4 730int
4be44fcd
LB
731acpi_processor_register_performance(struct acpi_processor_performance
732 *performance, unsigned int cpu)
1da177e4
LT
733{
734 struct acpi_processor *pr;
735
1da177e4
LT
736
737 if (!(acpi_processor_ppc_status & PPC_REGISTERED))
d550d98d 738 return -EINVAL;
1da177e4 739
65c19bbd 740 mutex_lock(&performance_mutex);
1da177e4 741
706546d0 742 pr = per_cpu(processors, cpu);
1da177e4 743 if (!pr) {
65c19bbd 744 mutex_unlock(&performance_mutex);
d550d98d 745 return -ENODEV;
1da177e4
LT
746 }
747
748 if (pr->performance) {
65c19bbd 749 mutex_unlock(&performance_mutex);
d550d98d 750 return -EBUSY;
1da177e4
LT
751 }
752
a913f507
AM
753 WARN_ON(!performance);
754
1da177e4
LT
755 pr->performance = performance;
756
757 if (acpi_processor_get_performance_info(pr)) {
758 pr->performance = NULL;
65c19bbd 759 mutex_unlock(&performance_mutex);
d550d98d 760 return -EIO;
1da177e4
LT
761 }
762
763 acpi_cpufreq_add_file(pr);
764
65c19bbd 765 mutex_unlock(&performance_mutex);
d550d98d 766 return 0;
1da177e4 767}
1da177e4 768
4be44fcd 769EXPORT_SYMBOL(acpi_processor_register_performance);
1da177e4
LT
770
771void
4be44fcd
LB
772acpi_processor_unregister_performance(struct acpi_processor_performance
773 *performance, unsigned int cpu)
1da177e4
LT
774{
775 struct acpi_processor *pr;
776
1da177e4 777
65c19bbd 778 mutex_lock(&performance_mutex);
1da177e4 779
706546d0 780 pr = per_cpu(processors, cpu);
1da177e4 781 if (!pr) {
65c19bbd 782 mutex_unlock(&performance_mutex);
d550d98d 783 return;
1da177e4
LT
784 }
785
a913f507
AM
786 if (pr->performance)
787 kfree(pr->performance->states);
1da177e4
LT
788 pr->performance = NULL;
789
790 acpi_cpufreq_remove_file(pr);
791
65c19bbd 792 mutex_unlock(&performance_mutex);
1da177e4 793
d550d98d 794 return;
1da177e4 795}
4be44fcd 796
1da177e4 797EXPORT_SYMBOL(acpi_processor_unregister_performance);