From f59a905b962c34642e862b5edec35c0eda72d70d Mon Sep 17 00:00:00 2001 From: Alex Deucher Date: Wed, 5 May 2021 09:20:32 -0400 Subject: ACPI: PM: s2idle: Add missing LPS0 functions for AMD These are supposedly not required for AMD platforms, but at least some HP laptops seem to require it to properly turn off the keyboard backlight. Based on a patch from Marcin Bachry . Bug: https://gitlab.freedesktop.org/drm/amd/-/issues/1230 Reviewed-by: Hans de Goede Signed-off-by: Alex Deucher Signed-off-by: Rafael J. Wysocki --- drivers/acpi/x86/s2idle.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/acpi/x86/s2idle.c b/drivers/acpi/x86/s2idle.c index 2b69536cdccb..2d7ddb8a8cb6 100644 --- a/drivers/acpi/x86/s2idle.c +++ b/drivers/acpi/x86/s2idle.c @@ -42,6 +42,8 @@ static const struct acpi_device_id lps0_device_ids[] = { /* AMD */ #define ACPI_LPS0_DSM_UUID_AMD "e3f32452-febc-43ce-9039-932122d37721" +#define ACPI_LPS0_ENTRY_AMD 2 +#define ACPI_LPS0_EXIT_AMD 3 #define ACPI_LPS0_SCREEN_OFF_AMD 4 #define ACPI_LPS0_SCREEN_ON_AMD 5 @@ -408,6 +410,7 @@ int acpi_s2idle_prepare_late(void) if (acpi_s2idle_vendor_amd()) { acpi_sleep_run_lps0_dsm(ACPI_LPS0_SCREEN_OFF_AMD); + acpi_sleep_run_lps0_dsm(ACPI_LPS0_ENTRY_AMD); } else { acpi_sleep_run_lps0_dsm(ACPI_LPS0_SCREEN_OFF); acpi_sleep_run_lps0_dsm(ACPI_LPS0_ENTRY); @@ -422,6 +425,7 @@ void acpi_s2idle_restore_early(void) return; if (acpi_s2idle_vendor_amd()) { + acpi_sleep_run_lps0_dsm(ACPI_LPS0_EXIT_AMD); acpi_sleep_run_lps0_dsm(ACPI_LPS0_SCREEN_ON_AMD); } else { acpi_sleep_run_lps0_dsm(ACPI_LPS0_EXIT); -- cgit v1.2.3 From b9370dceabb7841c5e65ce4ee4405b9db5231fc4 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Fri, 14 May 2021 21:08:51 +0200 Subject: ACPI: PM / fan: Put fan device IDs into separate header file The ACPI fan device IDs are shared between the fan driver and the device power management code. The former is modular, so it needs to include the table of device IDs for module autoloading and the latter needs that list to avoid attaching the generic ACPI PM domain to fan devices (which doesn't make sense) possibly before the fan driver module is loaded. Unfortunately, that requires the list of fan device IDs to be updated in two places which is prone to mistakes, so put it into a symbol definition in a separate header file so there is only one copy of it in case it needs to be updated again in the future. Fixes: b9ea0bae260f ("ACPI: PM: Avoid attaching ACPI PM domain to certain devices") Signed-off-by: Rafael J. Wysocki --- drivers/acpi/device_pm.c | 6 ++---- drivers/acpi/fan.c | 7 +++---- drivers/acpi/fan.h | 13 +++++++++++++ 3 files changed, 18 insertions(+), 8 deletions(-) create mode 100644 drivers/acpi/fan.h diff --git a/drivers/acpi/device_pm.c b/drivers/acpi/device_pm.c index d260bc1f3e6e..9d2d3b9bb8b5 100644 --- a/drivers/acpi/device_pm.c +++ b/drivers/acpi/device_pm.c @@ -20,6 +20,7 @@ #include #include +#include "fan.h" #include "internal.h" /** @@ -1310,10 +1311,7 @@ int acpi_dev_pm_attach(struct device *dev, bool power_on) * with the generic ACPI PM domain. */ static const struct acpi_device_id special_pm_ids[] = { - {"PNP0C0B", }, /* Generic ACPI fan */ - {"INT3404", }, /* Fan */ - {"INTC1044", }, /* Fan for Tiger Lake generation */ - {"INTC1048", }, /* Fan for Alder Lake generation */ + ACPI_FAN_DEVICE_IDS, {} }; struct acpi_device *adev = ACPI_COMPANION(dev); diff --git a/drivers/acpi/fan.c b/drivers/acpi/fan.c index 66c3983f0ccc..5cd0ceb50bc8 100644 --- a/drivers/acpi/fan.c +++ b/drivers/acpi/fan.c @@ -16,6 +16,8 @@ #include #include +#include "fan.h" + MODULE_AUTHOR("Paul Diefenbaugh"); MODULE_DESCRIPTION("ACPI Fan Driver"); MODULE_LICENSE("GPL"); @@ -24,10 +26,7 @@ static int acpi_fan_probe(struct platform_device *pdev); static int acpi_fan_remove(struct platform_device *pdev); static const struct acpi_device_id fan_device_ids[] = { - {"PNP0C0B", 0}, - {"INT3404", 0}, - {"INTC1044", 0}, - {"INTC1048", 0}, + ACPI_FAN_DEVICE_IDS, {"", 0}, }; MODULE_DEVICE_TABLE(acpi, fan_device_ids); diff --git a/drivers/acpi/fan.h b/drivers/acpi/fan.h new file mode 100644 index 000000000000..dc9a6efa514b --- /dev/null +++ b/drivers/acpi/fan.h @@ -0,0 +1,13 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ + +/* + * ACPI fan device IDs are shared between the fan driver and the device power + * management code. + * + * Add new device IDs before the generic ACPI fan one. + */ +#define ACPI_FAN_DEVICE_IDS \ + {"INT3404", }, /* Fan */ \ + {"INTC1044", }, /* Fan for Tiger Lake generation */ \ + {"INTC1048", }, /* Fan for Alder Lake generation */ \ + {"PNP0C0B", } /* Generic ACPI fan */ -- cgit v1.2.3 From 587024b8210d4eeeab457323141c92e134cd3dbf Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Mon, 24 May 2021 17:24:16 +0200 Subject: ACPI: power: Use u8 as the power resource state data type Use u8 as the data type for representing the state of an ACPI power resource. It is s not necessary to use int for that and because subsequent changes are going to use ACPI_POWER_RESOURCE_STATE_UNKNOWN, it is better to adjust the data type so that the "unknown" state is represented by the "all ones" value. While at it, clean up acpi_power_get_state() somewhat. No intentional functional impact. Tested-by: Dave Olsthoorn Tested-by: Shujun Wang Signed-off-by: Rafael J. Wysocki --- drivers/acpi/power.c | 27 +++++++++++++++------------ 1 file changed, 15 insertions(+), 12 deletions(-) diff --git a/drivers/acpi/power.c b/drivers/acpi/power.c index 97c9a94a1a30..106a5d37486c 100644 --- a/drivers/acpi/power.c +++ b/drivers/acpi/power.c @@ -182,10 +182,11 @@ int acpi_extract_power_resources(union acpi_object *package, unsigned int start, return err; } -static int acpi_power_get_state(acpi_handle handle, int *state) +static int acpi_power_get_state(acpi_handle handle, u8 *state) { acpi_status status = AE_OK; unsigned long long sta = 0; + u8 cur_state; if (!handle || !state) return -EINVAL; @@ -194,25 +195,24 @@ static int acpi_power_get_state(acpi_handle handle, int *state) if (ACPI_FAILURE(status)) return -ENODEV; - *state = (sta & 0x01)?ACPI_POWER_RESOURCE_STATE_ON: - ACPI_POWER_RESOURCE_STATE_OFF; + cur_state = sta & ACPI_POWER_RESOURCE_STATE_ON; acpi_handle_debug(handle, "Power resource is %s\n", - *state ? "on" : "off"); + cur_state ? "on" : "off"); + *state = cur_state; return 0; } -static int acpi_power_get_list_state(struct list_head *list, int *state) +static int acpi_power_get_list_state(struct list_head *list, u8 *state) { struct acpi_power_resource_entry *entry; - int cur_state; + u8 cur_state = ACPI_POWER_RESOURCE_STATE_OFF; if (!list || !state) return -EINVAL; /* The state of the list is 'on' IFF all resources are 'on'. */ - cur_state = 0; list_for_each_entry(entry, list, node) { struct acpi_power_resource *resource = entry->resource; acpi_handle handle = resource->device.handle; @@ -592,7 +592,7 @@ int acpi_power_wakeup_list_init(struct list_head *list, int *system_level_p) struct acpi_power_resource *resource = entry->resource; acpi_handle handle = resource->device.handle; int result; - int state; + u8 state; mutex_lock(&resource->resource_lock); @@ -789,8 +789,8 @@ int acpi_disable_wakeup_device_power(struct acpi_device *dev) int acpi_power_get_inferred_state(struct acpi_device *device, int *state) { + u8 list_state = ACPI_POWER_RESOURCE_STATE_OFF; int result = 0; - int list_state = 0; int i = 0; if (!device || !state) @@ -919,7 +919,8 @@ struct acpi_device *acpi_add_power_resource(acpi_handle handle) union acpi_object acpi_object; struct acpi_buffer buffer = { sizeof(acpi_object), &acpi_object }; acpi_status status; - int state, result = -ENODEV; + int result; + u8 state; acpi_bus_get_device(handle, &device); if (device) @@ -979,7 +980,8 @@ void acpi_resume_power_resources(void) mutex_lock(&power_resource_list_lock); list_for_each_entry(resource, &acpi_power_resource_list, list_node) { - int result, state; + int result; + u8 state; mutex_lock(&resource->resource_lock); @@ -1012,7 +1014,8 @@ static void acpi_power_turn_off_if_unused(struct acpi_power_resource *resource, if (resource->users > 0) return; } else { - int result, state; + int result; + u8 state; result = acpi_power_get_state(resource->device.handle, &state); if (result || state == ACPI_POWER_RESOURCE_STATE_OFF) -- cgit v1.2.3 From ca84f18798a457e9a92c52882813901e15a3b38b Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Mon, 24 May 2021 17:25:23 +0200 Subject: ACPI: power: Save the last known state of each power resource Currently, there are two ways to check the state of an ACPI power resource and they may not be consistent with each other. The first one is to evaluate the power resource's _STA object and the other one is to check its reference counter value. However, on some systems the value returned by _STA may not be consistent with the value of the power resource's reference counter (for example, on some systems it returns the same value every time for certain power resources). Moreover, evaluating _STA is unnecessary overhead for a power resource for which it has been evaluated already or whose state is otherwise known, because either the _ON or the _OFF method has been executed for it. For this reason, save the state of each power resource in its struct acpi_power_resource object and use the saved value whenever its state needs to be checked, except when its stats is unknown, in which case the _STA method is evaluated for it and the value returned by that method is saved as the last known state of the power resource. Moreover, drop the power resource _STA method evaluation from acpi_add_power_resource(), so as to avoid doing that unnecessarily for power resources that will never be used. Tested-by: Dave Olsthoorn Tested-by: Shujun Wang Signed-off-by: Rafael J. Wysocki --- drivers/acpi/power.c | 50 ++++++++++++++++++++++++++++++++------------------ 1 file changed, 32 insertions(+), 18 deletions(-) diff --git a/drivers/acpi/power.c b/drivers/acpi/power.c index 106a5d37486c..6aeea7649852 100644 --- a/drivers/acpi/power.c +++ b/drivers/acpi/power.c @@ -53,6 +53,7 @@ struct acpi_power_resource { u32 order; unsigned int ref_count; unsigned int users; + u8 state; bool wakeup_enabled; struct mutex resource_lock; struct list_head dependents; @@ -182,15 +183,12 @@ int acpi_extract_power_resources(union acpi_object *package, unsigned int start, return err; } -static int acpi_power_get_state(acpi_handle handle, u8 *state) +static int __get_state(acpi_handle handle, u8 *state) { acpi_status status = AE_OK; unsigned long long sta = 0; u8 cur_state; - if (!handle || !state) - return -EINVAL; - status = acpi_evaluate_integer(handle, "_STA", NULL, &sta); if (ACPI_FAILURE(status)) return -ENODEV; @@ -204,6 +202,20 @@ static int acpi_power_get_state(acpi_handle handle, u8 *state) return 0; } +static int acpi_power_get_state(struct acpi_power_resource *resource, u8 *state) +{ + if (resource->state == ACPI_POWER_RESOURCE_STATE_UNKNOWN) { + int ret; + + ret = __get_state(resource->device.handle, &resource->state); + if (ret) + return ret; + } + + *state = resource->state; + return 0; +} + static int acpi_power_get_list_state(struct list_head *list, u8 *state) { struct acpi_power_resource_entry *entry; @@ -215,11 +227,10 @@ static int acpi_power_get_list_state(struct list_head *list, u8 *state) /* The state of the list is 'on' IFF all resources are 'on'. */ list_for_each_entry(entry, list, node) { struct acpi_power_resource *resource = entry->resource; - acpi_handle handle = resource->device.handle; int result; mutex_lock(&resource->resource_lock); - result = acpi_power_get_state(handle, &cur_state); + result = acpi_power_get_state(resource, &cur_state); mutex_unlock(&resource->resource_lock); if (result) return result; @@ -352,8 +363,12 @@ static int __acpi_power_on(struct acpi_power_resource *resource) acpi_status status = AE_OK; status = acpi_evaluate_object(resource->device.handle, "_ON", NULL, NULL); - if (ACPI_FAILURE(status)) + if (ACPI_FAILURE(status)) { + resource->state = ACPI_POWER_RESOURCE_STATE_UNKNOWN; return -ENODEV; + } + + resource->state = ACPI_POWER_RESOURCE_STATE_ON; pr_debug("Power resource [%s] turned on\n", resource->name); @@ -405,8 +420,12 @@ static int __acpi_power_off(struct acpi_power_resource *resource) status = acpi_evaluate_object(resource->device.handle, "_OFF", NULL, NULL); - if (ACPI_FAILURE(status)) + if (ACPI_FAILURE(status)) { + resource->state = ACPI_POWER_RESOURCE_STATE_UNKNOWN; return -ENODEV; + } + + resource->state = ACPI_POWER_RESOURCE_STATE_OFF; pr_debug("Power resource [%s] turned off\n", resource->name); @@ -590,13 +609,12 @@ int acpi_power_wakeup_list_init(struct list_head *list, int *system_level_p) list_for_each_entry(entry, list, node) { struct acpi_power_resource *resource = entry->resource; - acpi_handle handle = resource->device.handle; int result; u8 state; mutex_lock(&resource->resource_lock); - result = acpi_power_get_state(handle, &state); + result = acpi_power_get_state(resource, &state); if (result) { mutex_unlock(&resource->resource_lock); return result; @@ -920,7 +938,6 @@ struct acpi_device *acpi_add_power_resource(acpi_handle handle) struct acpi_buffer buffer = { sizeof(acpi_object), &acpi_object }; acpi_status status; int result; - u8 state; acpi_bus_get_device(handle, &device); if (device) @@ -947,13 +964,9 @@ struct acpi_device *acpi_add_power_resource(acpi_handle handle) resource->system_level = acpi_object.power_resource.system_level; resource->order = acpi_object.power_resource.resource_order; + resource->state = ACPI_POWER_RESOURCE_STATE_UNKNOWN; - result = acpi_power_get_state(handle, &state); - if (result) - goto err; - - pr_info("%s [%s] (%s)\n", acpi_device_name(device), - acpi_device_bid(device), state ? "on" : "off"); + pr_info("%s [%s]\n", acpi_device_name(device), acpi_device_bid(device)); device->flags.match_driver = true; result = acpi_device_add(device, acpi_release_power_resource); @@ -985,7 +998,8 @@ void acpi_resume_power_resources(void) mutex_lock(&resource->resource_lock); - result = acpi_power_get_state(resource->device.handle, &state); + resource->state = ACPI_POWER_RESOURCE_STATE_UNKNOWN; + result = acpi_power_get_state(resource, &state); if (result) { mutex_unlock(&resource->resource_lock); continue; -- cgit v1.2.3 From 6381195ad7d06ef979528c7452f3ff93659f86b1 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Mon, 24 May 2021 17:26:16 +0200 Subject: ACPI: power: Rework turning off unused power resources Make turning off unused power resources (after the enumeration of devices and during system-wide resume from S3) more straightforward by using the observation that the power resource state stored in struct acpi_power_resource can be used to determine whether or not the give power resource has any users. Namely, when the state of the power resource is unknown, its _STA method has never been evaluated (or the evaluation of it has failed) and its _ON and _OFF methods have never been executed (or they have failed to execute), so for all practical purposes it can be assumed to have no users (or to be unusable). Therefore, instead of checking the number of power resource users, it is sufficient to check if its state is known. Moreover, if the last known state of a given power resource is "off", it is not necessary to turn it off, because it has been used to initialize the power state or the wakeup power resources list of at least one device and either its _STA method has returned 0 ("off"), or its _OFF method has been successfully executed already. Accordingly, modify acpi_turn_off_unused_power_resources() to do the above checks (which are suitable for both uses of it) instead of using the number of power resource users or evaluating its _STA method, drop its argument (which is not useful any more) and update its callers. Also drop the users field from struct acpi_power_resource as it is not useful any more. Tested-by: Dave Olsthoorn Tested-by: Shujun Wang Signed-off-by: Rafael J. Wysocki --- drivers/acpi/internal.h | 2 +- drivers/acpi/power.c | 45 +++++++++++---------------------------------- drivers/acpi/scan.c | 2 +- drivers/acpi/sleep.c | 2 +- 4 files changed, 14 insertions(+), 37 deletions(-) diff --git a/drivers/acpi/internal.h b/drivers/acpi/internal.h index e21611c9a170..bc05a7c976f8 100644 --- a/drivers/acpi/internal.h +++ b/drivers/acpi/internal.h @@ -142,7 +142,7 @@ int acpi_device_sleep_wake(struct acpi_device *dev, int acpi_power_get_inferred_state(struct acpi_device *device, int *state); int acpi_power_on_resources(struct acpi_device *device, int state); int acpi_power_transition(struct acpi_device *device, int state); -void acpi_turn_off_unused_power_resources(bool init); +void acpi_turn_off_unused_power_resources(void); /* -------------------------------------------------------------------------- Device Power Management diff --git a/drivers/acpi/power.c b/drivers/acpi/power.c index 6aeea7649852..f145f645ab06 100644 --- a/drivers/acpi/power.c +++ b/drivers/acpi/power.c @@ -52,7 +52,6 @@ struct acpi_power_resource { u32 system_level; u32 order; unsigned int ref_count; - unsigned int users; u8 state; bool wakeup_enabled; struct mutex resource_lock; @@ -174,8 +173,6 @@ int acpi_extract_power_resources(union acpi_object *package, unsigned int start, err = acpi_power_resources_list_add(rhandle, list); if (err) break; - - to_power_resource(rdev)->users++; } if (err) acpi_power_resources_list_free(list); @@ -1018,39 +1015,10 @@ void acpi_resume_power_resources(void) } #endif -static void acpi_power_turn_off_if_unused(struct acpi_power_resource *resource, - bool init) -{ - if (resource->ref_count > 0) - return; - - if (init) { - if (resource->users > 0) - return; - } else { - int result; - u8 state; - - result = acpi_power_get_state(resource->device.handle, &state); - if (result || state == ACPI_POWER_RESOURCE_STATE_OFF) - return; - } - - dev_info(&resource->device.dev, "Turning OFF\n"); - __acpi_power_off(resource); -} - /** * acpi_turn_off_unused_power_resources - Turn off power resources not in use. - * @init: Control switch. - * - * If @ainit is set, unconditionally turn off all of the ACPI power resources - * without any users. - * - * Otherwise, turn off all ACPI power resources without active references (that - * is, the ones that should be "off" at the moment) that are "on". */ -void acpi_turn_off_unused_power_resources(bool init) +void acpi_turn_off_unused_power_resources(void) { struct acpi_power_resource *resource; @@ -1059,7 +1027,16 @@ void acpi_turn_off_unused_power_resources(bool init) list_for_each_entry_reverse(resource, &acpi_power_resource_list, list_node) { mutex_lock(&resource->resource_lock); - acpi_power_turn_off_if_unused(resource, init); + /* + * Turn off power resources in an unknown state too, because the + * platform firmware on some system expects the OS to turn off + * power resources without any users unconditionally. + */ + if (!resource->ref_count && + resource->state != ACPI_POWER_RESOURCE_STATE_OFF) { + dev_info(&resource->device.dev, "Turning OFF\n"); + __acpi_power_off(resource); + } mutex_unlock(&resource->resource_lock); } diff --git a/drivers/acpi/scan.c b/drivers/acpi/scan.c index e10d38ac7cf2..453eff8ec8c3 100644 --- a/drivers/acpi/scan.c +++ b/drivers/acpi/scan.c @@ -2360,7 +2360,7 @@ int __init acpi_scan_init(void) } } - acpi_turn_off_unused_power_resources(true); + acpi_turn_off_unused_power_resources(); acpi_scan_initialized = true; diff --git a/drivers/acpi/sleep.c b/drivers/acpi/sleep.c index df386571da98..09fd13757b65 100644 --- a/drivers/acpi/sleep.c +++ b/drivers/acpi/sleep.c @@ -504,7 +504,7 @@ static void acpi_pm_start(u32 acpi_state) */ static void acpi_pm_end(void) { - acpi_turn_off_unused_power_resources(false); + acpi_turn_off_unused_power_resources(); acpi_scan_lock_release(); /* * This is necessary in case acpi_pm_finish() is not called during a -- cgit v1.2.3