processor_perflib.c 20.0 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 88 89 90 91 92
	if (!ppc)
		goto out;

	if (ppc > pr->performance->state_count)
		goto out;

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

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

	return 0;
}

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

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


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

	/*
	 * _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 已提交
124
	if (ACPI_FAILURE(status) && status != AE_NOT_FOUND) {
125
		ACPI_EXCEPTION((AE_INFO, status, "Evaluating _PPC"));
126
		return -ENODEV;
L
Linus Torvalds 已提交
127 128
	}

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

131
	return 0;
L
Linus Torvalds 已提交
132 133
}

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

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

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

	acpi_processor_ppc_status &= ~PPC_REGISTERED;
}

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


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

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

	/*
	 * control_register
	 */

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

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

	/*
	 * status_register
	 */

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

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

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

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

221
	return result;
L
Linus Torvalds 已提交
222 223
}

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


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

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

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

	pr->performance->state_count = pss->package.count;
L
Len Brown 已提交
252 253 254
	pr->performance->states =
	    kmalloc(sizeof(struct acpi_processor_px) * pss->package.count,
		    GFP_KERNEL);
L
Linus Torvalds 已提交
255 256 257 258 259 260 261 262 263 264 265 266 267 268 269
	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 已提交
270
					      &format, &state);
L
Linus Torvalds 已提交
271
		if (ACPI_FAILURE(status)) {
272
			ACPI_EXCEPTION((AE_INFO, status, "Invalid _PSS data"));
L
Linus Torvalds 已提交
273 274 275 276 277 278
			result = -EFAULT;
			kfree(pr->performance->states);
			goto end;
		}

		ACPI_DEBUG_PRINT((ACPI_DB_INFO,
L
Len Brown 已提交
279 280 281 282 283 284 285
				  "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 已提交
286 287

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

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

299
	return result;
L
Linus Torvalds 已提交
300 301
}

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


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

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

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

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

	result = acpi_processor_get_platform_limit(pr);
	if (result)
329
		return result;
L
Linus Torvalds 已提交
330

331
	return 0;
L
Linus Torvalds 已提交
332 333
}

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


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

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

	/* 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);
353
		return 0;
L
Len Brown 已提交
354
	} else if (is_done < 0) {
L
Linus Torvalds 已提交
355
		module_put(calling_module);
356
		return is_done;
L
Linus Torvalds 已提交
357 358 359 360 361
	}

	is_done = -EIO;

	/* Can't write pstate_cnt to smi_cmd if either value is zero */
L
Len Brown 已提交
362 363
	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 已提交
364
		module_put(calling_module);
365
		return 0;
L
Linus Torvalds 已提交
366 367
	}

L
Len Brown 已提交
368 369 370
	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 已提交
371 372 373 374

	/* 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 已提交
375 376
		ACPI_DEBUG_PRINT((ACPI_DB_INFO,
				  "Using v1.0 FADT reserved value for pstate_cnt\n"));
L
Linus Torvalds 已提交
377 378
	}

L
Len Brown 已提交
379 380 381
	status = acpi_os_write_port(acpi_fadt.smi_cmd,
				    (u32) acpi_fadt.pstate_cnt, 8);
	if (ACPI_FAILURE(status)) {
382 383 384 385
		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 已提交
386
		module_put(calling_module);
387
		return status;
L
Linus Torvalds 已提交
388 389 390 391 392 393 394 395 396
	}

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

397
	return 0;
L
Linus Torvalds 已提交
398 399
}

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

#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 已提交
407 408 409 410
	.open = acpi_processor_perf_open_fs,
	.read = seq_read,
	.llseek = seq_lseek,
	.release = single_release,
L
Linus Torvalds 已提交
411 412 413 414
};

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


	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 已提交
428 429
		   "active state:            P%d\n",
		   pr->performance->state_count, pr->performance->state);
L
Linus Torvalds 已提交
430 431 432

	seq_puts(seq, "states:\n");
	for (i = 0; i < pr->performance->state_count; i++)
L
Len Brown 已提交
433 434 435 436 437 438 439 440
		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:
441
	return 0;
L
Linus Torvalds 已提交
442 443 444 445 446
}

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 已提交
447
			   PDE(inode)->data);
L
Linus Torvalds 已提交
448 449 450
}

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


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

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

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

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

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

	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)
488
		return result;
L
Linus Torvalds 已提交
489

490
	return count;
L
Linus Torvalds 已提交
491 492
}

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


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

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

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


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

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

527
	return;
L
Linus Torvalds 已提交
528 529 530
}

#else
L
Len Brown 已提交
531 532 533 534 535 536 537 538 539
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 已提交
540

541 542 543 544 545 546 547 548 549 550 551 552
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)) {
553
		return -ENODEV;
554 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 593 594
	}

	psd = (union acpi_object *) buffer.pointer;
	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:
595
	kfree(buffer.pointer);
596
	return result;
597 598 599 600 601 602 603 604 605 606 607 608 609 610
}

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;

611
	mutex_lock(&performance_mutex);
612 613 614 615

	retval = 0;

	/* Call _PSD for all CPUs */
