processor_perflib.c 19.3 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 72
/* ignore_ppc:
 * -1 -> cpufreq low level drivers not initialized -> _PSS, etc. not called yet
 *       ignore _PPC
 *  0 -> cpufreq low level drivers initialized -> consider _PPC values
 *  1 -> ignore _PPC totally -> forced by user through boot param
 */
M
Milan Broz 已提交
73
static int ignore_ppc = -1;
74 75 76 77
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 已提交
78 79 80
#define PPC_REGISTERED   1
#define PPC_IN_USE       2

81
static int acpi_processor_ppc_status;
L
Linus Torvalds 已提交
82 83

static int acpi_processor_ppc_notifier(struct notifier_block *nb,
L
Len Brown 已提交
84
				       unsigned long event, void *data)
L
Linus Torvalds 已提交
85 86 87 88 89
{
	struct cpufreq_policy *policy = data;
	struct acpi_processor *pr;
	unsigned int ppc = 0;

90 91 92 93 94
	if (event == CPUFREQ_START && ignore_ppc <= 0) {
		ignore_ppc = 0;
		return 0;
	}

95 96 97
	if (ignore_ppc)
		return 0;

L
Linus Torvalds 已提交
98
	if (event != CPUFREQ_INCOMPATIBLE)
99 100 101
		return 0;

	mutex_lock(&performance_mutex);
L
Linus Torvalds 已提交
102

103
	pr = per_cpu(processors, policy->cpu);
L
Linus Torvalds 已提交
104 105 106
	if (!pr || !pr->performance)
		goto out;

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

109
	if (ppc >= pr->performance->state_count)
L
Linus Torvalds 已提交
110 111 112
		goto out;

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

L
Len Brown 已提交
116
      out:
117
	mutex_unlock(&performance_mutex);
L
Linus Torvalds 已提交
118 119 120 121 122 123 124 125

	return 0;
}

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

