processor_perflib.c 18.9 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

#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_FILE_PERFORMANCE	"performance"
#define _COMPONENT		ACPI_PROCESSOR_COMPONENT
49
ACPI_MODULE_NAME("processor_perflib");
L
Linus Torvalds 已提交
50

51
static DEFINE_MUTEX(performance_mutex);
L
Linus Torvalds 已提交
52

53 54 55 56
/* Use cpufreq debug layer for _PPC changes. */
#define cpufreq_printk(msg...) cpufreq_debug_printk(CPUFREQ_DEBUG_CORE, \
						"cpufreq-core", msg)

L
Linus Torvalds 已提交
57 58 59 60 61 62 63 64 65 66
/*
 * _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.
 */

67 68 69 70 71
static unsigned int ignore_ppc = 0;
module_param(ignore_ppc, uint, 0644);
MODULE_PARM_DESC(ignore_ppc, "If the frequency of your machine gets wrongly" \
		 "limited by BIOS, this should help");

L
Linus Torvalds 已提交
72 73 74 75 76 77
#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 已提交
78
				       unsigned long event, void *data)
L
Linus Torvalds 已提交
79 80 81 82 83
{
	struct cpufreq_policy *policy = data;
	struct acpi_processor *pr;
	unsigned int ppc = 0;

84 85 86
	if (ignore_ppc)
		return 0;

87
	mutex_lock(&performance_mutex);
L
Linus Torvalds 已提交
88 89 90 91 92 93 94 95

	if (event != CPUFREQ_INCOMPATIBLE)
		goto out;

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

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

98
	if (ppc >= pr->performance->state_count)
L
Linus Torvalds 已提交
99 100 101
		goto out;

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

L
Len Brown 已提交
105
      out:
106
	mutex_unlock(&performance_mutex);
L
Linus Torvalds 已提交
107 108 109 110 111 112 113 114

	return 0;
}

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

