processor_perflib.c 18.7 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 57 58 59 60 61 62

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

63 64 65 66 67
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 已提交
68 69 70 71 72 73
#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 已提交
74
				       unsigned long event, void *data)
L
Linus Torvalds 已提交
75 76 77 78 79
{
	struct cpufreq_policy *policy = data;
	struct acpi_processor *pr;
	unsigned int ppc = 0;

80 81 82
	if (ignore_ppc)
		return 0;

83
	mutex_lock(&performance_mutex);
L
Linus Torvalds 已提交
84 85 86 87 88 89 90 91

	if (event != CPUFREQ_INCOMPATIBLE)
		goto out;

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

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

94
	if (ppc >= pr->performance->state_count)
L
Linus Torvalds 已提交
95 96 97
		goto out;

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

L
Len Brown 已提交
101
      out:
102
	mutex_unlock(&performance_mutex);
L
Linus Torvalds 已提交
103 104 105 106 107 108 109 110

	return 0;
}

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

L
Len Brown 已提交
111
static int acpi_processor_get_platform_limit(struct acpi_processor *pr)
L
Linus Torvalds 已提交
112
{
L
Len Brown 已提交
113 114
	acpi_status status = 0;
	unsigned long ppc = 0;
L
Linus Torvalds 已提交
115 116 117


	if (!pr)
118
		return -EINVAL;
L
Linus Torvalds 已提交
119 120 121 122 123 124 125 126 127 128

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

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

136
	return 0;
L
Linus Torvalds 已提交
137 138
}

L
Len Brown 已提交
139
int acpi_processor_ppc_has_changed(struct acpi_processor *pr)
L
Linus Torvalds 已提交
140
{
141 142 143 144 145 146 147
	int ret;

	if (ignore_ppc)
		return 0;

	ret = acpi_processor_get_platform_limit(pr);

L
Linus Torvalds 已提交
148 149 150 151 152 153
	if (ret < 0)
		return (ret);
	else
		return cpufreq_update_policy(pr->id);
}

L
Len Brown 已提交
154 155 156 157
void acpi_processor_ppc_init(void)
{
	if (!cpufreq_register_notifier
	    (&acpi_ppc_notifier_block, CPUFREQ_POLICY_NOTIFIER))
L
Linus Torvalds 已提交
158 159
		acpi_processor_ppc_status |= PPC_REGISTERED;
	else
L
Len Brown 已提交
160 161
		printk(KERN_DEBUG
		       "Warning: Processor Platform Limit not supported.\n");
L
Linus Torvalds 已提交
162 163
}

L
Len Brown 已提交
164 165
void acpi_processor_ppc_exit(void)
{
L
Linus Torvalds 已提交
166
	if (acpi_processor_ppc_status & PPC_REGISTERED)
L
Len Brown 已提交
167 168
		cpufreq_unregister_notifier(&acpi_ppc_notifier_block,
					    CPUFREQ_POLICY_NOTIFIER);
L
Linus Torvalds 已提交
169 170 171 172

	acpi_processor_ppc_status &= ~PPC_REGISTERED;
}

L
Len Brown 已提交
173
static int acpi_processor_get_performance_control(struct acpi_processor *pr)
L
Linus Torvalds 已提交
174
{
L
Len Brown 已提交
175 176 177 178 179
	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 已提交
180 181 182


	status = acpi_evaluate_object(pr->handle, "_PCT", NULL, &buffer);
L
Len Brown 已提交
183
	if (ACPI_FAILURE(status)) {
184
		ACPI_EXCEPTION((AE_INFO, status, "Evaluating _PCT"));
185
		return -ENODEV;
L
Linus Torvalds 已提交
186 187
	}

L
Len Brown 已提交
188
	pct = (union acpi_object *)buffer.pointer;
L
Linus Torvalds 已提交
189
	if (!pct || (pct->type != ACPI_TYPE_PACKAGE)
L
Len Brown 已提交
190
	    || (pct->package.count != 2)) {
191
		printk(KERN_ERR PREFIX "Invalid _PCT data\n");
L
Linus Torvalds 已提交
192 193 194 195 196 197 198 199 200 201 202
		result = -EFAULT;
		goto end;
	}

	/*
	 * control_register
	 */

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

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

	/*
	 * status_register
	 */

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

	if ((obj.type != ACPI_TYPE_BUFFER)
L
Len Brown 已提交
219 220
	    || (obj.buffer.length < sizeof(struct acpi_pct_register))
	    || (obj.buffer.pointer == NULL)) {
221
		printk(KERN_ERR PREFIX "Invalid _PCT data (status_register)\n");
L
Linus Torvalds 已提交
222 223 224 225
		result = -EFAULT;
		goto end;
	}

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

L
Len Brown 已提交
229
      end:
230
	kfree(buffer.pointer);
L
Linus Torvalds 已提交
231

232
	return result;
L
Linus Torvalds 已提交
233 234
}

