From 4b9ee772eaa82188b0eb8e05bdd1707c2a992004 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Thu, 18 Mar 2021 19:25:12 +0100 Subject: ACPI: scan: Turn off unused power resources during initialization It is reported that on certain platforms there are power resources that are not associated with any devices physically present in the platform. Those power resources are expected to be turned off by the OS in accordance with the ACPI specification (section 7.3 of ACPI 6.4) which currently is not done by Linux and that may lead to obscure issues. For instance, leaving those power resources in the "on" state may prevent the platform from reaching the lowest power state in suspend-to-idle which leads to excessive power draw. For this reason, turn all of the unused ACPI power resources off at the end of the initial namespace scan for devices in analogy with resume from suspend-to-RAM. Link: https://uefi.org/specs/ACPI/6.4/07_Power_and_Performance_Mgmt/device-power-management-objects.html Reported-by: David Box Tested-by: Wendy Wang Signed-off-by: Rafael J. Wysocki --- drivers/acpi/power.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/acpi/power.c') diff --git a/drivers/acpi/power.c b/drivers/acpi/power.c index 9b608b55d2b2..46c38627addd 100644 --- a/drivers/acpi/power.c +++ b/drivers/acpi/power.c @@ -996,6 +996,7 @@ void acpi_resume_power_resources(void) mutex_unlock(&power_resource_list_lock); } +#endif void acpi_turn_off_unused_power_resources(void) { @@ -1025,4 +1026,3 @@ void acpi_turn_off_unused_power_resources(void) mutex_unlock(&power_resource_list_lock); } -#endif -- cgit v1.2.3-58-ga151 From 7e4fdeafa61f2b653fcf9678f09935e55756aed2 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Thu, 18 Mar 2021 19:28:28 +0100 Subject: ACPI: power: Turn off unused power resources unconditionally According to the ACPI specification (section 7.2.2 in ACPI 6.4), the OS may evaluate the _OFF method of a power resource that is "off" already [1], and in particular that can be done in the case of unused power resources. Accordingly, modify acpi_turn_off_unused_power_resources() to evaluate the _OFF method for each of the unused power resources unconditionally which may help to work around BIOS issues where the return value of _STA for a power resource does not reflect the actual state of the power resource [2]. Link: https://uefi.org/specs/ACPI/6.4/07_Power_and_Performance_Mgmt/declaring-a-power-resource-object.html#off # [1] Link: https://lore.kernel.org/lkml/20210314000439.3138941-1-luzmaximilian@gmail.com/ # [2] Tested-by: Wendy Wang Signed-off-by: Rafael J. Wysocki --- drivers/acpi/power.c | 11 +---------- 1 file changed, 1 insertion(+), 10 deletions(-) (limited to 'drivers/acpi/power.c') diff --git a/drivers/acpi/power.c b/drivers/acpi/power.c index 46c38627addd..bacae6d178ff 100644 --- a/drivers/acpi/power.c +++ b/drivers/acpi/power.c @@ -1005,18 +1005,9 @@ void acpi_turn_off_unused_power_resources(void) mutex_lock(&power_resource_list_lock); list_for_each_entry_reverse(resource, &acpi_power_resource_list, list_node) { - int result, state; - mutex_lock(&resource->resource_lock); - result = acpi_power_get_state(resource->device.handle, &state); - if (result) { - mutex_unlock(&resource->resource_lock); - continue; - } - - if (state == ACPI_POWER_RESOURCE_STATE_ON - && !resource->ref_count) { + if (!resource->ref_count) { dev_info(&resource->device.dev, "Turning OFF\n"); __acpi_power_off(resource); } -- cgit v1.2.3-58-ga151 From f5d9ab1d803456f5215f853e9286659933b59afe Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Wed, 7 Apr 2021 16:32:43 +0200 Subject: ACPI: scan: Drop sta argument from acpi_init_device_object() Use the observation that the initial status check for ACPI_BUS_TYPE_PROCESSOR objects can be carried out in the same way as for ACPI_BUS_TYPE_DEVICE objects and it is not necessary to fail acpi_add_single_object() if acpi_bus_get_status_handle() returns an error for a processor (its status can be set to 0 instead) to simplify acpi_add_single_object(). Accordingly, drop the "sta" argument from acpi_init_device_object() as it can always set the initial status to ACPI_STA_DEFAULT and let its caller correct that later on. Signed-off-by: Rafael J. Wysocki Reviewed-by: Hans de Goede --- drivers/acpi/internal.h | 3 +-- drivers/acpi/power.c | 3 +-- drivers/acpi/scan.c | 28 ++++++++++++++-------------- 3 files changed, 16 insertions(+), 18 deletions(-) (limited to 'drivers/acpi/power.c') diff --git a/drivers/acpi/internal.h b/drivers/acpi/internal.h index cb8f70842249..53947e3e41ee 100644 --- a/drivers/acpi/internal.h +++ b/drivers/acpi/internal.h @@ -109,8 +109,7 @@ struct acpi_device_bus_id { int acpi_device_add(struct acpi_device *device, void (*release)(struct device *)); void acpi_init_device_object(struct acpi_device *device, acpi_handle handle, - int type, unsigned long long sta, - struct acpi_device_info *info); + int type, struct acpi_device_info *info); int acpi_device_setup_files(struct acpi_device *dev); void acpi_device_remove_files(struct acpi_device *dev); void acpi_device_add_finalize(struct acpi_device *device); diff --git a/drivers/acpi/power.c b/drivers/acpi/power.c index 9b608b55d2b2..664e2e94ce61 100644 --- a/drivers/acpi/power.c +++ b/drivers/acpi/power.c @@ -925,8 +925,7 @@ int acpi_add_power_resource(acpi_handle handle) return -ENOMEM; device = &resource->device; - acpi_init_device_object(device, handle, ACPI_BUS_TYPE_POWER, - ACPI_STA_DEFAULT, NULL); + acpi_init_device_object(device, handle, ACPI_BUS_TYPE_POWER, NULL); mutex_init(&resource->resource_lock); INIT_LIST_HEAD(&resource->list_node); INIT_LIST_HEAD(&resource->dependents); diff --git a/drivers/acpi/scan.c b/drivers/acpi/scan.c index 787ba8aac9b5..902cecb218d3 100644 --- a/drivers/acpi/scan.c +++ b/drivers/acpi/scan.c @@ -1649,15 +1649,14 @@ static bool acpi_device_enumeration_by_parent(struct acpi_device *device) } void acpi_init_device_object(struct acpi_device *device, acpi_handle handle, - int type, unsigned long long sta, - struct acpi_device_info *info) + int type, struct acpi_device_info *info) { INIT_LIST_HEAD(&device->pnp.ids); device->device_type = type; device->handle = handle; device->parent = acpi_bus_get_parent(handle); fwnode_init(&device->fwnode, &acpi_device_fwnode_ops); - acpi_set_device_status(device, sta); + acpi_set_device_status(device, ACPI_STA_DEFAULT); acpi_device_get_busid(device); acpi_set_pnp_ids(handle, &device->pnp, type, info); acpi_init_properties(device); @@ -1680,19 +1679,21 @@ void acpi_device_add_finalize(struct acpi_device *device) kobject_uevent(&device->dev.kobj, KOBJ_ADD); } +static void acpi_scan_init_status(struct acpi_device *adev) +{ + if (acpi_bus_get_status(adev)) + acpi_set_device_status(adev, 0); +} + static int acpi_add_single_object(struct acpi_device **child, acpi_handle handle, int type) { struct acpi_device_info *info = NULL; - unsigned long long sta = ACPI_STA_DEFAULT; struct acpi_device *device; int result; if (type == ACPI_BUS_TYPE_DEVICE && handle != ACPI_ROOT_OBJECT) acpi_get_object_info(handle, &info); - else if (type == ACPI_BUS_TYPE_PROCESSOR && - ACPI_FAILURE(acpi_bus_get_status_handle(handle, &sta))) - return -ENODEV; device = kzalloc(sizeof(struct acpi_device), GFP_KERNEL); if (!device) { @@ -1700,16 +1701,15 @@ static int acpi_add_single_object(struct acpi_device **child, return -ENOMEM; } - acpi_init_device_object(device, handle, type, sta, info); + acpi_init_device_object(device, handle, type, info); kfree(info); /* - * For ACPI_BUS_TYPE_DEVICE getting the status is delayed till here so - * that we can call acpi_bus_get_status() and use its quirk handling. - * Note this must be done before the get power-/wakeup_dev-flags calls. + * Getting the status is delayed till here so that we can call + * acpi_bus_get_status() and use its quirk handling. Note that + * this must be done before the get power-/wakeup_dev-flags calls. */ - if (type == ACPI_BUS_TYPE_DEVICE) - if (acpi_bus_get_status(device) < 0) - acpi_set_device_status(device, 0); + if (type == ACPI_BUS_TYPE_DEVICE || type == ACPI_BUS_TYPE_PROCESSOR) + acpi_scan_init_status(device); acpi_bus_get_power_flags(device); acpi_bus_get_wakeup_device_flags(device); -- cgit v1.2.3-58-ga151 From c830dbcfccbf70be94f15dbb4781be5ffb210d98 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Wed, 7 Apr 2021 16:33:38 +0200 Subject: ACPI: scan: Call acpi_get_object_info() from acpi_set_pnp_ids() Notice that it is not necessary to call acpi_get_object_info() from acpi_add_single_object() in order to pass the pointer returned by it to acpi_init_device_object() and from there to acpi_set_pnp_ids(). It is more straightforward to call acpi_get_object_info() from acpi_set_pnp_ids() and avoid unnecessary pointer passing, so change the code accordingly. No intentional functional impact. Signed-off-by: Rafael J. Wysocki Reviewed-by: Hans de Goede --- drivers/acpi/internal.h | 2 +- drivers/acpi/power.c | 2 +- drivers/acpi/scan.c | 21 +++++++++------------ 3 files changed, 11 insertions(+), 14 deletions(-) (limited to 'drivers/acpi/power.c') diff --git a/drivers/acpi/internal.h b/drivers/acpi/internal.h index 53947e3e41ee..b852cff80287 100644 --- a/drivers/acpi/internal.h +++ b/drivers/acpi/internal.h @@ -109,7 +109,7 @@ struct acpi_device_bus_id { int acpi_device_add(struct acpi_device *device, void (*release)(struct device *)); void acpi_init_device_object(struct acpi_device *device, acpi_handle handle, - int type, struct acpi_device_info *info); + int type); int acpi_device_setup_files(struct acpi_device *dev); void acpi_device_remove_files(struct acpi_device *dev); void acpi_device_add_finalize(struct acpi_device *device); diff --git a/drivers/acpi/power.c b/drivers/acpi/power.c index 664e2e94ce61..b68a6e33ac34 100644 --- a/drivers/acpi/power.c +++ b/drivers/acpi/power.c @@ -925,7 +925,7 @@ int acpi_add_power_resource(acpi_handle handle) return -ENOMEM; device = &resource->device; - acpi_init_device_object(device, handle, ACPI_BUS_TYPE_POWER, NULL); + acpi_init_device_object(device, handle, ACPI_BUS_TYPE_POWER); mutex_init(&resource->resource_lock); INIT_LIST_HEAD(&resource->list_node); INIT_LIST_HEAD(&resource->dependents); diff --git a/drivers/acpi/scan.c b/drivers/acpi/scan.c index 902cecb218d3..053777845475 100644 --- a/drivers/acpi/scan.c +++ b/drivers/acpi/scan.c @@ -1307,8 +1307,9 @@ static bool acpi_object_is_system_bus(acpi_handle handle) } static void acpi_set_pnp_ids(acpi_handle handle, struct acpi_device_pnp *pnp, - int device_type, struct acpi_device_info *info) + int device_type) { + struct acpi_device_info *info = NULL; struct acpi_pnp_device_id_list *cid_list; int i; @@ -1319,6 +1320,7 @@ static void acpi_set_pnp_ids(acpi_handle handle, struct acpi_device_pnp *pnp, break; } + acpi_get_object_info(handle, &info); if (!info) { pr_err(PREFIX "%s: Error reading device info\n", __func__); @@ -1344,6 +1346,8 @@ static void acpi_set_pnp_ids(acpi_handle handle, struct acpi_device_pnp *pnp, if (info->valid & ACPI_VALID_CLS) acpi_add_id(pnp, info->class_code.string); + kfree(info); + /* * Some devices don't reliably have _HIDs & _CIDs, so add * synthetic HIDs to make sure drivers can find them. @@ -1649,7 +1653,7 @@ static bool acpi_device_enumeration_by_parent(struct acpi_device *device) } void acpi_init_device_object(struct acpi_device *device, acpi_handle handle, - int type, struct acpi_device_info *info) + int type) { INIT_LIST_HEAD(&device->pnp.ids); device->device_type = type; @@ -1658,7 +1662,7 @@ void acpi_init_device_object(struct acpi_device *device, acpi_handle handle, fwnode_init(&device->fwnode, &acpi_device_fwnode_ops); acpi_set_device_status(device, ACPI_STA_DEFAULT); acpi_device_get_busid(device); - acpi_set_pnp_ids(handle, &device->pnp, type, info); + acpi_set_pnp_ids(handle, &device->pnp, type); acpi_init_properties(device); acpi_bus_get_flags(device); device->flags.match_driver = false; @@ -1688,21 +1692,14 @@ static void acpi_scan_init_status(struct acpi_device *adev) static int acpi_add_single_object(struct acpi_device **child, acpi_handle handle, int type) { - struct acpi_device_info *info = NULL; struct acpi_device *device; int result; - if (type == ACPI_BUS_TYPE_DEVICE && handle != ACPI_ROOT_OBJECT) - acpi_get_object_info(handle, &info); - device = kzalloc(sizeof(struct acpi_device), GFP_KERNEL); - if (!device) { - kfree(info); + if (!device) return -ENOMEM; - } - acpi_init_device_object(device, handle, type, info); - kfree(info); + acpi_init_device_object(device, handle, type); /* * Getting the status is delayed till here so that we can call * acpi_bus_get_status() and use its quirk handling. Note that -- cgit v1.2.3-58-ga151