processor_perflib.c 19.8 KB
Newer Older
L
Linus Torvalds 已提交
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36
/*
 * 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
 *
 *
 * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 *
 *  This program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation; either version 2 of the License, or (at
 *  your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful, but
 *  WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 *  General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License along
 *  with this program; if not, write to the Free Software Foundation, Inc.,
 *  59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
 *
 */

#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/cpufreq.h>

#ifdef CONFIG_X86_ACPI_CPUFREQ_PROC_INTF
#include <linux/proc_fs.h>
#include <linux/seq_file.h>
37
#include <linux/mutex.h>
L
Linus Torvalds 已提交
38 39 40 41 42 43 44 45 46 47 48 49

#include <asm/uaccess.h>
#endif

#include <acpi/acpi_bus.h>
#include <acpi/processor.h>

#define ACPI_PROCESSOR_COMPONENT	0x01000000
#define ACPI_PROCESSOR_CLASS		"processor"
#define ACPI_PROCESSOR_DRIVER_NAME	"ACPI Processor Driver"
#define ACPI_PROCESSOR_FILE_PERFORMANCE	"performance"
#define _COMPONENT		ACPI_PROCESSOR_COMPONENT
L
Len Brown 已提交
50
ACPI_MODULE_NAME("acpi_processor")
L
Linus Torvalds 已提交
51

52
static DEFINE_MUTEX(performance_mutex);
L
Linus Torvalds 已提交
53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69

/*
 * _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.
 */

#define PPC_REGISTERED   1
#define PPC_IN_USE       2

static int acpi_processor_ppc_status = 0;

static int acpi_processor_ppc_notifier(struct notifier_block *nb,
L
Len Brown 已提交
70
				       unsigned long event, void *data)
L
Linus Torvalds 已提交
71 72 73 74 75
{
	struct cpufreq_policy *policy = data;
	struct acpi_processor *pr;
	unsigned int ppc = 0;

76
	mutex_lock(&performance_mutex);
L
Linus Torvalds 已提交
77 78 79 80 81 82 83 84

	if (event != CPUFREQ_INCOMPATIBLE)
		goto out;

	pr = processors[policy->cpu];
	if (!pr || !pr->performance)
		goto out;

L
Len Brown 已提交
85
	ppc = (unsigned int)pr->performance_platform_limit;
L
Linus Torvalds 已提交
86

87
	if (ppc >= pr->performance->state_count)
L
Linus Torvalds 已提交
88 89 90
		goto out;

	cpufreq_verify_within_limits(policy, 0,
L
Len Brown 已提交
91 92
				     pr->performance->states[ppc].
				     core_frequency * 1000);
L
Linus Torvalds 已提交
93

L
Len Brown 已提交
94
      out:
95
	mutex_unlock(&performance_mutex);
L
Linus Torvalds 已提交
96 97 98 99 100 101 102 103

	return 0;
}

static struct notifier_block acpi_ppc_notifier_block = {
	.notifier_call = acpi_processor_ppc_notifier,
};