L
Len Brown 已提交
126
static int acpi_processor_get_platform_limit(struct acpi_processor *pr)
L
Linus Torvalds 已提交
127
{
L
Len Brown 已提交
128 129
	acpi_status status = 0;
	unsigned long ppc = 0;
L
Linus Torvalds 已提交
130 131 132


	if (!pr)
133
		return -EINVAL;
L
Linus Torvalds 已提交
134 135 136 137 138 139 140 141 142 143

	/*
	 * _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 已提交
144
	if (ACPI_FAILURE(status) && status != AE_NOT_FOUND) {
145
		ACPI_EXCEPTION((AE_INFO, status, "Evaluating _PPC"));
146
		return -ENODEV;
L
Linus Torvalds 已提交
147 148
	}

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

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

154
	return 0;
L
Linus Torvalds 已提交
155 156
}

L
Len Brown 已提交
157
int acpi_processor_ppc_has_changed(struct acpi_processor *pr)
L
Linus Torvalds 已提交
158
{
159 160 161 162 163 164 165
	int ret;

	if (ignore_ppc)
		return 0;

	ret = acpi_processor_get_platform_limit(pr);

L
Linus Torvalds 已提交
166 167 168 169 170 171
	if (ret < 0)
		return (ret);
	else
		return cpufreq_update_policy(pr->id);
}

L
Len Brown 已提交
172 173 174 175
void acpi_processor_ppc_init(void)
{
	if (!cpufreq_register_notifier
	    (&acpi_ppc_notifier_block, CPUFREQ_POLICY_NOTIFIER))
L
Linus Torvalds 已提交
176 177
		acpi_processor_ppc_status |= PPC_REGISTERED;
	else
L
Len Brown 已提交
178 179
		printk(KERN_DEBUG
		       "Warning: Processor Platform Limit not supported.\n");
L
Linus Torvalds 已提交
180 181
}

L
Len Brown 已提交
182 183
void acpi_processor_ppc_exit(void)
{
L
Linus Torvalds 已提交
184
	if (acpi_processor_ppc_status & PPC_REGISTERED)
L
Len Brown 已提交
185 186
		cpufreq_unregister_notifier(&acpi_ppc_notifier_block,
					    CPUFREQ_POLICY_NOTIFIER);
L
Linus Torvalds 已提交
187 188 189 190

	acpi_processor_ppc_status &= ~PPC_REGISTERED;
}

L
Len Brown 已提交
191
static int acpi_processor_get_performance_control(struct acpi_processor *pr)
L
Linus Torvalds 已提交
192
{
L
Len Brown 已提交
193 194 195 196 197
	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 已提交
198 199 200


	status = acpi_evaluate_object(pr->handle, "_PCT", NULL, &buffer);
L
Len Brown 已提交
201
	if (ACPI_FAILURE(status)) {
202
		ACPI_EXCEPTION((AE_INFO, status, "Evaluating _PCT"));
203
		return -ENODEV;
L
Linus Torvalds 已提交
204 205
	}

L
Len Brown 已提交
206
	pct = (union acpi_object *)buffer.pointer;
L
Linus Torvalds 已提交
207
	if (!pct || (pct->type != ACPI_TYPE_PACKAGE)
L
Len Brown 已提交
208
	    || (pct->package.count != 2)) {
209
		printk(KERN_ERR PREFIX "Invalid _PCT data\n");
L
Linus Torvalds 已提交
210 211 212 213 214 215 216 217 218 219 220
		result = -EFAULT;
		goto end;
	}

	/*
	 * control_register
	 */

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

	if ((obj.type != ACPI_TYPE_BUFFER)
L
Len Brown 已提交
221 222
	    || (obj.buffer.length < sizeof(struct acpi_pct_register))
	    || (obj.buffer.pointer == NULL)) {
223
		printk(KERN_ERR PREFIX "Invalid _PCT data (control_register)\n");
L
Linus Torvalds 已提交
224 225 226
		result = -EFAULT;
		goto end;
	}
L
Len Brown 已提交
227 228
	memcpy(&pr->performance->control_register, obj.buffer.pointer,
	       sizeof(struct acpi_pct_register));
L
Linus Torvalds 已提交
229 230 231 232 233 234 235 236

	/*
	 * status_register
	 */

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

	if ((obj.type != ACPI_TYPE_BUFFER)
L
Len Brown 已提交
237 238
	    || (obj.buffer.length < sizeof(struct acpi_pct_register))
	    || (obj.buffer.pointer == NULL)) {
239
		printk(KERN_ERR PREFIX "Invalid _PCT data (status_register)\n");
L
Linus Torvalds 已提交
240 241 242 243
		result = -EFAULT;
		goto end;
	}

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

L
Len Brown 已提交
247
      end:
248
	kfree(buffer.pointer);
L
Linus Torvalds 已提交
249

250
	return result;
L
Linus Torvalds 已提交
251 252
}

L
Len Brown 已提交
253
static int acpi_processor_get_performance_states(struct acpi_processor *pr)
L
Linus Torvalds 已提交
254
{
L
Len Brown 已提交
255 256 257 258 259 260 261
	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 已提交
262 263 264


	status = acpi_evaluate_object(pr->handle, "_PSS", NULL, &buffer);
L
Len Brown 已提交
265
	if (ACPI_FAILURE(status)) {
266
		ACPI_EXCEPTION((AE_INFO, status, "Evaluating _PSS"));
267
		return -ENODEV;
L
Linus Torvalds 已提交
268 269
	}

270
	pss = buffer.pointer;
L
Linus Torvalds 已提交
271
	if (!pss || (pss->type != ACPI_TYPE_PACKAGE)) {
272
		printk(KERN_ERR PREFIX "Invalid _PSS data\n");
L
Linus Torvalds 已提交
273 274 275 276 277
		result = -EFAULT;
		goto end;
	}

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

	pr->performance->state_count = pss->package.count;
L
Len Brown 已提交
281 282 283
	pr->performance->states =
	    kmalloc(sizeof(struct acpi_processor_px) * pss->package.count,
		    GFP_KERNEL);
L
Linus Torvalds 已提交
284 285 286 287 288 289 290 291 292 293 294 295 296 297 298
	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 已提交
299
					      &format, &state);
