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

PM / PCI / ACPI: Kick devices that might have been reset by firmware

There is a concern that if the platform firmware was involved in
the system resume that's being completed, some devices might have
been reset by it and if those devices had the power.direct_complete
flag set during the preceding suspend transition, they may stay
in a reset-power-on state indefinitely (until they are runtime-resumed
and then suspended again). That may not be a big deal from the
individual device's perspective, but if the system is an SoC, it may
be prevented from entering deep SoC-wide low-power states on idle
because of that.

The devices that are most likely to be affected by this issue are
PCI devices and ACPI-enumerated devices using the general ACPI PM
domain, so to prevent it from happening for those devices, force a
runtime resume for them if they have their power.direct_complete
flags set and the platform firmware was involved in the resume
transition currently in progress.

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

+27 -20
+1 -1
drivers/acpi/acpi_lpss.c
··· 664 664 #ifdef CONFIG_PM 665 665 #ifdef CONFIG_PM_SLEEP 666 666 .prepare = acpi_subsys_prepare, 667 - .complete = acpi_subsys_complete, 667 + .complete = pm_complete_with_resume_check, 668 668 .suspend = acpi_subsys_suspend, 669 669 .suspend_late = acpi_lpss_suspend_late, 670 670 .resume_early = acpi_lpss_resume_early,
+1 -18
drivers/acpi/device_pm.c
··· 963 963 EXPORT_SYMBOL_GPL(acpi_subsys_prepare); 964 964 965 965 /** 966 - * acpi_subsys_complete - Finalize device's resume during system resume. 967 - * @dev: Device to handle. 968 - */ 969 - void acpi_subsys_complete(struct device *dev) 970 - { 971 - pm_generic_complete(dev); 972 - /* 973 - * If the device had been runtime-suspended before the system went into 974 - * the sleep state it is going out of and it has never been resumed till 975 - * now, resume it in case the firmware powered it up. 976 - */ 977 - if (dev->power.direct_complete) 978 - pm_request_resume(dev); 979 - } 980 - EXPORT_SYMBOL_GPL(acpi_subsys_complete); 981 - 982 - /** 983 966 * acpi_subsys_suspend - Run the device driver's suspend callback. 984 967 * @dev: Device to handle. 985 968 * ··· 1030 1047 .runtime_resume = acpi_subsys_runtime_resume, 1031 1048 #ifdef CONFIG_PM_SLEEP 1032 1049 .prepare = acpi_subsys_prepare, 1033 - .complete = acpi_subsys_complete, 1050 + .complete = pm_complete_with_resume_check, 1034 1051 .suspend = acpi_subsys_suspend, 1035 1052 .suspend_late = acpi_subsys_suspend_late, 1036 1053 .resume_early = acpi_subsys_resume_early,
+23
drivers/base/power/generic_ops.c
··· 9 9 #include <linux/pm.h> 10 10 #include <linux/pm_runtime.h> 11 11 #include <linux/export.h> 12 + #include <linux/suspend.h> 12 13 13 14 #ifdef CONFIG_PM 14 15 /** ··· 298 297 if (drv && drv->pm && drv->pm->complete) 299 298 drv->pm->complete(dev); 300 299 } 300 + 301 + /** 302 + * pm_complete_with_resume_check - Complete a device power transition. 303 + * @dev: Device to handle. 304 + * 305 + * Complete a device power transition during a system-wide power transition and 306 + * optionally schedule a runtime resume of the device if the system resume in 307 + * progress has been initated by the platform firmware and the device had its 308 + * power.direct_complete flag set. 309 + */ 310 + void pm_complete_with_resume_check(struct device *dev) 311 + { 312 + pm_generic_complete(dev); 313 + /* 314 + * If the device had been runtime-suspended before the system went into 315 + * the sleep state it is going out of and it has never been resumed till 316 + * now, resume it in case the firmware powered it up. 317 + */ 318 + if (dev->power.direct_complete && pm_resume_via_firmware()) 319 + pm_request_resume(dev); 320 + } 321 + EXPORT_SYMBOL_GPL(pm_complete_with_resume_check); 301 322 #endif /* CONFIG_PM_SLEEP */
+1 -1
drivers/pci/pci-driver.c
··· 687 687 static void pci_pm_complete(struct device *dev) 688 688 { 689 689 pci_dev_complete_resume(to_pci_dev(dev)); 690 - pm_generic_complete(dev); 690 + pm_complete_with_resume_check(dev); 691 691 } 692 692 693 693 #else /* !CONFIG_PM_SLEEP */
+1
include/linux/pm.h
··· 732 732 extern int pm_generic_poweroff_late(struct device *dev); 733 733 extern int pm_generic_poweroff(struct device *dev); 734 734 extern void pm_generic_complete(struct device *dev); 735 + extern void pm_complete_with_resume_check(struct device *dev); 735 736 736 737 #else /* !CONFIG_PM_SLEEP */ 737 738