L
Len Brown 已提交
104
static int acpi_processor_get_platform_limit(struct acpi_processor *pr)
L
Linus Torvalds 已提交
105
{
L
Len Brown 已提交
106 107
	acpi_status status = 0;
	unsigned long ppc = 0;
L
Linus Torvalds 已提交
108 109 110


	if (!pr)
111
		return -EINVAL;
L
Linus Torvalds 已提交
112 113 114 115 116 117 118 119 120 121

	/*
	 * _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)
		acpi_processor_ppc_status |= PPC_IN_USE;

L
Len Brown 已提交
122
	if (ACPI_FAILURE(status) && status != AE_NOT_FOUND) {
123
		ACPI_EXCEPTION((AE_INFO, status, "Evaluating _PPC"));
124
		return -ENODEV;
L
Linus Torvalds 已提交
125 126
	}

L
Len Brown 已提交
127
	pr->performance_platform_limit = (int)ppc;
L
Linus Torvalds 已提交
128

129
	return 0;
L
Linus Torvalds 已提交
130 131
}

L
Len Brown 已提交
132
int acpi_processor_ppc_has_changed(struct acpi_processor *pr)
L
Linus Torvalds 已提交
133 134 135 136 137 138 139 140
{
	int ret = acpi_processor_get_platform_limit(pr);
	if (ret < 0)
		return (ret);
	else
		return cpufreq_update_policy(pr->id);
}

L
Len Brown 已提交
141 142 143 144
void acpi_processor_ppc_init(void)
{
	if (!cpufreq_register_notifier
	    (&acpi_ppc_notifier_block, CPUFREQ_POLICY_NOTIFIER))
L
Linus Torvalds 已提交
145 146
		acpi_processor_ppc_status |= PPC_REGISTERED;
	else
L
Len Brown 已提交
147 148
		printk(KERN_DEBUG
		       "Warning: Processor Platform Limit not supported.\n");
L
Linus Torvalds 已提交
149 150
}

L
Len Brown 已提交
151 152
void acpi_processor_ppc_exit(void)
{
L
Linus Torvalds 已提交
153
	if (acpi_processor_ppc_status & PPC_REGISTERED)
L
Len Brown 已提交
154 155
		cpufreq_unregister_notifier(&acpi_ppc_notifier_block,
					    CPUFREQ_POLICY_NOTIFIER);
L
Linus Torvalds 已提交
156 157 158 159

	acpi_processor_ppc_status &= ~PPC_REGISTERED;
}

L
Len Brown 已提交
160
static int acpi_processor_get_performance_control(struct acpi_processor *pr)
L
Linus Torvalds 已提交
161
{
L
Len Brown 已提交
162 163 164 165 166
	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 };
L
Linus Torvalds 已提交
167 168 169


	status = acpi_evaluate_object(pr->handle, "_PCT", NULL, &buffer);
L
Len Brown 已提交
170
	if (ACPI_FAILURE(status)) {
171
		ACPI_EXCEPTION((AE_INFO, status, "Evaluating _PCT"));
172
		return -ENODEV;
L
Linus Torvalds 已提交
173 174
	}

L
Len Brown 已提交
175
	pct = (union acpi_object *)buffer.pointer;
L
Linus Torvalds 已提交
176
	if (!pct || (pct->type != ACPI_TYPE_PACKAGE)
L
Len Brown 已提交
177
	    || (pct->package.count != 2)) {
178
		printk(KERN_ERR PREFIX "Invalid _PCT data\n");
L
Linus Torvalds 已提交
179 180 181 182 183 184 185 186 187 188 189
		result = -EFAULT;
		goto end;
	}

	/*
	 * control_register
	 */

	obj = pct->package.elements[0];

	if ((obj.type != ACPI_TYPE_BUFFER)
L
Len Brown 已提交
190 191
	    || (obj.buffer.length < sizeof(struct acpi_pct_register))
	    || (obj.buffer.pointer == NULL)) {
192
		printk(KERN_ERR PREFIX "Invalid _PCT data (control_register)\n");
L
Linus Torvalds 已提交
193 194 195
		result = -EFAULT;
		goto end;
	}
L
Len Brown 已提交
196 197
	memcpy(&pr->performance->control_register, obj.buffer.pointer,
	       sizeof(struct acpi_pct_register));
L
Linus Torvalds 已提交
198 199 200 201 202 203 204 205

	/*
	 * status_register
	 */

	obj = pct->package.elements[1];

	if ((obj.type != ACPI_TYPE_BUFFER)
L
Len Brown 已提交
206 207
	    || (obj.buffer.length < sizeof(struct acpi_pct_register))
	    || (obj.buffer.pointer == NULL)) {
208
		printk(KERN_ERR PREFIX "Invalid _PCT data (status_register)\n");
L
Linus Torvalds 已提交
209 210 211 212
		result = -EFAULT;
		goto end;
	}

L
Len Brown 已提交
213 214
	memcpy(&pr->performance->status_register, obj.buffer.pointer,
	       sizeof(struct acpi_pct_register));
L
Linus Torvalds 已提交
215

L
Len Brown 已提交
216
      end:
217
	kfree(buffer.pointer);
L
Linus Torvalds 已提交
218

219
	return result;
L
Linus Torvalds 已提交
220 221
}