L
Linus Torvalds 已提交
300
		if (ACPI_FAILURE(status)) {
301
			ACPI_EXCEPTION((AE_INFO, status, "Invalid _PSS data"));
L
Linus Torvalds 已提交
302 303 304 305 306 307
			result = -EFAULT;
			kfree(pr->performance->states);
			goto end;
		}

		ACPI_DEBUG_PRINT((ACPI_DB_INFO,
L
Len Brown 已提交
308 309 310 311 312 313 314
				  "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 已提交
315 316

		if (!px->core_frequency) {
317 318
			printk(KERN_ERR PREFIX
				    "Invalid _PSS data: freq is zero\n");
L
Linus Torvalds 已提交
319 320 321 322 323 324
			result = -EFAULT;
			kfree(pr->performance->states);
			goto end;
		}
	}

L
Len Brown 已提交
325
      end:
326
	kfree(buffer.pointer);
L
Linus Torvalds 已提交
327

328
	return result;
L
Linus Torvalds 已提交
329 330
}

L
Len Brown 已提交
331
static int acpi_processor_get_performance_info(struct acpi_processor *pr)
L
Linus Torvalds 已提交
332
{
L
Len Brown 已提交
333 334 335
	int result = 0;
	acpi_status status = AE_OK;
	acpi_handle handle = NULL;
L
Linus Torvalds 已提交
336 337 338


	if (!pr || !pr->performance || !pr->handle)
339
		return -EINVAL;
L
Linus Torvalds 已提交
340 341 342 343

	status = acpi_get_handle(pr->handle, "_PCT", &handle);
	if (ACPI_FAILURE(status)) {
		ACPI_DEBUG_PRINT((ACPI_DB_INFO,
L
Len Brown 已提交
344
				  "ACPI-based processor performance control unavailable\n"));
345
		return -ENODEV;
L
Linus Torvalds 已提交
346 347 348 349
	}

	result = acpi_processor_get_performance_control(pr);
	if (result)
350
		return result;
L
Linus Torvalds 已提交
351 352 353

	result = acpi_processor_get_performance_states(pr);
	if (result)
354
		return result;
L
Linus Torvalds 已提交
355

356
	return 0;
L
Linus Torvalds 已提交
357 358
}

L
Len Brown 已提交
359 360 361 362
int acpi_processor_notify_smm(struct module *calling_module)
{
	acpi_status status;
	static int is_done = 0;
L
Linus Torvalds 已提交
363 364 365


	if (!(acpi_processor_ppc_status & PPC_REGISTERED))
366
		return -EBUSY;
L
Linus Torvalds 已提交
367 368

	if (!try_module_get(calling_module))
369
		return -EINVAL;
L
Linus Torvalds 已提交
370 371 372 373 374 375 376 377

	/* 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);
378
		return 0;
L
Len Brown 已提交
379
	} else if (is_done < 0) {
L
Linus Torvalds 已提交
380
		module_put(calling_module);
381
		return is_done;
L
Linus Torvalds 已提交
382 383 384 385
	}

	is_done = -EIO;

386
	/* Can't write pstate_control to smi_command if either value is zero */
387
	if ((!acpi_gbl_FADT.smi_command) || (!acpi_gbl_FADT.pstate_control)) {
388
		ACPI_DEBUG_PRINT((ACPI_DB_INFO, "No SMI port or pstate_control\n"));
L
Linus Torvalds 已提交
389
		module_put(calling_module);
390
		return 0;
L
Linus Torvalds 已提交
391 392
	}

L
Len Brown 已提交
393
	ACPI_DEBUG_PRINT((ACPI_DB_INFO,
394
			  "Writing pstate_control [0x%x] to smi_command [0x%x]\n",
395
			  acpi_gbl_FADT.pstate_control, acpi_gbl_FADT.smi_command));
L
Linus Torvalds 已提交
396

397 398
	status = acpi_os_write_port(acpi_gbl_FADT.smi_command,
				    (u32) acpi_gbl_FADT.pstate_control, 8);
L
Len Brown 已提交
399
	if (ACPI_FAILURE(status)) {
400
		ACPI_EXCEPTION((AE_INFO, status,
401
				"Failed to write pstate_control [0x%x] to "
402 403
				"smi_command [0x%x]", acpi_gbl_FADT.pstate_control,
				acpi_gbl_FADT.smi_command));
L
Linus Torvalds 已提交
404
		module_put(calling_module);
405
		return status;
L
Linus Torvalds 已提交
406 407 408 409 410 411 412 413 414
	}

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

415
	return 0;
L
Linus Torvalds 已提交
416 417
}

