1c942fddfSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later
20131aa3dSAlex Chiang /*
3ac212b69SRafael J. Wysocki  * processor_driver.c - ACPI Processor Driver
40131aa3dSAlex Chiang  *
50131aa3dSAlex Chiang  *  Copyright (C) 2001, 2002 Andy Grover <andrew.grover@intel.com>
60131aa3dSAlex Chiang  *  Copyright (C) 2001, 2002 Paul Diefenbaugh <paul.s.diefenbaugh@intel.com>
70131aa3dSAlex Chiang  *  Copyright (C) 2004       Dominik Brodowski <linux@brodo.de>
80131aa3dSAlex Chiang  *  Copyright (C) 2004  Anil S Keshavamurthy <anil.s.keshavamurthy@intel.com>
90131aa3dSAlex Chiang  *  			- Added processor hotplug support
10ac212b69SRafael J. Wysocki  *  Copyright (C) 2013, Intel Corporation
11ac212b69SRafael J. Wysocki  *                      Rafael J. Wysocki <rafael.j.wysocki@intel.com>
120131aa3dSAlex Chiang  */
130131aa3dSAlex Chiang 
140131aa3dSAlex Chiang #include <linux/kernel.h>
150131aa3dSAlex Chiang #include <linux/module.h>
160131aa3dSAlex Chiang #include <linux/init.h>
170131aa3dSAlex Chiang #include <linux/cpufreq.h>
180131aa3dSAlex Chiang #include <linux/cpu.h>
190131aa3dSAlex Chiang #include <linux/cpuidle.h>
205a0e3ad6STejun Heo #include <linux/slab.h>
2147db4547SToshi Kani #include <linux/acpi.h>
220131aa3dSAlex Chiang 
230131aa3dSAlex Chiang #include <acpi/processor.h>
240131aa3dSAlex Chiang 
25ac212b69SRafael J. Wysocki #include "internal.h"
26ac212b69SRafael J. Wysocki 
270131aa3dSAlex Chiang #define ACPI_PROCESSOR_NOTIFY_PERFORMANCE 0x80
280131aa3dSAlex Chiang #define ACPI_PROCESSOR_NOTIFY_POWER	0x81
290131aa3dSAlex Chiang #define ACPI_PROCESSOR_NOTIFY_THROTTLING	0x82
300131aa3dSAlex Chiang 
310131aa3dSAlex Chiang MODULE_AUTHOR("Paul Diefenbaugh");
320131aa3dSAlex Chiang MODULE_DESCRIPTION("ACPI Processor Driver");
330131aa3dSAlex Chiang MODULE_LICENSE("GPL");
340131aa3dSAlex Chiang 
35ac212b69SRafael J. Wysocki static int acpi_processor_start(struct device *dev);
36ac212b69SRafael J. Wysocki static int acpi_processor_stop(struct device *dev);
370131aa3dSAlex Chiang 
380131aa3dSAlex Chiang static const struct acpi_device_id processor_device_ids[] = {
390131aa3dSAlex Chiang 	{ACPI_PROCESSOR_OBJECT_HID, 0},
409f324bdaSToshi Kani 	{ACPI_PROCESSOR_DEVICE_HID, 0},
410131aa3dSAlex Chiang 	{"", 0},
420131aa3dSAlex Chiang };
430131aa3dSAlex Chiang MODULE_DEVICE_TABLE(acpi, processor_device_ids);
440131aa3dSAlex Chiang 
45ac212b69SRafael J. Wysocki static struct device_driver acpi_processor_driver = {
460131aa3dSAlex Chiang 	.name = "processor",
47ac212b69SRafael J. Wysocki 	.bus = &cpu_subsys,
48ac212b69SRafael J. Wysocki 	.acpi_match_table = processor_device_ids,
49ac212b69SRafael J. Wysocki 	.probe = acpi_processor_start,
50ac212b69SRafael J. Wysocki 	.remove = acpi_processor_stop,
510131aa3dSAlex Chiang };
520131aa3dSAlex Chiang 
acpi_processor_notify(acpi_handle handle,u32 event,void * data)53ac212b69SRafael J. Wysocki static void acpi_processor_notify(acpi_handle handle, u32 event, void *data)
540131aa3dSAlex Chiang {
55ac212b69SRafael J. Wysocki 	struct acpi_device *device = data;
560131aa3dSAlex Chiang 	struct acpi_processor *pr;
570131aa3dSAlex Chiang 	int saved;
580131aa3dSAlex Chiang 
59ac212b69SRafael J. Wysocki 	if (device->handle != handle)
60ac212b69SRafael J. Wysocki 		return;
61ac212b69SRafael J. Wysocki 
62ac212b69SRafael J. Wysocki 	pr = acpi_driver_data(device);
630131aa3dSAlex Chiang 	if (!pr)
640131aa3dSAlex Chiang 		return;
650131aa3dSAlex Chiang 
660131aa3dSAlex Chiang 	switch (event) {
670131aa3dSAlex Chiang 	case ACPI_PROCESSOR_NOTIFY_PERFORMANCE:
680131aa3dSAlex Chiang 		saved = pr->performance_platform_limit;
690131aa3dSAlex Chiang 		acpi_processor_ppc_has_changed(pr, 1);
700131aa3dSAlex Chiang 		if (saved == pr->performance_platform_limit)
710131aa3dSAlex Chiang 			break;
720131aa3dSAlex Chiang 		acpi_bus_generate_netlink_event(device->pnp.device_class,
730131aa3dSAlex Chiang 						  dev_name(&device->dev), event,
740131aa3dSAlex Chiang 						  pr->performance_platform_limit);
750131aa3dSAlex Chiang 		break;
760131aa3dSAlex Chiang 	case ACPI_PROCESSOR_NOTIFY_POWER:
77a36a7fecSSudeep Holla 		acpi_processor_power_state_has_changed(pr);
780131aa3dSAlex Chiang 		acpi_bus_generate_netlink_event(device->pnp.device_class,
790131aa3dSAlex Chiang 						  dev_name(&device->dev), event, 0);
800131aa3dSAlex Chiang 		break;
810131aa3dSAlex Chiang 	case ACPI_PROCESSOR_NOTIFY_THROTTLING:
820131aa3dSAlex Chiang 		acpi_processor_tstate_has_changed(pr);
830131aa3dSAlex Chiang 		acpi_bus_generate_netlink_event(device->pnp.device_class,
840131aa3dSAlex Chiang 						  dev_name(&device->dev), event, 0);
85879dca01SAlan Cox 		break;
860131aa3dSAlex Chiang 	default:
8752af99c3SRafael J. Wysocki 		acpi_handle_debug(handle, "Unsupported event [0x%x]\n", event);
880131aa3dSAlex Chiang 		break;
890131aa3dSAlex Chiang 	}
900131aa3dSAlex Chiang 
910131aa3dSAlex Chiang 	return;
920131aa3dSAlex Chiang }
930131aa3dSAlex Chiang 
94fe7bf106SPaul Gortmaker static int __acpi_processor_start(struct acpi_device *device);
95ac212b69SRafael J. Wysocki 
acpi_soft_cpu_online(unsigned int cpu)9664f3bf2fSSebastian Andrzej Siewior static int acpi_soft_cpu_online(unsigned int cpu)
970131aa3dSAlex Chiang {
980131aa3dSAlex Chiang 	struct acpi_processor *pr = per_cpu(processors, cpu);
99ac212b69SRafael J. Wysocki 	struct acpi_device *device;
1008da83734SToshi Kani 
10199ece713SRafael J. Wysocki 	if (!pr)
10264f3bf2fSSebastian Andrzej Siewior 		return 0;
10399ece713SRafael J. Wysocki 
10499ece713SRafael J. Wysocki 	device = acpi_fetch_acpi_dev(pr->handle);
10599ece713SRafael J. Wysocki 	if (!device)
10699ece713SRafael J. Wysocki 		return 0;
10799ece713SRafael J. Wysocki 
108ac212b69SRafael J. Wysocki 	/*
109ac212b69SRafael J. Wysocki 	 * CPU got physically hotplugged and onlined for the first time:
110ac212b69SRafael J. Wysocki 	 * Initialize missing things.
11199b72508SThomas Renninger 	 */
11299b72508SThomas Renninger 	if (pr->flags.need_hotplug_init) {
113ac212b69SRafael J. Wysocki 		int ret;
114ac212b69SRafael J. Wysocki 
11547db4547SToshi Kani 		pr_info("Will online and init hotplugged CPU: %d\n",
11647db4547SToshi Kani 			pr->id);
11799b72508SThomas Renninger 		pr->flags.need_hotplug_init = 0;
118ac212b69SRafael J. Wysocki 		ret = __acpi_processor_start(device);
119ac212b69SRafael J. Wysocki 		WARN(ret, "Failed to start CPU: %d\n", pr->id);
12099b72508SThomas Renninger 	} else {
121ac212b69SRafael J. Wysocki 		/* Normal CPU soft online event. */
1220131aa3dSAlex Chiang 		acpi_processor_ppc_has_changed(pr, 0);
123b7db60f4SFeng Tang 		acpi_processor_hotplug(pr);
12464f3bf2fSSebastian Andrzej Siewior 		acpi_processor_reevaluate_tstate(pr, false);
1250131aa3dSAlex Chiang 		acpi_processor_tstate_has_changed(pr);
1260131aa3dSAlex Chiang 	}
12764f3bf2fSSebastian Andrzej Siewior 	return 0;
1280131aa3dSAlex Chiang }
1290131aa3dSAlex Chiang 
acpi_soft_cpu_dead(unsigned int cpu)13064f3bf2fSSebastian Andrzej Siewior static int acpi_soft_cpu_dead(unsigned int cpu)
13164f3bf2fSSebastian Andrzej Siewior {
13264f3bf2fSSebastian Andrzej Siewior 	struct acpi_processor *pr = per_cpu(processors, cpu);
13364f3bf2fSSebastian Andrzej Siewior 
13499ece713SRafael J. Wysocki 	if (!pr || !acpi_fetch_acpi_dev(pr->handle))
13564f3bf2fSSebastian Andrzej Siewior 		return 0;
13664f3bf2fSSebastian Andrzej Siewior 
13764f3bf2fSSebastian Andrzej Siewior 	acpi_processor_reevaluate_tstate(pr, true);
13864f3bf2fSSebastian Andrzej Siewior 	return 0;
13964f3bf2fSSebastian Andrzej Siewior }
1400131aa3dSAlex Chiang 
141239708a3SAshwin Chaugule #ifdef CONFIG_ACPI_CPU_FREQ_PSS
acpi_pss_perf_init(struct acpi_processor * pr)1427fdc74daSRiwen Lu static void acpi_pss_perf_init(struct acpi_processor *pr)
14354d5dcc4SThomas Renninger {
14454d5dcc4SThomas Renninger 	acpi_processor_ppc_has_changed(pr, 0);
145239708a3SAshwin Chaugule 
14654d5dcc4SThomas Renninger 	acpi_processor_get_throttling_info(pr);
14722e7551eSLan Tianyu 
14822e7551eSLan Tianyu 	if (pr->flags.throttling)
14922e7551eSLan Tianyu 		pr->flags.limit = 1;
150239708a3SAshwin Chaugule }
151239708a3SAshwin Chaugule #else
acpi_pss_perf_init(struct acpi_processor * pr)1527fdc74daSRiwen Lu static inline void acpi_pss_perf_init(struct acpi_processor *pr) {}
153239708a3SAshwin Chaugule #endif /* CONFIG_ACPI_CPU_FREQ_PSS */
154239708a3SAshwin Chaugule 
__acpi_processor_start(struct acpi_device * device)155239708a3SAshwin Chaugule static int __acpi_processor_start(struct acpi_device *device)
156239708a3SAshwin Chaugule {
157239708a3SAshwin Chaugule 	struct acpi_processor *pr = acpi_driver_data(device);
158239708a3SAshwin Chaugule 	acpi_status status;
159239708a3SAshwin Chaugule 	int result = 0;
160239708a3SAshwin Chaugule 
161239708a3SAshwin Chaugule 	if (!pr)
162239708a3SAshwin Chaugule 		return -ENODEV;
163239708a3SAshwin Chaugule 
164239708a3SAshwin Chaugule 	if (pr->flags.need_hotplug_init)
165239708a3SAshwin Chaugule 		return 0;
166239708a3SAshwin Chaugule 
1674f2f7573SAshwin Chaugule 	result = acpi_cppc_processor_probe(pr);
16865e95891SSrinivas Pandruvada 	if (result && !IS_ENABLED(CONFIG_ACPI_CPU_FREQ_PSS))
169512bb03fSHanjun Guo 		dev_dbg(&device->dev, "CPPC data invalid or not present\n");
1704f2f7573SAshwin Chaugule 
171239708a3SAshwin Chaugule 	if (!cpuidle_get_driver() || cpuidle_get_driver() == &acpi_idle_driver)
172239708a3SAshwin Chaugule 		acpi_processor_power_init(pr);
173239708a3SAshwin Chaugule 
1747fdc74daSRiwen Lu 	acpi_pss_perf_init(pr);
1757fdc74daSRiwen Lu 
1767fdc74daSRiwen Lu 	result = acpi_processor_thermal_init(pr, device);
177239708a3SAshwin Chaugule 	if (result)
178239708a3SAshwin Chaugule 		goto err_power_exit;
179239708a3SAshwin Chaugule 
180239708a3SAshwin Chaugule 	status = acpi_install_notify_handler(device->handle, ACPI_DEVICE_NOTIFY,
181239708a3SAshwin Chaugule 					     acpi_processor_notify, device);
182239708a3SAshwin Chaugule 	if (ACPI_SUCCESS(status))
183239708a3SAshwin Chaugule 		return 0;
184239708a3SAshwin Chaugule 
185a5cbdf69SThomas Gleixner 	result = -ENODEV;
1867fdc74daSRiwen Lu 	acpi_processor_thermal_exit(pr, device);
187a5cbdf69SThomas Gleixner 
18854d5dcc4SThomas Renninger err_power_exit:
18938a991b6SDaniel Lezcano 	acpi_processor_power_exit(pr);
19054d5dcc4SThomas Renninger 	return result;
19154d5dcc4SThomas Renninger }
19254d5dcc4SThomas Renninger 
acpi_processor_start(struct device * dev)193fe7bf106SPaul Gortmaker static int acpi_processor_start(struct device *dev)
1940131aa3dSAlex Chiang {
195d0a7edbbSLan Tianyu 	struct acpi_device *device = ACPI_COMPANION(dev);
1968153f9acSThomas Gleixner 	int ret;
1970131aa3dSAlex Chiang 
198d0a7edbbSLan Tianyu 	if (!device)
199ac212b69SRafael J. Wysocki 		return -ENODEV;
2000131aa3dSAlex Chiang 
2018153f9acSThomas Gleixner 	/* Protect against concurrent CPU hotplug operations */
202fdaf0a51SThomas Gleixner 	cpu_hotplug_disable();
2038153f9acSThomas Gleixner 	ret = __acpi_processor_start(device);
204fdaf0a51SThomas Gleixner 	cpu_hotplug_enable();
2058153f9acSThomas Gleixner 	return ret;
2060131aa3dSAlex Chiang }
2070131aa3dSAlex Chiang 
acpi_processor_stop(struct device * dev)208ac212b69SRafael J. Wysocki static int acpi_processor_stop(struct device *dev)
2090131aa3dSAlex Chiang {
210d0a7edbbSLan Tianyu 	struct acpi_device *device = ACPI_COMPANION(dev);
211ac212b69SRafael J. Wysocki 	struct acpi_processor *pr;
2120131aa3dSAlex Chiang 
213d0a7edbbSLan Tianyu 	if (!device)
214ac212b69SRafael J. Wysocki 		return 0;
2150131aa3dSAlex Chiang 
216ac212b69SRafael J. Wysocki 	acpi_remove_notify_handler(device->handle, ACPI_DEVICE_NOTIFY,
217ac212b69SRafael J. Wysocki 				   acpi_processor_notify);
2180131aa3dSAlex Chiang 
2190131aa3dSAlex Chiang 	pr = acpi_driver_data(device);
220ac212b69SRafael J. Wysocki 	if (!pr)
221ac212b69SRafael J. Wysocki 		return 0;
22238a991b6SDaniel Lezcano 	acpi_processor_power_exit(pr);
2230131aa3dSAlex Chiang 
2244f2f7573SAshwin Chaugule 	acpi_cppc_processor_exit(pr);
2254f2f7573SAshwin Chaugule 
2267fdc74daSRiwen Lu 	acpi_processor_thermal_exit(pr, device);
2277fdc74daSRiwen Lu 
2280131aa3dSAlex Chiang 	return 0;
2290131aa3dSAlex Chiang }
2300131aa3dSAlex Chiang 
231d15ce412SViresh Kumar bool acpi_processor_cpufreq_init;
232d15ce412SViresh Kumar 
acpi_processor_notifier(struct notifier_block * nb,unsigned long event,void * data)233d15ce412SViresh Kumar static int acpi_processor_notifier(struct notifier_block *nb,
234d15ce412SViresh Kumar 				   unsigned long event, void *data)
235d15ce412SViresh Kumar {
236d15ce412SViresh Kumar 	struct cpufreq_policy *policy = data;
237d15ce412SViresh Kumar 
238d15ce412SViresh Kumar 	if (event == CPUFREQ_CREATE_POLICY) {
2393000ce3cSRafael J. Wysocki 		acpi_thermal_cpufreq_init(policy);
2403000ce3cSRafael J. Wysocki 		acpi_processor_ppc_init(policy);
241d15ce412SViresh Kumar 	} else if (event == CPUFREQ_REMOVE_POLICY) {
2423000ce3cSRafael J. Wysocki 		acpi_processor_ppc_exit(policy);
2433000ce3cSRafael J. Wysocki 		acpi_thermal_cpufreq_exit(policy);
244d15ce412SViresh Kumar 	}
245d15ce412SViresh Kumar 
246d15ce412SViresh Kumar 	return 0;
247d15ce412SViresh Kumar }
248d15ce412SViresh Kumar 
249d15ce412SViresh Kumar static struct notifier_block acpi_processor_notifier_block = {
250d15ce412SViresh Kumar 	.notifier_call = acpi_processor_notifier,
251d15ce412SViresh Kumar };
252d15ce412SViresh Kumar 
2530131aa3dSAlex Chiang /*
2540131aa3dSAlex Chiang  * We keep the driver loaded even when ACPI is not running.
2550131aa3dSAlex Chiang  * This is needed for the powernow-k8 driver, that works even without
2560131aa3dSAlex Chiang  * ACPI, but needs symbols from this driver
2570131aa3dSAlex Chiang  */
25864f3bf2fSSebastian Andrzej Siewior static enum cpuhp_state hp_online;
acpi_processor_driver_init(void)259ac212b69SRafael J. Wysocki static int __init acpi_processor_driver_init(void)
2600131aa3dSAlex Chiang {
2610131aa3dSAlex Chiang 	int result = 0;
2620131aa3dSAlex Chiang 
2630131aa3dSAlex Chiang 	if (acpi_disabled)
2640131aa3dSAlex Chiang 		return 0;
2650131aa3dSAlex Chiang 
266*c0e0421aSRafael J. Wysocki 	if (!cpufreq_register_notifier(&acpi_processor_notifier_block,
267*c0e0421aSRafael J. Wysocki 				       CPUFREQ_POLICY_NOTIFIER)) {
268*c0e0421aSRafael J. Wysocki 		acpi_processor_cpufreq_init = true;
269*c0e0421aSRafael J. Wysocki 		acpi_processor_ignore_ppc_init();
270*c0e0421aSRafael J. Wysocki 	}
271*c0e0421aSRafael J. Wysocki 
272ac212b69SRafael J. Wysocki 	result = driver_register(&acpi_processor_driver);
2730131aa3dSAlex Chiang 	if (result < 0)
27446bcfad7SDeepthi Dharwar 		return result;
2750131aa3dSAlex Chiang 
27664f3bf2fSSebastian Andrzej Siewior 	result = cpuhp_setup_state_nocalls(CPUHP_AP_ONLINE_DYN,
27764f3bf2fSSebastian Andrzej Siewior 					   "acpi/cpu-drv:online",
27864f3bf2fSSebastian Andrzej Siewior 					   acpi_soft_cpu_online, NULL);
27964f3bf2fSSebastian Andrzej Siewior 	if (result < 0)
28064f3bf2fSSebastian Andrzej Siewior 		goto err;
28164f3bf2fSSebastian Andrzej Siewior 	hp_online = result;
28264f3bf2fSSebastian Andrzej Siewior 	cpuhp_setup_state_nocalls(CPUHP_ACPI_CPUDRV_DEAD, "acpi/cpu-drv:dead",
28364f3bf2fSSebastian Andrzej Siewior 				  NULL, acpi_soft_cpu_dead);
28464f3bf2fSSebastian Andrzej Siewior 
2850131aa3dSAlex Chiang 	acpi_processor_throttling_init();
2860131aa3dSAlex Chiang 	return 0;
28764f3bf2fSSebastian Andrzej Siewior err:
28864f3bf2fSSebastian Andrzej Siewior 	driver_unregister(&acpi_processor_driver);
28964f3bf2fSSebastian Andrzej Siewior 	return result;
2900131aa3dSAlex Chiang }
2910131aa3dSAlex Chiang 
acpi_processor_driver_exit(void)292ac212b69SRafael J. Wysocki static void __exit acpi_processor_driver_exit(void)
2930131aa3dSAlex Chiang {
2940131aa3dSAlex Chiang 	if (acpi_disabled)
2950131aa3dSAlex Chiang 		return;
2960131aa3dSAlex Chiang 
297d15ce412SViresh Kumar 	if (acpi_processor_cpufreq_init) {
298d15ce412SViresh Kumar 		cpufreq_unregister_notifier(&acpi_processor_notifier_block,
299d15ce412SViresh Kumar 					    CPUFREQ_POLICY_NOTIFIER);
300d15ce412SViresh Kumar 		acpi_processor_cpufreq_init = false;
301d15ce412SViresh Kumar 	}
302d15ce412SViresh Kumar 
30364f3bf2fSSebastian Andrzej Siewior 	cpuhp_remove_state_nocalls(hp_online);
30464f3bf2fSSebastian Andrzej Siewior 	cpuhp_remove_state_nocalls(CPUHP_ACPI_CPUDRV_DEAD);
305ac212b69SRafael J. Wysocki 	driver_unregister(&acpi_processor_driver);
3060131aa3dSAlex Chiang }
3070131aa3dSAlex Chiang 
308ac212b69SRafael J. Wysocki module_init(acpi_processor_driver_init);
309ac212b69SRafael J. Wysocki module_exit(acpi_processor_driver_exit);
3100131aa3dSAlex Chiang 
3110131aa3dSAlex Chiang MODULE_ALIAS("processor");
312