L
Len Brown 已提交
222
static int acpi_processor_get_performance_states(struct acpi_processor *pr)
L
Linus Torvalds 已提交
223
{
L
Len Brown 已提交
224 225 226 227 228 229 230
	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;
L
Linus Torvalds 已提交
231 232 233


	status = acpi_evaluate_object(pr->handle, "_PSS", NULL, &buffer);
L
Len Brown 已提交
234
	if (ACPI_FAILURE(status)) {
235
		ACPI_EXCEPTION((AE_INFO, status, "Evaluating _PSS"));
236
		return -ENODEV;
L
Linus Torvalds 已提交
237 238
	}

239
	pss = buffer.pointer;
L
Linus Torvalds 已提交
240
	if (!pss || (pss->type != ACPI_TYPE_PACKAGE)) {
241
		printk(KERN_ERR PREFIX "Invalid _PSS data\n");
L
Linus Torvalds 已提交
242 243 244 245 246
		result = -EFAULT;
		goto end;
	}

	ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Found %d performance states\n",
L
Len Brown 已提交
247
			  pss->package.count));
L
Linus Torvalds 已提交
248 249

	pr->performance->state_count = pss->package.count;
L
Len Brown 已提交
250 251 252
	pr->performance->states =
	    kmalloc(sizeof(struct acpi_processor_px) * pss->package.count,
		    GFP_KERNEL);
L
Linus Torvalds 已提交
253 254 255 256 257 258 259 260 261 262 263 264 265 266 267
	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\n", i));

		status = acpi_extract_package(&(pss->package.elements[i]),
L
Len Brown 已提交
268
					      &format, &state);
L
Linus Torvalds 已提交
269
		if (ACPI_FAILURE(status)) {
270
			ACPI_EXCEPTION((AE_INFO, status, "Invalid _PSS data"));
L
Linus Torvalds 已提交
271 272 273 274 275 276
			result = -EFAULT;
			kfree(pr->performance->states);
			goto end;
		}

		ACPI_DEBUG_PRINT((ACPI_DB_INFO,
L
Len Brown 已提交
277 278 279 280 281 282 283
				  "State [%d]: core_frequency[%d] power[%d] transition_latency[%d] bus_master_latency[%d] control[0x%x] status[0x%x]\n",
				  i,
				  (u32) px->core_frequency,
				  (u32) px->power,
				  (u32) px->transition_latency,
				  (u32) px->bus_master_latency,
				  (u32) px->control, (u32) px->status));
L
Linus Torvalds 已提交
284 285

		if (!px->core_frequency) {
286 287
			printk(KERN_ERR PREFIX
				    "Invalid _PSS data: freq is zero\n");
L
Linus Torvalds 已提交
288 289 290 291 292 293
			result = -EFAULT;
			kfree(pr->performance->states);
			goto end;
		}
	}

L
Len Brown 已提交
294
      end:
295
	kfree(buffer.pointer);
L
Linus Torvalds 已提交
296

297
	return result;
L
Linus Torvalds 已提交
298 299
}

L
Len Brown 已提交
300
static int acpi_processor_get_performance_info(struct acpi_processor *pr)
L
Linus Torvalds 已提交
301
{
L
Len Brown 已提交
302 303 304
	int result = 0;
	acpi_status status = AE_OK;
	acpi_handle handle = NULL;
L
Linus Torvalds 已提交
305 306 307


	if (!pr || !pr->performance || !pr->handle)
308
		return -EINVAL;
L
Linus Torvalds 已提交
309 310 311 312

	status = acpi_get_handle(pr->handle, "_PCT", &handle);
	if (ACPI_FAILURE(status)) {
		ACPI_DEBUG_PRINT((ACPI_DB_INFO,
L
Len Brown 已提交
313
				  "ACPI-based processor performance control unavailable\n"));
314
		return -ENODEV;
L
Linus Torvalds 已提交
315 316 317 318
	}

	result = acpi_processor_get_performance_control(pr);
	if (result)
319
		return result;
L
Linus Torvalds 已提交
320 321 322

	result = acpi_processor_get_performance_states(pr);
	if (result)
323
		return result;
L
Linus Torvalds 已提交
324 325 326

	result = acpi_processor_get_platform_limit(pr);
	if (result)
327
		return result;
L
Linus Torvalds 已提交
328

329
	return 0;
L
Linus Torvalds 已提交
330 331
}