L
Len Brown 已提交
115
static int acpi_processor_get_platform_limit(struct acpi_processor *pr)
L
Linus Torvalds 已提交
116
{
L
Len Brown 已提交
117 118
	acpi_status status = 0;
	unsigned long ppc = 0;
L
Linus Torvalds 已提交
119 120 121


	if (!pr)
122
		return -EINVAL;
L
Linus Torvalds 已提交
123 124 125 126 127 128 129 130 131 132

	/*
	 * _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 已提交
133
	if (ACPI_FAILURE(status) && status != AE_NOT_FOUND) {
134
		ACPI_EXCEPTION((AE_INFO, status, "Evaluating _PPC"));
135
		return -ENODEV;
L
Linus Torvalds 已提交
136 137
	}

138 139 140
	cpufreq_printk("CPU %d: _PPC is %d - frequency %s limited\n", pr->id,
		       (int)ppc, ppc ? "" : "not");

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

143
	return 0;
L
Linus Torvalds 已提交
144 145
}

L
Len Brown 已提交
146
int acpi_processor_ppc_has_changed(struct acpi_processor *pr)
L
Linus Torvalds 已提交
147
{
148 149 150 151 152 153 154
	int ret;

	if (ignore_ppc)
		return 0;

	ret = acpi_processor_get_platform_limit(pr);

L
Linus Torvalds 已提交
155 156 157 158 159 160
	if (ret < 0)
		return (ret);
	else
		return cpufreq_update_policy(pr->id);
}

L
Len Brown 已提交
161 162 163 164
void acpi_processor_ppc_init(void)
{
	if (!cpufreq_register_notifier
	    (&acpi_ppc_notifier_block, CPUFREQ_POLICY_NOTIFIER))
L
Linus Torvalds 已提交
165 166
		acpi_processor_ppc_status |= PPC_REGISTERED;
	else
L
Len Brown 已提交
167 168
		printk(KERN_DEBUG
		       "Warning: Processor Platform Limit not supported.\n");
L
Linus Torvalds 已提交
169 170
}

L
Len Brown 已提交
171 172
void acpi_processor_ppc_exit(void)
{
L
Linus Torvalds 已提交
173
	if (acpi_processor_ppc_status & PPC_REGISTERED)
L
Len Brown 已提交
174 175
		cpufreq_unregister_notifier(&acpi_ppc_notifier_block,
					    CPUFREQ_POLICY_NOTIFIER);
L
Linus Torvalds 已提交
176 177 178 179

	acpi_processor_ppc_status &= ~PPC_REGISTERED;
}

L
Len Brown 已提交
180
static int acpi_processor_get_performance_control(struct acpi_processor *pr)
L
Linus Torvalds 已提交
181
{
L
Len Brown 已提交
182 183 184 185 186
	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 已提交
187 188 189


	status = acpi_evaluate_object(pr->handle, "_PCT", NULL, &buffer);
L
Len Brown 已提交
190
	if (ACPI_FAILURE(status)) {
191
		ACPI_EXCEPTION((AE_INFO, status, "Evaluating _PCT"));
192
		return -ENODEV;
L
Linus Torvalds 已提交
193 194
	}

L
Len Brown 已提交
195
	pct = (union acpi_object *)buffer.pointer;
L
Linus Torvalds 已提交
196
	if (!pct || (pct->type != ACPI_TYPE_PACKAGE)
L
Len Brown 已提交
197
	    || (pct->package.count != 2)) {
198
		printk(KERN_ERR PREFIX "Invalid _PCT data\n");
L
Linus Torvalds 已提交
199 200 201 202 203 204 205 206 207 208 209
		result = -EFAULT;
		goto end;
	}

	/*
	 * control_register
	 */

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

	if ((obj.type != ACPI_TYPE_BUFFER)
L
Len Brown 已提交
210 211
	    || (obj.buffer.length < sizeof(struct acpi_pct_register))
	    || (obj.buffer.pointer == NULL)) {
212
		printk(KERN_ERR PREFIX "Invalid _PCT data (control_register)\n");
L
Linus Torvalds 已提交
213 214 215
		result = -EFAULT;
		goto end;
	}
L
Len Brown 已提交
216 217
	memcpy(&pr->performance->control_register, obj.buffer.pointer,
	       sizeof(struct acpi_pct_register));
L
Linus Torvalds 已提交
218 219 220 221 222 223 224 225

	/*
	 * status_register
	 */

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

	if ((obj.type != ACPI_TYPE_BUFFER)
L
Len Brown 已提交
226 227
	    || (obj.buffer.length < sizeof(struct acpi_pct_register))
	    || (obj.buffer.pointer == NULL)) {
228
		printk(KERN_ERR PREFIX "Invalid _PCT data (status_register)\n");
L
Linus Torvalds 已提交
229 230 231 232
		result = -EFAULT;
		goto end;
	}

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

L
Len Brown 已提交
236
      end:
237
	kfree(buffer.pointer);
L
Linus Torvalds 已提交
238

239
	return result;
L
Linus Torvalds 已提交
240 241
}

L
Len Brown 已提交
242
static int acpi_processor_get_performance_states(struct acpi_processor *pr)
L
Linus Torvalds 已提交
243
{
L
Len Brown 已提交
244 245 246 247 248 249 250
	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 已提交
251 252 253


	status = acpi_evaluate_object(pr->handle, "_PSS", NULL, &buffer);
L
Len Brown 已提交
254
	if (ACPI_FAILURE(status)) {
255
		ACPI_EXCEPTION((AE_INFO, status, "Evaluating _PSS"));
256
		return -ENODEV;
L
Linus Torvalds 已提交
257 258
	}

259
	pss = buffer.pointer;
L
Linus Torvalds 已提交
260
	if (!pss || (pss->type != ACPI_TYPE_PACKAGE)) {
261
		printk(KERN_ERR PREFIX "Invalid _PSS data\n");
L
Linus Torvalds 已提交
262 263 264 265 266
		result = -EFAULT;
		goto end;
	}

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

	pr->performance->state_count = pss->package.count;
L
Len Brown 已提交
270 271 272
	pr->performance->states =
	    kmalloc(sizeof(struct acpi_processor_px) * pss->package.count,
		    GFP_KERNEL);
L
Linus Torvalds 已提交
273 274 275 276 277 278 279 280 281 282 283 284 285 286 287
	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 已提交
288
					      &format, &state);
