ACPI: scan: Release PM resources blocked by unused objects
authorRafael J. Wysocki <rafael.j.wysocki@intel.com>
Sat, 9 Oct 2021 14:22:09 +0000 (16:22 +0200)
committerRafael J. Wysocki <rafael.j.wysocki@intel.com>
Wed, 13 Oct 2021 17:57:01 +0000 (19:57 +0200)
On some systems the ACPI namespace contains device objects that are
not used in certain configurations of the system.  If they start off
in the D0 power state configuration, they will stay in it until the
system reboots, because of the lack of any mechanism possibly causing
their configuration to change.  If that happens, they may prevent
some power resources from being turned off or generally they may
prevent the platform from getting into the deepest low-power states
thus causing some energy to be wasted.

Address this issue by changing the configuration of unused ACPI
device objects to the D3cold power state one after carrying out
the ACPI-based enumeration of devices.

BugLink: https://bugzilla.kernel.org/show_bug.cgi?id=214091
Link: https://lore.kernel.org/linux-acpi/20211007205126.11769-1-mario.limonciello@amd.com/
Reported-by: Mario Limonciello <mario.limonciello@amd.com>
Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
Tested-by: Mario Limonciello <mario.limonciello@amd.com>
drivers/acpi/glue.c
drivers/acpi/internal.h
drivers/acpi/scan.c

index 7a33a6d985f8902e04129a6315cb9470c7eee407..1cfafa254e3d4904c65cfa60de8a62d9c9c1e881 100644 (file)
@@ -340,3 +340,28 @@ void acpi_device_notify_remove(struct device *dev)
 
        acpi_unbind_one(dev);
 }
+
+int acpi_dev_turn_off_if_unused(struct device *dev, void *not_used)
+{
+       struct acpi_device *adev = to_acpi_device(dev);
+
+       /*
+        * Skip device objects with device IDs, because they may be in use even
+        * if they are not companions of any physical device objects.
+        */
+       if (adev->pnp.type.hardware_id)
+               return 0;
+
+       mutex_lock(&adev->physical_node_lock);
+
+       /*
+        * Device objects without device IDs are not in use if they have no
+        * corresponding physical device objects.
+        */
+       if (list_empty(&adev->physical_node_list))
+               acpi_device_set_power(adev, ACPI_STATE_D3_COLD);
+
+       mutex_unlock(&adev->physical_node_lock);
+
+       return 0;
+}
index d91b560e8867472d006c0bffbd9399b822a1636d..8fbdc172864b0ae230bbb4a142156bb31f330d9a 100644 (file)
@@ -117,6 +117,7 @@ bool acpi_device_is_battery(struct acpi_device *adev);
 bool acpi_device_is_first_physical_node(struct acpi_device *adev,
                                        const struct device *dev);
 int acpi_bus_register_early_device(int type);
+int acpi_dev_turn_off_if_unused(struct device *dev, void *not_used);
 
 /* --------------------------------------------------------------------------
                      Device Matching and Notification
index 5b54c80b9d32a45bceeb6fc48a0ce0581f46f438..770b82483d74de970a30c6d24486673f1cd09e6e 100644 (file)
@@ -2559,6 +2559,12 @@ int __init acpi_scan_init(void)
                }
        }
 
+       /*
+        * Make sure that power management resources are not blocked by ACPI
+        * device objects with no users.
+        */
+       bus_for_each_dev(&acpi_bus_type, NULL, NULL, acpi_dev_turn_off_if_unused);
+
        acpi_turn_off_unused_power_resources();
 
        acpi_scan_initialized = true;