L
Len Brown 已提交
332 333 334 335
int acpi_processor_notify_smm(struct module *calling_module)
{
	acpi_status status;
	static int is_done = 0;
L
Linus Torvalds 已提交
336 337 338


	if (!(acpi_processor_ppc_status & PPC_REGISTERED))
339
		return -EBUSY;
L
Linus Torvalds 已提交
340 341

	if (!try_module_get(calling_module))
342
		return -EINVAL;
L
Linus Torvalds 已提交
343 344 345 346 347 348 349 350

	/* is_done is set to negative if an error occured,
	 * and to postitive if _no_ error occured, but SMM
	 * was already notified. This avoids double notification
	 * which might lead to unexpected results...
	 */
	if (is_done > 0) {
		module_put(calling_module);
351
		return 0;
L
Len Brown 已提交
352
	} else if (is_done < 0) {
L
Linus Torvalds 已提交
353
		module_put(calling_module);
354
		return is_done;
L
Linus Torvalds 已提交
355 356 357 358 359
	}

	is_done = -EIO;

	/* Can't write pstate_cnt to smi_cmd if either value is zero */
L
Len Brown 已提交
360 361
	if ((!acpi_fadt.smi_cmd) || (!acpi_fadt.pstate_cnt)) {
		ACPI_DEBUG_PRINT((ACPI_DB_INFO, "No SMI port or pstate_cnt\n"));
L
Linus Torvalds 已提交
362
		module_put(calling_module);
363
		return 0;
L
Linus Torvalds 已提交
364 365
	}

L
Len Brown 已提交
366 367 368
	ACPI_DEBUG_PRINT((ACPI_DB_INFO,
			  "Writing pstate_cnt [0x%x] to smi_cmd [0x%x]\n",
			  acpi_fadt.pstate_cnt, acpi_fadt.smi_cmd));
L
Linus Torvalds 已提交
369 370 371 372

	/* FADT v1 doesn't support pstate_cnt, many BIOS vendors use
	 * it anyway, so we need to support it... */
	if (acpi_fadt_is_v1) {
L
Len Brown 已提交
373 374
		ACPI_DEBUG_PRINT((ACPI_DB_INFO,
				  "Using v1.0 FADT reserved value for pstate_cnt\n"));
L
Linus Torvalds 已提交
375 376
	}

L
Len Brown 已提交
377 378 379
	status = acpi_os_write_port(acpi_fadt.smi_cmd,
				    (u32) acpi_fadt.pstate_cnt, 8);
	if (ACPI_FAILURE(status)) {
380 381 382 383
		ACPI_EXCEPTION((AE_INFO, status,
				"Failed to write pstate_cnt [0x%x] to "
				"smi_cmd [0x%x]", acpi_fadt.pstate_cnt,
				acpi_fadt.smi_cmd));
L
Linus Torvalds 已提交
384
		module_put(calling_module);
385
		return status;
L
Linus Torvalds 已提交
386 387 388 389 390 391 392 393 394
	}

	/* 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;

	if (!(acpi_processor_ppc_status & PPC_IN_USE))
		module_put(calling_module);

395
	return 0;
L
Linus Torvalds 已提交
396 397
}

L
Len Brown 已提交
398
EXPORT_SYMBOL(acpi_processor_notify_smm);
L
Linus Torvalds 已提交
399 400 401 402 403 404

#ifdef CONFIG_X86_ACPI_CPUFREQ_PROC_INTF
/* /proc/acpi/processor/../performance interface (DEPRECATED) */

static int acpi_processor_perf_open_fs(struct inode *inode, struct file *file);
static struct file_operations acpi_processor_perf_fops = {
L
Len Brown 已提交
405 406 407 408
	.open = acpi_processor_perf_open_fs,
	.read = seq_read,
	.llseek = seq_lseek,
	.release = single_release,
L
Linus Torvalds 已提交
409 410 411 412
};

static int acpi_processor_perf_seq_show(struct seq_file *seq, void *offset)
{
413
	struct acpi_processor *pr = seq->private;
L
Len Brown 已提交
414
	int i;
L
Linus Torvalds 已提交
415 416 417 418 419 420 421 422 423 424 425


	if (!pr)
		goto end;

	if (!pr->performance) {
		seq_puts(seq, "<not supported>\n");
		goto end;
	}

	seq_printf(seq, "state count:             %d\n"
L
Len Brown 已提交
426 427
		   "active state:            P%d\n",
		   pr->performance->state_count, pr->performance->state);
L
Linus Torvalds 已提交
428 429 430

	seq_puts(seq, "states:\n");
	for (i = 0; i < pr->performance->state_count; i++)
L
Len Brown 已提交
431 432 433 434 435 436 437 438
		seq_printf(seq,
			   "   %cP%d:                  %d MHz, %d mW, %d uS\n",
			   (i == pr->performance->state ? '*' : ' '), i,
			   (u32) pr->performance->states[i].core_frequency,
			   (u32) pr->performance->states[i].power,
			   (u32) pr->performance->states[i].transition_latency);

      end:
439
	return 0;
L
Linus Torvalds 已提交
440 441 442 443 444
}

static int acpi_processor_perf_open_fs(struct inode *inode, struct file *file)
{
	return single_open(file, acpi_processor_perf_seq_show,
L
Len Brown 已提交
445
			   PDE(inode)->data);
L
Linus Torvalds 已提交
446 447 448
}

static ssize_t
L
Len Brown 已提交
449 450 451
acpi_processor_write_performance(struct file *file,
				 const char __user * buffer,
				 size_t count, loff_t * data)
L
Linus Torvalds 已提交
452
{
L
Len Brown 已提交
453
	int result = 0;
454 455
	struct seq_file *m = file->private_data;
	struct acpi_processor *pr = m->private;
L
Linus Torvalds 已提交
456
	struct acpi_processor_performance *perf;
L
Len Brown 已提交
457 458 459
	char state_string[12] = { '\0' };
	unsigned int new_state = 0;
	struct cpufreq_policy policy;
L
Linus Torvalds 已提交
460 461 462


	if (!pr || (count > sizeof(state_string) - 1))
463
		return -EINVAL;
L
Linus Torvalds 已提交
464 465 466

	perf = pr->performance;
	if (!perf)
467
		return -EINVAL;
L
Linus Torvalds 已提交
468 469

	if (copy_from_user(state_string, buffer, count))
470
		return -EFAULT;
L
Linus Torvalds 已提交
471 472 473 474 475

	state_string[count] = '\0';
	new_state = simple_strtoul(state_string, NULL, 0);

	if (new_state >= perf->state_count)
476
		return -EINVAL;
L
Linus Torvalds 已提交
477 478 479 480 481 482 483 484 485

	cpufreq_get_policy(&policy, pr->id);

	policy.cpu = pr->id;
	policy.min = perf->states[new_state].core_frequency * 1000;
	policy.max = perf->states[new_state].core_frequency * 1000;

	result = cpufreq_set_policy(&policy);
	if (result)
486
		return result;
L
Linus Torvalds 已提交
487

488
	return count;
L
Linus Torvalds 已提交
489 490
}

L
Len Brown 已提交
491
static void acpi_cpufreq_add_file(struct acpi_processor *pr)
L
Linus Torvalds 已提交
492
{
L
Len Brown 已提交
493 494
	struct proc_dir_entry *entry = NULL;
	struct acpi_device *device = NULL;
L
Linus Torvalds 已提交
495 496 497


	if (acpi_bus_get_device(pr->handle, &device))
498
		return;
L
Linus Torvalds 已提交
499 500 501

	/* add file 'performance' [R/W] */
	entry = create_proc_entry(ACPI_PROCESSOR_FILE_PERFORMANCE,
L
Len Brown 已提交
502 503
				  S_IFREG | S_IRUGO | S_IWUSR,
				  acpi_device_dir(device));
504
	if (entry){
505
		acpi_processor_perf_fops.write = acpi_processor_write_performance;
L
Linus Torvalds 已提交
506 507 508 509
		entry->proc_fops = &acpi_processor_perf_fops;
		entry->data = acpi_driver_data(device);
		entry->owner = THIS_MODULE;
	}
510
	return;
L
Linus Torvalds 已提交
511 512
}

L
Len Brown 已提交
513
static void acpi_cpufreq_remove_file(struct acpi_processor *pr)
L
Linus Torvalds 已提交
514
{
L
Len Brown 已提交
515
	struct acpi_device *device = NULL;
L
Linus Torvalds 已提交
516 517 518


	if (acpi_bus_get_device(pr->handle, &device))
519
		return;
L
Linus Torvalds 已提交
520 521 522

	/* remove file 'performance' */
	remove_proc_entry(ACPI_PROCESSOR_FILE_PERFORMANCE,
L
Len Brown 已提交
523
			  acpi_device_dir(device));
L
Linus Torvalds 已提交
524

525
	return;
L
Linus Torvalds 已提交
526 527 528
}

#else
L
Len Brown 已提交
529 530 531 532 533 534 535 536 537
static void acpi_cpufreq_add_file(struct acpi_processor *pr)
{
	return;
}
static void acpi_cpufreq_remove_file(struct acpi_processor *pr)
{
	return;
}
#endif				/* CONFIG_X86_ACPI_CPUFREQ_PROC_INTF */
L
Linus Torvalds 已提交
538

539 540 541 542 543 544 545 546 547 548 549 550
static int acpi_processor_get_psd(struct acpi_processor	*pr)
{
	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;
	struct acpi_psd_package *pdomain;

	status = acpi_evaluate_object(pr->handle, "_PSD", NULL, &buffer);
	if (ACPI_FAILURE(status)) {
551
		return -ENODEV;
552 553
	}

554
	psd = buffer.pointer;
555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592
	if (!psd || (psd->type != ACPI_TYPE_PACKAGE)) {
		ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "Invalid _PSD data\n"));
		result = -EFAULT;
		goto end;
	}

	if (psd->package.count != 1) {
		ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "Invalid _PSD data\n"));
		result = -EFAULT;
		goto end;
	}

	pdomain = &(pr->performance->domain_info);

	state.length = sizeof(struct acpi_psd_package);
	state.pointer = pdomain;

	status = acpi_extract_package(&(psd->package.elements[0]),
		&format, &state);
	if (ACPI_FAILURE(status)) {
		ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "Invalid _PSD data\n"));
		result = -EFAULT;
		goto end;
	}

	if (pdomain->num_entries != ACPI_PSD_REV0_ENTRIES) {
		ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "Unknown _PSD:num_entries\n"));
		result = -EFAULT;
		goto end;
	}

	if (pdomain->revision != ACPI_PSD_REV0_REVISION) {
		ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "Unknown _PSD:revision\n"));
		result = -EFAULT;
		goto end;
	}

