Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

ACPI / PM: Turn power resources on and off in the right order during resume

According to Section 7.2 of ACPI 6.0, power resources should
always be enabled and disabled in order given by the "resourceorder"
field of the corresponding Power Resource objects: "Power Resource
levels are enabled from low values to high values and are disabled
from high values to low values."

However, this is not what happens during system resume, because
in that case the enabling/disabling is carried out in the power
resource registration order which may not reflect the ordering
required by the platform.

For this reason, make the ordering of the global list of all
power resources in the system (used by the system resume code)
reflect the one given by the "resourceorder" attributes of the
Power Resource objects in the ACPI namespace and modify
acpi_resume_power_resources() to walk the list in the reverse
order when turning off the power resources that had been off
before the system was suspended.

Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com>

+36 -4
+36 -4
drivers/acpi/power.c
··· 760 760 device_remove_file(&device->dev, &dev_attr_resource_in_use); 761 761 } 762 762 763 + static void acpi_power_add_resource_to_list(struct acpi_power_resource *resource) 764 + { 765 + mutex_lock(&power_resource_list_lock); 766 + 767 + if (!list_empty(&acpi_power_resource_list)) { 768 + struct acpi_power_resource *r; 769 + 770 + list_for_each_entry(r, &acpi_power_resource_list, list_node) 771 + if (r->order > resource->order) { 772 + list_add_tail(&resource->list_node, &r->list_node); 773 + goto out; 774 + } 775 + } 776 + list_add_tail(&resource->list_node, &acpi_power_resource_list); 777 + 778 + out: 779 + mutex_unlock(&power_resource_list_lock); 780 + } 781 + 763 782 int acpi_add_power_resource(acpi_handle handle) 764 783 { 765 784 struct acpi_power_resource *resource; ··· 829 810 if (!device_create_file(&device->dev, &dev_attr_resource_in_use)) 830 811 device->remove = acpi_power_sysfs_remove; 831 812 832 - mutex_lock(&power_resource_list_lock); 833 - list_add(&resource->list_node, &acpi_power_resource_list); 834 - mutex_unlock(&power_resource_list_lock); 813 + acpi_power_add_resource_to_list(resource); 835 814 acpi_device_add_finalize(device); 836 815 return 0; 837 816 ··· 860 843 && resource->ref_count) { 861 844 dev_info(&resource->device.dev, "Turning ON\n"); 862 845 __acpi_power_on(resource); 863 - } else if (state == ACPI_POWER_RESOURCE_STATE_ON 846 + } 847 + 848 + mutex_unlock(&resource->resource_lock); 849 + } 850 + list_for_each_entry_reverse(resource, &acpi_power_resource_list, list_node) { 851 + int result, state; 852 + 853 + mutex_lock(&resource->resource_lock); 854 + 855 + result = acpi_power_get_state(resource->device.handle, &state); 856 + if (result) { 857 + mutex_unlock(&resource->resource_lock); 858 + continue; 859 + } 860 + 861 + if (state == ACPI_POWER_RESOURCE_STATE_ON 864 862 && !resource->ref_count) { 865 863 dev_info(&resource->device.dev, "Turning OFF\n"); 866 864 __acpi_power_off(resource);