summaryrefslogtreecommitdiffstats
path: root/drivers
diff options
context:
space:
mode:
authorRafael J. Wysocki <rafael.j.wysocki@intel.com>2017-12-10 01:02:13 +0100
committerRafael J. Wysocki <rafael.j.wysocki@intel.com>2018-01-03 00:30:00 +0100
commit32bfa56ac158c1ebcc82df2518860f824be5e5be (patch)
treedc96e70410c19847ed630c97d7aab91779b0f4c0 /drivers
parent75e94645fc3b1007eacb4c7863059f8e8d098cda (diff)
downloadlinux-32bfa56ac158c1ebcc82df2518860f824be5e5be.tar.gz
linux-32bfa56ac158c1ebcc82df2518860f824be5e5be.tar.bz2
linux-32bfa56ac158c1ebcc82df2518860f824be5e5be.zip
PM / core: Direct DPM_FLAG_LEAVE_SUSPENDED handling
Make the PM core handle DPM_FLAG_LEAVE_SUSPENDED directly for devices whose "noirq", "late" and "early" driver callbacks are invoked directly by it. Namely, make it skip all of the system-wide resume callbacks for such devices with DPM_FLAG_LEAVE_SUSPENDED set if they are in runtime suspend during the "noirq" phase of system-wide suspend (or analogous) transitions or the system transition under way is a proper suspend (rather than anything related to hibernation) and the device's wakeup settings are compatible with runtime PM (that is, the device cannot generate wakeup signals at all or it is allowed to wake up the system from sleep). Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
Diffstat (limited to 'drivers')
-rw-r--r--drivers/base/power/main.c51
1 files changed, 40 insertions, 11 deletions
diff --git a/drivers/base/power/main.c b/drivers/base/power/main.c
index 154f7b4db8d0..70398e7b3569 100644
--- a/drivers/base/power/main.c
+++ b/drivers/base/power/main.c
@@ -619,6 +619,7 @@ static int device_resume_noirq(struct device *dev, pm_message_t state, bool asyn
{
pm_callback_t callback;
const char *info;
+ bool skip_resume;
int error = 0;
TRACE_DEVICE(dev);
@@ -632,10 +633,15 @@ static int device_resume_noirq(struct device *dev, pm_message_t state, bool asyn
dpm_wait_for_superior(dev, async);
+ skip_resume = dev_pm_may_skip_resume(dev);
+
callback = dpm_subsys_resume_noirq_cb(dev, state, &info);
if (callback)
goto Run;
+ if (skip_resume)
+ goto Skip;
+
if (dev_pm_smart_suspend_and_suspended(dev)) {
pm_message_t suspend_msg = suspend_event(state);
@@ -650,7 +656,7 @@ static int device_resume_noirq(struct device *dev, pm_message_t state, bool asyn
if (!dpm_subsys_suspend_late_cb(dev, suspend_msg, NULL) &&
!dpm_subsys_suspend_noirq_cb(dev, suspend_msg, NULL)) {
if (state.event == PM_EVENT_THAW) {
- dev_pm_skip_next_resume_phases(dev);
+ skip_resume = true;
goto Skip;
} else {
pm_runtime_set_active(dev);
@@ -669,7 +675,7 @@ Run:
Skip:
dev->power.is_noirq_suspended = false;
- if (dev_pm_may_skip_resume(dev)) {
+ if (skip_resume) {
/*
* The device is going to be left in suspend, but it might not
* have been in runtime suspend before the system suspended, so
@@ -1244,6 +1250,32 @@ static pm_callback_t dpm_subsys_suspend_noirq_cb(struct device *dev,
return callback;
}
+static bool device_must_resume(struct device *dev, pm_message_t state,
+ bool no_subsys_suspend_noirq)
+{
+ pm_message_t resume_msg = resume_event(state);
+
+ /*
+ * If all of the device driver's "noirq", "late" and "early" callbacks
+ * are invoked directly by the core, the decision to allow the device to
+ * stay in suspend can be based on its current runtime PM status and its
+ * wakeup settings.
+ */
+ if (no_subsys_suspend_noirq &&
+ !dpm_subsys_suspend_late_cb(dev, state, NULL) &&
+ !dpm_subsys_resume_early_cb(dev, resume_msg, NULL) &&
+ !dpm_subsys_resume_noirq_cb(dev, resume_msg, NULL))
+ return !pm_runtime_status_suspended(dev) &&
+ (resume_msg.event != PM_EVENT_RESUME ||
+ (device_can_wakeup(dev) && !device_may_wakeup(dev)));
+
+ /*
+ * The only safe strategy here is to require that if the device may not
+ * be left in suspend, resume callbacks must be invoked for it.
+ */
+ return !dev->power.may_skip_resume;
+}
+
/**
* __device_suspend_noirq - Execute a "noirq suspend" callback for given device.
* @dev: Device to handle.
@@ -1257,6 +1289,7 @@ static int __device_suspend_noirq(struct device *dev, pm_message_t state, bool a
{
pm_callback_t callback;
const char *info;
+ bool no_subsys_cb = false;
int error = 0;
TRACE_DEVICE(dev);
@@ -1279,8 +1312,9 @@ static int __device_suspend_noirq(struct device *dev, pm_message_t state, bool a
if (callback)
goto Run;
- if (dev_pm_smart_suspend_and_suspended(dev) &&
- !dpm_subsys_suspend_late_cb(dev, state, NULL))
+ no_subsys_cb = !dpm_subsys_suspend_late_cb(dev, state, NULL);
+
+ if (dev_pm_smart_suspend_and_suspended(dev) && no_subsys_cb)
goto Skip;
if (dev->driver && dev->driver->pm) {
@@ -1299,14 +1333,9 @@ Skip:
dev->power.is_noirq_suspended = true;
if (dev_pm_test_driver_flags(dev, DPM_FLAG_LEAVE_SUSPENDED)) {
- /*
- * The only safe strategy here is to require that if the device
- * may not be left in suspend, resume callbacks must be invoked
- * for it.
- */
dev->power.must_resume = dev->power.must_resume ||
- !dev->power.may_skip_resume ||
- atomic_read(&dev->power.usage_count) > 1;
+ atomic_read(&dev->power.usage_count) > 1 ||
+ device_must_resume(dev, state, no_subsys_cb);
} else {
dev->power.must_resume = true;
}