end:
593
	kfree(buffer.pointer);
594
	return result;
595 596 597 598 599 600 601 602 603 604 605 606 607 608
}

int acpi_processor_preregister_performance(
		struct acpi_processor_performance **performance)
{
	int count, count_target;
	int retval = 0;
	unsigned int i, j;
	cpumask_t covered_cpus;
	struct acpi_processor *pr;
	struct acpi_psd_package *pdomain;
	struct acpi_processor *match_pr;
	struct acpi_psd_package *match_pdomain;

609
	mutex_lock(&performance_mutex);
610 611 612 613

	retval = 0;

	/* Call _PSD for all CPUs */
614
	for_each_possible_cpu(i) {
615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644
		pr = processors[i];
		if (!pr) {
			/* Look only at processors in ACPI namespace */
			continue;
		}

		if (pr->performance) {
			retval = -EBUSY;
			continue;
		}

		if (!performance || !performance[i]) {
			retval = -EINVAL;
			continue;
		}

		pr->performance = performance[i];
		cpu_set(i, pr->performance->shared_cpu_map);
		if (acpi_processor_get_psd(pr)) {
			retval = -EINVAL;
			continue;
		}
	}
	if (retval)
		goto err_ret;

	/*
	 * Now that we have _PSD data from all CPUs, lets setup P-state 
	 * domain info.
	 */
645
	for_each_possible_cpu(i) {
646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665
		pr = processors[i];
		if (!pr)
			continue;

		/* Basic validity check for domain info */
		pdomain = &(pr->performance->domain_info);
		if ((pdomain->revision != ACPI_PSD_REV0_REVISION) ||
		    (pdomain->num_entries != ACPI_PSD_REV0_ENTRIES)) {
			retval = -EINVAL;
			goto err_ret;
		}
		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) {
			retval = -EINVAL;
			goto err_ret;
		}
	}

	cpus_clear(covered_cpus);
