From 58a1fbbb2ee873dd1fe327e80bc7b08e80866269 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Wed, 7 Oct 2015 00:50:24 +0200 Subject: [PATCH] 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 --- drivers/acpi/acpi_lpss.c | 2 +- drivers/acpi/device_pm.c | 19 +------------------ drivers/base/power/generic_ops.c | 23 +++++++++++++++++++++++ drivers/pci/pci-driver.c | 2 +- include/linux/pm.h | 1 + 5 files changed, 27 insertions(+), 20 deletions(-) diff --git a/drivers/acpi/acpi_lpss.c b/drivers/acpi/acpi_lpss.c index f51bd0d0bc17..f9e0d09f7c66 100644 --- a/drivers/acpi/acpi_lpss.c +++ b/drivers/acpi/acpi_lpss.c @@ -664,7 +664,7 @@ static struct dev_pm_domain acpi_lpss_pm_domain = { #ifdef CONFIG_PM #ifdef CONFIG_PM_SLEEP .prepare = acpi_subsys_prepare, - .complete = acpi_subsys_complete, + .complete = pm_complete_with_resume_check, .suspend = acpi_subsys_suspend, .suspend_late = acpi_lpss_suspend_late, .resume_early = acpi_lpss_resume_early, diff --git a/drivers/acpi/device_pm.c b/drivers/acpi/device_pm.c index 4806b7f856c4..08a02cdc737c 100644 --- a/drivers/acpi/device_pm.c +++ b/drivers/acpi/device_pm.c @@ -962,23 +962,6 @@ int acpi_subsys_prepare(struct device *dev) } EXPORT_SYMBOL_GPL(acpi_subsys_prepare); -/** - * acpi_subsys_complete - Finalize device's resume during system resume. - * @dev: Device to handle. - */ -void acpi_subsys_complete(struct device *dev) -{ - pm_generic_complete(dev); - /* - * If the device had been runtime-suspended before the system went into - * the sleep state it is going out of and it has never been resumed till - * now, resume it in case the firmware powered it up. - */ - if (dev->power.direct_complete) - pm_request_resume(dev); -} -EXPORT_SYMBOL_GPL(acpi_subsys_complete); - /** * acpi_subsys_suspend - Run the device driver's suspend callback. * @dev: Device to handle. @@ -1047,7 +1030,7 @@ static struct dev_pm_domain acpi_general_pm_domain = { .runtime_resume = acpi_subsys_runtime_resume, #ifdef CONFIG_PM_SLEEP .prepare = acpi_subsys_prepare, - .complete = acpi_subsys_complete, + .complete = pm_complete_with_resume_check, .suspend = acpi_subsys_suspend, .suspend_late = acpi_subsys_suspend_late, .resume_early = acpi_subsys_resume_early, diff --git a/drivers/base/power/generic_ops.c b/drivers/base/power/generic_ops.c index b2ed606265a8..07c3c4a9522d 100644 --- a/drivers/base/power/generic_ops.c +++ b/drivers/base/power/generic_ops.c @@ -9,6 +9,7 @@ #include #include #include +#include #ifdef CONFIG_PM /** @@ -297,4 +298,26 @@ void pm_generic_complete(struct device *dev) if (drv && drv->pm && drv->pm->complete) drv->pm->complete(dev); } + +/** + * pm_complete_with_resume_check - Complete a device power transition. + * @dev: Device to handle. + * + * Complete a device power transition during a system-wide power transition and + * optionally schedule a runtime resume of the device if the system resume in + * progress has been initated by the platform firmware and the device had its + * power.direct_complete flag set. + */ +void pm_complete_with_resume_check(struct device *dev) +{ + pm_generic_complete(dev); + /* + * If the device had been runtime-suspended before the system went into + * the sleep state it is going out of and it has never been resumed till + * now, resume it in case the firmware powered it up. + */ + if (dev->power.direct_complete && pm_resume_via_firmware()) + pm_request_resume(dev); +} +EXPORT_SYMBOL_GPL(pm_complete_with_resume_check); #endif /* CONFIG_PM_SLEEP */ diff --git a/drivers/pci/pci-driver.c b/drivers/pci/pci-driver.c index c9ce3073d7fe..306124bba61e 100644 --- a/drivers/pci/pci-driver.c +++ b/drivers/pci/pci-driver.c @@ -687,7 +687,7 @@ static int pci_pm_prepare(struct device *dev) static void pci_pm_complete(struct device *dev) { pci_dev_complete_resume(to_pci_dev(dev)); - pm_generic_complete(dev); + pm_complete_with_resume_check(dev); } #else /* !CONFIG_PM_SLEEP */ diff --git a/include/linux/pm.h b/include/linux/pm.h index 35d599e7250d..528be6787796 100644 --- a/include/linux/pm.h +++ b/include/linux/pm.h @@ -732,6 +732,7 @@ extern int pm_generic_poweroff_noirq(struct device *dev); extern int pm_generic_poweroff_late(struct device *dev); extern int pm_generic_poweroff(struct device *dev); extern void pm_generic_complete(struct device *dev); +extern void pm_complete_with_resume_check(struct device *dev); #else /* !CONFIG_PM_SLEEP */