L
Linus Torvalds 已提交
289
		if (ACPI_FAILURE(status)) {
290
			ACPI_EXCEPTION((AE_INFO, status, "Invalid _PSS data"));
L
Linus Torvalds 已提交
291 292 293 294 295 296
			result = -EFAULT;
			kfree(pr->performance->states);
			goto end;
		}

		ACPI_DEBUG_PRINT((ACPI_DB_INFO,
L
Len Brown 已提交
297 298 299 300 301 302 303
				  "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 已提交
304 305

		if (!px->core_frequency) {
306 307
			printk(KERN_ERR PREFIX
				    "Invalid _PSS data: freq is zero\n");
L
Linus Torvalds 已提交
308 309 310 311 312 313
			result = -EFAULT;
			kfree(pr->performance->states);
			goto end;
		}
	}

L
Len Brown 已提交
314
      end:
315
	kfree(buffer.pointer);
L
Linus Torvalds 已提交
316

317
	return result;
L
Linus Torvalds 已提交
318 319
}

L
Len Brown 已提交
320
static int acpi_processor_get_performance_info(struct acpi_processor *pr)
L
Linus Torvalds 已提交
321
{
L
Len Brown 已提交
322 323 324
	int result = 0;
	acpi_status status = AE_OK;
	acpi_handle handle = NULL;
L
Linus Torvalds 已提交
325 326 327


	if (!pr || !pr->performance || !pr->handle)
328
		return -EINVAL;
L
Linus Torvalds 已提交
329 330 331 332

	status = acpi_get_handle(pr->handle, "_PCT", &handle);
	if (ACPI_FAILURE(status)) {
		ACPI_DEBUG_PRINT((ACPI_DB_INFO,
L
Len Brown 已提交
333
				  "ACPI-based processor performance control unavailable\n"));
334
		return -ENODEV;
L
Linus Torvalds 已提交
335 336 337 338
	}

	result = acpi_processor_get_performance_control(pr);
	if (result)
339
		return result;
L
Linus Torvalds 已提交
340 341 342

	result = acpi_processor_get_performance_states(pr);
	if (result)
343
		return result;
L
Linus Torvalds 已提交
344

345
	return 0;
L
Linus Torvalds 已提交
346 347
}

L
Len Brown 已提交
348 349 350 351
int acpi_processor_notify_smm(struct module *calling_module)
{
	acpi_status status;
	static int is_done = 0;
L
Linus Torvalds 已提交
352 353 354


	if (!(acpi_processor_ppc_status & PPC_REGISTERED))
355
		return -EBUSY;
L
Linus Torvalds 已提交
356 357

	if (!try_module_get(calling_module))
358
		return -EINVAL;
L
Linus Torvalds 已提交
359 360 361 362 363 364 365 366

	/* 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);
367
		return 0;
L
Len Brown 已提交
368
	} else if (is_done < 0) {
L
Linus Torvalds 已提交
369
		module_put(calling_module);
370
		return is_done;
L
Linus Torvalds 已提交
371 372 373 374
	}

	is_done = -EIO;

375
	/* Can't write pstate_control to smi_command if either value is zero */
376
	if ((!acpi_gbl_FADT.smi_command) || (!acpi_gbl_FADT.pstate_control)) {
377
		ACPI_DEBUG_PRINT((ACPI_DB_INFO, "No SMI port or pstate_control\n"));
L
Linus Torvalds 已提交
378
		module_put(calling_module);
379
		return 0;
L
Linus Torvalds 已提交
380 381
	}

L
Len Brown 已提交
382
	ACPI_DEBUG_PRINT((ACPI_DB_INFO,
383
			  "Writing pstate_control [0x%x] to smi_command [0x%x]\n",
384
			  acpi_gbl_FADT.pstate_control, acpi_gbl_FADT.smi_command));
L
Linus Torvalds 已提交
385

386 387
	status = acpi_os_write_port(acpi_gbl_FADT.smi_command,
				    (u32) acpi_gbl_FADT.pstate_control, 8);
L
Len Brown 已提交
388
	if (ACPI_FAILURE(status)) {
389
		ACPI_EXCEPTION((AE_INFO, status,
390
				"Failed to write pstate_control [0x%x] to "
391 392
				"smi_command [0x%x]", acpi_gbl_FADT.pstate_control,
				acpi_gbl_FADT.smi_command));
L
Linus Torvalds 已提交
393
		module_put(calling_module);
394
		return status;
L
Linus Torvalds 已提交
395 396 397 398 399 400 401 402 403
	}

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

404
	return 0;
L
Linus Torvalds 已提交
405 406
}