666
	for_each_possible_cpu(i) {
667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682
		pr = processors[i];
		if (!pr)
			continue;

		if (cpu_isset(i, covered_cpus))
			continue;

		pdomain = &(pr->performance->domain_info);
		cpu_set(i, pr->performance->shared_cpu_map);
		cpu_set(i, covered_cpus);
		if (pdomain->num_processors <= 1)
			continue;

		/* Validate the Domain info */
		count_target = pdomain->num_processors;
		count = 1;
683
		if (pdomain->coord_type == DOMAIN_COORD_TYPE_SW_ALL)
684
			pr->performance->shared_type = CPUFREQ_SHARED_TYPE_ALL;
685 686 687
		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)
688 689
			pr->performance->shared_type = CPUFREQ_SHARED_TYPE_ANY;

690
		for_each_possible_cpu(j) {
691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718
			if (i == j)
				continue;

			match_pr = processors[j];
			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;
			}

			cpu_set(j, covered_cpus);
			cpu_set(j, pr->performance->shared_cpu_map);
			count++;
		}

719
		for_each_possible_cpu(j) {
720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738
			if (i == j)
				continue;

			match_pr = processors[j];
			if (!match_pr)
				continue;

			match_pdomain = &(match_pr->performance->domain_info);
			if (match_pdomain->domain != pdomain->domain)
				continue;

			match_pr->performance->shared_type = 
					pr->performance->shared_type;
			match_pr->performance->shared_cpu_map =
				pr->performance->shared_cpu_map;
		}
	}

