|
@@ -525,6 +525,18 @@ static void dpm_watchdog_clear(struct dpm_watchdog *wd)
|
|
|
|
|
|
/*------------------------- Resume routines -------------------------*/
|
|
|
|
|
|
+/**
|
|
|
+ * dev_pm_may_skip_resume - System-wide device resume optimization check.
|
|
|
+ * @dev: Target device.
|
|
|
+ *
|
|
|
+ * Checks whether or not the device may be left in suspend after a system-wide
|
|
|
+ * transition to the working state.
|
|
|
+ */
|
|
|
+bool dev_pm_may_skip_resume(struct device *dev)
|
|
|
+{
|
|
|
+ return !dev->power.must_resume && pm_transition.event != PM_EVENT_RESTORE;
|
|
|
+}
|
|
|
+
|
|
|
/**
|
|
|
* device_resume_noirq - Execute a "noirq resume" callback for given device.
|
|
|
* @dev: Device to handle.
|
|
@@ -573,6 +585,19 @@ static int device_resume_noirq(struct device *dev, pm_message_t state, bool asyn
|
|
|
error = dpm_run_callback(callback, dev, state, info);
|
|
|
dev->power.is_noirq_suspended = false;
|
|
|
|
|
|
+ if (dev_pm_may_skip_resume(dev)) {
|
|
|
+ /*
|
|
|
+ * The device is going to be left in suspend, but it might not
|
|
|
+ * have been in runtime suspend before the system suspended, so
|
|
|
+ * its runtime PM status needs to be updated to avoid confusing
|
|
|
+ * the runtime PM framework when runtime PM is enabled for the
|
|
|
+ * device again.
|
|
|
+ */
|
|
|
+ pm_runtime_set_suspended(dev);
|
|
|
+ dev->power.is_late_suspended = false;
|
|
|
+ dev->power.is_suspended = false;
|
|
|
+ }
|
|
|
+
|
|
|
Out:
|
|
|
complete_all(&dev->power.completion);
|
|
|
TRACE_RESUME(error);
|
|
@@ -1074,6 +1099,22 @@ static pm_message_t resume_event(pm_message_t sleep_state)
|
|
|
return PMSG_ON;
|
|
|
}
|
|
|
|
|
|
+static void dpm_superior_set_must_resume(struct device *dev)
|
|
|
+{
|
|
|
+ struct device_link *link;
|
|
|
+ int idx;
|
|
|
+
|
|
|
+ if (dev->parent)
|
|
|
+ dev->parent->power.must_resume = true;
|
|
|
+
|
|
|
+ idx = device_links_read_lock();
|
|
|
+
|
|
|
+ list_for_each_entry_rcu(link, &dev->links.suppliers, c_node)
|
|
|
+ link->supplier->power.must_resume = true;
|
|
|
+
|
|
|
+ device_links_read_unlock(idx);
|
|
|
+}
|
|
|
+
|
|
|
/**
|
|
|
* __device_suspend_noirq - Execute a "noirq suspend" callback for given device.
|
|
|
* @dev: Device to handle.
|
|
@@ -1125,10 +1166,28 @@ static int __device_suspend_noirq(struct device *dev, pm_message_t state, bool a
|
|
|
}
|
|
|
|
|
|
error = dpm_run_callback(callback, dev, state, info);
|
|
|
- if (!error)
|
|
|
- dev->power.is_noirq_suspended = true;
|
|
|
- else
|
|
|
+ if (error) {
|
|
|
async_error = error;
|
|
|
+ goto Complete;
|
|
|
+ }
|
|
|
+
|
|
|
+ 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;
|
|
|
+ } else {
|
|
|
+ dev->power.must_resume = true;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (dev->power.must_resume)
|
|
|
+ dpm_superior_set_must_resume(dev);
|
|
|
|
|
|
Complete:
|
|
|
complete_all(&dev->power.completion);
|
|
@@ -1485,6 +1544,9 @@ static int __device_suspend(struct device *dev, pm_message_t state, bool async)
|
|
|
dev->power.direct_complete = false;
|
|
|
}
|
|
|
|
|
|
+ dev->power.may_skip_resume = false;
|
|
|
+ dev->power.must_resume = false;
|
|
|
+
|
|
|
dpm_watchdog_set(&wd, dev);
|
|
|
device_lock(dev);
|
|
|
|
|
@@ -1650,8 +1712,9 @@ static int device_prepare(struct device *dev, pm_message_t state)
|
|
|
if (dev->power.syscore)
|
|
|
return 0;
|
|
|
|
|
|
- WARN_ON(dev_pm_test_driver_flags(dev, DPM_FLAG_SMART_SUSPEND) &&
|
|
|
- !pm_runtime_enabled(dev));
|
|
|
+ WARN_ON(!pm_runtime_enabled(dev) &&
|
|
|
+ dev_pm_test_driver_flags(dev, DPM_FLAG_SMART_SUSPEND |
|
|
|
+ DPM_FLAG_LEAVE_SUSPENDED));
|
|
|
|
|
|
/*
|
|
|
* If a device's parent goes into runtime suspend at the wrong time,
|