processor_perflib.c 18.5 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 63 64 65 66 67 68

/*
 * _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 已提交
69
				       unsigned long event, void *data)
L
Linus Torvalds 已提交
70 71 72 73 74
{
	struct cpufreq_policy *policy = data;
	struct acpi_processor *pr;
	unsigned int ppc = 0;

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

	if (event != CPUFREQ_INCOMPATIBLE)
		goto out;

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

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

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

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

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

	return 0;
}

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

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


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

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

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

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

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

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

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

	acpi_processor_ppc_status &= ~PPC_REGISTERED;
}

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


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

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

	/*
	 * control_register
	 */

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

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

	/*
	 * status_register
	 */

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

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

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

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

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

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


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

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

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

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

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

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

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

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

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


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

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

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

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

324
	return 0;
L
Linus Torvalds 已提交
325 326
}

L
Len Brown 已提交
327 328 329 330
int acpi_processor_notify_smm(struct module *calling_module)
{
	acpi_status status;
	static int is_done = 0;
L
Linus Torvalds 已提交
331 332 333


	if (!(acpi_processor_ppc_status & PPC_REGISTERED))
334
		return -EBUSY;
L
Linus Torvalds 已提交
335 336

	if (!try_module_get(calling_module))
337
		return -EINVAL;
L
Linus Torvalds 已提交
338 339 340 341 342 343 344 345

	/* 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);
346
		return 0;
L
Len Brown 已提交
347
	} else if (is_done < 0) {
L
Linus Torvalds 已提交
348
		module_put(calling_module);
349
		return is_done;
L
Linus Torvalds 已提交
350 351 352 353
	}

	is_done = -EIO;

354
	/* Can't write pstate_control to smi_command if either value is zero */
355
	if ((!acpi_gbl_FADT.smi_command) || (!acpi_gbl_FADT.pstate_control)) {
356
		ACPI_DEBUG_PRINT((ACPI_DB_INFO, "No SMI port or pstate_control\n"));
L
Linus Torvalds 已提交
357
		module_put(calling_module);
358
		return 0;
L
Linus Torvalds 已提交
359 360
	}

L
Len Brown 已提交
361
	ACPI_DEBUG_PRINT((ACPI_DB_INFO,
362
			  "Writing pstate_control [0x%x] to smi_command [0x%x]\n",
363
			  acpi_gbl_FADT.pstate_control, acpi_gbl_FADT.smi_command));
L
Linus Torvalds 已提交
364

365 366
	status = acpi_os_write_port(acpi_gbl_FADT.smi_command,
				    (u32) acpi_gbl_FADT.pstate_control, 8);
L
Len Brown 已提交
367
	if (ACPI_FAILURE(status)) {
368
		ACPI_EXCEPTION((AE_INFO, status,
369
				"Failed to write pstate_control [0x%x] to "
370 371
				"smi_command [0x%x]", acpi_gbl_FADT.pstate_control,
				acpi_gbl_FADT.smi_command));
L
Linus Torvalds 已提交
372
		module_put(calling_module);
373
		return status;
L
Linus Torvalds 已提交
374 375 376 377 378 379 380 381 382
	}

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

383
	return 0;
L
Linus Torvalds 已提交
384 385
}

L
Len Brown 已提交
386
EXPORT_SYMBOL(acpi_processor_notify_smm);
L
Linus Torvalds 已提交
387 388 389 390 391 392

#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 已提交
393 394 395 396
	.open = acpi_processor_perf_open_fs,
	.read = seq_read,
	.llseek = seq_lseek,
	.release = single_release,
L
Linus Torvalds 已提交
397 398 399 400
};

static int acpi_processor_perf_seq_show(struct seq_file *seq, void *offset)
{
401
	struct acpi_processor *pr = seq->private;
L
Len Brown 已提交
402
	int i;
L
Linus Torvalds 已提交
403 404 405 406 407 408 409 410 411 412 413


	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 已提交
414 415
		   "active state:            P%d\n",
		   pr->performance->state_count, pr->performance->state);
L
Linus Torvalds 已提交
416 417 418

	seq_puts(seq, "states:\n");
	for (i = 0; i < pr->performance->state_count; i++)
L
Len Brown 已提交
419 420 421 422 423 424 425 426
		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:
427
	return 0;
L
Linus Torvalds 已提交
428 429 430 431 432
}

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 已提交
433
			   PDE(inode)->data);
L
Linus Torvalds 已提交
434 435
}

