1 | // SPDX-License-Identifier: GPL-2.0-or-later |
2 | /* |
3 | * processor_driver.c - ACPI Processor Driver |
4 | * |
5 | * Copyright (C) 2001, 2002 Andy Grover <andrew.grover@intel.com> |
6 | * Copyright (C) 2001, 2002 Paul Diefenbaugh <paul.s.diefenbaugh@intel.com> |
7 | * Copyright (C) 2004 Dominik Brodowski <linux@brodo.de> |
8 | * Copyright (C) 2004 Anil S Keshavamurthy <anil.s.keshavamurthy@intel.com> |
9 | * - Added processor hotplug support |
10 | * Copyright (C) 2013, Intel Corporation |
11 | * Rafael J. Wysocki <rafael.j.wysocki@intel.com> |
12 | */ |
13 | |
14 | #include <linux/kernel.h> |
15 | #include <linux/module.h> |
16 | #include <linux/init.h> |
17 | #include <linux/cpufreq.h> |
18 | #include <linux/cpu.h> |
19 | #include <linux/cpuidle.h> |
20 | #include <linux/slab.h> |
21 | #include <linux/acpi.h> |
22 | |
23 | #include <acpi/processor.h> |
24 | |
25 | #include "internal.h" |
26 | |
27 | #define ACPI_PROCESSOR_NOTIFY_PERFORMANCE 0x80 |
28 | #define ACPI_PROCESSOR_NOTIFY_POWER 0x81 |
29 | #define ACPI_PROCESSOR_NOTIFY_THROTTLING 0x82 |
30 | #define ACPI_PROCESSOR_NOTIFY_HIGEST_PERF_CHANGED 0x85 |
31 | |
32 | MODULE_AUTHOR("Paul Diefenbaugh" ); |
33 | MODULE_DESCRIPTION("ACPI Processor Driver" ); |
34 | MODULE_LICENSE("GPL" ); |
35 | |
36 | static int acpi_processor_start(struct device *dev); |
37 | static int acpi_processor_stop(struct device *dev); |
38 | |
39 | static const struct acpi_device_id processor_device_ids[] = { |
40 | {ACPI_PROCESSOR_OBJECT_HID, 0}, |
41 | {ACPI_PROCESSOR_DEVICE_HID, 0}, |
42 | {"" , 0}, |
43 | }; |
44 | MODULE_DEVICE_TABLE(acpi, processor_device_ids); |
45 | |
46 | static struct device_driver acpi_processor_driver = { |
47 | .name = "processor" , |
48 | .bus = &cpu_subsys, |
49 | .acpi_match_table = processor_device_ids, |
50 | .probe = acpi_processor_start, |
51 | .remove = acpi_processor_stop, |
52 | }; |
53 | |
54 | static void acpi_processor_notify(acpi_handle handle, u32 event, void *data) |
55 | { |
56 | struct acpi_device *device = data; |
57 | struct acpi_processor *pr; |
58 | int saved; |
59 | |
60 | if (device->handle != handle) |
61 | return; |
62 | |
63 | pr = acpi_driver_data(d: device); |
64 | if (!pr) |
65 | return; |
66 | |
67 | switch (event) { |
68 | case ACPI_PROCESSOR_NOTIFY_PERFORMANCE: |
69 | saved = pr->performance_platform_limit; |
70 | acpi_processor_ppc_has_changed(pr, event_flag: 1); |
71 | if (saved == pr->performance_platform_limit) |
72 | break; |
73 | acpi_bus_generate_netlink_event(device->pnp.device_class, |
74 | dev_name(dev: &device->dev), event, |
75 | pr->performance_platform_limit); |
76 | break; |
77 | case ACPI_PROCESSOR_NOTIFY_POWER: |
78 | acpi_processor_power_state_has_changed(pr); |
79 | acpi_bus_generate_netlink_event(device->pnp.device_class, |
80 | dev_name(dev: &device->dev), event, 0); |
81 | break; |
82 | case ACPI_PROCESSOR_NOTIFY_THROTTLING: |
83 | acpi_processor_tstate_has_changed(pr); |
84 | acpi_bus_generate_netlink_event(device->pnp.device_class, |
85 | dev_name(dev: &device->dev), event, 0); |
86 | break; |
87 | case ACPI_PROCESSOR_NOTIFY_HIGEST_PERF_CHANGED: |
88 | cpufreq_update_limits(cpu: pr->id); |
89 | acpi_bus_generate_netlink_event(device->pnp.device_class, |
90 | dev_name(dev: &device->dev), event, 0); |
91 | break; |
92 | default: |
93 | acpi_handle_debug(handle, "Unsupported event [0x%x]\n" , event); |
94 | break; |
95 | } |
96 | |
97 | return; |
98 | } |
99 | |
100 | static int __acpi_processor_start(struct acpi_device *device); |
101 | |
102 | static int acpi_soft_cpu_online(unsigned int cpu) |
103 | { |
104 | struct acpi_processor *pr = per_cpu(processors, cpu); |
105 | struct acpi_device *device; |
106 | |
107 | if (!pr) |
108 | return 0; |
109 | |
110 | device = acpi_fetch_acpi_dev(handle: pr->handle); |
111 | if (!device) |
112 | return 0; |
113 | |
114 | /* |
115 | * CPU got physically hotplugged and onlined for the first time: |
116 | * Initialize missing things. |
117 | */ |
118 | if (pr->flags.need_hotplug_init) { |
119 | int ret; |
120 | |
121 | pr_info("Will online and init hotplugged CPU: %d\n" , |
122 | pr->id); |
123 | pr->flags.need_hotplug_init = 0; |
124 | ret = __acpi_processor_start(device); |
125 | WARN(ret, "Failed to start CPU: %d\n" , pr->id); |
126 | } else { |
127 | /* Normal CPU soft online event. */ |
128 | acpi_processor_ppc_has_changed(pr, event_flag: 0); |
129 | acpi_processor_hotplug(pr); |
130 | acpi_processor_reevaluate_tstate(pr, is_dead: false); |
131 | acpi_processor_tstate_has_changed(pr); |
132 | } |
133 | return 0; |
134 | } |
135 | |
136 | static int acpi_soft_cpu_dead(unsigned int cpu) |
137 | { |
138 | struct acpi_processor *pr = per_cpu(processors, cpu); |
139 | |
140 | if (!pr || !acpi_fetch_acpi_dev(handle: pr->handle)) |
141 | return 0; |
142 | |
143 | acpi_processor_reevaluate_tstate(pr, is_dead: true); |
144 | return 0; |
145 | } |
146 | |
147 | #ifdef CONFIG_ACPI_CPU_FREQ_PSS |
148 | static void acpi_pss_perf_init(struct acpi_processor *pr) |
149 | { |
150 | acpi_processor_ppc_has_changed(pr, event_flag: 0); |
151 | |
152 | acpi_processor_get_throttling_info(pr); |
153 | |
154 | if (pr->flags.throttling) |
155 | pr->flags.limit = 1; |
156 | } |
157 | #else |
158 | static inline void acpi_pss_perf_init(struct acpi_processor *pr) {} |
159 | #endif /* CONFIG_ACPI_CPU_FREQ_PSS */ |
160 | |
161 | static int __acpi_processor_start(struct acpi_device *device) |
162 | { |
163 | struct acpi_processor *pr = acpi_driver_data(d: device); |
164 | acpi_status status; |
165 | int result = 0; |
166 | |
167 | if (!pr) |
168 | return -ENODEV; |
169 | |
170 | if (pr->flags.need_hotplug_init) |
171 | return 0; |
172 | |
173 | result = acpi_cppc_processor_probe(pr); |
174 | if (result && !IS_ENABLED(CONFIG_ACPI_CPU_FREQ_PSS)) |
175 | dev_dbg(&device->dev, "CPPC data invalid or not present\n" ); |
176 | |
177 | if (!cpuidle_get_driver() || cpuidle_get_driver() == &acpi_idle_driver) |
178 | acpi_processor_power_init(pr); |
179 | |
180 | acpi_pss_perf_init(pr); |
181 | |
182 | result = acpi_processor_thermal_init(pr, device); |
183 | if (result) |
184 | goto err_power_exit; |
185 | |
186 | status = acpi_install_notify_handler(device: device->handle, ACPI_DEVICE_NOTIFY, |
187 | handler: acpi_processor_notify, context: device); |
188 | if (ACPI_SUCCESS(status)) |
189 | return 0; |
190 | |
191 | result = -ENODEV; |
192 | acpi_processor_thermal_exit(pr, device); |
193 | |
194 | err_power_exit: |
195 | acpi_processor_power_exit(pr); |
196 | return result; |
197 | } |
198 | |
199 | static int acpi_processor_start(struct device *dev) |
200 | { |
201 | struct acpi_device *device = ACPI_COMPANION(dev); |
202 | int ret; |
203 | |
204 | if (!device) |
205 | return -ENODEV; |
206 | |
207 | /* Protect against concurrent CPU hotplug operations */ |
208 | cpu_hotplug_disable(); |
209 | ret = __acpi_processor_start(device); |
210 | cpu_hotplug_enable(); |
211 | return ret; |
212 | } |
213 | |
214 | static int acpi_processor_stop(struct device *dev) |
215 | { |
216 | struct acpi_device *device = ACPI_COMPANION(dev); |
217 | struct acpi_processor *pr; |
218 | |
219 | if (!device) |
220 | return 0; |
221 | |
222 | acpi_remove_notify_handler(device: device->handle, ACPI_DEVICE_NOTIFY, |
223 | handler: acpi_processor_notify); |
224 | |
225 | pr = acpi_driver_data(d: device); |
226 | if (!pr) |
227 | return 0; |
228 | acpi_processor_power_exit(pr); |
229 | |
230 | acpi_cppc_processor_exit(pr); |
231 | |
232 | acpi_processor_thermal_exit(pr, device); |
233 | |
234 | return 0; |
235 | } |
236 | |
237 | bool acpi_processor_cpufreq_init; |
238 | |
239 | static int acpi_processor_notifier(struct notifier_block *nb, |
240 | unsigned long event, void *data) |
241 | { |
242 | struct cpufreq_policy *policy = data; |
243 | |
244 | if (event == CPUFREQ_CREATE_POLICY) { |
245 | acpi_thermal_cpufreq_init(policy); |
246 | acpi_processor_ppc_init(policy); |
247 | } else if (event == CPUFREQ_REMOVE_POLICY) { |
248 | acpi_processor_ppc_exit(policy); |
249 | acpi_thermal_cpufreq_exit(policy); |
250 | } |
251 | |
252 | return 0; |
253 | } |
254 | |
255 | static struct notifier_block acpi_processor_notifier_block = { |
256 | .notifier_call = acpi_processor_notifier, |
257 | }; |
258 | |
259 | /* |
260 | * We keep the driver loaded even when ACPI is not running. |
261 | * This is needed for the powernow-k8 driver, that works even without |
262 | * ACPI, but needs symbols from this driver |
263 | */ |
264 | static enum cpuhp_state hp_online; |
265 | static int __init acpi_processor_driver_init(void) |
266 | { |
267 | int result = 0; |
268 | |
269 | if (acpi_disabled) |
270 | return 0; |
271 | |
272 | if (!cpufreq_register_notifier(nb: &acpi_processor_notifier_block, |
273 | CPUFREQ_POLICY_NOTIFIER)) { |
274 | acpi_processor_cpufreq_init = true; |
275 | acpi_processor_ignore_ppc_init(); |
276 | } |
277 | |
278 | result = driver_register(drv: &acpi_processor_driver); |
279 | if (result < 0) |
280 | return result; |
281 | |
282 | result = cpuhp_setup_state_nocalls(state: CPUHP_AP_ONLINE_DYN, |
283 | name: "acpi/cpu-drv:online" , |
284 | startup: acpi_soft_cpu_online, NULL); |
285 | if (result < 0) |
286 | goto err; |
287 | hp_online = result; |
288 | cpuhp_setup_state_nocalls(state: CPUHP_ACPI_CPUDRV_DEAD, name: "acpi/cpu-drv:dead" , |
289 | NULL, teardown: acpi_soft_cpu_dead); |
290 | |
291 | acpi_processor_throttling_init(); |
292 | return 0; |
293 | err: |
294 | driver_unregister(drv: &acpi_processor_driver); |
295 | return result; |
296 | } |
297 | |
298 | static void __exit acpi_processor_driver_exit(void) |
299 | { |
300 | if (acpi_disabled) |
301 | return; |
302 | |
303 | if (acpi_processor_cpufreq_init) { |
304 | cpufreq_unregister_notifier(nb: &acpi_processor_notifier_block, |
305 | CPUFREQ_POLICY_NOTIFIER); |
306 | acpi_processor_cpufreq_init = false; |
307 | } |
308 | |
309 | cpuhp_remove_state_nocalls(state: hp_online); |
310 | cpuhp_remove_state_nocalls(state: CPUHP_ACPI_CPUDRV_DEAD); |
311 | driver_unregister(drv: &acpi_processor_driver); |
312 | } |
313 | |
314 | module_init(acpi_processor_driver_init); |
315 | module_exit(acpi_processor_driver_exit); |
316 | |
317 | MODULE_ALIAS("processor" ); |
318 | |