L
Len Brown 已提交
418
EXPORT_SYMBOL(acpi_processor_notify_smm);
L
Linus Torvalds 已提交
419 420 421 422 423 424

#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 = {
425
	.owner = THIS_MODULE,
L
Len Brown 已提交
426 427 428 429
	.open = acpi_processor_perf_open_fs,
	.read = seq_read,
	.llseek = seq_lseek,
	.release = single_release,
L
Linus Torvalds 已提交
430 431 432 433
};

static int acpi_processor_perf_seq_show(struct seq_file *seq, void *offset)
{
434
	struct acpi_processor *pr = seq->private;
L
Len Brown 已提交
435
	int i;
L
Linus Torvalds 已提交
436 437 438 439 440 441 442 443 444 445 446


	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 已提交
447 448
		   "active state:            P%d\n",
		   pr->performance->state_count, pr->performance->state);
L
Linus Torvalds 已提交
449 450 451

	seq_puts(seq, "states:\n");
	for (i = 0; i < pr->performance->state_count; i++)
L
Len Brown 已提交
452 453 454 455 456 457 458 459
		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:
460
	return 0;
L
Linus Torvalds 已提交
461 462 463 464 465
}

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 已提交
466
			   PDE(inode)->data);
L
Linus Torvalds 已提交
467 468
}

L
Len Brown 已提交
469
static void acpi_cpufreq_add_file(struct acpi_processor *pr)
L
Linus Torvalds 已提交
470
{
L
Len Brown 已提交
471
	struct acpi_device *device = NULL;
L
Linus Torvalds 已提交
472 473 474


	if (acpi_bus_get_device(pr->handle, &device))
475
		return;
L
Linus Torvalds 已提交
476 477

	/* add file 'performance' [R/W] */
478 479 480
	proc_create_data(ACPI_PROCESSOR_FILE_PERFORMANCE, S_IFREG | S_IRUGO,
			 acpi_device_dir(device),
			 &acpi_processor_perf_fops, acpi_driver_data(device));
481
	return;
L
Linus Torvalds 已提交
482 483
}

L
Len Brown 已提交
484
static void acpi_cpufreq_remove_file(struct acpi_processor *pr)
L
Linus Torvalds 已提交
485
{
L
Len Brown 已提交
486
	struct acpi_device *device = NULL;
L
Linus Torvalds 已提交
487 488 489


	if (acpi_bus_get_device(pr->handle, &device))
490
		return;
L
Linus Torvalds 已提交
491 492 493

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

496
	return;
L
Linus Torvalds 已提交
497 498 499
}

#else
L
Len Brown 已提交
500 501 502 503 504 505 506 507 508
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 已提交
509

510 511 512 513 514 515 516 517 518 519 520 521
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)) {
522
		return -ENODEV;
523 524
	}

525
	psd = buffer.pointer;
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 553 554 555 556 557 558 559 560 561 562 563
	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:
564
	kfree(buffer.pointer);
565
	return result;
566 567 568
}

int acpi_processor_preregister_performance(
569
		struct acpi_processor_performance *performance)
