Blame view
drivers/acpi/processor_driver.c
8.51 KB
1da177e4c
|
1 |
/* |
ac212b698
|
2 |
* processor_driver.c - ACPI Processor Driver |
1da177e4c
|
3 4 5 6 7 8 |
* * 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 |
ac212b698
|
9 10 |
* Copyright (C) 2013, Intel Corporation * Rafael J. Wysocki <rafael.j.wysocki@intel.com> |
1da177e4c
|
11 12 13 14 15 16 17 18 19 20 21 22 23 |
* * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ * * 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. * |
1da177e4c
|
24 |
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
1da177e4c
|
25 26 27 28 29 |
*/ #include <linux/kernel.h> #include <linux/module.h> #include <linux/init.h> |
1da177e4c
|
30 31 |
#include <linux/cpufreq.h> #include <linux/cpu.h> |
4f86d3a8e
|
32 |
#include <linux/cpuidle.h> |
5a0e3ad6a
|
33 |
#include <linux/slab.h> |
47db4547f
|
34 |
#include <linux/acpi.h> |
ac212b698
|
35 |
|
1da177e4c
|
36 |
#include <acpi/processor.h> |
ac212b698
|
37 |
#include "internal.h" |
1da177e4c
|
38 39 |
#define ACPI_PROCESSOR_NOTIFY_PERFORMANCE 0x80 #define ACPI_PROCESSOR_NOTIFY_POWER 0x81 |
01854e697
|
40 |
#define ACPI_PROCESSOR_NOTIFY_THROTTLING 0x82 |
1da177e4c
|
41 |
|
1da177e4c
|
42 |
#define _COMPONENT ACPI_PROCESSOR_COMPONENT |
0131aa3dd
|
43 |
ACPI_MODULE_NAME("processor_driver"); |
1da177e4c
|
44 |
|
f52fd66d2
|
45 |
MODULE_AUTHOR("Paul Diefenbaugh"); |
7cda93e00
|
46 |
MODULE_DESCRIPTION("ACPI Processor Driver"); |
1da177e4c
|
47 |
MODULE_LICENSE("GPL"); |
ac212b698
|
48 49 |
static int acpi_processor_start(struct device *dev); static int acpi_processor_stop(struct device *dev); |
1da177e4c
|
50 |
|
1ba90e3a8
|
51 |
static const struct acpi_device_id processor_device_ids[] = { |
ad93a765c
|
52 |
{ACPI_PROCESSOR_OBJECT_HID, 0}, |
9f324bda9
|
53 |
{ACPI_PROCESSOR_DEVICE_HID, 0}, |
1ba90e3a8
|
54 55 56 |
{"", 0}, }; MODULE_DEVICE_TABLE(acpi, processor_device_ids); |
ac212b698
|
57 |
static struct device_driver acpi_processor_driver = { |
c2b6705b7
|
58 |
.name = "processor", |
ac212b698
|
59 60 61 62 |
.bus = &cpu_subsys, .acpi_match_table = processor_device_ids, .probe = acpi_processor_start, .remove = acpi_processor_stop, |
1da177e4c
|
63 |
}; |
ac212b698
|
64 |
static void acpi_processor_notify(acpi_handle handle, u32 event, void *data) |
1da177e4c
|
65 |
{ |
ac212b698
|
66 |
struct acpi_device *device = data; |
b26e9286f
|
67 |
struct acpi_processor *pr; |
e790cc8bb
|
68 |
int saved; |
1da177e4c
|
69 |
|
ac212b698
|
70 71 72 73 |
if (device->handle != handle) return; pr = acpi_driver_data(device); |
1da177e4c
|
74 |
if (!pr) |
d550d98d3
|
75 |
return; |
1da177e4c
|
76 |
|
1da177e4c
|
77 78 |
switch (event) { case ACPI_PROCESSOR_NOTIFY_PERFORMANCE: |
e790cc8bb
|
79 |
saved = pr->performance_platform_limit; |
d81c45e1c
|
80 |
acpi_processor_ppc_has_changed(pr, 1); |
e790cc8bb
|
81 82 |
if (saved == pr->performance_platform_limit) break; |
962ce8ca0
|
83 |
acpi_bus_generate_netlink_event(device->pnp.device_class, |
0794469da
|
84 |
dev_name(&device->dev), event, |
962ce8ca0
|
85 |
pr->performance_platform_limit); |
1da177e4c
|
86 87 88 |
break; case ACPI_PROCESSOR_NOTIFY_POWER: acpi_processor_cst_has_changed(pr); |
962ce8ca0
|
89 |
acpi_bus_generate_netlink_event(device->pnp.device_class, |
0794469da
|
90 |
dev_name(&device->dev), event, 0); |
1da177e4c
|
91 |
break; |
01854e697
|
92 93 |
case ACPI_PROCESSOR_NOTIFY_THROTTLING: acpi_processor_tstate_has_changed(pr); |
962ce8ca0
|
94 |
acpi_bus_generate_netlink_event(device->pnp.device_class, |
0794469da
|
95 |
dev_name(&device->dev), event, 0); |
879dca019
|
96 |
break; |
1da177e4c
|
97 98 |
default: ACPI_DEBUG_PRINT((ACPI_DB_INFO, |
4be44fcd3
|
99 100 |
"Unsupported event [0x%x] ", event)); |
1da177e4c
|
101 102 |
break; } |
d550d98d3
|
103 |
return; |
1da177e4c
|
104 |
} |
fe7bf106e
|
105 |
static int __acpi_processor_start(struct acpi_device *device); |
ac212b698
|
106 |
|
fe7bf106e
|
107 |
static int acpi_cpu_soft_notify(struct notifier_block *nfb, |
ac212b698
|
108 |
unsigned long action, void *hcpu) |
729c6ba33
|
109 110 |
{ unsigned int cpu = (unsigned long)hcpu; |
706546d02
|
111 |
struct acpi_processor *pr = per_cpu(processors, cpu); |
ac212b698
|
112 |
struct acpi_device *device; |
ea9c167d9
|
113 |
action &= ~CPU_TASKS_FROZEN; |
729c6ba33
|
114 |
|
8da837344
|
115 116 117 118 119 120 |
/* * CPU_STARTING and CPU_DYING must not sleep. Return here since * acpi_bus_get_device() may sleep. */ if (action == CPU_STARTING || action == CPU_DYING) return NOTIFY_DONE; |
ac212b698
|
121 122 123 124 125 126 127 |
if (!pr || acpi_bus_get_device(pr->handle, &device)) return NOTIFY_DONE; if (action == CPU_ONLINE) { /* * CPU got physically hotplugged and onlined for the first time: * Initialize missing things. |
99b725084
|
128 129 |
*/ if (pr->flags.need_hotplug_init) { |
ac212b698
|
130 |
int ret; |
47db4547f
|
131 132 133 |
pr_info("Will online and init hotplugged CPU: %d ", pr->id); |
99b725084
|
134 |
pr->flags.need_hotplug_init = 0; |
ac212b698
|
135 136 137 |
ret = __acpi_processor_start(device); WARN(ret, "Failed to start CPU: %d ", pr->id); |
99b725084
|
138 |
} else { |
ac212b698
|
139 |
/* Normal CPU soft online event. */ |
99b725084
|
140 |
acpi_processor_ppc_has_changed(pr, 0); |
b7db60f45
|
141 |
acpi_processor_hotplug(pr); |
99b725084
|
142 143 144 |
acpi_processor_reevaluate_tstate(pr, action); acpi_processor_tstate_has_changed(pr); } |
ac212b698
|
145 146 |
} else if (action == CPU_DEAD) { /* Invalidate flag.throttling after the CPU is offline. */ |
5a344a505
|
147 148 |
acpi_processor_reevaluate_tstate(pr, action); } |
729c6ba33
|
149 150 |
return NOTIFY_OK; } |
8ce344c66
|
151 |
static struct notifier_block acpi_cpu_notifier = { |
729c6ba33
|
152 153 |
.notifier_call = acpi_cpu_soft_notify, }; |
239708a3a
|
154 155 156 |
#ifdef CONFIG_ACPI_CPU_FREQ_PSS static int acpi_pss_perf_init(struct acpi_processor *pr, struct acpi_device *device) |
54d5dcc45
|
157 |
{ |
54d5dcc45
|
158 |
int result = 0; |
54d5dcc45
|
159 |
acpi_processor_ppc_has_changed(pr, 0); |
239708a3a
|
160 |
|
54d5dcc45
|
161 |
acpi_processor_get_throttling_info(pr); |
22e7551eb
|
162 163 164 |
if (pr->flags.throttling) pr->flags.limit = 1; |
54d5dcc45
|
165 |
|
54d5dcc45
|
166 167 168 169 |
pr->cdev = thermal_cooling_device_register("Processor", device, &processor_cooling_ops); if (IS_ERR(pr->cdev)) { result = PTR_ERR(pr->cdev); |
239708a3a
|
170 |
return result; |
54d5dcc45
|
171 172 173 174 175 176 177 178 179 180 |
} dev_dbg(&device->dev, "registered as cooling_device%d ", pr->cdev->id); result = sysfs_create_link(&device->dev.kobj, &pr->cdev->device.kobj, "thermal_cooling"); if (result) { |
47db4547f
|
181 182 183 |
dev_err(&device->dev, "Failed to create sysfs link 'thermal_cooling' "); |
54d5dcc45
|
184 185 |
goto err_thermal_unregister; } |
239708a3a
|
186 |
|
54d5dcc45
|
187 188 189 190 |
result = sysfs_create_link(&pr->cdev->device.kobj, &device->dev.kobj, "device"); if (result) { |
47db4547f
|
191 192 193 |
dev_err(&pr->cdev->device, "Failed to create sysfs link 'device' "); |
54d5dcc45
|
194 195 |
goto err_remove_sysfs_thermal; } |
ce360db70
|
196 |
return 0; |
ac212b698
|
197 |
err_remove_sysfs_thermal: |
54d5dcc45
|
198 |
sysfs_remove_link(&device->dev.kobj, "thermal_cooling"); |
ac212b698
|
199 |
err_thermal_unregister: |
54d5dcc45
|
200 |
thermal_cooling_device_unregister(pr->cdev); |
239708a3a
|
201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 |
return result; } static void acpi_pss_perf_exit(struct acpi_processor *pr, struct acpi_device *device) { if (pr->cdev) { sysfs_remove_link(&device->dev.kobj, "thermal_cooling"); sysfs_remove_link(&pr->cdev->device.kobj, "device"); thermal_cooling_device_unregister(pr->cdev); pr->cdev = NULL; } } #else static inline int acpi_pss_perf_init(struct acpi_processor *pr, struct acpi_device *device) { return 0; } static inline void acpi_pss_perf_exit(struct acpi_processor *pr, struct acpi_device *device) {} #endif /* CONFIG_ACPI_CPU_FREQ_PSS */ static int __acpi_processor_start(struct acpi_device *device) { struct acpi_processor *pr = acpi_driver_data(device); acpi_status status; int result = 0; if (!pr) return -ENODEV; if (pr->flags.need_hotplug_init) return 0; |
4f2f75735
|
237 238 239 |
result = acpi_cppc_processor_probe(pr); if (result) return -ENODEV; |
239708a3a
|
240 241 242 243 244 245 246 247 248 249 250 251 252 |
if (!cpuidle_get_driver() || cpuidle_get_driver() == &acpi_idle_driver) acpi_processor_power_init(pr); result = acpi_pss_perf_init(pr, device); if (result) goto err_power_exit; status = acpi_install_notify_handler(device->handle, ACPI_DEVICE_NOTIFY, acpi_processor_notify, device); if (ACPI_SUCCESS(status)) return 0; err_power_exit: |
38a991b62
|
253 |
acpi_processor_power_exit(pr); |
54d5dcc45
|
254 255 |
return result; } |
fe7bf106e
|
256 |
static int acpi_processor_start(struct device *dev) |
1da177e4c
|
257 |
{ |
d0a7edbb6
|
258 |
struct acpi_device *device = ACPI_COMPANION(dev); |
1da177e4c
|
259 |
|
d0a7edbb6
|
260 |
if (!device) |
ac212b698
|
261 |
return -ENODEV; |
1da177e4c
|
262 |
|
ac212b698
|
263 |
return __acpi_processor_start(device); |
1da177e4c
|
264 |
} |
ac212b698
|
265 |
static int acpi_processor_stop(struct device *dev) |
1da177e4c
|
266 |
{ |
d0a7edbb6
|
267 |
struct acpi_device *device = ACPI_COMPANION(dev); |
ac212b698
|
268 |
struct acpi_processor *pr; |
1da177e4c
|
269 |
|
d0a7edbb6
|
270 |
if (!device) |
ac212b698
|
271 |
return 0; |
1da177e4c
|
272 |
|
ac212b698
|
273 274 |
acpi_remove_notify_handler(device->handle, ACPI_DEVICE_NOTIFY, acpi_processor_notify); |
1da177e4c
|
275 |
|
50dd09697
|
276 |
pr = acpi_driver_data(device); |
ac212b698
|
277 278 |
if (!pr) return 0; |
38a991b62
|
279 |
acpi_processor_power_exit(pr); |
1da177e4c
|
280 |
|
239708a3a
|
281 |
acpi_pss_perf_exit(pr, device); |
4f2f75735
|
282 |
acpi_cppc_processor_exit(pr); |
d550d98d3
|
283 |
return 0; |
1da177e4c
|
284 |
} |
1da177e4c
|
285 286 287 288 289 |
/* * We keep the driver loaded even when ACPI is not running. * This is needed for the powernow-k8 driver, that works even without * ACPI, but needs symbols from this driver */ |
ac212b698
|
290 |
static int __init acpi_processor_driver_init(void) |
1da177e4c
|
291 |
{ |
4be44fcd3
|
292 |
int result = 0; |
1da177e4c
|
293 |
|
ce8442b55
|
294 295 |
if (acpi_disabled) return 0; |
ac212b698
|
296 |
result = driver_register(&acpi_processor_driver); |
4f86d3a8e
|
297 |
if (result < 0) |
46bcfad7a
|
298 |
return result; |
1da177e4c
|
299 |
|
ac212b698
|
300 |
register_hotcpu_notifier(&acpi_cpu_notifier); |
1da177e4c
|
301 |
acpi_thermal_cpufreq_init(); |
1da177e4c
|
302 |
acpi_processor_ppc_init(); |
1180509f6
|
303 |
acpi_processor_throttling_init(); |
d550d98d3
|
304 |
return 0; |
1da177e4c
|
305 |
} |
ac212b698
|
306 |
static void __exit acpi_processor_driver_exit(void) |
1da177e4c
|
307 |
{ |
ce8442b55
|
308 309 |
if (acpi_disabled) return; |
1da177e4c
|
310 |
acpi_processor_ppc_exit(); |
1da177e4c
|
311 |
acpi_thermal_cpufreq_exit(); |
ac212b698
|
312 |
unregister_hotcpu_notifier(&acpi_cpu_notifier); |
ac212b698
|
313 |
driver_unregister(&acpi_processor_driver); |
1da177e4c
|
314 |
} |
ac212b698
|
315 316 |
module_init(acpi_processor_driver_init); module_exit(acpi_processor_driver_exit); |
1da177e4c
|
317 |
|
1da177e4c
|
318 |
MODULE_ALIAS("processor"); |