L
Len Brown 已提交
436
static void acpi_cpufreq_add_file(struct acpi_processor *pr)
L
Linus Torvalds 已提交
437
{
L
Len Brown 已提交
438 439
	struct proc_dir_entry *entry = NULL;
	struct acpi_device *device = NULL;
L
Linus Torvalds 已提交
440 441 442


	if (acpi_bus_get_device(pr->handle, &device))
443
		return;
L
Linus Torvalds 已提交
444 445 446

	/* add file 'performance' [R/W] */
	entry = create_proc_entry(ACPI_PROCESSOR_FILE_PERFORMANCE,
447
				  S_IFREG | S_IRUGO,
L
Len Brown 已提交
448
				  acpi_device_dir(device));
449
	if (entry){
L
Linus Torvalds 已提交
450 451 452 453
		entry->proc_fops = &acpi_processor_perf_fops;
		entry->data = acpi_driver_data(device);
		entry->owner = THIS_MODULE;
	}
454
	return;
L
Linus Torvalds 已提交
455 456
}

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


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

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

469
	return;
L
Linus Torvalds 已提交
470 471 472
}

#else
L
Len Brown 已提交
473 474 475 476 477 478 479 480 481
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 已提交
482

483 484 485 486 487 488 489 490 491 492 493 494
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)) {
495
		return -ENODEV;
496 497
	}

498
	psd = buffer.pointer;
499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536
	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:
537
	kfree(buffer.pointer);
538
	return result;
539 540 541 542 543 544 545 546 547 548 549 550 551 552
}

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;

553
	mutex_lock(&performance_mutex);
554 555 556 557

	retval = 0;

	/* Call _PSD for all CPUs */
558
	for_each_possible_cpu(i) {
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
		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.
	 */
589
	for_each_possible_cpu(i) {
590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609
		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);
610
	for_each_possible_cpu(i) {
611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626
		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;
627
		if (pdomain->coord_type == DOMAIN_COORD_TYPE_SW_ALL)
628
			pr->performance->shared_type = CPUFREQ_SHARED_TYPE_ALL;
629 630 631
		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)
632 633
			pr->performance->shared_type = CPUFREQ_SHARED_TYPE_ANY;

634
		for_each_possible_cpu(j) {
635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662
			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++;
		}

663
		for_each_possible_cpu(j) {
664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682
			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:
683
	for_each_possible_cpu(i) {
684 685 686 687 688 689 690 691 692 693 694 695 696
		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 */
	}

697
	mutex_unlock(&performance_mutex);
698
	return retval;
699 700 701 702
}
EXPORT_SYMBOL(acpi_processor_preregister_performance);


L
Linus Torvalds 已提交
703
int
L
Len Brown 已提交
704 705
acpi_processor_register_performance(struct acpi_processor_performance
				    *performance, unsigned int cpu)
L
Linus Torvalds 已提交
706 707 708 709 710
{
	struct acpi_processor *pr;


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

713
	mutex_lock(&performance_mutex);
L
Linus Torvalds 已提交
714 715 716

	pr = processors[cpu];
	if (!pr) {
717
		mutex_unlock(&performance_mutex);
718
		return -ENODEV;
L
Linus Torvalds 已提交
719 720 721
	}

	if (pr->performance) {
722
		mutex_unlock(&performance_mutex);
723
		return -EBUSY;
L
Linus Torvalds 已提交
724 725
	}

726 727
	WARN_ON(!performance);

L
Linus Torvalds 已提交
728 729 730 731
	pr->performance = performance;

	if (acpi_processor_get_performance_info(pr)) {
		pr->performance = NULL;
732
		mutex_unlock(&performance_mutex);
733
		return -EIO;
L
Linus Torvalds 已提交
734 735 736 737
	}

	acpi_cpufreq_add_file(pr);

738
	mutex_unlock(&performance_mutex);
739
	return 0;
L
Linus Torvalds 已提交
740 741
}

L
Len Brown 已提交
742
EXPORT_SYMBOL(acpi_processor_register_performance);
L
Linus Torvalds 已提交
743 744

void
L
Len Brown 已提交
745 746
acpi_processor_unregister_performance(struct acpi_processor_performance
				      *performance, unsigned int cpu)
L
Linus Torvalds 已提交
747 748 749 750
{
	struct acpi_processor *pr;


751
	mutex_lock(&performance_mutex);
L
Linus Torvalds 已提交
752 753 754

	pr = processors[cpu];
	if (!pr) {
755
		mutex_unlock(&performance_mutex);
756
		return;
L
Linus Torvalds 已提交
757 758
	}

759 760
	if (pr->performance)
		kfree(pr->performance->states);
L
Linus Torvalds 已提交
761 762 763 764
	pr->performance = NULL;

	acpi_cpufreq_remove_file(pr);

765
	mutex_unlock(&performance_mutex);
L
Linus Torvalds 已提交
766

767
	return;
L
Linus Torvalds 已提交
768
}
L
Len Brown 已提交
769

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