570 571 572 573 574 575 576 577 578 579
{
	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;

580
	mutex_lock(&performance_mutex);
581 582 583 584

	retval = 0;

	/* Call _PSD for all CPUs */
585
	for_each_possible_cpu(i) {
586
		pr = per_cpu(processors, i);
587 588 589 590 591 592 593 594 595 596
		if (!pr) {
			/* Look only at processors in ACPI namespace */
			continue;
		}

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

597
		if (!performance || !percpu_ptr(performance, i)) {
598 599 600 601
			retval = -EINVAL;
			continue;
		}

602
		pr->performance = percpu_ptr(performance, i);
603 604 605 606 607 608 609 610 611 612 613 614 615
		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.
	 */
616
	for_each_possible_cpu(i) {
617
		pr = per_cpu(processors, i);
618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636
		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);
637
	for_each_possible_cpu(i) {
638
		pr = per_cpu(processors, i);
639 640 641 642 643 644 645 646 647 648 649 650 651 652 653
		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;
654
		if (pdomain->coord_type == DOMAIN_COORD_TYPE_SW_ALL)
655
			pr->performance->shared_type = CPUFREQ_SHARED_TYPE_ALL;
656 657 658
		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)
659 660
			pr->performance->shared_type = CPUFREQ_SHARED_TYPE_ANY;

661
		for_each_possible_cpu(j) {
662 663 664
			if (i == j)
				continue;

665
			match_pr = per_cpu(processors, j);
666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689
			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++;
		}

690
		for_each_possible_cpu(j) {
691 692 693
			if (i == j)
				continue;

694
			match_pr = per_cpu(processors, j);
695 696 697 698 699 700 701 702 703 704 705 706 707 708 709
			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:
710
	for_each_possible_cpu(i) {
711
		pr = per_cpu(processors, i);
712 713 714 715 716 717 718 719 720 721 722 723
		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 */
	}

724
	mutex_unlock(&performance_mutex);
725
	return retval;
726 727 728 729
}
EXPORT_SYMBOL(acpi_processor_preregister_performance);


L
Linus Torvalds 已提交
730
int
L
Len Brown 已提交
731 732
acpi_processor_register_performance(struct acpi_processor_performance
				    *performance, unsigned int cpu)
L
Linus Torvalds 已提交
733 734 735 736 737
{
	struct acpi_processor *pr;


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

740
	mutex_lock(&performance_mutex);
L
Linus Torvalds 已提交
741

742
	pr = per_cpu(processors, cpu);
L
Linus Torvalds 已提交
743
	if (!pr) {
744
		mutex_unlock(&performance_mutex);
745
		return -ENODEV;
L
Linus Torvalds 已提交
746 747 748
	}

	if (pr->performance) {
749
		mutex_unlock(&performance_mutex);
750
		return -EBUSY;
L
Linus Torvalds 已提交
751 752
	}

753 754
	WARN_ON(!performance);

L
Linus Torvalds 已提交
755 756 757 758
	pr->performance = performance;

	if (acpi_processor_get_performance_info(pr)) {
		pr->performance = NULL;
759
		mutex_unlock(&performance_mutex);
760
		return -EIO;
L
Linus Torvalds 已提交
761 762 763 764
	}

	acpi_cpufreq_add_file(pr);

765
	mutex_unlock(&performance_mutex);
766
	return 0;
L
Linus Torvalds 已提交
767 768
}

L
Len Brown 已提交
769
EXPORT_SYMBOL(acpi_processor_register_performance);
L
Linus Torvalds 已提交
770 771

void
L
Len Brown 已提交
772 773
acpi_processor_unregister_performance(struct acpi_processor_performance
				      *performance, unsigned int cpu)
L
Linus Torvalds 已提交
774 775 776 777
{
	struct acpi_processor *pr;


778
	mutex_lock(&performance_mutex);
L
Linus Torvalds 已提交
779

780
	pr = per_cpu(processors, cpu);
L
Linus Torvalds 已提交
781
	if (!pr) {
782
		mutex_unlock(&performance_mutex);
783
		return;
L
Linus Torvalds 已提交
784 785
	}

786 787
	if (pr->performance)
		kfree(pr->performance->states);
L
Linus Torvalds 已提交
788 789 790 791
	pr->performance = NULL;

	acpi_cpufreq_remove_file(pr);

792
	mutex_unlock(&performance_mutex);
L
Linus Torvalds 已提交
793

794
	return;
L
Linus Torvalds 已提交
795
}
L
Len Brown 已提交
796

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