From a00f3dea0352a5fb0b67b84c72daeb6563f8e67f Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Thu, 9 Oct 2025 21:25:56 +0200 Subject: [PATCH 01/30] ACPI: PM: s2idle: Drop acpi_get_lps0_constraint() Drop unused function acpi_get_lps0_constraint(). No functional impact. Signed-off-by: Rafael J. Wysocki Reviewed-by: Mario Limonciello (AMD) Link: https://patch.msgid.link/5032801.GXAFRqVoOG@rafael.j.wysocki --- drivers/acpi/x86/s2idle.c | 24 ------------------------ include/linux/acpi.h | 5 ----- 2 files changed, 29 deletions(-) diff --git a/drivers/acpi/x86/s2idle.c b/drivers/acpi/x86/s2idle.c index dd0b40b9bbe8..ea645853fc20 100644 --- a/drivers/acpi/x86/s2idle.c +++ b/drivers/acpi/x86/s2idle.c @@ -299,30 +299,6 @@ static void lpi_device_get_constraints(void) ACPI_FREE(out_obj); } -/** - * acpi_get_lps0_constraint - Get the LPS0 constraint for a device. - * @adev: Device to get the constraint for. - * - * The LPS0 constraint is the shallowest (minimum) power state in which the - * device can be so as to allow the platform as a whole to achieve additional - * energy conservation by utilizing a system-wide low-power state. - * - * Returns: - * - ACPI power state value of the constraint for @adev on success. - * - Otherwise, ACPI_STATE_UNKNOWN. - */ -int acpi_get_lps0_constraint(struct acpi_device *adev) -{ - struct lpi_constraints *entry; - - for_each_lpi_constraint(entry) { - if (adev->handle == entry->handle) - return entry->min_dstate; - } - - return ACPI_STATE_UNKNOWN; -} - static void lpi_check_constraints(void) { struct lpi_constraints *entry; diff --git a/include/linux/acpi.h b/include/linux/acpi.h index 5ff5d99f6ead..252768d007c7 100644 --- a/include/linux/acpi.h +++ b/include/linux/acpi.h @@ -1146,12 +1146,7 @@ struct acpi_s2idle_dev_ops { #if defined(CONFIG_SUSPEND) && defined(CONFIG_X86) int acpi_register_lps0_dev(struct acpi_s2idle_dev_ops *arg); void acpi_unregister_lps0_dev(struct acpi_s2idle_dev_ops *arg); -int acpi_get_lps0_constraint(struct acpi_device *adev); #else /* CONFIG_SUSPEND && CONFIG_X86 */ -static inline int acpi_get_lps0_constraint(struct device *dev) -{ - return ACPI_STATE_UNKNOWN; -} static inline int acpi_register_lps0_dev(struct acpi_s2idle_dev_ops *arg) { return -ENODEV; From bfc09902debd036e672148343c0caeab20924c9d Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Thu, 9 Oct 2025 21:26:30 +0200 Subject: [PATCH 02/30] ACPI: PM: s2idle: Staticise LPS0 callback functions The LPS0 callback functions in x86/s2idle.c can be made static, so do that and remove their declarations from sleep.h. While at it, add the _lps0 suffix to their names to indicate that they are LPS0-specific. No functional impact. Signed-off-by: Rafael J. Wysocki Reviewed-by: Mario Limonciello (AMD) Link: https://patch.msgid.link/2254836.irdbgypaU6@rafael.j.wysocki --- drivers/acpi/sleep.h | 3 --- drivers/acpi/x86/s2idle.c | 12 ++++++------ 2 files changed, 6 insertions(+), 9 deletions(-) diff --git a/drivers/acpi/sleep.h b/drivers/acpi/sleep.h index d960a238be4e..9c3cb109c5d2 100644 --- a/drivers/acpi/sleep.h +++ b/drivers/acpi/sleep.h @@ -17,10 +17,7 @@ static inline acpi_status acpi_set_waking_vector(u32 wakeup_address) extern int acpi_s2idle_begin(void); extern int acpi_s2idle_prepare(void); -extern int acpi_s2idle_prepare_late(void); -extern void acpi_s2idle_check(void); extern bool acpi_s2idle_wake(void); -extern void acpi_s2idle_restore_early(void); extern void acpi_s2idle_restore(void); extern void acpi_s2idle_end(void); diff --git a/drivers/acpi/x86/s2idle.c b/drivers/acpi/x86/s2idle.c index ea645853fc20..5ce556b1cfbc 100644 --- a/drivers/acpi/x86/s2idle.c +++ b/drivers/acpi/x86/s2idle.c @@ -515,7 +515,7 @@ static struct acpi_scan_handler lps0_handler = { .attach = lps0_device_attach, }; -int acpi_s2idle_prepare_late(void) +static int acpi_s2idle_prepare_late_lps0(void) { struct acpi_s2idle_dev_ops *handler; @@ -561,7 +561,7 @@ int acpi_s2idle_prepare_late(void) return 0; } -void acpi_s2idle_check(void) +static void acpi_s2idle_check_lps0(void) { struct acpi_s2idle_dev_ops *handler; @@ -574,7 +574,7 @@ void acpi_s2idle_check(void) } } -void acpi_s2idle_restore_early(void) +static void acpi_s2idle_restore_early_lps0(void) { struct acpi_s2idle_dev_ops *handler; @@ -614,10 +614,10 @@ void acpi_s2idle_restore_early(void) static const struct platform_s2idle_ops acpi_s2idle_ops_lps0 = { .begin = acpi_s2idle_begin, .prepare = acpi_s2idle_prepare, - .prepare_late = acpi_s2idle_prepare_late, - .check = acpi_s2idle_check, + .prepare_late = acpi_s2idle_prepare_late_lps0, + .check = acpi_s2idle_check_lps0, .wake = acpi_s2idle_wake, - .restore_early = acpi_s2idle_restore_early, + .restore_early = acpi_s2idle_restore_early_lps0, .restore = acpi_s2idle_restore, .end = acpi_s2idle_end, }; From 32ece31db4df792c36bbabb8a1d263a5e1f5017a Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Thu, 9 Oct 2025 21:27:11 +0200 Subject: [PATCH 03/30] ACPI: PM: s2idle: Only retrieve constraints when needed The evaluation of LPS0 _DSM Function 1 in lps0_device_attach() may be useless if pm_debug_messages_on is never set. For this reason, instead of evaluating it in lps0_device_attach(), do that in a new .begin() callback for s2idle, acpi_s2idle_begin_lps0(), only when pm_debug_messages_on is set at that point. However, never attempt to evaluate LPS0 _DSM Function 1 more than once to avoid recurring failures. Signed-off-by: Rafael J. Wysocki Reviewed-by: Mario Limonciello (AMD) Link: https://patch.msgid.link/3027060.e9J7NaK4W3@rafael.j.wysocki --- drivers/acpi/x86/s2idle.c | 29 +++++++++++++++++++++++------ 1 file changed, 23 insertions(+), 6 deletions(-) diff --git a/drivers/acpi/x86/s2idle.c b/drivers/acpi/x86/s2idle.c index 5ce556b1cfbc..6d4d06236f61 100644 --- a/drivers/acpi/x86/s2idle.c +++ b/drivers/acpi/x86/s2idle.c @@ -303,6 +303,9 @@ static void lpi_check_constraints(void) { struct lpi_constraints *entry; + if (IS_ERR_OR_NULL(lpi_constraints_table)) + return; + for_each_lpi_constraint(entry) { struct acpi_device *adev = acpi_fetch_acpi_dev(entry->handle); @@ -484,11 +487,6 @@ static int lps0_device_attach(struct acpi_device *adev, lps0_device_handle = adev->handle; - if (acpi_s2idle_vendor_amd()) - lpi_device_get_constraints_amd(); - else - lpi_device_get_constraints(); - /* * Use suspend-to-idle by default if ACPI_FADT_LOW_POWER_S0 is set in * the FADT and the default suspend mode was not set from the command @@ -515,6 +513,25 @@ static struct acpi_scan_handler lps0_handler = { .attach = lps0_device_attach, }; +static int acpi_s2idle_begin_lps0(void) +{ + if (pm_debug_messages_on && !lpi_constraints_table) { + if (acpi_s2idle_vendor_amd()) + lpi_device_get_constraints_amd(); + else + lpi_device_get_constraints(); + + /* + * Try to retrieve the constraints only once because failures + * to do so usually are sticky. + */ + if (!lpi_constraints_table) + lpi_constraints_table = ERR_PTR(-ENODATA); + } + + return acpi_s2idle_begin(); +} + static int acpi_s2idle_prepare_late_lps0(void) { struct acpi_s2idle_dev_ops *handler; @@ -612,7 +629,7 @@ static void acpi_s2idle_restore_early_lps0(void) } static const struct platform_s2idle_ops acpi_s2idle_ops_lps0 = { - .begin = acpi_s2idle_begin, + .begin = acpi_s2idle_begin_lps0, .prepare = acpi_s2idle_prepare, .prepare_late = acpi_s2idle_prepare_late_lps0, .check = acpi_s2idle_check_lps0, From 945661d581c50cb67fc04291d374156f4d807fe3 Mon Sep 17 00:00:00 2001 From: Huisong Li Date: Mon, 29 Sep 2025 17:37:49 +0800 Subject: [PATCH 04/30] ACPI: processor: idle: Relocate state flags initialization Since acpi_processor_setup_cstates() is a more logical place for setting idle state flags than acpi_processor_setup_cpuidle_cx(), move that code from the latter to the former. It also allows direct references to acpi_idle_driver in acpi_processor_setup_cpuidle_cx() to be avoided. No intentional functional impact. Signed-off-by: Huisong Li [ rjw: Subject and changelog rewrite ] Link: https://patch.msgid.link/20250929093754.3998136-5-lihuisong@huawei.com Signed-off-by: Rafael J. Wysocki --- drivers/acpi/processor_idle.c | 20 +++++++++----------- 1 file changed, 9 insertions(+), 11 deletions(-) diff --git a/drivers/acpi/processor_idle.c b/drivers/acpi/processor_idle.c index 22b051b94a86..341825e8ac63 100644 --- a/drivers/acpi/processor_idle.c +++ b/drivers/acpi/processor_idle.c @@ -737,13 +737,11 @@ static int acpi_processor_setup_cpuidle_cx(struct acpi_processor *pr, { int i, count = ACPI_IDLE_STATE_START; struct acpi_processor_cx *cx; - struct cpuidle_state *state; if (max_cstate == 0) max_cstate = 1; for (i = 1; i < ACPI_PROCESSOR_MAX_POWER && i <= max_cstate; i++) { - state = &acpi_idle_driver.states[count]; cx = &pr->power.states[i]; if (!cx->valid) @@ -751,15 +749,6 @@ static int acpi_processor_setup_cpuidle_cx(struct acpi_processor *pr, per_cpu(acpi_cstate[count], dev->cpu) = cx; - if (lapic_timer_needs_broadcast(pr, cx)) - state->flags |= CPUIDLE_FLAG_TIMER_STOP; - - if (cx->type == ACPI_STATE_C3) { - state->flags |= CPUIDLE_FLAG_TLB_FLUSHED; - if (pr->flags.bm_check) - state->flags |= CPUIDLE_FLAG_RCU_IDLE; - } - count++; if (count == CPUIDLE_STATE_MAX) break; @@ -818,6 +807,15 @@ static int acpi_processor_setup_cstates(struct acpi_processor *pr) if (cx->type != ACPI_STATE_C1 && !acpi_idle_fallback_to_c1(pr)) state->enter_s2idle = acpi_idle_enter_s2idle; + if (lapic_timer_needs_broadcast(pr, cx)) + state->flags |= CPUIDLE_FLAG_TIMER_STOP; + + if (cx->type == ACPI_STATE_C3) { + state->flags |= CPUIDLE_FLAG_TLB_FLUSHED; + if (pr->flags.bm_check) + state->flags |= CPUIDLE_FLAG_RCU_IDLE; + } + count++; if (count == CPUIDLE_STATE_MAX) break; From 159e85110891ebc12500d02d4bf214b1d203e305 Mon Sep 17 00:00:00 2001 From: Sakari Ailus Date: Wed, 1 Oct 2025 13:43:18 +0300 Subject: [PATCH 05/30] ACPI: property: Make acpi_get_next_subnode() static acpi_get_next_subnode() is only used in drivers/acpi/property.c. Remove its prototype from include/linux/acpi.h and make it static. Signed-off-by: Sakari Ailus Reviewed-by: Andy Shevchenko Reviewed-by: Laurent Pinchart Reviewed-by: Jonathan Cameron Link: https://patch.msgid.link/20251001104320.1272752-2-sakari.ailus@linux.intel.com Signed-off-by: Rafael J. Wysocki --- drivers/acpi/property.c | 7 ++++--- include/linux/acpi.h | 10 ---------- 2 files changed, 4 insertions(+), 13 deletions(-) diff --git a/drivers/acpi/property.c b/drivers/acpi/property.c index 43d5e457814e..dbf86bee62e1 100644 --- a/drivers/acpi/property.c +++ b/drivers/acpi/property.c @@ -1329,13 +1329,14 @@ static int stop_on_next(struct acpi_device *adev, void *data) return 0; } -/** +/* * acpi_get_next_subnode - Return the next child node handle for a fwnode * @fwnode: Firmware node to find the next child node for. * @child: Handle to one of the device's child nodes or a null handle. */ -struct fwnode_handle *acpi_get_next_subnode(const struct fwnode_handle *fwnode, - struct fwnode_handle *child) +static struct fwnode_handle * +acpi_get_next_subnode(const struct fwnode_handle *fwnode, + struct fwnode_handle *child) { struct acpi_device *adev = to_acpi_device_node(fwnode); diff --git a/include/linux/acpi.h b/include/linux/acpi.h index 5ff5d99f6ead..703323b9fe0c 100644 --- a/include/linux/acpi.h +++ b/include/linux/acpi.h @@ -1349,9 +1349,6 @@ acpi_data_add_props(struct acpi_device_data *data, const guid_t *guid, int acpi_node_prop_get(const struct fwnode_handle *fwnode, const char *propname, void **valptr); -struct fwnode_handle *acpi_get_next_subnode(const struct fwnode_handle *fwnode, - struct fwnode_handle *child); - struct acpi_probe_entry; typedef bool (*acpi_probe_entry_validate_subtbl)(struct acpi_subtable_header *, struct acpi_probe_entry *); @@ -1450,13 +1447,6 @@ static inline int acpi_node_prop_get(const struct fwnode_handle *fwnode, return -ENXIO; } -static inline struct fwnode_handle * -acpi_get_next_subnode(const struct fwnode_handle *fwnode, - struct fwnode_handle *child) -{ - return NULL; -} - static inline struct fwnode_handle * acpi_graph_get_next_endpoint(const struct fwnode_handle *fwnode, struct fwnode_handle *prev) From 5d010473cdeaabf6a2d3a9e2aed2186c1b73c213 Mon Sep 17 00:00:00 2001 From: Sakari Ailus Date: Wed, 1 Oct 2025 13:43:19 +0300 Subject: [PATCH 06/30] ACPI: property: Use ACPI functions in acpi_graph_get_next_endpoint() only Calling fwnode_get_next_child_node() in ACPI implementation of the fwnode property API is somewhat problematic as the latter is used in the impelementation of the former. Instead of using fwnode_get_next_child_node() in acpi_graph_get_next_endpoint(), call acpi_get_next_subnode() directly instead. Signed-off-by: Sakari Ailus Reviewed-by: Laurent Pinchart Reviewed-by: Jonathan Cameron Link: https://patch.msgid.link/20251001104320.1272752-3-sakari.ailus@linux.intel.com Signed-off-by: Rafael J. Wysocki --- drivers/acpi/property.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/acpi/property.c b/drivers/acpi/property.c index dbf86bee62e1..b88bad197fca 100644 --- a/drivers/acpi/property.c +++ b/drivers/acpi/property.c @@ -1473,7 +1473,7 @@ static struct fwnode_handle *acpi_graph_get_next_endpoint( if (!prev) { do { - port = fwnode_get_next_child_node(fwnode, port); + port = acpi_get_next_subnode(fwnode, port); /* * The names of the port nodes begin with "port@" * followed by the number of the port node and they also @@ -1491,13 +1491,13 @@ static struct fwnode_handle *acpi_graph_get_next_endpoint( if (!port) return NULL; - endpoint = fwnode_get_next_child_node(port, prev); + endpoint = acpi_get_next_subnode(port, prev); while (!endpoint) { - port = fwnode_get_next_child_node(fwnode, port); + port = acpi_get_next_subnode(fwnode, port); if (!port) break; if (is_acpi_graph_node(port, "port")) - endpoint = fwnode_get_next_child_node(port, NULL); + endpoint = acpi_get_next_subnode(port, NULL); } /* From b889ed5abf4796671b742692fdca35c4002892ee Mon Sep 17 00:00:00 2001 From: Sakari Ailus Date: Wed, 1 Oct 2025 13:43:20 +0300 Subject: [PATCH 07/30] ACPI: property: Rework acpi_graph_get_next_endpoint() Rework the code obtaining the next endpoint in acpi_graph_get_next_endpoint(). The resulting code removes unnecessary contitionals and should be easier to follow. Suggested-by: Andy Shevchenko Signed-off-by: Sakari Ailus Reviewed-by: Laurent Pinchart Link: https://patch.msgid.link/20251001104320.1272752-4-sakari.ailus@linux.intel.com Signed-off-by: Rafael J. Wysocki --- drivers/acpi/property.c | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/drivers/acpi/property.c b/drivers/acpi/property.c index b88bad197fca..73171d277ab6 100644 --- a/drivers/acpi/property.c +++ b/drivers/acpi/property.c @@ -1491,14 +1491,17 @@ static struct fwnode_handle *acpi_graph_get_next_endpoint( if (!port) return NULL; - endpoint = acpi_get_next_subnode(port, prev); - while (!endpoint) { - port = acpi_get_next_subnode(fwnode, port); - if (!port) + do { + endpoint = acpi_get_next_subnode(port, prev); + if (endpoint) break; - if (is_acpi_graph_node(port, "port")) - endpoint = acpi_get_next_subnode(port, NULL); - } + + prev = NULL; + + do { + port = acpi_get_next_subnode(fwnode, port); + } while (port && !is_acpi_graph_node(port, "port")); + } while (port); /* * The names of the endpoint nodes begin with "endpoint@" followed by From 2e00f7a4bb0ac25ec7477b55fe482da39fb4dce8 Mon Sep 17 00:00:00 2001 From: Armin Wolf Date: Wed, 8 Oct 2025 01:41:45 +0200 Subject: [PATCH 08/30] ACPI: fan: Workaround for 64-bit firmware bug Some firmware implementations use the "Ones" ASL opcode to produce an integer with all bits set in order to indicate missing speed or power readings. This however only works when using 32-bit integers, as the ACPI spec requires a 32-bit integer (0xFFFFFFFF) to be returned for missing speed/power readings. With 64-bit integers the "Ones" opcode produces a 64-bit integer with all bits set, violating the ACPI spec regarding the placeholder value for missing readings. Work around such buggy firmware implementation by also checking for 64-bit integers with all bits set when reading _FST. Signed-off-by: Armin Wolf [ rjw: Typo fix in the changelog ] Link: https://patch.msgid.link/20251007234149.2769-3-W_Armin@gmx.de Signed-off-by: Rafael J. Wysocki --- drivers/acpi/fan.h | 33 +++++++++++++++++++++++++++++++++ drivers/acpi/fan_hwmon.c | 10 +++------- 2 files changed, 36 insertions(+), 7 deletions(-) diff --git a/drivers/acpi/fan.h b/drivers/acpi/fan.h index bedbab0e8e4e..0d73433c3889 100644 --- a/drivers/acpi/fan.h +++ b/drivers/acpi/fan.h @@ -11,6 +11,7 @@ #define _ACPI_FAN_H_ #include +#include #define ACPI_FAN_DEVICE_IDS \ {"INT3404", }, /* Fan */ \ @@ -60,6 +61,38 @@ struct acpi_fan { struct device_attribute fine_grain_control; }; +/** + * acpi_fan_speed_valid - Check if fan speed value is valid + * @speeed: Speed value returned by the ACPI firmware + * + * Check if the fan speed value returned by the ACPI firmware is valid. This function is + * necessary as ACPI firmware implementations can return 0xFFFFFFFF to signal that the + * ACPI fan does not support speed reporting. Additionally, some buggy ACPI firmware + * implementations return a value larger than the 32-bit integer value defined by + * the ACPI specification when using placeholder values. Such invalid values are also + * detected by this function. + * + * Returns: True if the fan speed value is valid, false otherwise. + */ +static inline bool acpi_fan_speed_valid(u64 speed) +{ + return speed < U32_MAX; +} + +/** + * acpi_fan_power_valid - Check if fan power value is valid + * @power: Power value returned by the ACPI firmware + * + * Check if the fan power value returned by the ACPI firmware is valid. + * See acpi_fan_speed_valid() for details. + * + * Returns: True if the fan power value is valid, false otherwise. + */ +static inline bool acpi_fan_power_valid(u64 power) +{ + return power < U32_MAX; +} + int acpi_fan_get_fst(acpi_handle handle, struct acpi_fan_fst *fst); int acpi_fan_create_attributes(struct acpi_device *device); void acpi_fan_delete_attributes(struct acpi_device *device); diff --git a/drivers/acpi/fan_hwmon.c b/drivers/acpi/fan_hwmon.c index 4b2c2007f2d7..47a02ef5a606 100644 --- a/drivers/acpi/fan_hwmon.c +++ b/drivers/acpi/fan_hwmon.c @@ -15,10 +15,6 @@ #include "fan.h" -/* Returned when the ACPI fan does not support speed reporting */ -#define FAN_SPEED_UNAVAILABLE U32_MAX -#define FAN_POWER_UNAVAILABLE U32_MAX - static struct acpi_fan_fps *acpi_fan_get_current_fps(struct acpi_fan *fan, u64 control) { unsigned int i; @@ -77,7 +73,7 @@ static umode_t acpi_fan_hwmon_is_visible(const void *drvdata, enum hwmon_sensor_ * when the associated attribute should not be created. */ for (i = 0; i < fan->fps_count; i++) { - if (fan->fps[i].power != FAN_POWER_UNAVAILABLE) + if (acpi_fan_power_valid(fan->fps[i].power)) return 0444; } @@ -106,7 +102,7 @@ static int acpi_fan_hwmon_read(struct device *dev, enum hwmon_sensor_types type, case hwmon_fan: switch (attr) { case hwmon_fan_input: - if (fst.speed == FAN_SPEED_UNAVAILABLE) + if (!acpi_fan_speed_valid(fst.speed)) return -ENODEV; if (fst.speed > LONG_MAX) @@ -134,7 +130,7 @@ static int acpi_fan_hwmon_read(struct device *dev, enum hwmon_sensor_types type, if (!fps) return -EIO; - if (fps->power == FAN_POWER_UNAVAILABLE) + if (!acpi_fan_power_valid(fps->power)) return -ENODEV; if (fps->power > LONG_MAX / MICROWATT_PER_MILLIWATT) From 86bfd21a0baf0111f62f195c2bff11d25f4bd410 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Wed, 15 Oct 2025 15:52:41 +0200 Subject: [PATCH 09/30] ACPI: battery: Drop redundant locking All of the evaluations of objects in the ACPI namespace are carried out under the namespace lock and interpreter lock in ACPICA, so it is not necessary to put any additional locks around them for synchronization. However, the ACPI battery driver does just that, so remove the redundant locking around ACPI object evaluation from it. Signed-off-by: Rafael J. Wysocki Link: https://patch.msgid.link/2344462.iZASKD2KPV@rafael.j.wysocki --- drivers/acpi/battery.c | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/drivers/acpi/battery.c b/drivers/acpi/battery.c index 67b76492c839..34181fa52e93 100644 --- a/drivers/acpi/battery.c +++ b/drivers/acpi/battery.c @@ -91,7 +91,6 @@ enum { }; struct acpi_battery { - struct mutex lock; struct mutex update_lock; struct power_supply *bat; struct power_supply_desc bat_desc; @@ -535,11 +534,9 @@ static int acpi_battery_get_info(struct acpi_battery *battery) struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL }; acpi_status status = AE_ERROR; - mutex_lock(&battery->lock); status = acpi_evaluate_object(battery->device->handle, use_bix ? "_BIX":"_BIF", NULL, &buffer); - mutex_unlock(&battery->lock); if (ACPI_FAILURE(status)) { acpi_handle_info(battery->device->handle, @@ -576,11 +573,8 @@ static int acpi_battery_get_state(struct acpi_battery *battery) msecs_to_jiffies(cache_time))) return 0; - mutex_lock(&battery->lock); status = acpi_evaluate_object(battery->device->handle, "_BST", NULL, &buffer); - mutex_unlock(&battery->lock); - if (ACPI_FAILURE(status)) { acpi_handle_info(battery->device->handle, "_BST evaluation failed: %s", @@ -628,11 +622,8 @@ static int acpi_battery_set_alarm(struct acpi_battery *battery) !test_bit(ACPI_BATTERY_ALARM_PRESENT, &battery->flags)) return -ENODEV; - mutex_lock(&battery->lock); status = acpi_execute_simple_method(battery->device->handle, "_BTP", battery->alarm); - mutex_unlock(&battery->lock); - if (ACPI_FAILURE(status)) return -ENODEV; @@ -1235,9 +1226,6 @@ static int acpi_battery_add(struct acpi_device *device) strscpy(acpi_device_name(device), ACPI_BATTERY_DEVICE_NAME); strscpy(acpi_device_class(device), ACPI_BATTERY_CLASS); device->driver_data = battery; - result = devm_mutex_init(&device->dev, &battery->lock); - if (result) - return result; result = devm_mutex_init(&device->dev, &battery->update_lock); if (result) From 040beccb038b7a7dec60ef20383278347a2aa09e Mon Sep 17 00:00:00 2001 From: Siyuan Huang Date: Mon, 20 Oct 2025 11:12:04 +0800 Subject: [PATCH 10/30] rust: acpi: replace `core::mem::zeroed` with `pin_init::zeroed` All types in `bindings` implement `Zeroable` if they can, so use `pin_init::zeroed` instead of relying on `unsafe` code. If this ends up not compiling in the future, something in bindgen or on the C side changed and is most likely incorrect. Link: https://github.com/Rust-for-Linux/linux/issues/1189 Suggested-by: Benno Lossin Signed-off-by: Siyuan Huang Reviewed-by: Alice Ryhl Reviewed-by: Benno Lossin Acked-by: Danilo Krummrich Reviewed-by: Kunwu Chan Link: https://patch.msgid.link/20251020031204.78917-1-huangsiyuan@kylinos.cn Signed-off-by: Rafael J. Wysocki --- rust/kernel/acpi.rs | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/rust/kernel/acpi.rs b/rust/kernel/acpi.rs index 37e1161c1298..9b8efa623130 100644 --- a/rust/kernel/acpi.rs +++ b/rust/kernel/acpi.rs @@ -39,9 +39,7 @@ impl DeviceId { pub const fn new(id: &'static CStr) -> Self { let src = id.to_bytes_with_nul(); build_assert!(src.len() <= Self::ACPI_ID_LEN, "ID exceeds 16 bytes"); - // Replace with `bindings::acpi_device_id::default()` once stabilized for `const`. - // SAFETY: FFI type is valid to be zero-initialized. - let mut acpi: bindings::acpi_device_id = unsafe { core::mem::zeroed() }; + let mut acpi: bindings::acpi_device_id = pin_init::zeroed(); let mut i = 0; while i < src.len() { acpi.id[i] = src[i]; From f9f5e22b75b87a98e7645cb1f27c7b457c3c7edb Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Tue, 21 Oct 2025 19:33:39 +0200 Subject: [PATCH 11/30] ACPI: TAD: Rearrange runtime PM operations in acpi_tad_remove() It is not necessary to resume the device upfront in acpi_tad_remove() because both acpi_tad_disable_timer() and acpi_tad_clear_status() attempt to resume it, but it is better to prevent it from suspending between these calls by incrementing its runtime PM usage counter. Accordingly, replace the pm_runtime_get_sync() call in acpi_tad_remove() with a pm_runtime_get_noresume() one and put the latter right before the first invocation of acpi_tad_disable_timer(). In addition, use pm_runtime_put_noidle() to drop the device's runtime PM usage counter after using pm_runtime_get_noresume() to bump it up to follow a common pattern and use pm_runtime_suspend() for suspending the device afterward. No intentional functional impact. Signed-off-by: Rafael J. Wysocki Link: https://patch.msgid.link/5031965.GXAFRqVoOG@rafael.j.wysocki --- drivers/acpi/acpi_tad.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/drivers/acpi/acpi_tad.c b/drivers/acpi/acpi_tad.c index 33418dd6768a..651a2b995844 100644 --- a/drivers/acpi/acpi_tad.c +++ b/drivers/acpi/acpi_tad.c @@ -563,8 +563,6 @@ static void acpi_tad_remove(struct platform_device *pdev) device_init_wakeup(dev, false); - pm_runtime_get_sync(dev); - if (dd->capabilities & ACPI_TAD_RT) sysfs_remove_group(&dev->kobj, &acpi_tad_time_attr_group); @@ -573,6 +571,8 @@ static void acpi_tad_remove(struct platform_device *pdev) sysfs_remove_group(&dev->kobj, &acpi_tad_attr_group); + pm_runtime_get_noresume(dev); + acpi_tad_disable_timer(dev, ACPI_TAD_AC_TIMER); acpi_tad_clear_status(dev, ACPI_TAD_AC_TIMER); if (dd->capabilities & ACPI_TAD_DC_WAKE) { @@ -580,7 +580,8 @@ static void acpi_tad_remove(struct platform_device *pdev) acpi_tad_clear_status(dev, ACPI_TAD_DC_TIMER); } - pm_runtime_put_sync(dev); + pm_runtime_put_noidle(dev); + pm_runtime_suspend(dev); pm_runtime_disable(dev); acpi_remove_cmos_rtc_space_handler(handle); } From 58ca21d591994c4d1f9cb522397533927feef262 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Tue, 21 Oct 2025 19:35:54 +0200 Subject: [PATCH 12/30] ACPI: TAD: Improve runtime PM using guard macros Use guard pm_runtime_active_try to simplify runtime PM cleanup and implement runtime resume error handling in multiple places. Also use guard pm_runtime_noresume to simplify acpi_tad_remove(). Signed-off-by: Rafael J. Wysocki Reviewed-by: Jonathan Cameron Link: https://patch.msgid.link/13881356.uLZWGnKmhe@rafael.j.wysocki --- drivers/acpi/acpi_tad.c | 72 ++++++++++++++++++++++------------------- 1 file changed, 38 insertions(+), 34 deletions(-) diff --git a/drivers/acpi/acpi_tad.c b/drivers/acpi/acpi_tad.c index 651a2b995844..c9487c5bb7b3 100644 --- a/drivers/acpi/acpi_tad.c +++ b/drivers/acpi/acpi_tad.c @@ -90,19 +90,18 @@ static int acpi_tad_set_real_time(struct device *dev, struct acpi_tad_rt *rt) args[0].buffer.pointer = (u8 *)rt; args[0].buffer.length = sizeof(*rt); - pm_runtime_get_sync(dev); + ACQUIRE(pm_runtime_active_try, pm)(dev); + if (ACQUIRE_ERR(pm_runtime_active_try, &pm)) + return -ENXIO; status = acpi_evaluate_integer(handle, "_SRT", &arg_list, &retval); - - pm_runtime_put_sync(dev); - if (ACPI_FAILURE(status) || retval) return -EIO; return 0; } -static int acpi_tad_get_real_time(struct device *dev, struct acpi_tad_rt *rt) +static int acpi_tad_evaluate_grt(struct device *dev, struct acpi_tad_rt *rt) { acpi_handle handle = ACPI_HANDLE(dev); struct acpi_buffer output = { ACPI_ALLOCATE_BUFFER }; @@ -111,12 +110,7 @@ static int acpi_tad_get_real_time(struct device *dev, struct acpi_tad_rt *rt) acpi_status status; int ret = -EIO; - pm_runtime_get_sync(dev); - status = acpi_evaluate_object(handle, "_GRT", NULL, &output); - - pm_runtime_put_sync(dev); - if (ACPI_FAILURE(status)) goto out_free; @@ -139,6 +133,21 @@ static int acpi_tad_get_real_time(struct device *dev, struct acpi_tad_rt *rt) return ret; } +static int acpi_tad_get_real_time(struct device *dev, struct acpi_tad_rt *rt) +{ + int ret; + + ACQUIRE(pm_runtime_active_try, pm)(dev); + if (ACQUIRE_ERR(pm_runtime_active_try, &pm)) + return -ENXIO; + + ret = acpi_tad_evaluate_grt(dev, rt); + if (ret) + return ret; + + return 0; +} + static char *acpi_tad_rt_next_field(char *s, int *val) { char *p; @@ -266,12 +275,11 @@ static int acpi_tad_wake_set(struct device *dev, char *method, u32 timer_id, args[0].integer.value = timer_id; args[1].integer.value = value; - pm_runtime_get_sync(dev); + ACQUIRE(pm_runtime_active_try, pm)(dev); + if (ACQUIRE_ERR(pm_runtime_active_try, &pm)) + return -ENXIO; status = acpi_evaluate_integer(handle, method, &arg_list, &retval); - - pm_runtime_put_sync(dev); - if (ACPI_FAILURE(status) || retval) return -EIO; @@ -314,12 +322,11 @@ static ssize_t acpi_tad_wake_read(struct device *dev, char *buf, char *method, args[0].integer.value = timer_id; - pm_runtime_get_sync(dev); + ACQUIRE(pm_runtime_active_try, pm)(dev); + if (ACQUIRE_ERR(pm_runtime_active_try, &pm)) + return -ENXIO; status = acpi_evaluate_integer(handle, method, &arg_list, &retval); - - pm_runtime_put_sync(dev); - if (ACPI_FAILURE(status)) return -EIO; @@ -370,12 +377,11 @@ static int acpi_tad_clear_status(struct device *dev, u32 timer_id) args[0].integer.value = timer_id; - pm_runtime_get_sync(dev); + ACQUIRE(pm_runtime_active_try, pm)(dev); + if (ACQUIRE_ERR(pm_runtime_active_try, &pm)) + return -ENXIO; status = acpi_evaluate_integer(handle, "_CWS", &arg_list, &retval); - - pm_runtime_put_sync(dev); - if (ACPI_FAILURE(status) || retval) return -EIO; @@ -411,12 +417,11 @@ static ssize_t acpi_tad_status_read(struct device *dev, char *buf, u32 timer_id) args[0].integer.value = timer_id; - pm_runtime_get_sync(dev); + ACQUIRE(pm_runtime_active_try, pm)(dev); + if (ACQUIRE_ERR(pm_runtime_active_try, &pm)) + return -ENXIO; status = acpi_evaluate_integer(handle, "_GWS", &arg_list, &retval); - - pm_runtime_put_sync(dev); - if (ACPI_FAILURE(status)) return -EIO; @@ -571,16 +576,15 @@ static void acpi_tad_remove(struct platform_device *pdev) sysfs_remove_group(&dev->kobj, &acpi_tad_attr_group); - pm_runtime_get_noresume(dev); - - acpi_tad_disable_timer(dev, ACPI_TAD_AC_TIMER); - acpi_tad_clear_status(dev, ACPI_TAD_AC_TIMER); - if (dd->capabilities & ACPI_TAD_DC_WAKE) { - acpi_tad_disable_timer(dev, ACPI_TAD_DC_TIMER); - acpi_tad_clear_status(dev, ACPI_TAD_DC_TIMER); + scoped_guard(pm_runtime_noresume, dev) { + acpi_tad_disable_timer(dev, ACPI_TAD_AC_TIMER); + acpi_tad_clear_status(dev, ACPI_TAD_AC_TIMER); + if (dd->capabilities & ACPI_TAD_DC_WAKE) { + acpi_tad_disable_timer(dev, ACPI_TAD_DC_TIMER); + acpi_tad_clear_status(dev, ACPI_TAD_DC_TIMER); + } } - pm_runtime_put_noidle(dev); pm_runtime_suspend(dev); pm_runtime_disable(dev); acpi_remove_cmos_rtc_space_handler(handle); From 0670b9ad4d9c17461ce63761b02b4b594bec8916 Mon Sep 17 00:00:00 2001 From: Armin Wolf Date: Fri, 24 Oct 2025 20:38:22 +0200 Subject: [PATCH 13/30] ACPI: fan: Add basic notification support The ACPI specification states that the platform firmware can notify the ACPI fan device that the fan speed has changed an that the _FST control method should be reevaluated. Add support for this mechanism to prepare for future changes. Signed-off-by: Armin Wolf Link: https://patch.msgid.link/20251024183824.5656-2-W_Armin@gmx.de Signed-off-by: Rafael J. Wysocki --- drivers/acpi/fan_core.c | 50 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 50 insertions(+) diff --git a/drivers/acpi/fan_core.c b/drivers/acpi/fan_core.c index 46e7fe7a506d..9ee4ef2d6dbc 100644 --- a/drivers/acpi/fan_core.c +++ b/drivers/acpi/fan_core.c @@ -19,6 +19,8 @@ #include "fan.h" +#define ACPI_FAN_NOTIFY_STATE_CHANGED 0x80 + static const struct acpi_device_id fan_device_ids[] = { ACPI_FAN_DEVICE_IDS, {"", 0}, @@ -308,6 +310,50 @@ static int acpi_fan_get_fps(struct acpi_device *device) return status; } +static void acpi_fan_notify_handler(acpi_handle handle, u32 event, void *context) +{ + struct device *dev = context; + struct acpi_fan_fst fst; + int ret; + + switch (event) { + case ACPI_FAN_NOTIFY_STATE_CHANGED: + /* + * The ACPI specification says that we must evaluate _FST when we + * receive an ACPI event indicating that the fan state has changed. + */ + ret = acpi_fan_get_fst(handle, &fst); + if (ret < 0) + dev_err(dev, "Error retrieving current fan status: %d\n", ret); + + acpi_bus_generate_netlink_event("fan", dev_name(dev), event, 0); + break; + default: + dev_dbg(dev, "Unsupported ACPI notification 0x%x\n", event); + break; + } +} + +static void acpi_fan_notify_remove(void *data) +{ + struct acpi_fan *fan = data; + + acpi_remove_notify_handler(fan->handle, ACPI_DEVICE_NOTIFY, acpi_fan_notify_handler); +} + +static int devm_acpi_fan_notify_init(struct device *dev) +{ + struct acpi_fan *fan = dev_get_drvdata(dev); + acpi_status status; + + status = acpi_install_notify_handler(fan->handle, ACPI_DEVICE_NOTIFY, + acpi_fan_notify_handler, dev); + if (ACPI_FAILURE(status)) + return -EIO; + + return devm_add_action_or_reset(dev, acpi_fan_notify_remove, fan); +} + static int acpi_fan_probe(struct platform_device *pdev) { int result = 0; @@ -351,6 +397,10 @@ static int acpi_fan_probe(struct platform_device *pdev) if (result) return result; + result = devm_acpi_fan_notify_init(&pdev->dev); + if (result) + return result; + result = acpi_fan_create_attributes(device); if (result) return result; From 3d4ca76369782e9a55a6c07d4fdc492f9a731cdb Mon Sep 17 00:00:00 2001 From: Armin Wolf Date: Fri, 24 Oct 2025 20:38:23 +0200 Subject: [PATCH 14/30] ACPI: fan: Add hwmon notification support The platform firmware can notify the ACPI fan device that the fan speed has changed. Relay this notification to the hwmon device if present so that userspace applications can react to it. Signed-off-by: Armin Wolf Link: https://patch.msgid.link/20251024183824.5656-3-W_Armin@gmx.de Signed-off-by: Rafael J. Wysocki --- drivers/acpi/fan.h | 5 +++++ drivers/acpi/fan_core.c | 1 + drivers/acpi/fan_hwmon.c | 15 +++++++++++---- 3 files changed, 17 insertions(+), 4 deletions(-) diff --git a/drivers/acpi/fan.h b/drivers/acpi/fan.h index 0d73433c3889..dcc1ad3118ff 100644 --- a/drivers/acpi/fan.h +++ b/drivers/acpi/fan.h @@ -56,6 +56,9 @@ struct acpi_fan { struct acpi_fan_fif fif; struct acpi_fan_fps *fps; int fps_count; +#if IS_REACHABLE(CONFIG_HWMON) + struct device *hdev; +#endif struct thermal_cooling_device *cdev; struct device_attribute fst_speed; struct device_attribute fine_grain_control; @@ -99,8 +102,10 @@ void acpi_fan_delete_attributes(struct acpi_device *device); #if IS_REACHABLE(CONFIG_HWMON) int devm_acpi_fan_create_hwmon(struct device *dev); +void acpi_fan_notify_hwmon(struct device *dev); #else static inline int devm_acpi_fan_create_hwmon(struct device *dev) { return 0; }; +static inline void acpi_fan_notify_hwmon(struct device *dev) { }; #endif #endif diff --git a/drivers/acpi/fan_core.c b/drivers/acpi/fan_core.c index 9ee4ef2d6dbc..7be22c52670c 100644 --- a/drivers/acpi/fan_core.c +++ b/drivers/acpi/fan_core.c @@ -326,6 +326,7 @@ static void acpi_fan_notify_handler(acpi_handle handle, u32 event, void *context if (ret < 0) dev_err(dev, "Error retrieving current fan status: %d\n", ret); + acpi_fan_notify_hwmon(dev); acpi_bus_generate_netlink_event("fan", dev_name(dev), event, 0); break; default: diff --git a/drivers/acpi/fan_hwmon.c b/drivers/acpi/fan_hwmon.c index 47a02ef5a606..d3374f8f524b 100644 --- a/drivers/acpi/fan_hwmon.c +++ b/drivers/acpi/fan_hwmon.c @@ -162,12 +162,19 @@ static const struct hwmon_chip_info acpi_fan_hwmon_chip_info = { .info = acpi_fan_hwmon_info, }; +void acpi_fan_notify_hwmon(struct device *dev) +{ + struct acpi_fan *fan = dev_get_drvdata(dev); + + hwmon_notify_event(fan->hdev, hwmon_fan, hwmon_fan_input, 0); +} + int devm_acpi_fan_create_hwmon(struct device *dev) { struct acpi_fan *fan = dev_get_drvdata(dev); - struct device *hdev; - hdev = devm_hwmon_device_register_with_info(dev, "acpi_fan", fan, &acpi_fan_hwmon_chip_info, - NULL); - return PTR_ERR_OR_ZERO(hdev); + fan->hdev = devm_hwmon_device_register_with_info(dev, "acpi_fan", fan, + &acpi_fan_hwmon_chip_info, NULL); + + return PTR_ERR_OR_ZERO(fan->hdev); } From a5c2fcd82e04a6eea8f6097b473d5ec34413eeeb Mon Sep 17 00:00:00 2001 From: Armin Wolf Date: Fri, 24 Oct 2025 20:38:24 +0200 Subject: [PATCH 15/30] ACPI: fan: Add support for Microsoft fan extensions Microsoft has designed a set of extensions for the ACPI fan device allowing the OS to specify a set of fan speed trip points. The platform firmware will then notify the ACPI fan device when one of the trip points is triggered. Unfortunatly, some device manufacturers (like HP) blindly assume that the OS will use said extensions and thus only update the values returned by the _FST control method when receiving such a notification. As a result, the ACPI fan driver is currently unusable on such machines, always reporting a constant value. Fix this by adding support for the Microsoft extensions. During probe and when resuming from suspend, the driver will attempt to trigger an initial notification that will update the values returned by _FST. Said trip points will be updated each time a notification is received from the platform firmware to ensure that the values returned by the _FST control method are updated. Link: https://learn.microsoft.com/en-us/windows-hardware/design/device-experiences/design-guide Closes: https://github.com/lm-sensors/lm-sensors/issues/506 Signed-off-by: Armin Wolf [ rjw: Edits of the new code comments ] Link: https://patch.msgid.link/20251024183824.5656-4-W_Armin@gmx.de Signed-off-by: Rafael J. Wysocki --- drivers/acpi/fan.h | 2 + drivers/acpi/fan_core.c | 174 +++++++++++++++++++++++++++++++++++++++- 2 files changed, 174 insertions(+), 2 deletions(-) diff --git a/drivers/acpi/fan.h b/drivers/acpi/fan.h index dcc1ad3118ff..f85f9a0fbfcd 100644 --- a/drivers/acpi/fan.h +++ b/drivers/acpi/fan.h @@ -56,6 +56,8 @@ struct acpi_fan { struct acpi_fan_fif fif; struct acpi_fan_fps *fps; int fps_count; + /* A value of 0 means that trippoint-related functions are not supported */ + u32 fan_trip_granularity; #if IS_REACHABLE(CONFIG_HWMON) struct device *hdev; #endif diff --git a/drivers/acpi/fan_core.c b/drivers/acpi/fan_core.c index 7be22c52670c..fb08b8549ed7 100644 --- a/drivers/acpi/fan_core.c +++ b/drivers/acpi/fan_core.c @@ -7,11 +7,16 @@ * Copyright (C) 2022 Intel Corporation. All rights reserved. */ +#include #include +#include +#include +#include #include #include #include #include +#include #include #include #include @@ -21,6 +26,24 @@ #define ACPI_FAN_NOTIFY_STATE_CHANGED 0x80 +/* + * Defined inside the "Fan Noise Signal" section at + * https://learn.microsoft.com/en-us/windows-hardware/design/device-experiences/design-guide. + */ +static const guid_t acpi_fan_microsoft_guid = GUID_INIT(0xA7611840, 0x99FE, 0x41AE, 0xA4, 0x88, + 0x35, 0xC7, 0x59, 0x26, 0xC8, 0xEB); +#define ACPI_FAN_DSM_GET_TRIP_POINT_GRANULARITY 1 +#define ACPI_FAN_DSM_SET_TRIP_POINTS 2 +#define ACPI_FAN_DSM_GET_OPERATING_RANGES 3 + +/* + * Ensures that fans with a very low trip point granularity + * do not send too many notifications. + */ +static uint min_trip_distance = 100; +module_param(min_trip_distance, uint, 0); +MODULE_PARM_DESC(min_trip_distance, "Minimum distance between fan speed trip points in RPM"); + static const struct acpi_device_id fan_device_ids[] = { ACPI_FAN_DEVICE_IDS, {"", 0}, @@ -310,6 +333,132 @@ static int acpi_fan_get_fps(struct acpi_device *device) return status; } +static int acpi_fan_dsm_init(struct device *dev) +{ + union acpi_object dummy = { + .package = { + .type = ACPI_TYPE_PACKAGE, + .count = 0, + .elements = NULL, + }, + }; + struct acpi_fan *fan = dev_get_drvdata(dev); + union acpi_object *obj; + int ret = 0; + + if (!acpi_check_dsm(fan->handle, &acpi_fan_microsoft_guid, 0, + BIT(ACPI_FAN_DSM_GET_TRIP_POINT_GRANULARITY) | + BIT(ACPI_FAN_DSM_SET_TRIP_POINTS))) + return 0; + + dev_info(dev, "Using Microsoft fan extensions\n"); + + obj = acpi_evaluate_dsm_typed(fan->handle, &acpi_fan_microsoft_guid, 0, + ACPI_FAN_DSM_GET_TRIP_POINT_GRANULARITY, &dummy, + ACPI_TYPE_INTEGER); + if (!obj) + return -EIO; + + if (obj->integer.value > U32_MAX) + ret = -EOVERFLOW; + else + fan->fan_trip_granularity = obj->integer.value; + + kfree(obj); + + return ret; +} + +static int acpi_fan_dsm_set_trip_points(struct device *dev, u64 upper, u64 lower) +{ + union acpi_object args[2] = { + { + .integer = { + .type = ACPI_TYPE_INTEGER, + .value = lower, + }, + }, + { + .integer = { + .type = ACPI_TYPE_INTEGER, + .value = upper, + }, + }, + }; + struct acpi_fan *fan = dev_get_drvdata(dev); + union acpi_object in = { + .package = { + .type = ACPI_TYPE_PACKAGE, + .count = ARRAY_SIZE(args), + .elements = args, + }, + }; + union acpi_object *obj; + + obj = acpi_evaluate_dsm(fan->handle, &acpi_fan_microsoft_guid, 0, + ACPI_FAN_DSM_SET_TRIP_POINTS, &in); + kfree(obj); + + return 0; +} + +static int acpi_fan_dsm_start(struct device *dev) +{ + struct acpi_fan *fan = dev_get_drvdata(dev); + int ret; + + if (!fan->fan_trip_granularity) + return 0; + + /* + * Some firmware implementations only update the values returned by the + * _FST control method when a notification is received. This usually + * works with Microsoft Windows as setting up trip points will keep + * triggering said notifications, but will cause issues when using _FST + * without the Microsoft-specific trip point extension. + * + * Because of this, an initial notification needs to be triggered to + * start the cycle of trip points updates. This is achieved by setting + * the trip points sequencially to two separate ranges. As by the + * Microsoft specification the firmware should trigger a notification + * immediately if the fan speed is outside the trip point range. This + * _should_ result in at least one notification as both ranges do not + * overlap, meaning that the current fan speed needs to be outside at + * least one range. + */ + ret = acpi_fan_dsm_set_trip_points(dev, fan->fan_trip_granularity, 0); + if (ret < 0) + return ret; + + return acpi_fan_dsm_set_trip_points(dev, fan->fan_trip_granularity * 3, + fan->fan_trip_granularity * 2); +} + +static int acpi_fan_dsm_update_trips_points(struct device *dev, struct acpi_fan_fst *fst) +{ + struct acpi_fan *fan = dev_get_drvdata(dev); + u64 upper, lower; + + if (!fan->fan_trip_granularity) + return 0; + + if (!acpi_fan_speed_valid(fst->speed)) + return -EINVAL; + + upper = roundup_u64(fst->speed + min_trip_distance, fan->fan_trip_granularity); + if (fst->speed <= min_trip_distance) { + lower = 0; + } else { + /* + * Valid fan speed values cannot be larger than 32 bit, so + * we can safely assume that no overflow will happen here. + */ + lower = rounddown((u32)fst->speed - min_trip_distance, fan->fan_trip_granularity); + } + + return acpi_fan_dsm_set_trip_points(dev, upper, lower); +} + static void acpi_fan_notify_handler(acpi_handle handle, u32 event, void *context) { struct device *dev = context; @@ -323,8 +472,13 @@ static void acpi_fan_notify_handler(acpi_handle handle, u32 event, void *context * receive an ACPI event indicating that the fan state has changed. */ ret = acpi_fan_get_fst(handle, &fst); - if (ret < 0) + if (ret < 0) { dev_err(dev, "Error retrieving current fan status: %d\n", ret); + } else { + ret = acpi_fan_dsm_update_trips_points(dev, &fst); + if (ret < 0) + dev_err(dev, "Failed to update trip points: %d\n", ret); + } acpi_fan_notify_hwmon(dev); acpi_bus_generate_netlink_event("fan", dev_name(dev), event, 0); @@ -394,6 +548,10 @@ static int acpi_fan_probe(struct platform_device *pdev) } if (fan->has_fst) { + result = acpi_fan_dsm_init(&pdev->dev); + if (result) + return result; + result = devm_acpi_fan_create_hwmon(&pdev->dev); if (result) return result; @@ -402,6 +560,12 @@ static int acpi_fan_probe(struct platform_device *pdev) if (result) return result; + result = acpi_fan_dsm_start(&pdev->dev); + if (result) { + dev_err(&pdev->dev, "Failed to start Microsoft fan extensions\n"); + return result; + } + result = acpi_fan_create_attributes(device); if (result) return result; @@ -487,8 +651,14 @@ static int acpi_fan_suspend(struct device *dev) static int acpi_fan_resume(struct device *dev) { - int result; struct acpi_fan *fan = dev_get_drvdata(dev); + int result; + + if (fan->has_fst) { + result = acpi_fan_dsm_start(dev); + if (result) + dev_err(dev, "Failed to start Microsoft fan extensions: %d\n", result); + } if (fan->acpi4) return 0; From 0327c504e274538950abdf5a60f867b73ecf2a54 Mon Sep 17 00:00:00 2001 From: Marco Crivellari Date: Thu, 30 Oct 2025 16:47:35 +0100 Subject: [PATCH 16/30] ACPI: scan: replace use of system_unbound_wq with system_dfl_wq Currently if a user enqueue a work item using schedule_delayed_work() the used wq is "system_wq" (per-cpu wq) while queue_delayed_work() use WORK_CPU_UNBOUND (used when a cpu is not specified). The same applies to schedule_work() that is using system_wq and queue_work(), that makes use again of WORK_CPU_UNBOUND. This lack of consistency cannot be addressed without refactoring the API. system_unbound_wq should be the default workqueue so as not to enforce locality constraints for random work whenever it's not required. Adding system_dfl_wq to encourage its use when unbound work should be used. The old system_unbound_wq will be kept for a few release cycles. Suggested-by: Tejun Heo Signed-off-by: Marco Crivellari Link: https://patch.msgid.link/20251030154739.262582-2-marco.crivellari@suse.com Signed-off-by: Rafael J. Wysocki --- drivers/acpi/scan.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/acpi/scan.c b/drivers/acpi/scan.c index ef16d58b2949..e0c0b5a50d26 100644 --- a/drivers/acpi/scan.c +++ b/drivers/acpi/scan.c @@ -2397,7 +2397,7 @@ static bool acpi_scan_clear_dep_queue(struct acpi_device *adev) * initial enumeration of devices is complete, put it into the unbound * workqueue. */ - queue_work(system_unbound_wq, &cdw->work); + queue_work(system_dfl_wq, &cdw->work); return true; } From 6447ece47c7399894f36bbfda3dfce13d0330c76 Mon Sep 17 00:00:00 2001 From: Marco Crivellari Date: Thu, 30 Oct 2025 16:47:36 +0100 Subject: [PATCH 17/30] ACPI: OSL: replace use of system_wq with system_percpu_wq Currently if a user enqueue a work item using schedule_delayed_work() the used wq is "system_wq" (per-cpu wq) while queue_delayed_work() use WORK_CPU_UNBOUND (used when a cpu is not specified). The same applies to schedule_work() that is using system_wq and queue_work(), that makes use again of WORK_CPU_UNBOUND. This lack of consistency cannot be addressed without refactoring the API. system_wq should be the per-cpu workqueue, yet in this name nothing makes that clear, so replace system_wq with system_percpu_wq. The old wq (system_wq) will be kept for a few release cycles. Suggested-by: Tejun Heo Signed-off-by: Marco Crivellari Link: https://patch.msgid.link/20251030154739.262582-3-marco.crivellari@suse.com Signed-off-by: Rafael J. Wysocki --- drivers/acpi/osl.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/acpi/osl.c b/drivers/acpi/osl.c index 5ff343096ece..a79a5d47bdb8 100644 --- a/drivers/acpi/osl.c +++ b/drivers/acpi/osl.c @@ -398,7 +398,7 @@ static void acpi_os_drop_map_ref(struct acpi_ioremap *map) list_del_rcu(&map->list); INIT_RCU_WORK(&map->track.rwork, acpi_os_map_remove); - queue_rcu_work(system_wq, &map->track.rwork); + queue_rcu_work(system_percpu_wq, &map->track.rwork); } /** From 87c21e240659c859c7db538df6b1eee9db423013 Mon Sep 17 00:00:00 2001 From: Marco Crivellari Date: Thu, 30 Oct 2025 16:47:37 +0100 Subject: [PATCH 18/30] ACPI: EC: Add WQ_PERCPU to alloc_workqueue() users MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Currently if a user enqueue a work item using schedule_delayed_work() the used wq is "system_wq" (per-cpu wq) while queue_delayed_work() use WORK_CPU_UNBOUND (used when a cpu is not specified). The same applies to schedule_work() that is using system_wq and queue_work(), that makes use again of WORK_CPU_UNBOUND. This lack of consistentcy cannot be addressed without refactoring the API. alloc_workqueue() treats all queues as per-CPU by default, while unbound workqueues must opt-in via WQ_UNBOUND. This default is suboptimal: most workloads benefit from unbound queues, allowing the scheduler to place worker threads where they’re needed and reducing noise when CPUs are isolated. This change adds a new WQ_PERCPU flag to explicitly request alloc_workqueue() to be per-cpu when WQ_UNBOUND has not been specified. With the introduction of the WQ_PERCPU flag (equivalent to !WQ_UNBOUND), any alloc_workqueue() caller that doesn’t explicitly specify WQ_UNBOUND must now use WQ_PERCPU. Once migration is complete, WQ_UNBOUND can be removed and unbound will become the implicit default. Suggested-by: Tejun Heo Signed-off-by: Marco Crivellari [ rjw: Subject adjustment ] Link: https://patch.msgid.link/20251030154739.262582-4-marco.crivellari@suse.com Signed-off-by: Rafael J. Wysocki --- drivers/acpi/ec.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/acpi/ec.c b/drivers/acpi/ec.c index 7855bbf752b1..59b3d50ff01e 100644 --- a/drivers/acpi/ec.c +++ b/drivers/acpi/ec.c @@ -2294,7 +2294,8 @@ static int acpi_ec_init_workqueues(void) ec_wq = alloc_ordered_workqueue("kec", 0); if (!ec_query_wq) - ec_query_wq = alloc_workqueue("kec_query", 0, ec_max_queries); + ec_query_wq = alloc_workqueue("kec_query", WQ_PERCPU, + ec_max_queries); if (!ec_wq || !ec_query_wq) { acpi_ec_destroy_workqueues(); From ec4291f524a3218067e38dc51c1ea1d3deb8f5d9 Mon Sep 17 00:00:00 2001 From: Marco Crivellari Date: Thu, 30 Oct 2025 16:47:38 +0100 Subject: [PATCH 19/30] ACPI: OSL: Add WQ_PERCPU to alloc_workqueue() users MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Currently if a user enqueue a work item using schedule_delayed_work() the used wq is "system_wq" (per-cpu wq) while queue_delayed_work() use WORK_CPU_UNBOUND (used when a cpu is not specified). The same applies to schedule_work() that is using system_wq and queue_work(), that makes use again of WORK_CPU_UNBOUND. This lack of consistentcy cannot be addressed without refactoring the API. alloc_workqueue() treats all queues as per-CPU by default, while unbound workqueues must opt-in via WQ_UNBOUND. This default is suboptimal: most workloads benefit from unbound queues, allowing the scheduler to place worker threads where they’re needed and reducing noise when CPUs are isolated. This change adds a new WQ_PERCPU flag to explicitly request alloc_workqueue() to be per-cpu when WQ_UNBOUND has not been specified. With the introduction of the WQ_PERCPU flag (equivalent to !WQ_UNBOUND), any alloc_workqueue() caller that doesn’t explicitly specify WQ_UNBOUND must now use WQ_PERCPU. Once migration is complete, WQ_UNBOUND can be removed and unbound will become the implicit default. Suggested-by: Tejun Heo Signed-off-by: Marco Crivellari [ rjw: Subject adjustment ] Link: https://patch.msgid.link/20251030154739.262582-5-marco.crivellari@suse.com Signed-off-by: Rafael J. Wysocki --- drivers/acpi/osl.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/acpi/osl.c b/drivers/acpi/osl.c index a79a5d47bdb8..05393a7315fe 100644 --- a/drivers/acpi/osl.c +++ b/drivers/acpi/osl.c @@ -1694,8 +1694,8 @@ acpi_status __init acpi_os_initialize(void) acpi_status __init acpi_os_initialize1(void) { - kacpid_wq = alloc_workqueue("kacpid", 0, 1); - kacpi_notify_wq = alloc_workqueue("kacpi_notify", 0, 0); + kacpid_wq = alloc_workqueue("kacpid", WQ_PERCPU, 1); + kacpi_notify_wq = alloc_workqueue("kacpi_notify", WQ_PERCPU, 0); kacpi_hotplug_wq = alloc_ordered_workqueue("kacpi_hotplug", 0); BUG_ON(!kacpid_wq); BUG_ON(!kacpi_notify_wq); From 2817e6fa84ac8fe54efc208f5676698275ff6412 Mon Sep 17 00:00:00 2001 From: Marco Crivellari Date: Thu, 30 Oct 2025 16:47:39 +0100 Subject: [PATCH 20/30] ACPI: thermal: Add WQ_PERCPU to alloc_workqueue() users MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Currently if a user enqueue a work item using schedule_delayed_work() the used wq is "system_wq" (per-cpu wq) while queue_delayed_work() use WORK_CPU_UNBOUND (used when a cpu is not specified). The same applies to schedule_work() that is using system_wq and queue_work(), that makes use again of WORK_CPU_UNBOUND. This lack of consistentcy cannot be addressed without refactoring the API. alloc_workqueue() treats all queues as per-CPU by default, while unbound workqueues must opt-in via WQ_UNBOUND. This default is suboptimal: most workloads benefit from unbound queues, allowing the scheduler to place worker threads where they’re needed and reducing noise when CPUs are isolated. This change adds a new WQ_PERCPU flag to explicitly request alloc_workqueue() to be per-cpu when WQ_UNBOUND has not been specified. With the introduction of the WQ_PERCPU flag (equivalent to !WQ_UNBOUND), any alloc_workqueue() caller that doesn’t explicitly specify WQ_UNBOUND must now use WQ_PERCPU. Once migration is complete, WQ_UNBOUND can be removed and unbound will become the implicit default. Suggested-by: Tejun Heo Signed-off-by: Marco Crivellari [ rjw: Subject adjustment ] Link: https://patch.msgid.link/20251030154739.262582-6-marco.crivellari@suse.com Signed-off-by: Rafael J. Wysocki --- drivers/acpi/thermal.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/acpi/thermal.c b/drivers/acpi/thermal.c index 8537395b417b..a511f9ea0267 100644 --- a/drivers/acpi/thermal.c +++ b/drivers/acpi/thermal.c @@ -1060,7 +1060,8 @@ static int __init acpi_thermal_init(void) } acpi_thermal_pm_queue = alloc_workqueue("acpi_thermal_pm", - WQ_HIGHPRI | WQ_MEM_RECLAIM, 0); + WQ_HIGHPRI | WQ_MEM_RECLAIM | WQ_PERCPU, + 0); if (!acpi_thermal_pm_queue) return -ENODEV; From 77ca1612b80154c62d4002a3e303bc2c2999aac7 Mon Sep 17 00:00:00 2001 From: Huisong Li Date: Wed, 5 Nov 2025 17:36:47 +0800 Subject: [PATCH 21/30] ACPI: processor: idle: Drop redundant C-state count checks acpi_processor_setup_cstates() and acpi_processor_setup_cpuidle_cx() are called after successfully obtaining power information. Among other things, these setup functions check the C-state count against zero. However, that check is done by acpi_processor_get_power_info_cst() which will cause acpi_processor_get_power_info() to fail if it does no pass, so the checks in the two functions mentioned above are redundant. Drop those redundant checks. No intentional functional impact. Signed-off-by: Huisong Li [ rjw: Subject and changelog rewrite ] Link: https://patch.msgid.link/20251105093647.3557248-1-lihuisong@huawei.com Signed-off-by: Rafael J. Wysocki --- drivers/acpi/processor_idle.c | 22 +++++++--------------- 1 file changed, 7 insertions(+), 15 deletions(-) diff --git a/drivers/acpi/processor_idle.c b/drivers/acpi/processor_idle.c index 341825e8ac63..22909fccf0b1 100644 --- a/drivers/acpi/processor_idle.c +++ b/drivers/acpi/processor_idle.c @@ -732,8 +732,8 @@ static int __cpuidle acpi_idle_enter_s2idle(struct cpuidle_device *dev, return 0; } -static int acpi_processor_setup_cpuidle_cx(struct acpi_processor *pr, - struct cpuidle_device *dev) +static void acpi_processor_setup_cpuidle_cx(struct acpi_processor *pr, + struct cpuidle_device *dev) { int i, count = ACPI_IDLE_STATE_START; struct acpi_processor_cx *cx; @@ -753,14 +753,9 @@ static int acpi_processor_setup_cpuidle_cx(struct acpi_processor *pr, if (count == CPUIDLE_STATE_MAX) break; } - - if (!count) - return -EINVAL; - - return 0; } -static int acpi_processor_setup_cstates(struct acpi_processor *pr) +static void acpi_processor_setup_cstates(struct acpi_processor *pr) { int i, count; struct acpi_processor_cx *cx; @@ -822,11 +817,6 @@ static int acpi_processor_setup_cstates(struct acpi_processor *pr) } drv->state_count = count; - - if (!count) - return -EINVAL; - - return 0; } static inline void acpi_processor_cstate_first_run_checks(void) @@ -1241,7 +1231,8 @@ static int acpi_processor_setup_cpuidle_states(struct acpi_processor *pr) if (pr->flags.has_lpi) return acpi_processor_setup_lpi_states(pr); - return acpi_processor_setup_cstates(pr); + acpi_processor_setup_cstates(pr); + return 0; } /** @@ -1261,7 +1252,8 @@ static int acpi_processor_setup_cpuidle_dev(struct acpi_processor *pr, if (pr->flags.has_lpi) return acpi_processor_ffh_lpi_probe(pr->id); - return acpi_processor_setup_cpuidle_cx(pr, dev); + acpi_processor_setup_cpuidle_cx(pr, dev); + return 0; } static int acpi_processor_get_power_info(struct acpi_processor *pr) From 2f58be82fc2f17ff15f0224a2ad0732cc11ee04c Mon Sep 17 00:00:00 2001 From: Kaushlendra Kumar Date: Tue, 28 Oct 2025 10:45:54 +0530 Subject: [PATCH 22/30] ACPI: DPTF: Use ACPI_FREE() for ACPI buffer deallocation Replace kfree() with ACPI_FREE() in pch_fivr_read() to follow ACPICA memory management conventions. While functionally equivalent in Linux (ACPI_FREE() is implemented as kfree()), using ACPI_FREE() maintains consistency with ACPICA coding standards for deallocating ACPI buffer objects. Signed-off-by: Kaushlendra Kumar [ rjw: Subject and changelog edits ] Link: https://patch.msgid.link/20251028051554.2862049-1-kaushlendra.kumar@intel.com Signed-off-by: Rafael J. Wysocki --- drivers/acpi/dptf/dptf_pch_fivr.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/acpi/dptf/dptf_pch_fivr.c b/drivers/acpi/dptf/dptf_pch_fivr.c index 952216c67d58..8d7e555929d3 100644 --- a/drivers/acpi/dptf/dptf_pch_fivr.c +++ b/drivers/acpi/dptf/dptf_pch_fivr.c @@ -41,7 +41,7 @@ static int pch_fivr_read(acpi_handle handle, char *method, struct pch_fivr_resp ret = 0; release_buffer: - kfree(buffer.pointer); + ACPI_FREE(buffer.pointer); return ret; } From 593ee49222a0d751062fd9a5e4a963ade4ec028a Mon Sep 17 00:00:00 2001 From: Haotian Zhang Date: Tue, 11 Nov 2025 15:50:00 +0800 Subject: [PATCH 23/30] ACPI: property: Fix fwnode refcount leak in acpi_fwnode_graph_parse_endpoint() acpi_fwnode_graph_parse_endpoint() calls fwnode_get_parent() to obtain the parent fwnode but returns without calling fwnode_handle_put() on it. This potentially leads to a fwnode refcount leak and prevents the parent node from being released properly. Call fwnode_handle_put() on the parent fwnode before returning to prevent the leak from occurring. Fixes: 3b27d00e7b6d ("device property: Move fwnode graph ops to firmware specific locations") Signed-off-by: Haotian Zhang Reviewed-by: Sakari Ailus [ rjw: Changelog edits ] Link: https://patch.msgid.link/20251111075000.1828-1-vulab@iscas.ac.cn Signed-off-by: Rafael J. Wysocki --- drivers/acpi/property.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/acpi/property.c b/drivers/acpi/property.c index 73171d277ab6..57a9c18ec673 100644 --- a/drivers/acpi/property.c +++ b/drivers/acpi/property.c @@ -1718,6 +1718,7 @@ static int acpi_fwnode_graph_parse_endpoint(const struct fwnode_handle *fwnode, if (fwnode_property_read_u32(fwnode, "reg", &endpoint->id)) fwnode_property_read_u32(fwnode, "endpoint", &endpoint->id); + fwnode_handle_put(port_fwnode); return 0; } From e96190da17a2b2b2c3c92d06b31ce36c749fa97f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9=20Rebe?= Date: Tue, 18 Nov 2025 14:59:42 +0100 Subject: [PATCH 24/30] PNP: Fix ISAPNP to generate uevents to auto-load modules MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Currently ISAPNP devices do not generate an uevent for udev to auto-load the driver modules for Creative SoundBlaster or Gravis UltraSound to just work. Signed-off-by: René Rebe [ rjw: Subject edits ] Link: https://patch.msgid.link/20251118.145942.1445519082574147037.rene@exactco.de Signed-off-by: Rafael J. Wysocki --- drivers/pnp/driver.c | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/drivers/pnp/driver.c b/drivers/pnp/driver.c index 7de7aabb275e..05e9840bc3d4 100644 --- a/drivers/pnp/driver.c +++ b/drivers/pnp/driver.c @@ -150,6 +150,24 @@ static void pnp_device_shutdown(struct device *dev) drv->shutdown(pnp_dev); } +static int pnp_uevent(const struct device *dev, struct kobj_uevent_env *env) +{ + struct pnp_id *pos; + const struct pnp_dev *pnp_dev = to_pnp_dev(dev); + + if (!dev) + return -ENODEV; + + pos = pnp_dev->id; + while (pos) { + if (add_uevent_var(env, "MODALIAS=pnp:d%s", pos->id)) + return -ENOMEM; + pos = pos->next; + } + + return 0; +} + static int pnp_bus_match(struct device *dev, const struct device_driver *drv) { struct pnp_dev *pnp_dev = to_pnp_dev(dev); @@ -259,6 +277,7 @@ static const struct dev_pm_ops pnp_bus_dev_pm_ops = { const struct bus_type pnp_bus_type = { .name = "pnp", .match = pnp_bus_match, + .uevent = pnp_uevent, .probe = pnp_device_probe, .remove = pnp_device_remove, .shutdown = pnp_device_shutdown, From c964081d602f96f8811f99a938e5f8a164060bd7 Mon Sep 17 00:00:00 2001 From: David Laight Date: Wed, 19 Nov 2025 22:41:09 +0000 Subject: [PATCH 25/30] ACPI: property: use min() instead of min_t() min_t(unsigned int, a, b) casts an 'unsigned long' to 'unsigned int'. Use min(a, b) instead as it promotes any 'unsigned int' to 'unsigned long' and so cannot discard significant bits. In this case the 'unsigned long' value is small enough that the result is ok. Detected by an extra check added to min_t(). Signed-off-by: David Laight [ rjw: Subject adjustment ] Link: https://patch.msgid.link/20251119224140.8616-14-david.laight.linux@gmail.com Signed-off-by: Rafael J. Wysocki --- drivers/acpi/property.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/acpi/property.c b/drivers/acpi/property.c index 57a9c18ec673..18e90067d567 100644 --- a/drivers/acpi/property.c +++ b/drivers/acpi/property.c @@ -1280,7 +1280,7 @@ static int acpi_data_prop_read(const struct acpi_device_data *data, ret = acpi_copy_property_array_uint(items, (u64 *)val, nval); break; case DEV_PROP_STRING: - nval = min_t(u32, nval, obj->package.count); + nval = min(nval, obj->package.count); if (nval == 0) return -ENODATA; From 8974573ba45493af6120093cdf88ce5511aff935 Mon Sep 17 00:00:00 2001 From: Malaya Kumar Rout Date: Thu, 20 Nov 2025 22:29:58 +0530 Subject: [PATCH 26/30] ACPI: tools: pfrut: fix memory leak and resource leak in pfrut.c Static analysis found an issue in pfrut.c cppcheck output before this patch: tools/power/acpi/tools/pfrut/pfrut.c:225:3: error: Resource leak: fd_update [resourceLeak] tools/power/acpi/tools/pfrut/pfrut.c:269:3: error: Resource leak: fd_update [resourceLeak] tools/power/acpi/tools/pfrut/pfrut.c:269:3: error: Resource leak: fd_update_log [resourceLeak] tools/power/acpi/tools/pfrut/pfrut.c:365:4: error: Memory leak: addr_map_capsule [memleak] tools/power/acpi/tools/pfrut/pfrut.c:424:4: error: Memory leak: log_buf [memleak] cppcheck output after this patch: No resource leaks found Fix by closing file descriptors and freeing allocated memory. Signed-off-by: Malaya Kumar Rout Link: https://patch.msgid.link/20251120170001.251968-1-mrout@redhat.com Signed-off-by: Rafael J. Wysocki --- tools/power/acpi/tools/pfrut/pfrut.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/tools/power/acpi/tools/pfrut/pfrut.c b/tools/power/acpi/tools/pfrut/pfrut.c index 44a9ecbd91e8..4d9b0177c312 100644 --- a/tools/power/acpi/tools/pfrut/pfrut.c +++ b/tools/power/acpi/tools/pfrut/pfrut.c @@ -222,6 +222,7 @@ int main(int argc, char *argv[]) fd_update_log = open("/dev/acpi_pfr_telemetry0", O_RDWR); if (fd_update_log < 0) { printf("PFRT device not supported - Quit...\n"); + close(fd_update); return 1; } @@ -265,7 +266,8 @@ int main(int argc, char *argv[]) printf("chunk2_size:%d\n", data_info.chunk2_size); printf("rollover_cnt:%d\n", data_info.rollover_cnt); printf("reset_cnt:%d\n", data_info.reset_cnt); - + close(fd_update); + close(fd_update_log); return 0; } @@ -358,6 +360,7 @@ int main(int argc, char *argv[]) if (ret == -1) { perror("Failed to load capsule file"); + munmap(addr_map_capsule, st.st_size); close(fd_capsule); close(fd_update); close(fd_update_log); @@ -420,7 +423,7 @@ int main(int argc, char *argv[]) if (p_mmap == MAP_FAILED) { perror("mmap error."); close(fd_update_log); - + free(log_buf); return 1; } From 9d6c58dae8f6590c746ac5d0012ffe14a77539f0 Mon Sep 17 00:00:00 2001 From: Cryolitia PukNgae Date: Tue, 25 Nov 2025 16:14:38 +0800 Subject: [PATCH 27/30] ACPICA: Avoid walking the Namespace if start_node is NULL Although commit 0c9992315e73 ("ACPICA: Avoid walking the ACPI Namespace if it is not there") fixed the situation when both start_node and acpi_gbl_root_node are NULL, the Linux kernel mainline now still crashed on Honor Magicbook 14 Pro [1]. That happens due to the access to the member of parent_node in acpi_ns_get_next_node(). The NULL pointer dereference will always happen, no matter whether or not the start_node is equal to ACPI_ROOT_OBJECT, so move the check of start_node being NULL out of the if block. Unfortunately, all the attempts to contact Honor have failed, they refused to provide any technical support for Linux. The bad DSDT table's dump could be found on GitHub [2]. DMI: HONOR FMB-P/FMB-P-PCB, BIOS 1.13 05/08/2025 Link: https://github.com/acpica/acpica/commit/1c1b57b9eba4554cb132ee658dd942c0210ed20d Link: https://gist.github.com/Cryolitia/a860ffc97437dcd2cd988371d5b73ed7 [1] Link: https://github.com/denis-bb/honor-fmb-p-dsdt [2] Signed-off-by: Cryolitia PukNgae Reviewed-by: WangYuli [ rjw: Subject adjustment, changelog edits ] Link: https://patch.msgid.link/20251125-acpica-v1-1-99e63b1b25f8@linux.dev Signed-off-by: Rafael J. Wysocki --- drivers/acpi/acpica/nswalk.c | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/drivers/acpi/acpica/nswalk.c b/drivers/acpi/acpica/nswalk.c index a2ac06a26e92..5670ff5a43cd 100644 --- a/drivers/acpi/acpica/nswalk.c +++ b/drivers/acpi/acpica/nswalk.c @@ -169,9 +169,12 @@ acpi_ns_walk_namespace(acpi_object_type type, if (start_node == ACPI_ROOT_OBJECT) { start_node = acpi_gbl_root_node; - if (!start_node) { - return_ACPI_STATUS(AE_NO_NAMESPACE); - } + } + + /* Avoid walking the namespace if the StartNode is NULL */ + + if (!start_node) { + return_ACPI_STATUS(AE_NO_NAMESPACE); } /* Null child means "get first node" */ From 17e7972979e147cc51d4a165e6b6b0f93273ca68 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9=20Rebe?= Date: Wed, 26 Nov 2025 16:55:13 +0100 Subject: [PATCH 28/30] ACPI: processor_core: fix map_x2apic_id for amd-pstate on am4 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit On all AMD AM4 systems I have seen, e.g ASUS X470-i, Pro WS X570 Ace and equivalent Gigabyte, amd-pstate does not initialize when the x2apic is enabled in the BIOS. Kernel debug messages include: [ 0.315438] acpi LNXCPU:00: Failed to get CPU physical ID. [ 0.354756] ACPI CPPC: No CPC descriptor for CPU:0 [ 0.714951] amd_pstate: the _CPC object is not present in SBIOS or ACPI disabled I tracked this down to map_x2apic_id() checking device_declaration passed in via the type argument of acpi_get_phys_id() via map_madt_entry() while map_lapic_id() does not. It appears these BIOSes use Processor statements for declaring the CPUs in the ACPI namespace instead of processor device objects (which should have been used). CPU declarations via Processor statements were deprecated in ACPI 6.0 that was released 10 years ago. They should not be used any more in any contemporary platform firmware. I tried to contact Asus support multiple times, but never received a reply nor did any BIOS update ever change this. Fix amd-pstate w/ x2apic on am4 by allowing map_x2apic_id() to work with CPUs declared via Processor statements for IDs less than 255, which is consistent with ACPI 5.0 that still allowed Processor statements to be used for declaring CPUs. Fixes: 7237d3de78ff ("x86, ACPI: add support for x2apic ACPI extensions") Signed-off-by: René Rebe [ rjw: Changelog edits ] Link: https://patch.msgid.link/20251126.165513.1373131139292726554.rene@exactco.de Signed-off-by: Rafael J. Wysocki --- drivers/acpi/processor_core.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/acpi/processor_core.c b/drivers/acpi/processor_core.c index 9b6b71a2ffb5..a4498357bd16 100644 --- a/drivers/acpi/processor_core.c +++ b/drivers/acpi/processor_core.c @@ -54,7 +54,7 @@ static int map_x2apic_id(struct acpi_subtable_header *entry, if (!(apic->lapic_flags & ACPI_MADT_ENABLED)) return -ENODEV; - if (device_declaration && (apic->uid == acpi_id)) { + if (apic->uid == acpi_id && (device_declaration || acpi_id < 255)) { *apic_id = apic->local_apic_id; return 0; } From 037dada8bbb14e42b6596e30557b08ee051f7b1b Mon Sep 17 00:00:00 2001 From: Chu Guangqing Date: Tue, 25 Nov 2025 10:14:31 +0800 Subject: [PATCH 29/30] ACPI: LPSS: Fix a spelling mistake Fix spelling by replacing "successfull" with "successful". Signed-off-by: Chu Guangqing [ rjw: Changelog edits ] Link: https://patch.msgid.link/20251125021431.2243-1-chuguangqing@inspur.com Signed-off-by: Rafael J. Wysocki --- drivers/acpi/x86/lpss.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/acpi/x86/lpss.c b/drivers/acpi/x86/lpss.c index 6daa6372f980..1dcb80ab0d23 100644 --- a/drivers/acpi/x86/lpss.c +++ b/drivers/acpi/x86/lpss.c @@ -181,7 +181,7 @@ static void byt_i2c_setup(struct lpss_private_data *pdata) acpi_status status; u64 uid; - /* Expected to always be successfull, but better safe then sorry */ + /* Expected to always be successful, but better safe then sorry */ if (!acpi_dev_uid_to_integer(pdata->adev, &uid) && uid) { /* Detect I2C bus shared with PUNIT and ignore its d3 status */ status = acpi_evaluate_integer(handle, "_SEM", NULL, &shared_host); From a508939e15356d80369ea881c05526a168fcfd6c Mon Sep 17 00:00:00 2001 From: Chu Guangqing Date: Tue, 25 Nov 2025 10:24:03 +0800 Subject: [PATCH 30/30] ACPI: PM: Fix a spelling mistake Fix spelling by replacing "interrups" with "interrupts". Signed-off-by: Chu Guangqing [ rjw: Changelog edits ] Link: https://patch.msgid.link/20251125022403.2614-1-chuguangqing@inspur.com Signed-off-by: Rafael J. Wysocki --- drivers/acpi/sleep.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/acpi/sleep.c b/drivers/acpi/sleep.c index c8ee8e42b0f6..68943b98333d 100644 --- a/drivers/acpi/sleep.c +++ b/drivers/acpi/sleep.c @@ -642,7 +642,7 @@ static int acpi_suspend_enter(suspend_state_t pm_state) /* * Disable all GPE and clear their status bits before interrupts are * enabled. Some GPEs (like wakeup GPEs) have no handlers and this can - * prevent them from producing spurious interrups. + * prevent them from producing spurious interrupts. * * acpi_leave_sleep_state() will reenable specific GPEs later. *