err_ret:
739
	for_each_possible_cpu(i) {
740 741 742 743 744 745 746 747 748 749 750 751 752
		pr = processors[i];
		if (!pr || !pr->performance)
			continue;

		/* Assume no coordination on any error parsing domain info */
		if (retval) {
			cpus_clear(pr->performance->shared_cpu_map);
			cpu_set(i, pr->performance->shared_cpu_map);
			pr->performance->shared_type = CPUFREQ_SHARED_TYPE_ALL;
		}
		pr->performance = NULL; /* Will be set for real in register */
	}

753
	mutex_unlock(&performance_mutex);
754
	return retval;
755 756 757 758
}
EXPORT_SYMBOL(acpi_processor_preregister_performance);


L
Linus Torvalds 已提交
759
int
L
Len Brown 已提交
760 761
acpi_processor_register_performance(struct acpi_processor_performance
				    *performance, unsigned int cpu)
L
Linus Torvalds 已提交
762 763 764 765 766
{
	struct acpi_processor *pr;


	if (!(acpi_processor_ppc_status & PPC_REGISTERED))
767
		return -EINVAL;
L
Linus Torvalds 已提交
768

769
	mutex_lock(&performance_mutex);
L
Linus Torvalds 已提交
770 771 772

	pr = processors[cpu];
	if (!pr) {
773
		mutex_unlock(&performance_mutex);
774
		return -ENODEV;
L
Linus Torvalds 已提交
775 776 777
	}

	if (pr->performance) {
778
		mutex_unlock(&performance_mutex);
779
		return -EBUSY;
L
Linus Torvalds 已提交
780 781
	}

782 783
	WARN_ON(!performance);

L
Linus Torvalds 已提交
784 785 786 787
	pr->performance = performance;

	if (acpi_processor_get_performance_info(pr)) {
		pr->performance = NULL;
788
		mutex_unlock(&performance_mutex);
789
		return -EIO;
L
Linus Torvalds 已提交
790 791 792 793
	}

	acpi_cpufreq_add_file(pr);

794
	mutex_unlock(&performance_mutex);
795
	return 0;
L
Linus Torvalds 已提交
796 797
}

L
Len Brown 已提交
798
EXPORT_SYMBOL(acpi_processor_register_performance);
L
Linus Torvalds 已提交
799 800

void
L
Len Brown 已提交
801 802
acpi_processor_unregister_performance(struct acpi_processor_performance
				      *performance, unsigned int cpu)
L
Linus Torvalds 已提交
803 804 805 806
{
	struct acpi_processor *pr;


807
	mutex_lock(&performance_mutex);
L
Linus Torvalds 已提交
808 809 810

	pr = processors[cpu];
	if (!pr) {
811
		mutex_unlock(&performance_mutex);
812
		return;
L
Linus Torvalds 已提交
813 814
	}

815 816
	if (pr->performance)
		kfree(pr->performance->states);
L
Linus Torvalds 已提交
817 818 819 820
	pr->performance = NULL;

	acpi_cpufreq_remove_file(pr);

821
	mutex_unlock(&performance_mutex);
L
Linus Torvalds 已提交
822

823
	return;
L
Linus Torvalds 已提交
824
}
L
Len Brown 已提交
825

L
Linus Torvalds 已提交
826
EXPORT_SYMBOL(acpi_processor_unregister_performance);