616
	for_each_possible_cpu(i) {
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 645 646
		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.
	 */
647
	for_each_possible_cpu(i) {
648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667
		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);
668
	for_each_possible_cpu(i) {
669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684
		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;
685
		if (pdomain->coord_type == DOMAIN_COORD_TYPE_SW_ALL)
686
			pr->performance->shared_type = CPUFREQ_SHARED_TYPE_ALL;
687 688 689
		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)
690 691
			pr->performance->shared_type = CPUFREQ_SHARED_TYPE_ANY;

692
		for_each_possible_cpu(j) {
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 719 720
			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++;
		}

721
		for_each_possible_cpu(j) {
722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744
			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:
	if (retval) {
		ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "Error while parsing _PSD domain information. Assuming no coordination\n"));
	}

745
	for_each_possible_cpu(i) {
746 747 748 749 750 751 752 753 754 755 756 757 758
		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 */
	}

759
	mutex_unlock(&performance_mutex);
760
	return retval;
761 762 763 764
}
EXPORT_SYMBOL(acpi_processor_preregister_performance);


L
Linus Torvalds 已提交
765
int
L
Len Brown 已提交
766 767
acpi_processor_register_performance(struct acpi_processor_performance
				    *performance, unsigned int cpu)
L
Linus Torvalds 已提交
768 769 770 771 772
{
	struct acpi_processor *pr;


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

775
	mutex_lock(&performance_mutex);
L
Linus Torvalds 已提交
776 777 778

	pr = processors[cpu];
	if (!pr) {
779
		mutex_unlock(&performance_mutex);
780
		return -ENODEV;
L
Linus Torvalds 已提交
781 782 783
	}

	if (pr->performance) {
784
		mutex_unlock(&performance_mutex);
785
		return -EBUSY;
L
Linus Torvalds 已提交
786 787
	}

788 789
	WARN_ON(!performance);

L
Linus Torvalds 已提交
790 791 792 793
	pr->performance = performance;

	if (acpi_processor_get_performance_info(pr)) {
		pr->performance = NULL;
794
		mutex_unlock(&performance_mutex);
795
		return -EIO;
L
Linus Torvalds 已提交
796 797 798 799
	}

	acpi_cpufreq_add_file(pr);

800
	mutex_unlock(&performance_mutex);
801
	return 0;
L
Linus Torvalds 已提交
802 803
}

L
Len Brown 已提交
804
EXPORT_SYMBOL(acpi_processor_register_performance);
L
Linus Torvalds 已提交
805 806

void
L
Len Brown 已提交
807 808
acpi_processor_unregister_performance(struct acpi_processor_performance
				      *performance, unsigned int cpu)
L
Linus Torvalds 已提交
809 810 811 812
{
	struct acpi_processor *pr;


813
	mutex_lock(&performance_mutex);
L
Linus Torvalds 已提交
814 815 816

	pr = processors[cpu];
	if (!pr) {
817
		mutex_unlock(&performance_mutex);
818
		return;
L
Linus Torvalds 已提交
819 820
	}

821 822
	if (pr->performance)
		kfree(pr->performance->states);
L
Linus Torvalds 已提交
823 824 825 826
	pr->performance = NULL;

	acpi_cpufreq_remove_file(pr);

827
	mutex_unlock(&performance_mutex);
L
Linus Torvalds 已提交
828

829
	return;
L
Linus Torvalds 已提交
830
}
L
Len Brown 已提交
831

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