diff options
Diffstat (limited to 'drivers/base/power/runtime.c')
| -rw-r--r-- | drivers/base/power/runtime.c | 169 | 
1 files changed, 127 insertions, 42 deletions
diff --git a/drivers/base/power/runtime.c b/drivers/base/power/runtime.c index 268a3509757..67c7938e430 100644 --- a/drivers/base/power/runtime.c +++ b/drivers/base/power/runtime.c @@ -13,6 +13,43 @@  #include <trace/events/rpm.h>  #include "power.h" +#define RPM_GET_CALLBACK(dev, cb)				\ +({								\ +	int (*__rpm_cb)(struct device *__d);			\ +								\ +	if (dev->pm_domain)					\ +		__rpm_cb = dev->pm_domain->ops.cb;		\ +	else if (dev->type && dev->type->pm)			\ +		__rpm_cb = dev->type->pm->cb;			\ +	else if (dev->class && dev->class->pm)			\ +		__rpm_cb = dev->class->pm->cb;			\ +	else if (dev->bus && dev->bus->pm)			\ +		__rpm_cb = dev->bus->pm->cb;			\ +	else							\ +		__rpm_cb = NULL;				\ +								\ +	if (!__rpm_cb && dev->driver && dev->driver->pm)	\ +		__rpm_cb = dev->driver->pm->cb;			\ +								\ +	__rpm_cb;						\ +}) + +static int (*rpm_get_suspend_cb(struct device *dev))(struct device *) +{ +	return RPM_GET_CALLBACK(dev, runtime_suspend); +} + +static int (*rpm_get_resume_cb(struct device *dev))(struct device *) +{ +	return RPM_GET_CALLBACK(dev, runtime_resume); +} + +#ifdef CONFIG_PM_RUNTIME +static int (*rpm_get_idle_cb(struct device *dev))(struct device *) +{ +	return RPM_GET_CALLBACK(dev, runtime_idle); +} +  static int rpm_resume(struct device *dev, int rpmflags);  static int rpm_suspend(struct device *dev, int rpmflags); @@ -258,7 +295,8 @@ static int __rpm_callback(int (*cb)(struct device *), struct device *dev)   * Check if the device's runtime PM status allows it to be suspended.  If   * another idle notification has been started earlier, return immediately.  If   * the RPM_ASYNC flag is set then queue an idle-notification request; otherwise - * run the ->runtime_idle() callback directly. + * run the ->runtime_idle() callback directly. If the ->runtime_idle callback + * doesn't exist or if it returns 0, call rpm_suspend with the RPM_AUTO flag.   *   * This function must be called under dev->power.lock with interrupts disabled.   */ @@ -309,19 +347,7 @@ static int rpm_idle(struct device *dev, int rpmflags)  	dev->power.idle_notification = true; -	if (dev->pm_domain) -		callback = dev->pm_domain->ops.runtime_idle; -	else if (dev->type && dev->type->pm) -		callback = dev->type->pm->runtime_idle; -	else if (dev->class && dev->class->pm) -		callback = dev->class->pm->runtime_idle; -	else if (dev->bus && dev->bus->pm) -		callback = dev->bus->pm->runtime_idle; -	else -		callback = NULL; - -	if (!callback && dev->driver && dev->driver->pm) -		callback = dev->driver->pm->runtime_idle; +	callback = rpm_get_idle_cb(dev);  	if (callback)  		retval = __rpm_callback(callback, dev); @@ -331,7 +357,7 @@ static int rpm_idle(struct device *dev, int rpmflags)   out:  	trace_rpm_return_int(dev, _THIS_IP_, retval); -	return retval ? retval : rpm_suspend(dev, rpmflags); +	return retval ? retval : rpm_suspend(dev, rpmflags | RPM_AUTO);  }  /** @@ -491,19 +517,7 @@ static int rpm_suspend(struct device *dev, int rpmflags)  	__update_runtime_status(dev, RPM_SUSPENDING); -	if (dev->pm_domain) -		callback = dev->pm_domain->ops.runtime_suspend; -	else if (dev->type && dev->type->pm) -		callback = dev->type->pm->runtime_suspend; -	else if (dev->class && dev->class->pm) -		callback = dev->class->pm->runtime_suspend; -	else if (dev->bus && dev->bus->pm) -		callback = dev->bus->pm->runtime_suspend; -	else -		callback = NULL; - -	if (!callback && dev->driver && dev->driver->pm) -		callback = dev->driver->pm->runtime_suspend; +	callback = rpm_get_suspend_cb(dev);  	retval = rpm_callback(callback, dev);  	if (retval) @@ -723,19 +737,7 @@ static int rpm_resume(struct device *dev, int rpmflags)  	__update_runtime_status(dev, RPM_RESUMING); -	if (dev->pm_domain) -		callback = dev->pm_domain->ops.runtime_resume; -	else if (dev->type && dev->type->pm) -		callback = dev->type->pm->runtime_resume; -	else if (dev->class && dev->class->pm) -		callback = dev->class->pm->runtime_resume; -	else if (dev->bus && dev->bus->pm) -		callback = dev->bus->pm->runtime_resume; -	else -		callback = NULL; - -	if (!callback && dev->driver && dev->driver->pm) -		callback = dev->driver->pm->runtime_resume; +	callback = rpm_get_resume_cb(dev);  	retval = rpm_callback(callback, dev);  	if (retval) { @@ -1129,7 +1131,7 @@ EXPORT_SYMBOL_GPL(pm_runtime_barrier);   * @dev: Device to handle.   * @check_resume: If set, check if there's a resume request for the device.   * - * Increment power.disable_depth for the device and if was zero previously, + * Increment power.disable_depth for the device and if it was zero previously,   * cancel all pending runtime PM requests for the device and wait for all   * operations in progress to complete.  The device can be either active or   * suspended after its runtime PM has been disabled. @@ -1400,3 +1402,86 @@ void pm_runtime_remove(struct device *dev)  	if (dev->power.irq_safe && dev->parent)  		pm_runtime_put(dev->parent);  } +#endif + +/** + * pm_runtime_force_suspend - Force a device into suspend state if needed. + * @dev: Device to suspend. + * + * Disable runtime PM so we safely can check the device's runtime PM status and + * if it is active, invoke it's .runtime_suspend callback to bring it into + * suspend state. Keep runtime PM disabled to preserve the state unless we + * encounter errors. + * + * Typically this function may be invoked from a system suspend callback to make + * sure the device is put into low power state. + */ +int pm_runtime_force_suspend(struct device *dev) +{ +	int (*callback)(struct device *); +	int ret = 0; + +	pm_runtime_disable(dev); + +	/* +	 * Note that pm_runtime_status_suspended() returns false while +	 * !CONFIG_PM_RUNTIME, which means the device will be put into low +	 * power state. +	 */ +	if (pm_runtime_status_suspended(dev)) +		return 0; + +	callback = rpm_get_suspend_cb(dev); + +	if (!callback) { +		ret = -ENOSYS; +		goto err; +	} + +	ret = callback(dev); +	if (ret) +		goto err; + +	pm_runtime_set_suspended(dev); +	return 0; +err: +	pm_runtime_enable(dev); +	return ret; +} +EXPORT_SYMBOL_GPL(pm_runtime_force_suspend); + +/** + * pm_runtime_force_resume - Force a device into resume state. + * @dev: Device to resume. + * + * Prior invoking this function we expect the user to have brought the device + * into low power state by a call to pm_runtime_force_suspend(). Here we reverse + * those actions and brings the device into full power. We update the runtime PM + * status and re-enables runtime PM. + * + * Typically this function may be invoked from a system resume callback to make + * sure the device is put into full power state. + */ +int pm_runtime_force_resume(struct device *dev) +{ +	int (*callback)(struct device *); +	int ret = 0; + +	callback = rpm_get_resume_cb(dev); + +	if (!callback) { +		ret = -ENOSYS; +		goto out; +	} + +	ret = callback(dev); +	if (ret) +		goto out; + +	pm_runtime_set_active(dev); +	pm_runtime_mark_last_busy(dev); +out: +	pm_runtime_enable(dev); +	return ret; +} +EXPORT_SYMBOL_GPL(pm_runtime_force_resume);  | 