L
Len Brown 已提交
407
EXPORT_SYMBOL(acpi_processor_notify_smm);
L
Linus Torvalds 已提交
408 409 410 411 412 413

#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 = {
414
	.owner = THIS_MODULE,
L
Len Brown 已提交
415 416 417 418
	.open = acpi_processor_perf_open_fs,
	.read = seq_read,
	.llseek = seq_lseek,
	.release = single_release,
L
Linus Torvalds 已提交
419 420 421 422
};

static int acpi_processor_perf_seq_show(struct seq_file *seq, void *offset)
{
423
	struct acpi_processor *pr = seq->private;
L
Len Brown 已提交
424
	int i;
L
Linus Torvalds 已提交
425 426 427 428 429 430 431 432 433 434 435


	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 已提交
436 437
		   "active state:            P%d\n",
		   pr->performance->state_count, pr->performance->state);
L
Linus Torvalds 已提交
438 439 440

	seq_puts(seq, "states:\n");
	for (i = 0; i < pr->performance->state_count; i++)
L
Len Brown 已提交
441 442 443 444 445 446 447 448
		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:
449
	return 0;
L
Linus Torvalds 已提交
450 451 452 453 454
}

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 已提交
455
			   PDE(inode)->data);
L
Linus Torvalds 已提交
456 457
}

L
Len Brown 已提交
458
static void acpi_cpufreq_add_file(struct acpi_processor *pr)
L
Linus Torvalds 已提交
459
{
L
Len Brown 已提交
460
	struct acpi_device *device = NULL;
L
Linus Torvalds 已提交
461 462 463


	if (acpi_bus_get_device(pr->handle, &device))
464
		return;
L
Linus Torvalds 已提交
465 466

	/* add file 'performance' [R/W] */
467 468 469
	proc_create_data(ACPI_PROCESSOR_FILE_PERFORMANCE, S_IFREG | S_IRUGO,
			 acpi_device_dir(device),
			 &acpi_processor_perf_fops, acpi_driver_data(device));
470
	return;
L
Linus Torvalds 已提交
471 472
}

L
Len Brown 已提交
473
static void acpi_cpufreq_remove_file(struct acpi_processor *pr)
L
Linus Torvalds 已提交
474
{
L
Len Brown 已提交
475
	struct acpi_device *device = NULL;
L
Linus Torvalds 已提交
476 477 478


	if (acpi_bus_get_device(pr->handle, &device))
479
		return;
L
Linus Torvalds 已提交
480 481 482

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

485
	return;
L
Linus Torvalds 已提交
486 487 488
}

#else
L
Len Brown 已提交
489 490 491 492 493 494 495 496 497
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 已提交
498

499 500 501 502 503 504 505 506 507 508 509 510
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)) {
511
		return -ENODEV;
512 513
	}

