blob: 0133af49cf06851ebb60d756404f48938b299c6f [file] [log] [blame]
Linus Torvalds1da177e2005-04-16 15:20:36 -07001/*
2 * processor_perflib.c - ACPI Processor P-States Library ($Revision: 71 $)
3 *
4 * Copyright (C) 2001, 2002 Andy Grover <andrew.grover@intel.com>
5 * Copyright (C) 2001, 2002 Paul Diefenbaugh <paul.s.diefenbaugh@intel.com>
6 * Copyright (C) 2004 Dominik Brodowski <linux@brodo.de>
7 * Copyright (C) 2004 Anil S Keshavamurthy <anil.s.keshavamurthy@intel.com>
8 * - Added processor hotplug support
9 *
10 *
11 * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
12 *
13 * This program is free software; you can redistribute it and/or modify
14 * it under the terms of the GNU General Public License as published by
15 * the Free Software Foundation; either version 2 of the License, or (at
16 * your option) any later version.
17 *
18 * This program is distributed in the hope that it will be useful, but
19 * WITHOUT ANY WARRANTY; without even the implied warranty of
20 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
21 * General Public License for more details.
22 *
23 * You should have received a copy of the GNU General Public License along
24 * with this program; if not, write to the Free Software Foundation, Inc.,
25 * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
26 *
27 */
28
Linus Torvalds1da177e2005-04-16 15:20:36 -070029#include <linux/kernel.h>
30#include <linux/module.h>
31#include <linux/init.h>
32#include <linux/cpufreq.h>
33
34#ifdef CONFIG_X86_ACPI_CPUFREQ_PROC_INTF
35#include <linux/proc_fs.h>
36#include <linux/seq_file.h>
Arjan van de Ven65c19bb2006-04-27 05:25:00 -040037#include <linux/mutex.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070038
39#include <asm/uaccess.h>
40#endif
41
42#include <acpi/acpi_bus.h>
43#include <acpi/processor.h>
44
Linus Torvalds1da177e2005-04-16 15:20:36 -070045#define ACPI_PROCESSOR_COMPONENT 0x01000000
46#define ACPI_PROCESSOR_CLASS "processor"
Linus Torvalds1da177e2005-04-16 15:20:36 -070047#define ACPI_PROCESSOR_FILE_PERFORMANCE "performance"
48#define _COMPONENT ACPI_PROCESSOR_COMPONENT
Len Brownf52fd662007-02-12 22:42:12 -050049ACPI_MODULE_NAME("processor_perflib");
Linus Torvalds1da177e2005-04-16 15:20:36 -070050
Arjan van de Ven65c19bb2006-04-27 05:25:00 -040051static DEFINE_MUTEX(performance_mutex);
Linus Torvalds1da177e2005-04-16 15:20:36 -070052
Thomas Renninger919158d2007-10-31 15:41:42 +010053/* Use cpufreq debug layer for _PPC changes. */
54#define cpufreq_printk(msg...) cpufreq_debug_printk(CPUFREQ_DEBUG_CORE, \
55 "cpufreq-core", msg)
56
Linus Torvalds1da177e2005-04-16 15:20:36 -070057/*
58 * _PPC support is implemented as a CPUfreq policy notifier:
59 * This means each time a CPUfreq driver registered also with
60 * the ACPI core is asked to change the speed policy, the maximum
61 * value is adjusted so that it is within the platform limit.
62 *
63 * Also, when a new platform limit value is detected, the CPUfreq
64 * policy is adjusted accordingly.
65 */
66
Thomas Renningera1531ac2008-07-29 22:32:58 -070067/* ignore_ppc:
68 * -1 -> cpufreq low level drivers not initialized -> _PSS, etc. not called yet
69 * ignore _PPC
70 * 0 -> cpufreq low level drivers initialized -> consider _PPC values
71 * 1 -> ignore _PPC totally -> forced by user through boot param
72 */
73static unsigned int ignore_ppc = -1;
Thomas Renninger623b78c2007-05-18 21:59:28 -050074module_param(ignore_ppc, uint, 0644);
75MODULE_PARM_DESC(ignore_ppc, "If the frequency of your machine gets wrongly" \
76 "limited by BIOS, this should help");
77
Linus Torvalds1da177e2005-04-16 15:20:36 -070078#define PPC_REGISTERED 1
79#define PPC_IN_USE 2
80
Thomas Renningera1531ac2008-07-29 22:32:58 -070081static int acpi_processor_ppc_status;
Linus Torvalds1da177e2005-04-16 15:20:36 -070082
83static int acpi_processor_ppc_notifier(struct notifier_block *nb,
Len Brown4be44fc2005-08-05 00:44:28 -040084 unsigned long event, void *data)
Linus Torvalds1da177e2005-04-16 15:20:36 -070085{
86 struct cpufreq_policy *policy = data;
87 struct acpi_processor *pr;
88 unsigned int ppc = 0;
89
Thomas Renningera1531ac2008-07-29 22:32:58 -070090 if (event == CPUFREQ_START && ignore_ppc <= 0) {
91 ignore_ppc = 0;
92 return 0;
93 }
94
Thomas Renninger623b78c2007-05-18 21:59:28 -050095 if (ignore_ppc)
96 return 0;
97
Linus Torvalds1da177e2005-04-16 15:20:36 -070098 if (event != CPUFREQ_INCOMPATIBLE)
Thomas Renninger9b67c5d2008-07-29 22:32:59 -070099 return 0;
100
101 mutex_lock(&performance_mutex);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700102
Mike Travis706546d2008-06-09 16:22:23 -0700103 pr = per_cpu(processors, policy->cpu);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700104 if (!pr || !pr->performance)
105 goto out;
106
Len Brown4be44fc2005-08-05 00:44:28 -0400107 ppc = (unsigned int)pr->performance_platform_limit;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700108
Dave Jones0916bd32006-11-22 20:42:01 -0500109 if (ppc >= pr->performance->state_count)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700110 goto out;
111
112 cpufreq_verify_within_limits(policy, 0,
Len Brown4be44fc2005-08-05 00:44:28 -0400113 pr->performance->states[ppc].
114 core_frequency * 1000);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700115
Len Brown4be44fc2005-08-05 00:44:28 -0400116 out:
Arjan van de Ven65c19bb2006-04-27 05:25:00 -0400117 mutex_unlock(&performance_mutex);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700118
119 return 0;
120}
121
Linus Torvalds1da177e2005-04-16 15:20:36 -0700122static struct notifier_block acpi_ppc_notifier_block = {
123 .notifier_call = acpi_processor_ppc_notifier,
124};
125
Len Brown4be44fc2005-08-05 00:44:28 -0400126static int acpi_processor_get_platform_limit(struct acpi_processor *pr)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700127{
Len Brown4be44fc2005-08-05 00:44:28 -0400128 acpi_status status = 0;
129 unsigned long ppc = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700130
Linus Torvalds1da177e2005-04-16 15:20:36 -0700131
132 if (!pr)
Patrick Mocheld550d982006-06-27 00:41:40 -0400133 return -EINVAL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700134
135 /*
136 * _PPC indicates the maximum state currently supported by the platform
137 * (e.g. 0 = states 0..n; 1 = states 1..n; etc.
138 */
139 status = acpi_evaluate_integer(pr->handle, "_PPC", NULL, &ppc);
140
141 if (status != AE_NOT_FOUND)
142 acpi_processor_ppc_status |= PPC_IN_USE;
143
Len Brown4be44fc2005-08-05 00:44:28 -0400144 if (ACPI_FAILURE(status) && status != AE_NOT_FOUND) {
Thomas Renningera6fc6722006-06-26 23:58:43 -0400145 ACPI_EXCEPTION((AE_INFO, status, "Evaluating _PPC"));
Patrick Mocheld550d982006-06-27 00:41:40 -0400146 return -ENODEV;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700147 }
148
Thomas Renninger919158d2007-10-31 15:41:42 +0100149 cpufreq_printk("CPU %d: _PPC is %d - frequency %s limited\n", pr->id,
150 (int)ppc, ppc ? "" : "not");
151
Len Brown4be44fc2005-08-05 00:44:28 -0400152 pr->performance_platform_limit = (int)ppc;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700153
Patrick Mocheld550d982006-06-27 00:41:40 -0400154 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700155}
156
Len Brown4be44fc2005-08-05 00:44:28 -0400157int acpi_processor_ppc_has_changed(struct acpi_processor *pr)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700158{
Thomas Renninger623b78c2007-05-18 21:59:28 -0500159 int ret;
160
161 if (ignore_ppc)
162 return 0;
163
164 ret = acpi_processor_get_platform_limit(pr);
165
Linus Torvalds1da177e2005-04-16 15:20:36 -0700166 if (ret < 0)
167 return (ret);
168 else
169 return cpufreq_update_policy(pr->id);
170}
171
Len Brown4be44fc2005-08-05 00:44:28 -0400172void acpi_processor_ppc_init(void)
173{
174 if (!cpufreq_register_notifier
175 (&acpi_ppc_notifier_block, CPUFREQ_POLICY_NOTIFIER))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700176 acpi_processor_ppc_status |= PPC_REGISTERED;
177 else
Len Brown4be44fc2005-08-05 00:44:28 -0400178 printk(KERN_DEBUG
179 "Warning: Processor Platform Limit not supported.\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700180}
181
Len Brown4be44fc2005-08-05 00:44:28 -0400182void acpi_processor_ppc_exit(void)
183{
Linus Torvalds1da177e2005-04-16 15:20:36 -0700184 if (acpi_processor_ppc_status & PPC_REGISTERED)
Len Brown4be44fc2005-08-05 00:44:28 -0400185 cpufreq_unregister_notifier(&acpi_ppc_notifier_block,
186 CPUFREQ_POLICY_NOTIFIER);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700187
188 acpi_processor_ppc_status &= ~PPC_REGISTERED;
189}
190
Len Brown4be44fc2005-08-05 00:44:28 -0400191static int acpi_processor_get_performance_control(struct acpi_processor *pr)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700192{
Len Brown4be44fc2005-08-05 00:44:28 -0400193 int result = 0;
194 acpi_status status = 0;
195 struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL };
196 union acpi_object *pct = NULL;
197 union acpi_object obj = { 0 };
Linus Torvalds1da177e2005-04-16 15:20:36 -0700198
Linus Torvalds1da177e2005-04-16 15:20:36 -0700199
200 status = acpi_evaluate_object(pr->handle, "_PCT", NULL, &buffer);
Len Brown4be44fc2005-08-05 00:44:28 -0400201 if (ACPI_FAILURE(status)) {
Thomas Renningera6fc6722006-06-26 23:58:43 -0400202 ACPI_EXCEPTION((AE_INFO, status, "Evaluating _PCT"));
Patrick Mocheld550d982006-06-27 00:41:40 -0400203 return -ENODEV;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700204 }
205
Len Brown4be44fc2005-08-05 00:44:28 -0400206 pct = (union acpi_object *)buffer.pointer;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700207 if (!pct || (pct->type != ACPI_TYPE_PACKAGE)
Len Brown4be44fc2005-08-05 00:44:28 -0400208 || (pct->package.count != 2)) {
Len Brown64684632006-06-26 23:41:38 -0400209 printk(KERN_ERR PREFIX "Invalid _PCT data\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700210 result = -EFAULT;
211 goto end;
212 }
213
214 /*
215 * control_register
216 */
217
218 obj = pct->package.elements[0];
219
220 if ((obj.type != ACPI_TYPE_BUFFER)
Len Brown4be44fc2005-08-05 00:44:28 -0400221 || (obj.buffer.length < sizeof(struct acpi_pct_register))
222 || (obj.buffer.pointer == NULL)) {
Len Brown64684632006-06-26 23:41:38 -0400223 printk(KERN_ERR PREFIX "Invalid _PCT data (control_register)\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700224 result = -EFAULT;
225 goto end;
226 }
Len Brown4be44fc2005-08-05 00:44:28 -0400227 memcpy(&pr->performance->control_register, obj.buffer.pointer,
228 sizeof(struct acpi_pct_register));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700229
230 /*
231 * status_register
232 */
233
234 obj = pct->package.elements[1];
235
236 if ((obj.type != ACPI_TYPE_BUFFER)
Len Brown4be44fc2005-08-05 00:44:28 -0400237 || (obj.buffer.length < sizeof(struct acpi_pct_register))
238 || (obj.buffer.pointer == NULL)) {
Len Brown64684632006-06-26 23:41:38 -0400239 printk(KERN_ERR PREFIX "Invalid _PCT data (status_register)\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700240 result = -EFAULT;
241 goto end;
242 }
243
Len Brown4be44fc2005-08-05 00:44:28 -0400244 memcpy(&pr->performance->status_register, obj.buffer.pointer,
245 sizeof(struct acpi_pct_register));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700246
Len Brown4be44fc2005-08-05 00:44:28 -0400247 end:
Len Brown02438d82006-06-30 03:19:10 -0400248 kfree(buffer.pointer);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700249
Patrick Mocheld550d982006-06-27 00:41:40 -0400250 return result;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700251}
252
Len Brown4be44fc2005-08-05 00:44:28 -0400253static int acpi_processor_get_performance_states(struct acpi_processor *pr)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700254{
Len Brown4be44fc2005-08-05 00:44:28 -0400255 int result = 0;
256 acpi_status status = AE_OK;
257 struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL };
258 struct acpi_buffer format = { sizeof("NNNNNN"), "NNNNNN" };
259 struct acpi_buffer state = { 0, NULL };
260 union acpi_object *pss = NULL;
261 int i;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700262
Linus Torvalds1da177e2005-04-16 15:20:36 -0700263
264 status = acpi_evaluate_object(pr->handle, "_PSS", NULL, &buffer);
Len Brown4be44fc2005-08-05 00:44:28 -0400265 if (ACPI_FAILURE(status)) {
Thomas Renningera6fc6722006-06-26 23:58:43 -0400266 ACPI_EXCEPTION((AE_INFO, status, "Evaluating _PSS"));
Patrick Mocheld550d982006-06-27 00:41:40 -0400267 return -ENODEV;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700268 }
269
Jan Engelhardt50dd0962006-10-01 00:28:50 +0200270 pss = buffer.pointer;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700271 if (!pss || (pss->type != ACPI_TYPE_PACKAGE)) {
Len Brown64684632006-06-26 23:41:38 -0400272 printk(KERN_ERR PREFIX "Invalid _PSS data\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700273 result = -EFAULT;
274 goto end;
275 }
276
277 ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Found %d performance states\n",
Len Brown4be44fc2005-08-05 00:44:28 -0400278 pss->package.count));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700279
280 pr->performance->state_count = pss->package.count;
Len Brown4be44fc2005-08-05 00:44:28 -0400281 pr->performance->states =
282 kmalloc(sizeof(struct acpi_processor_px) * pss->package.count,
283 GFP_KERNEL);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700284 if (!pr->performance->states) {
285 result = -ENOMEM;
286 goto end;
287 }
288
289 for (i = 0; i < pr->performance->state_count; i++) {
290
291 struct acpi_processor_px *px = &(pr->performance->states[i]);
292
293 state.length = sizeof(struct acpi_processor_px);
294 state.pointer = px;
295
296 ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Extracting state %d\n", i));
297
298 status = acpi_extract_package(&(pss->package.elements[i]),
Len Brown4be44fc2005-08-05 00:44:28 -0400299 &format, &state);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700300 if (ACPI_FAILURE(status)) {
Thomas Renningera6fc6722006-06-26 23:58:43 -0400301 ACPI_EXCEPTION((AE_INFO, status, "Invalid _PSS data"));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700302 result = -EFAULT;
303 kfree(pr->performance->states);
304 goto end;
305 }
306
307 ACPI_DEBUG_PRINT((ACPI_DB_INFO,
Len Brown4be44fc2005-08-05 00:44:28 -0400308 "State [%d]: core_frequency[%d] power[%d] transition_latency[%d] bus_master_latency[%d] control[0x%x] status[0x%x]\n",
309 i,
310 (u32) px->core_frequency,
311 (u32) px->power,
312 (u32) px->transition_latency,
313 (u32) px->bus_master_latency,
314 (u32) px->control, (u32) px->status));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700315
316 if (!px->core_frequency) {
Len Brown64684632006-06-26 23:41:38 -0400317 printk(KERN_ERR PREFIX
318 "Invalid _PSS data: freq is zero\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700319 result = -EFAULT;
320 kfree(pr->performance->states);
321 goto end;
322 }
323 }
324
Len Brown4be44fc2005-08-05 00:44:28 -0400325 end:
Len Brown02438d82006-06-30 03:19:10 -0400326 kfree(buffer.pointer);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700327
Patrick Mocheld550d982006-06-27 00:41:40 -0400328 return result;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700329}
330
Len Brown4be44fc2005-08-05 00:44:28 -0400331static int acpi_processor_get_performance_info(struct acpi_processor *pr)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700332{
Len Brown4be44fc2005-08-05 00:44:28 -0400333 int result = 0;
334 acpi_status status = AE_OK;
335 acpi_handle handle = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700336
Linus Torvalds1da177e2005-04-16 15:20:36 -0700337
338 if (!pr || !pr->performance || !pr->handle)
Patrick Mocheld550d982006-06-27 00:41:40 -0400339 return -EINVAL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700340
Linus Torvalds1da177e2005-04-16 15:20:36 -0700341 status = acpi_get_handle(pr->handle, "_PCT", &handle);
342 if (ACPI_FAILURE(status)) {
343 ACPI_DEBUG_PRINT((ACPI_DB_INFO,
Len Brown4be44fc2005-08-05 00:44:28 -0400344 "ACPI-based processor performance control unavailable\n"));
Patrick Mocheld550d982006-06-27 00:41:40 -0400345 return -ENODEV;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700346 }
347
348 result = acpi_processor_get_performance_control(pr);
349 if (result)
Patrick Mocheld550d982006-06-27 00:41:40 -0400350 return result;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700351
352 result = acpi_processor_get_performance_states(pr);
353 if (result)
Patrick Mocheld550d982006-06-27 00:41:40 -0400354 return result;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700355
Patrick Mocheld550d982006-06-27 00:41:40 -0400356 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700357}
358
Len Brown4be44fc2005-08-05 00:44:28 -0400359int acpi_processor_notify_smm(struct module *calling_module)
360{
361 acpi_status status;
362 static int is_done = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700363
Linus Torvalds1da177e2005-04-16 15:20:36 -0700364
365 if (!(acpi_processor_ppc_status & PPC_REGISTERED))
Patrick Mocheld550d982006-06-27 00:41:40 -0400366 return -EBUSY;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700367
368 if (!try_module_get(calling_module))
Patrick Mocheld550d982006-06-27 00:41:40 -0400369 return -EINVAL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700370
371 /* is_done is set to negative if an error occured,
372 * and to postitive if _no_ error occured, but SMM
373 * was already notified. This avoids double notification
374 * which might lead to unexpected results...
375 */
376 if (is_done > 0) {
377 module_put(calling_module);
Patrick Mocheld550d982006-06-27 00:41:40 -0400378 return 0;
Len Brown4be44fc2005-08-05 00:44:28 -0400379 } else if (is_done < 0) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700380 module_put(calling_module);
Patrick Mocheld550d982006-06-27 00:41:40 -0400381 return is_done;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700382 }
383
384 is_done = -EIO;
385
Alexey Starikovskiyad71860a2007-02-02 19:48:19 +0300386 /* Can't write pstate_control to smi_command if either value is zero */
Alexey Starikovskiycee324b2007-02-02 19:48:22 +0300387 if ((!acpi_gbl_FADT.smi_command) || (!acpi_gbl_FADT.pstate_control)) {
Alexey Starikovskiyad71860a2007-02-02 19:48:19 +0300388 ACPI_DEBUG_PRINT((ACPI_DB_INFO, "No SMI port or pstate_control\n"));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700389 module_put(calling_module);
Patrick Mocheld550d982006-06-27 00:41:40 -0400390 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700391 }
392
Len Brown4be44fc2005-08-05 00:44:28 -0400393 ACPI_DEBUG_PRINT((ACPI_DB_INFO,
Alexey Starikovskiyad71860a2007-02-02 19:48:19 +0300394 "Writing pstate_control [0x%x] to smi_command [0x%x]\n",
Alexey Starikovskiycee324b2007-02-02 19:48:22 +0300395 acpi_gbl_FADT.pstate_control, acpi_gbl_FADT.smi_command));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700396
Alexey Starikovskiycee324b2007-02-02 19:48:22 +0300397 status = acpi_os_write_port(acpi_gbl_FADT.smi_command,
398 (u32) acpi_gbl_FADT.pstate_control, 8);
Len Brown4be44fc2005-08-05 00:44:28 -0400399 if (ACPI_FAILURE(status)) {
Thomas Renningera6fc6722006-06-26 23:58:43 -0400400 ACPI_EXCEPTION((AE_INFO, status,
Alexey Starikovskiyad71860a2007-02-02 19:48:19 +0300401 "Failed to write pstate_control [0x%x] to "
Alexey Starikovskiycee324b2007-02-02 19:48:22 +0300402 "smi_command [0x%x]", acpi_gbl_FADT.pstate_control,
403 acpi_gbl_FADT.smi_command));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700404 module_put(calling_module);
Patrick Mocheld550d982006-06-27 00:41:40 -0400405 return status;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700406 }
407
408 /* Success. If there's no _PPC, we need to fear nothing, so
409 * we can allow the cpufreq driver to be rmmod'ed. */
410 is_done = 1;
411
412 if (!(acpi_processor_ppc_status & PPC_IN_USE))
413 module_put(calling_module);
414
Patrick Mocheld550d982006-06-27 00:41:40 -0400415 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700416}
Linus Torvalds1da177e2005-04-16 15:20:36 -0700417
Len Brown4be44fc2005-08-05 00:44:28 -0400418EXPORT_SYMBOL(acpi_processor_notify_smm);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700419
420#ifdef CONFIG_X86_ACPI_CPUFREQ_PROC_INTF
421/* /proc/acpi/processor/../performance interface (DEPRECATED) */
422
423static int acpi_processor_perf_open_fs(struct inode *inode, struct file *file);
424static struct file_operations acpi_processor_perf_fops = {
Denis V. Lunevcf7acfa2008-04-29 01:02:27 -0700425 .owner = THIS_MODULE,
Len Brown4be44fc2005-08-05 00:44:28 -0400426 .open = acpi_processor_perf_open_fs,
427 .read = seq_read,
428 .llseek = seq_lseek,
429 .release = single_release,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700430};
431
432static int acpi_processor_perf_seq_show(struct seq_file *seq, void *offset)
433{
Jan Engelhardt50dd0962006-10-01 00:28:50 +0200434 struct acpi_processor *pr = seq->private;
Len Brown4be44fc2005-08-05 00:44:28 -0400435 int i;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700436
Linus Torvalds1da177e2005-04-16 15:20:36 -0700437
438 if (!pr)
439 goto end;
440
441 if (!pr->performance) {
442 seq_puts(seq, "<not supported>\n");
443 goto end;
444 }
445
446 seq_printf(seq, "state count: %d\n"
Len Brown4be44fc2005-08-05 00:44:28 -0400447 "active state: P%d\n",
448 pr->performance->state_count, pr->performance->state);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700449
450 seq_puts(seq, "states:\n");
451 for (i = 0; i < pr->performance->state_count; i++)
Len Brown4be44fc2005-08-05 00:44:28 -0400452 seq_printf(seq,
453 " %cP%d: %d MHz, %d mW, %d uS\n",
454 (i == pr->performance->state ? '*' : ' '), i,
455 (u32) pr->performance->states[i].core_frequency,
456 (u32) pr->performance->states[i].power,
457 (u32) pr->performance->states[i].transition_latency);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700458
Len Brown4be44fc2005-08-05 00:44:28 -0400459 end:
Patrick Mocheld550d982006-06-27 00:41:40 -0400460 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700461}
462
463static int acpi_processor_perf_open_fs(struct inode *inode, struct file *file)
464{
465 return single_open(file, acpi_processor_perf_seq_show,
Len Brown4be44fc2005-08-05 00:44:28 -0400466 PDE(inode)->data);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700467}
468
Len Brown4be44fc2005-08-05 00:44:28 -0400469static void acpi_cpufreq_add_file(struct acpi_processor *pr)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700470{
Len Brown4be44fc2005-08-05 00:44:28 -0400471 struct acpi_device *device = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700472
Linus Torvalds1da177e2005-04-16 15:20:36 -0700473
474 if (acpi_bus_get_device(pr->handle, &device))
Patrick Mocheld550d982006-06-27 00:41:40 -0400475 return;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700476
477 /* add file 'performance' [R/W] */
Denis V. Lunevcf7acfa2008-04-29 01:02:27 -0700478 proc_create_data(ACPI_PROCESSOR_FILE_PERFORMANCE, S_IFREG | S_IRUGO,
479 acpi_device_dir(device),
480 &acpi_processor_perf_fops, acpi_driver_data(device));
Patrick Mocheld550d982006-06-27 00:41:40 -0400481 return;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700482}
483
Len Brown4be44fc2005-08-05 00:44:28 -0400484static void acpi_cpufreq_remove_file(struct acpi_processor *pr)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700485{
Len Brown4be44fc2005-08-05 00:44:28 -0400486 struct acpi_device *device = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700487
Linus Torvalds1da177e2005-04-16 15:20:36 -0700488
489 if (acpi_bus_get_device(pr->handle, &device))
Patrick Mocheld550d982006-06-27 00:41:40 -0400490 return;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700491
492 /* remove file 'performance' */
493 remove_proc_entry(ACPI_PROCESSOR_FILE_PERFORMANCE,
Len Brown4be44fc2005-08-05 00:44:28 -0400494 acpi_device_dir(device));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700495
Patrick Mocheld550d982006-06-27 00:41:40 -0400496 return;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700497}
498
499#else
Len Brown4be44fc2005-08-05 00:44:28 -0400500static void acpi_cpufreq_add_file(struct acpi_processor *pr)
501{
502 return;
503}
504static void acpi_cpufreq_remove_file(struct acpi_processor *pr)
505{
506 return;
507}
508#endif /* CONFIG_X86_ACPI_CPUFREQ_PROC_INTF */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700509
Venkatesh Pallipadi3b2d9942005-12-14 15:05:00 -0500510static int acpi_processor_get_psd(struct acpi_processor *pr)
511{
512 int result = 0;
513 acpi_status status = AE_OK;
514 struct acpi_buffer buffer = {ACPI_ALLOCATE_BUFFER, NULL};
515 struct acpi_buffer format = {sizeof("NNNNN"), "NNNNN"};
516 struct acpi_buffer state = {0, NULL};
517 union acpi_object *psd = NULL;
518 struct acpi_psd_package *pdomain;
519
Venkatesh Pallipadi3b2d9942005-12-14 15:05:00 -0500520 status = acpi_evaluate_object(pr->handle, "_PSD", NULL, &buffer);
521 if (ACPI_FAILURE(status)) {
Len Brown9011bff42006-05-11 00:28:12 -0400522 return -ENODEV;
Venkatesh Pallipadi3b2d9942005-12-14 15:05:00 -0500523 }
524
Jan Engelhardt50dd0962006-10-01 00:28:50 +0200525 psd = buffer.pointer;
Venkatesh Pallipadi3b2d9942005-12-14 15:05:00 -0500526 if (!psd || (psd->type != ACPI_TYPE_PACKAGE)) {
527 ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "Invalid _PSD data\n"));
528 result = -EFAULT;
529 goto end;
530 }
531
532 if (psd->package.count != 1) {
533 ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "Invalid _PSD data\n"));
534 result = -EFAULT;
535 goto end;
536 }
537
538 pdomain = &(pr->performance->domain_info);
539
540 state.length = sizeof(struct acpi_psd_package);
541 state.pointer = pdomain;
542
543 status = acpi_extract_package(&(psd->package.elements[0]),
544 &format, &state);
545 if (ACPI_FAILURE(status)) {
546 ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "Invalid _PSD data\n"));
547 result = -EFAULT;
548 goto end;
549 }
550
551 if (pdomain->num_entries != ACPI_PSD_REV0_ENTRIES) {
552 ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "Unknown _PSD:num_entries\n"));
553 result = -EFAULT;
554 goto end;
555 }
556
557 if (pdomain->revision != ACPI_PSD_REV0_REVISION) {
558 ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "Unknown _PSD:revision\n"));
559 result = -EFAULT;
560 goto end;
561 }
562
563end:
Len Brown02438d82006-06-30 03:19:10 -0400564 kfree(buffer.pointer);
Len Brown9011bff42006-05-11 00:28:12 -0400565 return result;
Venkatesh Pallipadi3b2d9942005-12-14 15:05:00 -0500566}
567
568int acpi_processor_preregister_performance(
Fenghua Yu50109292007-08-07 18:40:30 -0400569 struct acpi_processor_performance *performance)
Venkatesh Pallipadi3b2d9942005-12-14 15:05:00 -0500570{
571 int count, count_target;
572 int retval = 0;
573 unsigned int i, j;
574 cpumask_t covered_cpus;
575 struct acpi_processor *pr;
576 struct acpi_psd_package *pdomain;
577 struct acpi_processor *match_pr;
578 struct acpi_psd_package *match_pdomain;
579
Len Brown785fccc2006-06-15 22:19:31 -0400580 mutex_lock(&performance_mutex);
Venkatesh Pallipadi3b2d9942005-12-14 15:05:00 -0500581
582 retval = 0;
583
584 /* Call _PSD for all CPUs */
KAMEZAWA Hiroyuki193de0c2006-04-27 05:25:00 -0400585 for_each_possible_cpu(i) {
Mike Travis706546d2008-06-09 16:22:23 -0700586 pr = per_cpu(processors, i);
Venkatesh Pallipadi3b2d9942005-12-14 15:05:00 -0500587 if (!pr) {
588 /* Look only at processors in ACPI namespace */
589 continue;
590 }
591
592 if (pr->performance) {
593 retval = -EBUSY;
594 continue;
595 }
596
Fenghua Yu50109292007-08-07 18:40:30 -0400597 if (!performance || !percpu_ptr(performance, i)) {
Venkatesh Pallipadi3b2d9942005-12-14 15:05:00 -0500598 retval = -EINVAL;
599 continue;
600 }
601
Fenghua Yu50109292007-08-07 18:40:30 -0400602 pr->performance = percpu_ptr(performance, i);
Venkatesh Pallipadi3b2d9942005-12-14 15:05:00 -0500603 cpu_set(i, pr->performance->shared_cpu_map);
604 if (acpi_processor_get_psd(pr)) {
605 retval = -EINVAL;
606 continue;
607 }
608 }
609 if (retval)
610 goto err_ret;
611
612 /*
613 * Now that we have _PSD data from all CPUs, lets setup P-state
614 * domain info.
615 */
KAMEZAWA Hiroyuki193de0c2006-04-27 05:25:00 -0400616 for_each_possible_cpu(i) {
Mike Travis706546d2008-06-09 16:22:23 -0700617 pr = per_cpu(processors, i);
Venkatesh Pallipadi3b2d9942005-12-14 15:05:00 -0500618 if (!pr)
619 continue;
620
621 /* Basic validity check for domain info */
622 pdomain = &(pr->performance->domain_info);
623 if ((pdomain->revision != ACPI_PSD_REV0_REVISION) ||
624 (pdomain->num_entries != ACPI_PSD_REV0_ENTRIES)) {
625 retval = -EINVAL;
626 goto err_ret;
627 }
628 if (pdomain->coord_type != DOMAIN_COORD_TYPE_SW_ALL &&
629 pdomain->coord_type != DOMAIN_COORD_TYPE_SW_ANY &&
630 pdomain->coord_type != DOMAIN_COORD_TYPE_HW_ALL) {
631 retval = -EINVAL;
632 goto err_ret;
633 }
634 }
635
636 cpus_clear(covered_cpus);
KAMEZAWA Hiroyuki193de0c2006-04-27 05:25:00 -0400637 for_each_possible_cpu(i) {
Mike Travis706546d2008-06-09 16:22:23 -0700638 pr = per_cpu(processors, i);
Venkatesh Pallipadi3b2d9942005-12-14 15:05:00 -0500639 if (!pr)
640 continue;
641
642 if (cpu_isset(i, covered_cpus))
643 continue;
644
645 pdomain = &(pr->performance->domain_info);
646 cpu_set(i, pr->performance->shared_cpu_map);
647 cpu_set(i, covered_cpus);
648 if (pdomain->num_processors <= 1)
649 continue;
650
651 /* Validate the Domain info */
652 count_target = pdomain->num_processors;
653 count = 1;
Venkatesh Pallipadi46f18e32006-06-26 00:34:43 -0400654 if (pdomain->coord_type == DOMAIN_COORD_TYPE_SW_ALL)
Venkatesh Pallipadi3b2d9942005-12-14 15:05:00 -0500655 pr->performance->shared_type = CPUFREQ_SHARED_TYPE_ALL;
Venkatesh Pallipadi46f18e32006-06-26 00:34:43 -0400656 else if (pdomain->coord_type == DOMAIN_COORD_TYPE_HW_ALL)
657 pr->performance->shared_type = CPUFREQ_SHARED_TYPE_HW;
658 else if (pdomain->coord_type == DOMAIN_COORD_TYPE_SW_ANY)
Venkatesh Pallipadi3b2d9942005-12-14 15:05:00 -0500659 pr->performance->shared_type = CPUFREQ_SHARED_TYPE_ANY;
Venkatesh Pallipadi3b2d9942005-12-14 15:05:00 -0500660
KAMEZAWA Hiroyuki193de0c2006-04-27 05:25:00 -0400661 for_each_possible_cpu(j) {
Venkatesh Pallipadi3b2d9942005-12-14 15:05:00 -0500662 if (i == j)
663 continue;
664
Mike Travis706546d2008-06-09 16:22:23 -0700665 match_pr = per_cpu(processors, j);
Venkatesh Pallipadi3b2d9942005-12-14 15:05:00 -0500666 if (!match_pr)
667 continue;
668
669 match_pdomain = &(match_pr->performance->domain_info);
670 if (match_pdomain->domain != pdomain->domain)
671 continue;
672
673 /* Here i and j are in the same domain */
674
675 if (match_pdomain->num_processors != count_target) {
676 retval = -EINVAL;
677 goto err_ret;
678 }
679
680 if (pdomain->coord_type != match_pdomain->coord_type) {
681 retval = -EINVAL;
682 goto err_ret;
683 }
684
685 cpu_set(j, covered_cpus);
686 cpu_set(j, pr->performance->shared_cpu_map);
687 count++;
688 }
689
KAMEZAWA Hiroyuki193de0c2006-04-27 05:25:00 -0400690 for_each_possible_cpu(j) {
Venkatesh Pallipadi3b2d9942005-12-14 15:05:00 -0500691 if (i == j)
692 continue;
693
Mike Travis706546d2008-06-09 16:22:23 -0700694 match_pr = per_cpu(processors, j);
Venkatesh Pallipadi3b2d9942005-12-14 15:05:00 -0500695 if (!match_pr)
696 continue;
697
698 match_pdomain = &(match_pr->performance->domain_info);
699 if (match_pdomain->domain != pdomain->domain)
700 continue;
701
702 match_pr->performance->shared_type =
703 pr->performance->shared_type;
704 match_pr->performance->shared_cpu_map =
705 pr->performance->shared_cpu_map;
706 }
707 }
708
709err_ret:
KAMEZAWA Hiroyuki193de0c2006-04-27 05:25:00 -0400710 for_each_possible_cpu(i) {
Mike Travis706546d2008-06-09 16:22:23 -0700711 pr = per_cpu(processors, i);
Venkatesh Pallipadi3b2d9942005-12-14 15:05:00 -0500712 if (!pr || !pr->performance)
713 continue;
714
715 /* Assume no coordination on any error parsing domain info */
716 if (retval) {
717 cpus_clear(pr->performance->shared_cpu_map);
718 cpu_set(i, pr->performance->shared_cpu_map);
719 pr->performance->shared_type = CPUFREQ_SHARED_TYPE_ALL;
720 }
721 pr->performance = NULL; /* Will be set for real in register */
722 }
723
Len Brown785fccc2006-06-15 22:19:31 -0400724 mutex_unlock(&performance_mutex);
Len Brown9011bff42006-05-11 00:28:12 -0400725 return retval;
Venkatesh Pallipadi3b2d9942005-12-14 15:05:00 -0500726}
727EXPORT_SYMBOL(acpi_processor_preregister_performance);
728
729
Linus Torvalds1da177e2005-04-16 15:20:36 -0700730int
Len Brown4be44fc2005-08-05 00:44:28 -0400731acpi_processor_register_performance(struct acpi_processor_performance
732 *performance, unsigned int cpu)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700733{
734 struct acpi_processor *pr;
735
Linus Torvalds1da177e2005-04-16 15:20:36 -0700736
737 if (!(acpi_processor_ppc_status & PPC_REGISTERED))
Patrick Mocheld550d982006-06-27 00:41:40 -0400738 return -EINVAL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700739
Arjan van de Ven65c19bb2006-04-27 05:25:00 -0400740 mutex_lock(&performance_mutex);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700741
Mike Travis706546d2008-06-09 16:22:23 -0700742 pr = per_cpu(processors, cpu);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700743 if (!pr) {
Arjan van de Ven65c19bb2006-04-27 05:25:00 -0400744 mutex_unlock(&performance_mutex);
Patrick Mocheld550d982006-06-27 00:41:40 -0400745 return -ENODEV;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700746 }
747
748 if (pr->performance) {
Arjan van de Ven65c19bb2006-04-27 05:25:00 -0400749 mutex_unlock(&performance_mutex);
Patrick Mocheld550d982006-06-27 00:41:40 -0400750 return -EBUSY;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700751 }
752
Andrew Mortona913f502006-06-10 09:54:13 -0700753 WARN_ON(!performance);
754
Linus Torvalds1da177e2005-04-16 15:20:36 -0700755 pr->performance = performance;
756
757 if (acpi_processor_get_performance_info(pr)) {
758 pr->performance = NULL;
Arjan van de Ven65c19bb2006-04-27 05:25:00 -0400759 mutex_unlock(&performance_mutex);
Patrick Mocheld550d982006-06-27 00:41:40 -0400760 return -EIO;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700761 }
762
763 acpi_cpufreq_add_file(pr);
764
Arjan van de Ven65c19bb2006-04-27 05:25:00 -0400765 mutex_unlock(&performance_mutex);
Patrick Mocheld550d982006-06-27 00:41:40 -0400766 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700767}
Len Brown4be44fc2005-08-05 00:44:28 -0400768
Linus Torvalds1da177e2005-04-16 15:20:36 -0700769EXPORT_SYMBOL(acpi_processor_register_performance);
770
Linus Torvalds1da177e2005-04-16 15:20:36 -0700771void
Len Brown4be44fc2005-08-05 00:44:28 -0400772acpi_processor_unregister_performance(struct acpi_processor_performance
773 *performance, unsigned int cpu)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700774{
775 struct acpi_processor *pr;
776
Linus Torvalds1da177e2005-04-16 15:20:36 -0700777
Arjan van de Ven65c19bb2006-04-27 05:25:00 -0400778 mutex_lock(&performance_mutex);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700779
Mike Travis706546d2008-06-09 16:22:23 -0700780 pr = per_cpu(processors, cpu);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700781 if (!pr) {
Arjan van de Ven65c19bb2006-04-27 05:25:00 -0400782 mutex_unlock(&performance_mutex);
Patrick Mocheld550d982006-06-27 00:41:40 -0400783 return;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700784 }
785
Andrew Mortona913f502006-06-10 09:54:13 -0700786 if (pr->performance)
787 kfree(pr->performance->states);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700788 pr->performance = NULL;
789
790 acpi_cpufreq_remove_file(pr);
791
Arjan van de Ven65c19bb2006-04-27 05:25:00 -0400792 mutex_unlock(&performance_mutex);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700793
Patrick Mocheld550d982006-06-27 00:41:40 -0400794 return;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700795}
Len Brown4be44fc2005-08-05 00:44:28 -0400796
Linus Torvalds1da177e2005-04-16 15:20:36 -0700797EXPORT_SYMBOL(acpi_processor_unregister_performance);