2019-05-27 08:55:06 +02:00
|
|
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
2005-04-16 15:20:36 -07:00
|
|
|
/*
|
2013-05-03 00:26:22 +02:00
|
|
|
* processor_driver.c - ACPI Processor Driver
|
2005-04-16 15:20:36 -07:00
|
|
|
*
|
|
|
|
|
* 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
|
2013-05-03 00:26:22 +02:00
|
|
|
* Copyright (C) 2013, Intel Corporation
|
|
|
|
|
* Rafael J. Wysocki <rafael.j.wysocki@intel.com>
|
2005-04-16 15:20:36 -07:00
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
#include <linux/kernel.h>
|
|
|
|
|
#include <linux/module.h>
|
|
|
|
|
#include <linux/init.h>
|
|
|
|
|
#include <linux/cpufreq.h>
|
|
|
|
|
#include <linux/cpu.h>
|
2007-10-03 18:58:00 -04:00
|
|
|
#include <linux/cpuidle.h>
|
2010-03-24 17:04:11 +09:00
|
|
|
#include <linux/slab.h>
|
2012-11-20 23:42:27 +00:00
|
|
|
#include <linux/acpi.h>
|
2005-04-16 15:20:36 -07:00
|
|
|
|
|
|
|
|
#include <acpi/processor.h>
|
|
|
|
|
|
2013-05-03 00:26:22 +02:00
|
|
|
#include "internal.h"
|
|
|
|
|
|
2005-04-16 15:20:36 -07:00
|
|
|
#define ACPI_PROCESSOR_NOTIFY_PERFORMANCE 0x80
|
|
|
|
|
#define ACPI_PROCESSOR_NOTIFY_POWER 0x81
|
2007-05-26 22:49:58 +08:00
|
|
|
#define ACPI_PROCESSOR_NOTIFY_THROTTLING 0x82
|
2024-01-19 17:04:59 +08:00
|
|
|
#define ACPI_PROCESSOR_NOTIFY_HIGEST_PERF_CHANGED 0x85
|
2005-04-16 15:20:36 -07:00
|
|
|
|
2007-02-12 22:42:12 -05:00
|
|
|
MODULE_AUTHOR("Paul Diefenbaugh");
|
2007-02-12 23:50:02 -05:00
|
|
|
MODULE_DESCRIPTION("ACPI Processor Driver");
|
2005-04-16 15:20:36 -07:00
|
|
|
MODULE_LICENSE("GPL");
|
|
|
|
|
|
2013-05-03 00:26:22 +02:00
|
|
|
static int acpi_processor_stop(struct device *dev);
|
2005-04-16 15:20:36 -07:00
|
|
|
|
2007-07-23 14:44:41 +02:00
|
|
|
static const struct acpi_device_id processor_device_ids[] = {
|
2008-11-04 14:52:55 -07:00
|
|
|
{ACPI_PROCESSOR_OBJECT_HID, 0},
|
2012-03-19 13:08:02 -06:00
|
|
|
{ACPI_PROCESSOR_DEVICE_HID, 0},
|
2007-07-23 14:44:41 +02:00
|
|
|
{"", 0},
|
|
|
|
|
};
|
|
|
|
|
MODULE_DEVICE_TABLE(acpi, processor_device_ids);
|
|
|
|
|
|
2013-05-03 00:26:22 +02:00
|
|
|
static struct device_driver acpi_processor_driver = {
|
2007-02-12 23:33:40 -05:00
|
|
|
.name = "processor",
|
2013-05-03 00:26:22 +02:00
|
|
|
.bus = &cpu_subsys,
|
|
|
|
|
.acpi_match_table = processor_device_ids,
|
|
|
|
|
.remove = acpi_processor_stop,
|
2005-04-16 15:20:36 -07:00
|
|
|
};
|
|
|
|
|
|
2013-05-03 00:26:22 +02:00
|
|
|
static void acpi_processor_notify(acpi_handle handle, u32 event, void *data)
|
2005-04-16 15:20:36 -07:00
|
|
|
{
|
2013-05-03 00:26:22 +02:00
|
|
|
struct acpi_device *device = data;
|
2008-11-04 14:53:00 -07:00
|
|
|
struct acpi_processor *pr;
|
2026-03-13 13:59:58 +01:00
|
|
|
int saved, ev_data = 0;
|
2005-04-16 15:20:36 -07:00
|
|
|
|
2013-05-03 00:26:22 +02:00
|
|
|
if (device->handle != handle)
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
pr = acpi_driver_data(device);
|
2005-04-16 15:20:36 -07:00
|
|
|
if (!pr)
|
2006-06-27 00:41:40 -04:00
|
|
|
return;
|
2005-04-16 15:20:36 -07:00
|
|
|
|
|
|
|
|
switch (event) {
|
|
|
|
|
case ACPI_PROCESSOR_NOTIFY_PERFORMANCE:
|
2007-11-21 03:23:32 +03:00
|
|
|
saved = pr->performance_platform_limit;
|
2009-10-16 09:20:41 +08:00
|
|
|
acpi_processor_ppc_has_changed(pr, 1);
|
2026-03-13 13:59:58 +01:00
|
|
|
ev_data = pr->performance_platform_limit;
|
|
|
|
|
if (saved == ev_data)
|
|
|
|
|
return;
|
|
|
|
|
|
2005-04-16 15:20:36 -07:00
|
|
|
break;
|
|
|
|
|
case ACPI_PROCESSOR_NOTIFY_POWER:
|
2016-07-21 17:18:07 +01:00
|
|
|
acpi_processor_power_state_has_changed(pr);
|
2005-04-16 15:20:36 -07:00
|
|
|
break;
|
2007-05-26 22:49:58 +08:00
|
|
|
case ACPI_PROCESSOR_NOTIFY_THROTTLING:
|
|
|
|
|
acpi_processor_tstate_has_changed(pr);
|
2012-10-26 01:05:56 +02:00
|
|
|
break;
|
2024-01-19 17:04:59 +08:00
|
|
|
case ACPI_PROCESSOR_NOTIFY_HIGEST_PERF_CHANGED:
|
|
|
|
|
cpufreq_update_limits(pr->id);
|
|
|
|
|
break;
|
2005-04-16 15:20:36 -07:00
|
|
|
default:
|
2021-02-22 19:59:13 +01:00
|
|
|
acpi_handle_debug(handle, "Unsupported event [0x%x]\n", event);
|
2026-03-13 13:59:58 +01:00
|
|
|
return;
|
2005-04-16 15:20:36 -07:00
|
|
|
}
|
|
|
|
|
|
2026-03-13 14:00:41 +01:00
|
|
|
acpi_bus_generate_netlink_event("processor", dev_name(&device->dev),
|
|
|
|
|
event, ev_data);
|
2005-04-16 15:20:36 -07:00
|
|
|
}
|
|
|
|
|
|
2013-06-19 14:30:58 -04:00
|
|
|
static int __acpi_processor_start(struct acpi_device *device);
|
2013-05-03 00:26:22 +02:00
|
|
|
|
2016-09-06 19:04:47 +02:00
|
|
|
static int acpi_soft_cpu_online(unsigned int cpu)
|
2007-09-16 15:36:43 +02:00
|
|
|
{
|
2008-06-09 16:22:23 -07:00
|
|
|
struct acpi_processor *pr = per_cpu(processors, cpu);
|
2013-05-03 00:26:22 +02:00
|
|
|
struct acpi_device *device;
|
2014-05-08 07:58:59 -06:00
|
|
|
|
2021-12-03 17:37:10 +01:00
|
|
|
if (!pr)
|
2016-09-06 19:04:47 +02:00
|
|
|
return 0;
|
2021-12-03 17:37:10 +01:00
|
|
|
|
|
|
|
|
device = acpi_fetch_acpi_dev(pr->handle);
|
|
|
|
|
if (!device)
|
|
|
|
|
return 0;
|
|
|
|
|
|
2016-09-06 19:04:47 +02:00
|
|
|
/*
|
|
|
|
|
* CPU got physically hotplugged and onlined for the first time:
|
|
|
|
|
* Initialize missing things.
|
|
|
|
|
*/
|
2024-05-29 14:34:28 +01:00
|
|
|
if (!pr->flags.previously_online) {
|
2016-09-06 19:04:47 +02:00
|
|
|
int ret;
|
2013-05-03 00:26:22 +02:00
|
|
|
|
2016-09-06 19:04:47 +02:00
|
|
|
ret = __acpi_processor_start(device);
|
|
|
|
|
WARN(ret, "Failed to start CPU: %d\n", pr->id);
|
|
|
|
|
} else {
|
|
|
|
|
/* Normal CPU soft online event. */
|
|
|
|
|
acpi_processor_ppc_has_changed(pr, 0);
|
|
|
|
|
acpi_processor_hotplug(pr);
|
|
|
|
|
acpi_processor_reevaluate_tstate(pr, false);
|
|
|
|
|
acpi_processor_tstate_has_changed(pr);
|
2011-01-10 16:35:45 +08:00
|
|
|
}
|
2016-09-06 19:04:47 +02:00
|
|
|
return 0;
|
2007-09-16 15:36:43 +02:00
|
|
|
}
|
|
|
|
|
|
2016-09-06 19:04:47 +02:00
|
|
|
static int acpi_soft_cpu_dead(unsigned int cpu)
|
|
|
|
|
{
|
|
|
|
|
struct acpi_processor *pr = per_cpu(processors, cpu);
|
|
|
|
|
|
2021-12-03 17:37:10 +01:00
|
|
|
if (!pr || !acpi_fetch_acpi_dev(pr->handle))
|
2016-09-06 19:04:47 +02:00
|
|
|
return 0;
|
|
|
|
|
|
|
|
|
|
acpi_processor_reevaluate_tstate(pr, true);
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
2007-09-16 15:36:43 +02:00
|
|
|
|
2015-08-05 09:40:25 -04:00
|
|
|
#ifdef CONFIG_ACPI_CPU_FREQ_PSS
|
2022-06-17 10:51:51 +08:00
|
|
|
static void acpi_pss_perf_init(struct acpi_processor *pr)
|
2012-01-19 18:18:42 +01:00
|
|
|
{
|
|
|
|
|
acpi_processor_ppc_has_changed(pr, 0);
|
2015-08-05 09:40:25 -04:00
|
|
|
|
2012-01-19 18:18:42 +01:00
|
|
|
acpi_processor_get_throttling_info(pr);
|
2013-08-13 12:11:22 +02:00
|
|
|
|
|
|
|
|
if (pr->flags.throttling)
|
|
|
|
|
pr->flags.limit = 1;
|
2015-08-05 09:40:25 -04:00
|
|
|
}
|
|
|
|
|
#else
|
2022-06-17 10:51:51 +08:00
|
|
|
static inline void acpi_pss_perf_init(struct acpi_processor *pr) {}
|
2015-08-05 09:40:25 -04:00
|
|
|
#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;
|
|
|
|
|
|
2015-09-09 16:27:07 -04:00
|
|
|
result = acpi_cppc_processor_probe(pr);
|
2016-09-01 13:37:08 -07:00
|
|
|
if (result && !IS_ENABLED(CONFIG_ACPI_CPU_FREQ_PSS))
|
2017-07-26 18:40:12 +08:00
|
|
|
dev_dbg(&device->dev, "CPPC data invalid or not present\n");
|
2015-09-09 16:27:07 -04:00
|
|
|
|
2025-12-23 18:09:14 +08:00
|
|
|
acpi_processor_power_init(pr);
|
2015-08-05 09:40:25 -04:00
|
|
|
|
2022-06-17 10:51:51 +08:00
|
|
|
acpi_pss_perf_init(pr);
|
|
|
|
|
|
|
|
|
|
result = acpi_processor_thermal_init(pr, device);
|
2015-08-05 09:40:25 -04:00
|
|
|
if (result)
|
|
|
|
|
goto err_power_exit;
|
|
|
|
|
|
|
|
|
|
status = acpi_install_notify_handler(device->handle, ACPI_DEVICE_NOTIFY,
|
|
|
|
|
acpi_processor_notify, device);
|
2024-05-29 14:34:28 +01:00
|
|
|
if (!ACPI_SUCCESS(status)) {
|
|
|
|
|
result = -ENODEV;
|
|
|
|
|
goto err_thermal_exit;
|
|
|
|
|
}
|
|
|
|
|
pr->flags.previously_online = 1;
|
2015-08-05 09:40:25 -04:00
|
|
|
|
2024-05-29 14:34:28 +01:00
|
|
|
return 0;
|
|
|
|
|
|
|
|
|
|
err_thermal_exit:
|
2022-06-17 10:51:51 +08:00
|
|
|
acpi_processor_thermal_exit(pr, device);
|
2015-08-05 09:40:25 -04:00
|
|
|
err_power_exit:
|
2012-09-15 22:42:54 +02:00
|
|
|
acpi_processor_power_exit(pr);
|
2012-01-19 18:18:42 +01:00
|
|
|
return result;
|
|
|
|
|
}
|
|
|
|
|
|
2013-05-03 00:26:22 +02:00
|
|
|
static int acpi_processor_stop(struct device *dev)
|
2005-04-16 15:20:36 -07:00
|
|
|
{
|
2013-12-06 15:36:26 +08:00
|
|
|
struct acpi_device *device = ACPI_COMPANION(dev);
|
2013-05-03 00:26:22 +02:00
|
|
|
struct acpi_processor *pr;
|
2005-04-16 15:20:36 -07:00
|
|
|
|
2013-12-06 15:36:26 +08:00
|
|
|
if (!device)
|
2013-05-03 00:26:22 +02:00
|
|
|
return 0;
|
2005-04-16 15:20:36 -07:00
|
|
|
|
2013-05-03 00:26:22 +02:00
|
|
|
acpi_remove_notify_handler(device->handle, ACPI_DEVICE_NOTIFY,
|
|
|
|
|
acpi_processor_notify);
|
2005-04-16 15:20:36 -07:00
|
|
|
|
2006-10-01 00:28:50 +02:00
|
|
|
pr = acpi_driver_data(device);
|
2013-05-03 00:26:22 +02:00
|
|
|
if (!pr)
|
|
|
|
|
return 0;
|
2012-09-15 22:42:54 +02:00
|
|
|
acpi_processor_power_exit(pr);
|
2005-04-16 15:20:36 -07:00
|
|
|
|
2015-09-09 16:27:07 -04:00
|
|
|
acpi_cppc_processor_exit(pr);
|
|
|
|
|
|
2022-06-17 10:51:51 +08:00
|
|
|
acpi_processor_thermal_exit(pr, device);
|
|
|
|
|
|
2006-06-27 00:41:40 -04:00
|
|
|
return 0;
|
2005-04-16 15:20:36 -07:00
|
|
|
}
|
|
|
|
|
|
2019-08-28 14:20:13 +05:30
|
|
|
bool acpi_processor_cpufreq_init;
|
|
|
|
|
|
|
|
|
|
static int acpi_processor_notifier(struct notifier_block *nb,
|
|
|
|
|
unsigned long event, void *data)
|
|
|
|
|
{
|
|
|
|
|
struct cpufreq_policy *policy = data;
|
|
|
|
|
|
|
|
|
|
if (event == CPUFREQ_CREATE_POLICY) {
|
2019-10-16 12:47:06 +02:00
|
|
|
acpi_thermal_cpufreq_init(policy);
|
|
|
|
|
acpi_processor_ppc_init(policy);
|
2019-08-28 14:20:13 +05:30
|
|
|
} else if (event == CPUFREQ_REMOVE_POLICY) {
|
2019-10-16 12:47:06 +02:00
|
|
|
acpi_processor_ppc_exit(policy);
|
|
|
|
|
acpi_thermal_cpufreq_exit(policy);
|
2019-08-28 14:20:13 +05:30
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static struct notifier_block acpi_processor_notifier_block = {
|
|
|
|
|
.notifier_call = acpi_processor_notifier,
|
|
|
|
|
};
|
|
|
|
|
|
2024-11-04 16:28:55 -06:00
|
|
|
void __weak acpi_processor_init_invariance_cppc(void)
|
|
|
|
|
{ }
|
|
|
|
|
|
2005-04-16 15:20:36 -07:00
|
|
|
/*
|
|
|
|
|
* 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
|
|
|
|
|
*/
|
2016-09-06 19:04:47 +02:00
|
|
|
static enum cpuhp_state hp_online;
|
2013-05-03 00:26:22 +02:00
|
|
|
static int __init acpi_processor_driver_init(void)
|
2005-04-16 15:20:36 -07:00
|
|
|
{
|
2005-08-05 00:44:28 -04:00
|
|
|
int result = 0;
|
2005-04-16 15:20:36 -07:00
|
|
|
|
2009-08-26 14:29:26 -07:00
|
|
|
if (acpi_disabled)
|
|
|
|
|
return 0;
|
|
|
|
|
|
2023-03-17 17:52:33 +01:00
|
|
|
if (!cpufreq_register_notifier(&acpi_processor_notifier_block,
|
|
|
|
|
CPUFREQ_POLICY_NOTIFIER)) {
|
|
|
|
|
acpi_processor_cpufreq_init = true;
|
|
|
|
|
acpi_processor_ignore_ppc_init();
|
|
|
|
|
}
|
|
|
|
|
|
2025-12-23 18:09:09 +08:00
|
|
|
acpi_processor_register_idle_driver();
|
|
|
|
|
|
2013-05-03 00:26:22 +02:00
|
|
|
result = driver_register(&acpi_processor_driver);
|
2007-10-03 18:58:00 -04:00
|
|
|
if (result < 0)
|
2025-12-23 18:09:09 +08:00
|
|
|
goto unregister_idle_drv;
|
2005-04-16 15:20:36 -07:00
|
|
|
|
2024-05-29 14:34:28 +01:00
|
|
|
result = cpuhp_setup_state(CPUHP_AP_ONLINE_DYN,
|
|
|
|
|
"acpi/cpu-drv:online",
|
|
|
|
|
acpi_soft_cpu_online, NULL);
|
2016-09-06 19:04:47 +02:00
|
|
|
if (result < 0)
|
|
|
|
|
goto err;
|
|
|
|
|
hp_online = result;
|
|
|
|
|
cpuhp_setup_state_nocalls(CPUHP_ACPI_CPUDRV_DEAD, "acpi/cpu-drv:dead",
|
|
|
|
|
NULL, acpi_soft_cpu_dead);
|
|
|
|
|
|
2008-01-28 13:53:42 +08:00
|
|
|
acpi_processor_throttling_init();
|
2024-11-04 16:28:55 -06:00
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* Frequency invariance calculations on AMD platforms can't be run until
|
|
|
|
|
* after acpi_cppc_processor_probe() has been called for all online CPUs
|
|
|
|
|
*/
|
|
|
|
|
acpi_processor_init_invariance_cppc();
|
2025-06-05 17:07:31 +02:00
|
|
|
|
|
|
|
|
acpi_idle_rescan_dead_smt_siblings();
|
|
|
|
|
|
2006-06-27 00:41:40 -04:00
|
|
|
return 0;
|
2025-12-23 18:09:09 +08:00
|
|
|
|
2016-09-06 19:04:47 +02:00
|
|
|
err:
|
|
|
|
|
driver_unregister(&acpi_processor_driver);
|
2025-12-23 18:09:09 +08:00
|
|
|
|
|
|
|
|
unregister_idle_drv:
|
|
|
|
|
acpi_processor_unregister_idle_driver();
|
|
|
|
|
|
2016-09-06 19:04:47 +02:00
|
|
|
return result;
|
2005-04-16 15:20:36 -07:00
|
|
|
}
|
|
|
|
|
|
2013-05-03 00:26:22 +02:00
|
|
|
static void __exit acpi_processor_driver_exit(void)
|
2005-04-16 15:20:36 -07:00
|
|
|
{
|
2009-08-26 14:29:26 -07:00
|
|
|
if (acpi_disabled)
|
|
|
|
|
return;
|
|
|
|
|
|
2019-08-28 14:20:13 +05:30
|
|
|
if (acpi_processor_cpufreq_init) {
|
|
|
|
|
cpufreq_unregister_notifier(&acpi_processor_notifier_block,
|
|
|
|
|
CPUFREQ_POLICY_NOTIFIER);
|
|
|
|
|
acpi_processor_cpufreq_init = false;
|
|
|
|
|
}
|
|
|
|
|
|
2016-09-06 19:04:47 +02:00
|
|
|
cpuhp_remove_state_nocalls(hp_online);
|
|
|
|
|
cpuhp_remove_state_nocalls(CPUHP_ACPI_CPUDRV_DEAD);
|
2013-05-03 00:26:22 +02:00
|
|
|
driver_unregister(&acpi_processor_driver);
|
2025-12-23 18:09:09 +08:00
|
|
|
acpi_processor_unregister_idle_driver();
|
2005-04-16 15:20:36 -07:00
|
|
|
}
|
|
|
|
|
|
2013-05-03 00:26:22 +02:00
|
|
|
module_init(acpi_processor_driver_init);
|
|
|
|
|
module_exit(acpi_processor_driver_exit);
|
2005-04-16 15:20:36 -07:00
|
|
|
|
|
|
|
|
MODULE_ALIAS("processor");
|