514
	psd = buffer.pointer;
515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552
	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:
553
	kfree(buffer.pointer);
554
	return result;
555 556 557
}

int acpi_processor_preregister_performance(
558
		struct acpi_processor_performance *performance)
559 560 561 562 563 564 565 566 567 568
{
	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;

569
	mutex_lock(&performance_mutex);
570 571 572 573

	retval = 0;

	/* Call _PSD for all CPUs */
574
	for_each_possible_cpu(i) {
575 576 577 578 579 580 581 582 583 584 585
		pr = processors[i];
		if (!pr) {
			/* Look only at processors in ACPI namespace */
			continue;
		}

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

586
		if (!performance || !percpu_ptr(performance, i)) {
587 588 589 590
			retval = -EINVAL;
			continue;
		}

591
		pr->performance = percpu_ptr(performance, i);
592 593 594 595 596 597 598 599 600 601 602 603 604
		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.
	 */
605
	for_each_possible_cpu(i) {
606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625
		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);
626
	for_each_possible_cpu(i) {
627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642
		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;
643
		if (pdomain->coord_type == DOMAIN_COORD_TYPE_SW_ALL)
644
			pr->performance->shared_type = CPUFREQ_SHARED_TYPE_ALL;
645 646 647
		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)
648 649
			pr->performance->shared_type = CPUFREQ_SHARED_TYPE_ANY;

650
		for_each_possible_cpu(j) {
651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678
			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++;
		}

679
		for_each_possible_cpu(j) {
680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698
			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:
699
	for_each_possible_cpu(i) {
700 701 702 703 704 705 706 707 708 709 710 711 712
		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 */
	}

713
	mutex_unlock(&performance_mutex);
714
	return retval;
715 716 717 718
}
EXPORT_SYMBOL(acpi_processor_preregister_performance);


L
Linus Torvalds 已提交
719
int
L
Len Brown 已提交
720 721
acpi_processor_register_performance(struct acpi_processor_performance
				    *performance, unsigned int cpu)
L
Linus Torvalds 已提交
722 723 724 725 726
{
	struct acpi_processor *pr;


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

729
	mutex_lock(&performance_mutex);
L
Linus Torvalds 已提交
730 731 732

	pr = processors[cpu];
	if (!pr) {
733
		mutex_unlock(&performance_mutex);
734
		return -ENODEV;
L
Linus Torvalds 已提交
735 736 737
	}

	if (pr->performance) {
738
		mutex_unlock(&performance_mutex);
739
		return -EBUSY;
L
Linus Torvalds 已提交
740 741
	}

742 743
	WARN_ON(!performance);

L
Linus Torvalds 已提交
744 745 746 747
	pr->performance = performance;

	if (acpi_processor_get_performance_info(pr)) {
		pr->performance = NULL;
748
		mutex_unlock(&performance_mutex);
749
		return -EIO;
L
Linus Torvalds 已提交
750 751 752 753
	}

	acpi_cpufreq_add_file(pr);

754
	mutex_unlock(&performance_mutex);
755
	return 0;
L
Linus Torvalds 已提交
756 757
}

L
Len Brown 已提交
758
EXPORT_SYMBOL(acpi_processor_register_performance);
L
Linus Torvalds 已提交
759 760

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


767
	mutex_lock(&performance_mutex);
L
Linus Torvalds 已提交
768 769 770

	pr = processors[cpu];
	if (!pr) {
771
		mutex_unlock(&performance_mutex);
772
		return;
L
Linus Torvalds 已提交
773 774
	}

775 776
	if (pr->performance)
		kfree(pr->performance->states);
L
Linus Torvalds 已提交
777 778 779 780
	pr->performance = NULL;

	acpi_cpufreq_remove_file(pr);

781
	mutex_unlock(&performance_mutex);
L
Linus Torvalds 已提交
782

783
	return;
L
Linus Torvalds 已提交
784
}
L
Len Brown 已提交
785

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