summaryrefslogtreecommitdiff
path: root/drivers/acpi/device_pm.c
diff options
context:
space:
mode:
authorRafael J. Wysocki <rafael.j.wysocki@intel.com>2019-07-01 12:54:29 +0200
committerRafael J. Wysocki <rafael.j.wysocki@intel.com>2019-07-03 00:13:24 +0200
commitc95b7595f85c688d5c569ddbbd6ab6a4bdae2f36 (patch)
treef94c10c0c704e8b847389127f90d93e6887d97d2 /drivers/acpi/device_pm.c
parent3cd7957e85e67120bb9f6bfb75d81dcc19af282b (diff)
ACPI: PM: Introduce "poweroff" callbacks for ACPI PM domain and LPSS
In general, it is not correct to call pm_generic_suspend(), pm_generic_suspend_late() and pm_generic_suspend_noirq() during the hibernation's "poweroff" transition, because device drivers may provide special callbacks to be invoked then and the wrappers in question cause system suspend callbacks to be run. Unfortunately, that happens in the ACPI PM domain and ACPI LPSS. To address this potential issue, introduce "poweroff" callbacks for the ACPI PM and LPSS that will use pm_generic_poweroff(), pm_generic_poweroff_late() and pm_generic_poweroff_noirq() as appropriate. Fixes: 05087360fd7a (ACPI / PM: Take SMART_SUSPEND driver flag into account) Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com> Reviewed-by: Mika Westerberg <mika.westerberg@linux.intel.com> Reviewed-by: Hans de Goede <hdegoede@redhat.com>
Diffstat (limited to 'drivers/acpi/device_pm.c')
-rw-r--r--drivers/acpi/device_pm.c58
1 files changed, 55 insertions, 3 deletions
diff --git a/drivers/acpi/device_pm.c b/drivers/acpi/device_pm.c
index 52fc9042a107..6a9d41c44b70 100644
--- a/drivers/acpi/device_pm.c
+++ b/drivers/acpi/device_pm.c
@@ -1133,6 +1133,58 @@ int acpi_subsys_restore_early(struct device *dev)
return ret ? ret : pm_generic_restore_early(dev);
}
EXPORT_SYMBOL_GPL(acpi_subsys_restore_early);
+
+/**
+ * acpi_subsys_poweroff - Run the device driver's poweroff callback.
+ * @dev: Device to handle.
+ *
+ * Follow PCI and resume devices from runtime suspend before running their
+ * system poweroff callbacks, unless the driver can cope with runtime-suspended
+ * devices during system suspend and there are no ACPI-specific reasons for
+ * resuming them.
+ */
+int acpi_subsys_poweroff(struct device *dev)
+{
+ if (!dev_pm_test_driver_flags(dev, DPM_FLAG_SMART_SUSPEND) ||
+ acpi_dev_needs_resume(dev, ACPI_COMPANION(dev)))
+ pm_runtime_resume(dev);
+
+ return pm_generic_poweroff(dev);
+}
+EXPORT_SYMBOL_GPL(acpi_subsys_poweroff);
+
+/**
+ * acpi_subsys_poweroff_late - Run the device driver's poweroff callback.
+ * @dev: Device to handle.
+ *
+ * Carry out the generic late poweroff procedure for @dev and use ACPI to put
+ * it into a low-power state during system transition into a sleep state.
+ */
+static int acpi_subsys_poweroff_late(struct device *dev)
+{
+ int ret;
+
+ if (dev_pm_smart_suspend_and_suspended(dev))
+ return 0;
+
+ ret = pm_generic_poweroff_late(dev);
+ if (ret)
+ return ret;
+
+ return acpi_dev_suspend(dev, device_may_wakeup(dev));
+}
+
+/**
+ * acpi_subsys_poweroff_noirq - Run the driver's "noirq" poweroff callback.
+ * @dev: Device to suspend.
+ */
+static int acpi_subsys_poweroff_noirq(struct device *dev)
+{
+ if (dev_pm_smart_suspend_and_suspended(dev))
+ return 0;
+
+ return pm_generic_poweroff_noirq(dev);
+}
#endif /* CONFIG_PM_SLEEP */
static struct dev_pm_domain acpi_general_pm_domain = {
@@ -1148,9 +1200,9 @@ static struct dev_pm_domain acpi_general_pm_domain = {
.resume_noirq = acpi_subsys_resume_noirq,
.resume_early = acpi_subsys_resume_early,
.freeze = acpi_subsys_freeze,
- .poweroff = acpi_subsys_suspend,
- .poweroff_late = acpi_subsys_suspend_late,
- .poweroff_noirq = acpi_subsys_suspend_noirq,
+ .poweroff = acpi_subsys_poweroff,
+ .poweroff_late = acpi_subsys_poweroff_late,
+ .poweroff_noirq = acpi_subsys_poweroff_noirq,
.restore_early = acpi_subsys_restore_early,
#endif
},