--- drivers/base/power/main.c | 20 ++++++++++++++++---- drivers/base/power/runtime.c | 41 ++++++++++++++++++++++++++++++++++++----- include/linux/pm_runtime.h | 5 +++++ 3 files changed, 57 insertions(+), 9 deletions(-) --- a/drivers/base/power/main.c +++ b/drivers/base/power/main.c @@ -655,14 +655,25 @@ * this device later, it needs to appear as "suspended" to PM-runtime, * so change its status accordingly. * - * Otherwise, the device is going to be resumed, so set its PM-runtime - * status to "active" unless its power.set_active flag is clear, in - * which case it is not necessary to update its PM-runtime status. + * Otherwise, the device is going to be resumed, so try to update its + * PM-runtime status unless its power.set_active flag is clear, in which + * case it is not necessary to do that. */ if (skip_resume) { pm_runtime_set_suspended(dev); } else if (dev->power.set_active) { - pm_runtime_set_active(dev); + /* + * If PM-runtime is not going to be actually enabled for the + * device by a subsequent pm_runtime_enabled() call, it may + * have never been enabled or pm_runtime_force_suspend() may + * have been used. Don't update the PM-runtime statue in + * that case and set power.needs_force_resume in case + * pm_runtime_force_resume() will be called for the device + * subsequently. + */ + if (pm_runtime_cond_set_active(dev) > 0) + dev->power.needs_force_resume = true; + dev->power.set_active = false; } @@ -988,6 +999,7 @@ End: error = dpm_run_callback(callback, dev, state, info); dev->power.is_suspended = false; + dev->power.needs_force_resume = false; Unlock: device_unlock(dev); --- a/drivers/base/power/runtime.c +++ b/drivers/base/power/runtime.c @@ -1253,9 +1253,10 @@ EXPORT_SYMBOL_GPL(pm_runtime_get_if_in_use); /** - * __pm_runtime_set_status - Set runtime PM status of a device. + * pm_runtime_set_status_internal - Set runtime PM status of a device. * @dev: Device to handle. * @status: New runtime PM status of the device. + * @cond: Change the status if runtime PM will be enabled by the next attempt. * * If runtime PM of the device is disabled or its power.runtime_error field is * different from zero, the status may be changed either to RPM_ACTIVE, or to @@ -1275,8 +1276,12 @@ * of the @status value) and the suppliers will be deacticated on exit. The * error returned by the failing supplier activation will be returned in that * case. + * + * If @cond is set, only change the status if power.disable_depth is equal to 1, + * or do nothing and return (power.disable_depth - 1) otherwise. */ -int __pm_runtime_set_status(struct device *dev, unsigned int status) +static int pm_runtime_set_status_internal(struct device *dev, + unsigned int status, bool cond) { struct device *parent = dev->parent; bool notify_parent = false; @@ -1292,10 +1297,26 @@ * Prevent PM-runtime from being enabled for the device or return an * error if it is enabled already and working. */ - if (dev->power.runtime_error || dev->power.disable_depth) - dev->power.disable_depth++; - else + if (dev->power.runtime_error) { + if (cond) + error = -EINVAL; + else + dev->power.disable_depth++; + } else if (dev->power.disable_depth) { + /* + * If cond is set, only attempt to change the status if the + * next invocation of pm_runtime_enable() for the device is + * going to actually enable runtime PM for it. + * + * This is used in a corner case during system-wide resume. + */ + if (cond && dev->power.disable_depth > 1) + error = dev->power.disable_depth - 1; + else + dev->power.disable_depth++; + } else { error = -EAGAIN; + } spin_unlock_irqrestore(&dev->power.lock, flags); @@ -1376,6 +1397,16 @@ return error; } + +int pm_runtime_cond_set_active(struct device *dev) +{ + return pm_runtime_set_status_internal(dev, RPM_ACTIVE, true); +} + +int __pm_runtime_set_status(struct device *dev, unsigned int status) +{ + return pm_runtime_set_status_internal(dev, status, false); +} EXPORT_SYMBOL_GPL(__pm_runtime_set_status); /** --- a/include/linux/pm_runtime.h +++ b/include/linux/pm_runtime.h @@ -75,6 +75,7 @@ extern int pm_runtime_get_if_active(struct device *dev); extern int pm_runtime_get_if_in_use(struct device *dev); extern int pm_schedule_suspend(struct device *dev, unsigned int delay); +extern int pm_runtime_cond_set_active(struct device *dev); extern int __pm_runtime_set_status(struct device *dev, unsigned int status); extern int pm_runtime_barrier(struct device *dev); extern void pm_runtime_enable(struct device *dev); @@ -268,6 +269,10 @@ { return -EINVAL; } +static inline int pm_runtime_cond_set_active(struct device *dev) +{ + return 1; +} static inline int __pm_runtime_set_status(struct device *dev, unsigned int status) { return 0; } static inline int pm_runtime_barrier(struct device *dev) { return 0; }