summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRafael J. Wysocki <rafael.j.wysocki@intel.com>2026-02-05 15:09:30 +0100
committerRafael J. Wysocki <rafael.j.wysocki@intel.com>2026-02-05 15:09:30 +0100
commit2b0181a52fcbeba25c6b2d701236231b19499861 (patch)
treefc87b0fa0505fdfebbd7f1966711f8fa8ca8691f
parent1a91d4e27d67d87673a0f2c1f90a98ae67b89885 (diff)
parentcac173bea57d62c599f8717fde77b7824c6021ed (diff)
Merge branch 'acpi-processor'
Merge ACPI processor driver changes for 6.20-rc1/7.0-rc1: - Rework the ACPI idle driver initialization to register it directly from the common initialization code instead of doing that from a CPU hotplug "online" callback and clean it up (Huisong Li, Rafael Wysocki) - Fix a possible NULL pointer dereference in acpi_processor_errata_piix4() (Tuo Li) * acpi-processor: ACPI: processor: idle: Rework the handling of acpi_processor_ffh_lpi_probe() ACPI: processor: idle: Convert acpi_processor_setup_cpuidle_dev() to void ACPI: processor: idle: Convert acpi_processor_setup_cpuidle_states() to void ACPI: processor: idle: Add debug log for states with invalid entry methods ACPI: processor: Fix NULL-pointer dereference in acpi_processor_errata_piix4() ACPI: processor: Do not expose global variable acpi_idle_driver ACPI: processor: idle: Rearrange declarations in header file ACPI: processor: idle: Redefine two functions as void ACPI: processor: Update cpuidle driver check in __acpi_processor_start() ACPI: processor: Remove unused empty stubs of some functions ACPI: processor: idle: Optimize ACPI idle driver registration
-rw-r--r--drivers/acpi/acpi_processor.c28
-rw-r--r--drivers/acpi/processor_driver.c13
-rw-r--r--drivers/acpi/processor_idle.c158
-rw-r--r--include/acpi/processor.h34
4 files changed, 124 insertions, 109 deletions
diff --git a/drivers/acpi/acpi_processor.c b/drivers/acpi/acpi_processor.c
index 7ec1dc04fd11..85096ce7b658 100644
--- a/drivers/acpi/acpi_processor.c
+++ b/drivers/acpi/acpi_processor.c
@@ -50,6 +50,7 @@ static int acpi_processor_errata_piix4(struct pci_dev *dev)
{
u8 value1 = 0;
u8 value2 = 0;
+ struct pci_dev *ide_dev = NULL, *isa_dev = NULL;
if (!dev)
@@ -107,12 +108,12 @@ static int acpi_processor_errata_piix4(struct pci_dev *dev)
* each IDE controller's DMA status to make sure we catch all
* DMA activity.
*/
- dev = pci_get_subsys(PCI_VENDOR_ID_INTEL,
+ ide_dev = pci_get_subsys(PCI_VENDOR_ID_INTEL,
PCI_DEVICE_ID_INTEL_82371AB,
PCI_ANY_ID, PCI_ANY_ID, NULL);
- if (dev) {
- errata.piix4.bmisx = pci_resource_start(dev, 4);
- pci_dev_put(dev);
+ if (ide_dev) {
+ errata.piix4.bmisx = pci_resource_start(ide_dev, 4);
+ pci_dev_put(ide_dev);
}
/*
@@ -124,24 +125,25 @@ static int acpi_processor_errata_piix4(struct pci_dev *dev)
* disable C3 support if this is enabled, as some legacy
* devices won't operate well if fast DMA is disabled.
*/
- dev = pci_get_subsys(PCI_VENDOR_ID_INTEL,
+ isa_dev = pci_get_subsys(PCI_VENDOR_ID_INTEL,
PCI_DEVICE_ID_INTEL_82371AB_0,
PCI_ANY_ID, PCI_ANY_ID, NULL);
- if (dev) {
- pci_read_config_byte(dev, 0x76, &value1);
- pci_read_config_byte(dev, 0x77, &value2);
+ if (isa_dev) {
+ pci_read_config_byte(isa_dev, 0x76, &value1);
+ pci_read_config_byte(isa_dev, 0x77, &value2);
if ((value1 & 0x80) || (value2 & 0x80))
errata.piix4.fdma = 1;
- pci_dev_put(dev);
+ pci_dev_put(isa_dev);
}
break;
}
- if (errata.piix4.bmisx)
- dev_dbg(&dev->dev, "Bus master activity detection (BM-IDE) erratum enabled\n");
- if (errata.piix4.fdma)
- dev_dbg(&dev->dev, "Type-F DMA livelock erratum (C3 disabled)\n");
+ if (ide_dev)
+ dev_dbg(&ide_dev->dev, "Bus master activity detection (BM-IDE) erratum enabled\n");
+
+ if (isa_dev)
+ dev_dbg(&isa_dev->dev, "Type-F DMA livelock erratum (C3 disabled)\n");
return 0;
}
diff --git a/drivers/acpi/processor_driver.c b/drivers/acpi/processor_driver.c
index 65e779be64ff..882709796b4f 100644
--- a/drivers/acpi/processor_driver.c
+++ b/drivers/acpi/processor_driver.c
@@ -166,8 +166,7 @@ static int __acpi_processor_start(struct acpi_device *device)
if (result && !IS_ENABLED(CONFIG_ACPI_CPU_FREQ_PSS))
dev_dbg(&device->dev, "CPPC data invalid or not present\n");
- if (!cpuidle_get_driver() || cpuidle_get_driver() == &acpi_idle_driver)
- acpi_processor_power_init(pr);
+ acpi_processor_power_init(pr);
acpi_pss_perf_init(pr);
@@ -259,9 +258,11 @@ static int __init acpi_processor_driver_init(void)
acpi_processor_ignore_ppc_init();
}
+ acpi_processor_register_idle_driver();
+
result = driver_register(&acpi_processor_driver);
if (result < 0)
- return result;
+ goto unregister_idle_drv;
result = cpuhp_setup_state(CPUHP_AP_ONLINE_DYN,
"acpi/cpu-drv:online",
@@ -283,8 +284,13 @@ static int __init acpi_processor_driver_init(void)
acpi_idle_rescan_dead_smt_siblings();
return 0;
+
err:
driver_unregister(&acpi_processor_driver);
+
+unregister_idle_drv:
+ acpi_processor_unregister_idle_driver();
+
return result;
}
@@ -302,6 +308,7 @@ static void __exit acpi_processor_driver_exit(void)
cpuhp_remove_state_nocalls(hp_online);
cpuhp_remove_state_nocalls(CPUHP_ACPI_CPUDRV_DEAD);
driver_unregister(&acpi_processor_driver);
+ acpi_processor_unregister_idle_driver();
}
module_init(acpi_processor_driver_init);
diff --git a/drivers/acpi/processor_idle.c b/drivers/acpi/processor_idle.c
index 89f2f08b2554..81f372c64074 100644
--- a/drivers/acpi/processor_idle.c
+++ b/drivers/acpi/processor_idle.c
@@ -51,7 +51,7 @@ module_param(latency_factor, uint, 0644);
static DEFINE_PER_CPU(struct cpuidle_device *, acpi_cpuidle_device);
-struct cpuidle_driver acpi_idle_driver = {
+static struct cpuidle_driver acpi_idle_driver = {
.name = "acpi_idle",
.owner = THIS_MODULE,
};
@@ -946,6 +946,8 @@ static int acpi_processor_evaluate_lpi(acpi_handle handle,
lpi_state->entry_method = ACPI_CSTATE_INTEGER;
lpi_state->address = obj->integer.value;
} else {
+ pr_debug("Entry method of state-%d is invalid, disable it.\n",
+ state_idx);
continue;
}
@@ -1178,7 +1180,7 @@ static int acpi_idle_lpi_enter(struct cpuidle_device *dev,
return -EINVAL;
}
-static int acpi_processor_setup_lpi_states(struct acpi_processor *pr)
+static void acpi_processor_setup_lpi_states(struct acpi_processor *pr)
{
int i;
struct acpi_lpi_state *lpi;
@@ -1186,7 +1188,7 @@ static int acpi_processor_setup_lpi_states(struct acpi_processor *pr)
struct cpuidle_driver *drv = &acpi_idle_driver;
if (!pr->flags.has_lpi)
- return -EOPNOTSUPP;
+ return;
for (i = 0; i < pr->power.count && i < CPUIDLE_STATE_MAX; i++) {
lpi = &pr->power.lpi_states[i];
@@ -1204,8 +1206,6 @@ static int acpi_processor_setup_lpi_states(struct acpi_processor *pr)
}
drv->state_count = i;
-
- return 0;
}
/**
@@ -1214,13 +1214,13 @@ static int acpi_processor_setup_lpi_states(struct acpi_processor *pr)
*
* @pr: the ACPI processor
*/
-static int acpi_processor_setup_cpuidle_states(struct acpi_processor *pr)
+static void acpi_processor_setup_cpuidle_states(struct acpi_processor *pr)
{
int i;
struct cpuidle_driver *drv = &acpi_idle_driver;
if (!pr->flags.power_setup_done || !pr->flags.power)
- return -EINVAL;
+ return;
drv->safe_state_index = -1;
for (i = ACPI_IDLE_STATE_START; i < CPUIDLE_STATE_MAX; i++) {
@@ -1228,32 +1228,30 @@ static int acpi_processor_setup_cpuidle_states(struct acpi_processor *pr)
drv->states[i].desc[0] = '\0';
}
- if (pr->flags.has_lpi)
- return acpi_processor_setup_lpi_states(pr);
+ if (pr->flags.has_lpi) {
+ acpi_processor_setup_lpi_states(pr);
+ return;
+ }
acpi_processor_setup_cstates(pr);
- return 0;
}
/**
- * acpi_processor_setup_cpuidle_dev - prepares and configures CPUIDLE
+ * acpi_processor_setup_cpuidle_dev - configures CPUIDLE
* device i.e. per-cpu data
*
* @pr: the ACPI processor
* @dev : the cpuidle device
*/
-static int acpi_processor_setup_cpuidle_dev(struct acpi_processor *pr,
- struct cpuidle_device *dev)
+static void acpi_processor_setup_cpuidle_dev(struct acpi_processor *pr,
+ struct cpuidle_device *dev)
{
if (!pr->flags.power_setup_done || !pr->flags.power || !dev)
- return -EINVAL;
+ return;
dev->cpu = pr->id;
- if (pr->flags.has_lpi)
- return acpi_processor_ffh_lpi_probe(pr->id);
-
- acpi_processor_setup_cpuidle_cx(pr, dev);
- return 0;
+ if (!pr->flags.has_lpi)
+ acpi_processor_setup_cpuidle_cx(pr, dev);
}
static int acpi_processor_get_power_info(struct acpi_processor *pr)
@@ -1262,7 +1260,13 @@ static int acpi_processor_get_power_info(struct acpi_processor *pr)
ret = acpi_processor_get_lpi_info(pr);
if (ret)
- ret = acpi_processor_get_cstate_info(pr);
+ return acpi_processor_get_cstate_info(pr);
+
+ if (pr->flags.has_lpi) {
+ ret = acpi_processor_ffh_lpi_probe(pr->id);
+ if (ret)
+ pr_err("CPU%u: Invalid FFH LPI data\n", pr->id);
+ }
return ret;
}
@@ -1347,79 +1351,103 @@ int acpi_processor_power_state_has_changed(struct acpi_processor *pr)
return 0;
}
-static int acpi_processor_registered;
+void acpi_processor_register_idle_driver(void)
+{
+ struct acpi_processor *pr;
+ int ret = -ENODEV;
+ int cpu;
+
+ /*
+ * ACPI idle driver is used by all possible CPUs.
+ * Use the processor power info of one in them to set up idle states.
+ * Note that the existing idle handler will be used on platforms that
+ * only support C1.
+ */
+ for_each_possible_cpu(cpu) {
+ pr = per_cpu(processors, cpu);
+ if (!pr)
+ continue;
+
+ acpi_processor_cstate_first_run_checks();
+ ret = acpi_processor_get_power_info(pr);
+ if (!ret) {
+ pr->flags.power_setup_done = 1;
+ acpi_processor_setup_cpuidle_states(pr);
+ break;
+ }
+ }
+
+ if (ret) {
+ pr_debug("No ACPI power information from any CPUs.\n");
+ return;
+ }
-int acpi_processor_power_init(struct acpi_processor *pr)
+ ret = cpuidle_register_driver(&acpi_idle_driver);
+ if (ret) {
+ pr_debug("register %s failed.\n", acpi_idle_driver.name);
+ return;
+ }
+ pr_debug("%s registered with cpuidle.\n", acpi_idle_driver.name);
+}
+
+void acpi_processor_unregister_idle_driver(void)
+{
+ cpuidle_unregister_driver(&acpi_idle_driver);
+}
+
+void acpi_processor_power_init(struct acpi_processor *pr)
{
- int retval;
struct cpuidle_device *dev;
+ /*
+ * The code below only works if the current cpuidle driver is the ACPI
+ * idle driver.
+ */
+ if (cpuidle_get_driver() != &acpi_idle_driver)
+ return;
+
if (disabled_by_idle_boot_param())
- return 0;
+ return;
acpi_processor_cstate_first_run_checks();
if (!acpi_processor_get_power_info(pr))
pr->flags.power_setup_done = 1;
- /*
- * Install the idle handler if processor power management is supported.
- * Note that we use previously set idle handler will be used on
- * platforms that only support C1.
- */
- if (pr->flags.power) {
- /* Register acpi_idle_driver if not already registered */
- if (!acpi_processor_registered) {
- acpi_processor_setup_cpuidle_states(pr);
- retval = cpuidle_register_driver(&acpi_idle_driver);
- if (retval)
- return retval;
- pr_debug("%s registered with cpuidle\n",
- acpi_idle_driver.name);
- }
+ if (!pr->flags.power)
+ return;
+
+ dev = kzalloc(sizeof(*dev), GFP_KERNEL);
+ if (!dev)
+ return;
- dev = kzalloc(sizeof(*dev), GFP_KERNEL);
- if (!dev)
- return -ENOMEM;
- per_cpu(acpi_cpuidle_device, pr->id) = dev;
+ per_cpu(acpi_cpuidle_device, pr->id) = dev;
- acpi_processor_setup_cpuidle_dev(pr, dev);
+ acpi_processor_setup_cpuidle_dev(pr, dev);
- /* Register per-cpu cpuidle_device. Cpuidle driver
- * must already be registered before registering device
- */
- retval = cpuidle_register_device(dev);
- if (retval) {
- if (acpi_processor_registered == 0)
- cpuidle_unregister_driver(&acpi_idle_driver);
-
- per_cpu(acpi_cpuidle_device, pr->id) = NULL;
- kfree(dev);
- return retval;
- }
- acpi_processor_registered++;
+ /*
+ * Register a cpuidle device for this CPU. The cpuidle driver using
+ * this device is expected to be registered.
+ */
+ if (cpuidle_register_device(dev)) {
+ per_cpu(acpi_cpuidle_device, pr->id) = NULL;
+ kfree(dev);
}
- return 0;
}
-int acpi_processor_power_exit(struct acpi_processor *pr)
+void acpi_processor_power_exit(struct acpi_processor *pr)
{
struct cpuidle_device *dev = per_cpu(acpi_cpuidle_device, pr->id);
if (disabled_by_idle_boot_param())
- return 0;
+ return;
if (pr->flags.power) {
cpuidle_unregister_device(dev);
- acpi_processor_registered--;
- if (acpi_processor_registered == 0)
- cpuidle_unregister_driver(&acpi_idle_driver);
-
kfree(dev);
}
pr->flags.power_setup_done = 0;
- return 0;
}
MODULE_IMPORT_NS("ACPI_PROCESSOR_IDLE");
diff --git a/include/acpi/processor.h b/include/acpi/processor.h
index d0eccbd920e5..7146a8e9e9c2 100644
--- a/include/acpi/processor.h
+++ b/include/acpi/processor.h
@@ -417,32 +417,15 @@ static inline void acpi_processor_throttling_init(void) {}
#endif /* CONFIG_ACPI_CPU_FREQ_PSS */
/* in processor_idle.c */
-extern struct cpuidle_driver acpi_idle_driver;
#ifdef CONFIG_ACPI_PROCESSOR_IDLE
-int acpi_processor_power_init(struct acpi_processor *pr);
-int acpi_processor_power_exit(struct acpi_processor *pr);
+void acpi_processor_power_init(struct acpi_processor *pr);
+void acpi_processor_power_exit(struct acpi_processor *pr);
int acpi_processor_power_state_has_changed(struct acpi_processor *pr);
int acpi_processor_hotplug(struct acpi_processor *pr);
-#else
-static inline int acpi_processor_power_init(struct acpi_processor *pr)
-{
- return -ENODEV;
-}
-
-static inline int acpi_processor_power_exit(struct acpi_processor *pr)
-{
- return -ENODEV;
-}
-
-static inline int acpi_processor_power_state_has_changed(struct acpi_processor *pr)
-{
- return -ENODEV;
-}
-
-static inline int acpi_processor_hotplug(struct acpi_processor *pr)
-{
- return -ENODEV;
-}
+void acpi_processor_register_idle_driver(void);
+void acpi_processor_unregister_idle_driver(void);
+int acpi_processor_ffh_lpi_probe(unsigned int cpu);
+int acpi_processor_ffh_lpi_enter(struct acpi_lpi_state *lpi);
#endif /* CONFIG_ACPI_PROCESSOR_IDLE */
/* in processor_thermal.c */
@@ -465,11 +448,6 @@ static inline void acpi_thermal_cpufreq_exit(struct cpufreq_policy *policy)
}
#endif /* CONFIG_CPU_FREQ */
-#ifdef CONFIG_ACPI_PROCESSOR_IDLE
-extern int acpi_processor_ffh_lpi_probe(unsigned int cpu);
-extern int acpi_processor_ffh_lpi_enter(struct acpi_lpi_state *lpi);
-#endif
-
void acpi_processor_init_invariance_cppc(void);
#endif