L
Len Brown 已提交
235
static int acpi_processor_get_performance_states(struct acpi_processor *pr)
L
Linus Torvalds 已提交
236
{
L
Len Brown 已提交
237 238 239 240 241 242 243
	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 已提交
244 245 246


	status = acpi_evaluate_object(pr->handle, "_PSS", NULL, &buffer);
L
Len Brown 已提交
247
	if (ACPI_FAILURE(status)) {
248
		ACPI_EXCEPTION((AE_INFO, status, "Evaluating _PSS"));
249
		return -ENODEV;
L
Linus Torvalds 已提交
250 251
	}

252
	pss = buffer.pointer;
L
Linus Torvalds 已提交
253
	if (!pss || (pss->type != ACPI_TYPE_PACKAGE)) {
254
		printk(KERN_ERR PREFIX "Invalid _PSS data\n");
L
Linus Torvalds 已提交
255 256 257 258 259
		result = -EFAULT;
		goto end;
	}

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

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

		ACPI_DEBUG_PRINT((ACPI_DB_INFO,
L
Len Brown 已提交
290 291 292 293 294 295 296
				  "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 已提交
297 298

		if (!px->core_frequency) {
299 300
			printk(KERN_ERR PREFIX
				    "Invalid _PSS data: freq is zero\n");
L
Linus Torvalds 已提交
301 302 303 304 305 306
			result = -EFAULT;
			kfree(pr->performance->states);
			goto end;
		}
	}

L
Len Brown 已提交
307
      end:
308
	kfree(buffer.pointer);
L
Linus Torvalds 已提交
309

310
	return result;
L
Linus Torvalds 已提交
311 312
}

L
Len Brown 已提交
313
static int acpi_processor_get_performance_info(struct acpi_processor *pr)
L
Linus Torvalds 已提交
314
{
L
Len Brown 已提交
315 316 317
	int result = 0;
	acpi_status status = AE_OK;
	acpi_handle handle = NULL;
L
Linus Torvalds 已提交
318 319 320


	if (!pr || !pr->performance || !pr->handle)
321
		return -EINVAL;
L
Linus Torvalds 已提交
322 323 324 325

	status = acpi_get_handle(pr->handle, "_PCT", &handle);
	if (ACPI_FAILURE(status)) {
		ACPI_DEBUG_PRINT((ACPI_DB_INFO,
L
Len Brown 已提交
326
				  "ACPI-based processor performance control unavailable\n"));
327
		return -ENODEV;
L
Linus Torvalds 已提交
328 329 330 331
	}

	result = acpi_processor_get_performance_control(pr);
	if (result)
332
		return result;
L
Linus Torvalds 已提交
333 334 335

	result = acpi_processor_get_performance_states(pr);
	if (result)
336
		return result;
L
Linus Torvalds 已提交
337

338
	return 0;
L
Linus Torvalds 已提交
339 340
}

L
Len Brown 已提交
341 342 343 344
int acpi_processor_notify_smm(struct module *calling_module)
{
	acpi_status status;
	static int is_done = 0;
L
Linus Torvalds 已提交
345 346 347


	if (!(acpi_processor_ppc_status & PPC_REGISTERED))
348
		return -EBUSY;
L
Linus Torvalds 已提交
349 350

	if (!try_module_get(calling_module))
351
		return -EINVAL;
L
Linus Torvalds 已提交
352 353 354 355 356 357 358 359

	/* 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);
360
		return 0;
L
Len Brown 已提交
361
	} else if (is_done < 0) {
L
Linus Torvalds 已提交
362
		module_put(calling_module);
363
		return is_done;
L
Linus Torvalds 已提交
364 365 366 367
	}

	is_done = -EIO;

368
	/* Can't write pstate_control to smi_command if either value is zero */
369
	if ((!acpi_gbl_FADT.smi_command) || (!acpi_gbl_FADT.pstate_control)) {
370
		ACPI_DEBUG_PRINT((ACPI_DB_INFO, "No SMI port or pstate_control\n"));
L
Linus Torvalds 已提交
371
		module_put(calling_module);
372
		return 0;
L
Linus Torvalds 已提交
373 374
	}

L
Len Brown 已提交
375
	ACPI_DEBUG_PRINT((ACPI_DB_INFO,
376
			  "Writing pstate_control [0x%x] to smi_command [0x%x]\n",
377
			  acpi_gbl_FADT.pstate_control, acpi_gbl_FADT.smi_command));
L
Linus Torvalds 已提交
378

379 380
	status = acpi_os_write_port(acpi_gbl_FADT.smi_command,
				    (u32) acpi_gbl_FADT.pstate_control, 8);
L
Len Brown 已提交
381
	if (ACPI_FAILURE(status)) {
382
		ACPI_EXCEPTION((AE_INFO, status,
383
				"Failed to write pstate_control [0x%x] to "
384 385
				"smi_command [0x%x]", acpi_gbl_FADT.pstate_control,
				acpi_gbl_FADT.smi_command));
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)
{
415
	struct acpi_processor *pr = seq->private;
L
Len Brown 已提交
416
	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
}

L
Len Brown 已提交
450
static void acpi_cpufreq_add_file(struct acpi_processor *pr)
L
Linus Torvalds 已提交
451
{
L
Len Brown 已提交
452 453
	struct proc_dir_entry *entry = NULL;
	struct acpi_device *device = NULL;
L
Linus Torvalds 已提交
454 455 456


	if (acpi_bus_get_device(pr->handle, &device))
457
		return;
L
Linus Torvalds 已提交
458 459 460

	/* add file 'performance' [R/W] */
	entry = create_proc_entry(ACPI_PROCESSOR_FILE_PERFORMANCE,
461
				  S_IFREG | S_IRUGO,
L
Len Brown 已提交
462
				  acpi_device_dir(device));
463
	if (entry){
L
Linus Torvalds 已提交
464 465 466 467
		entry->proc_fops = &acpi_processor_perf_fops;
		entry->data = acpi_driver_data(device);
		entry->owner = THIS_MODULE;
	}
468
	return;
L
Linus Torvalds 已提交
469 470
}

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


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

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

483
	return;
L
Linus Torvalds 已提交
484 485 486
}

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

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

512
	psd = buffer.pointer;
513 514 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
	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:
551
	kfree(buffer.pointer);
552
	return result;
553 554 555
}

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

567
	mutex_lock(&performance_mutex);
568 569 570 571

	retval = 0;

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

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

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

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

648
		for_each_possible_cpu(j) {
649 650 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
			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++;
		}

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

711
	mutex_unlock(&performance_mutex);
712
	return retval;
713 714 715 716
}
EXPORT_SYMBOL(acpi_processor_preregister_performance);


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


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

727
	mutex_lock(&performance_mutex);
L
Linus Torvalds 已提交
728 729 730

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

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

740 741
	WARN_ON(!performance);

L
Linus Torvalds 已提交
742 743 744 745
	pr->performance = performance;

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

	acpi_cpufreq_add_file(pr);

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

L
Len Brown 已提交
756
EXPORT_SYMBOL(acpi_processor_register_performance);
L
Linus Torvalds 已提交
757 758

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


765
	mutex_lock(&performance_mutex);
L
Linus Torvalds 已提交
766 767 768

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

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

	acpi_cpufreq_remove_file(pr);

779
	mutex_unlock(&performance_mutex);
L
Linus Torvalds 已提交
780

781
	return;
L
Linus Torvalds 已提交
782
}
L
Len Brown 已提交
783

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