PM / core: Add helpers for subsystem callback selection

Add helper routines to find and return a suitable subsystem callback
during the "noirq" phases of system suspend/resume (or analogous)
transitions as well as during the "late" phase of system suspend and
the "early" phase of system resume (or analogous) transitions.

The helpers will be called from additional sites going forward.

Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
Reviewed-by: Ulf Hansson <ulf.hansson@linaro.org>
Reviewed-by: Geert Uytterhoeven <geert+renesas@glider.be>
This commit is contained in:
Rafael J. Wysocki 2017-12-10 00:58:18 +01:00
parent 9dbc64a5d5
commit 4fa3061a68

View file

@ -551,31 +551,12 @@ bool dev_pm_may_skip_resume(struct device *dev)
return !dev->power.must_resume && pm_transition.event != PM_EVENT_RESTORE; return !dev->power.must_resume && pm_transition.event != PM_EVENT_RESTORE;
} }
/** static pm_callback_t dpm_subsys_resume_noirq_cb(struct device *dev,
* device_resume_noirq - Execute a "noirq resume" callback for given device. pm_message_t state,
* @dev: Device to handle. const char **info_p)
* @state: PM transition of the system being carried out.
* @async: If true, the device is being resumed asynchronously.
*
* The driver of @dev will not receive interrupts while this function is being
* executed.
*/
static int device_resume_noirq(struct device *dev, pm_message_t state, bool async)
{ {
pm_callback_t callback = NULL; pm_callback_t callback;
const char *info = NULL; const char *info;
int error = 0;
TRACE_DEVICE(dev);
TRACE_RESUME(0);
if (dev->power.syscore || dev->power.direct_complete)
goto Out;
if (!dev->power.is_noirq_suspended)
goto Out;
dpm_wait_for_superior(dev, async);
if (dev->pm_domain) { if (dev->pm_domain) {
info = "noirq power domain "; info = "noirq power domain ";
@ -589,8 +570,44 @@ static int device_resume_noirq(struct device *dev, pm_message_t state, bool asyn
} else if (dev->bus && dev->bus->pm) { } else if (dev->bus && dev->bus->pm) {
info = "noirq bus "; info = "noirq bus ";
callback = pm_noirq_op(dev->bus->pm, state); callback = pm_noirq_op(dev->bus->pm, state);
} else {
return NULL;
} }
if (info_p)
*info_p = info;
return callback;
}
/**
* device_resume_noirq - Execute a "noirq resume" callback for given device.
* @dev: Device to handle.
* @state: PM transition of the system being carried out.
* @async: If true, the device is being resumed asynchronously.
*
* The driver of @dev will not receive interrupts while this function is being
* executed.
*/
static int device_resume_noirq(struct device *dev, pm_message_t state, bool async)
{
pm_callback_t callback;
const char *info;
int error = 0;
TRACE_DEVICE(dev);
TRACE_RESUME(0);
if (dev->power.syscore || dev->power.direct_complete)
goto Out;
if (!dev->power.is_noirq_suspended)
goto Out;
dpm_wait_for_superior(dev, async);
callback = dpm_subsys_resume_noirq_cb(dev, state, &info);
if (!callback && dev->driver && dev->driver->pm) { if (!callback && dev->driver && dev->driver->pm) {
info = "noirq driver "; info = "noirq driver ";
callback = pm_noirq_op(dev->driver->pm, state); callback = pm_noirq_op(dev->driver->pm, state);
@ -704,30 +721,12 @@ void dpm_resume_noirq(pm_message_t state)
dpm_noirq_end(); dpm_noirq_end();
} }
/** static pm_callback_t dpm_subsys_resume_early_cb(struct device *dev,
* device_resume_early - Execute an "early resume" callback for given device. pm_message_t state,
* @dev: Device to handle. const char **info_p)
* @state: PM transition of the system being carried out.
* @async: If true, the device is being resumed asynchronously.
*
* Runtime PM is disabled for @dev while this function is being executed.
*/
static int device_resume_early(struct device *dev, pm_message_t state, bool async)
{ {
pm_callback_t callback = NULL; pm_callback_t callback;
const char *info = NULL; const char *info;
int error = 0;
TRACE_DEVICE(dev);
TRACE_RESUME(0);
if (dev->power.syscore || dev->power.direct_complete)
goto Out;
if (!dev->power.is_late_suspended)
goto Out;
dpm_wait_for_superior(dev, async);
if (dev->pm_domain) { if (dev->pm_domain) {
info = "early power domain "; info = "early power domain ";
@ -741,8 +740,43 @@ static int device_resume_early(struct device *dev, pm_message_t state, bool asyn
} else if (dev->bus && dev->bus->pm) { } else if (dev->bus && dev->bus->pm) {
info = "early bus "; info = "early bus ";
callback = pm_late_early_op(dev->bus->pm, state); callback = pm_late_early_op(dev->bus->pm, state);
} else {
return NULL;
} }
if (info_p)
*info_p = info;
return callback;
}
/**
* device_resume_early - Execute an "early resume" callback for given device.
* @dev: Device to handle.
* @state: PM transition of the system being carried out.
* @async: If true, the device is being resumed asynchronously.
*
* Runtime PM is disabled for @dev while this function is being executed.
*/
static int device_resume_early(struct device *dev, pm_message_t state, bool async)
{
pm_callback_t callback;
const char *info;
int error = 0;
TRACE_DEVICE(dev);
TRACE_RESUME(0);
if (dev->power.syscore || dev->power.direct_complete)
goto Out;
if (!dev->power.is_late_suspended)
goto Out;
dpm_wait_for_superior(dev, async);
callback = dpm_subsys_resume_early_cb(dev, state, &info);
if (!callback && dev->driver && dev->driver->pm) { if (!callback && dev->driver && dev->driver->pm) {
info = "early driver "; info = "early driver ";
callback = pm_late_early_op(dev->driver->pm, state); callback = pm_late_early_op(dev->driver->pm, state);
@ -1128,6 +1162,35 @@ static void dpm_superior_set_must_resume(struct device *dev)
device_links_read_unlock(idx); device_links_read_unlock(idx);
} }
static pm_callback_t dpm_subsys_suspend_noirq_cb(struct device *dev,
pm_message_t state,
const char **info_p)
{
pm_callback_t callback;
const char *info;
if (dev->pm_domain) {
info = "noirq power domain ";
callback = pm_noirq_op(&dev->pm_domain->ops, state);
} else if (dev->type && dev->type->pm) {
info = "noirq type ";
callback = pm_noirq_op(dev->type->pm, state);
} else if (dev->class && dev->class->pm) {
info = "noirq class ";
callback = pm_noirq_op(dev->class->pm, state);
} else if (dev->bus && dev->bus->pm) {
info = "noirq bus ";
callback = pm_noirq_op(dev->bus->pm, state);
} else {
return NULL;
}
if (info_p)
*info_p = info;
return callback;
}
/** /**
* __device_suspend_noirq - Execute a "noirq suspend" callback for given device. * __device_suspend_noirq - Execute a "noirq suspend" callback for given device.
* @dev: Device to handle. * @dev: Device to handle.
@ -1139,8 +1202,8 @@ static void dpm_superior_set_must_resume(struct device *dev)
*/ */
static int __device_suspend_noirq(struct device *dev, pm_message_t state, bool async) static int __device_suspend_noirq(struct device *dev, pm_message_t state, bool async)
{ {
pm_callback_t callback = NULL; pm_callback_t callback;
const char *info = NULL; const char *info;
int error = 0; int error = 0;
TRACE_DEVICE(dev); TRACE_DEVICE(dev);
@ -1159,19 +1222,7 @@ static int __device_suspend_noirq(struct device *dev, pm_message_t state, bool a
if (dev->power.syscore || dev->power.direct_complete) if (dev->power.syscore || dev->power.direct_complete)
goto Complete; goto Complete;
if (dev->pm_domain) { callback = dpm_subsys_suspend_noirq_cb(dev, state, &info);
info = "noirq power domain ";
callback = pm_noirq_op(&dev->pm_domain->ops, state);
} else if (dev->type && dev->type->pm) {
info = "noirq type ";
callback = pm_noirq_op(dev->type->pm, state);
} else if (dev->class && dev->class->pm) {
info = "noirq class ";
callback = pm_noirq_op(dev->class->pm, state);
} else if (dev->bus && dev->bus->pm) {
info = "noirq bus ";
callback = pm_noirq_op(dev->bus->pm, state);
}
if (!callback && dev->driver && dev->driver->pm) { if (!callback && dev->driver && dev->driver->pm) {
info = "noirq driver "; info = "noirq driver ";
@ -1306,6 +1357,35 @@ int dpm_suspend_noirq(pm_message_t state)
return ret; return ret;
} }
static pm_callback_t dpm_subsys_suspend_late_cb(struct device *dev,
pm_message_t state,
const char **info_p)
{
pm_callback_t callback;
const char *info;
if (dev->pm_domain) {
info = "late power domain ";
callback = pm_late_early_op(&dev->pm_domain->ops, state);
} else if (dev->type && dev->type->pm) {
info = "late type ";
callback = pm_late_early_op(dev->type->pm, state);
} else if (dev->class && dev->class->pm) {
info = "late class ";
callback = pm_late_early_op(dev->class->pm, state);
} else if (dev->bus && dev->bus->pm) {
info = "late bus ";
callback = pm_late_early_op(dev->bus->pm, state);
} else {
return NULL;
}
if (info_p)
*info_p = info;
return callback;
}
/** /**
* __device_suspend_late - Execute a "late suspend" callback for given device. * __device_suspend_late - Execute a "late suspend" callback for given device.
* @dev: Device to handle. * @dev: Device to handle.
@ -1316,8 +1396,8 @@ int dpm_suspend_noirq(pm_message_t state)
*/ */
static int __device_suspend_late(struct device *dev, pm_message_t state, bool async) static int __device_suspend_late(struct device *dev, pm_message_t state, bool async)
{ {
pm_callback_t callback = NULL; pm_callback_t callback;
const char *info = NULL; const char *info;
int error = 0; int error = 0;
TRACE_DEVICE(dev); TRACE_DEVICE(dev);
@ -1338,19 +1418,7 @@ static int __device_suspend_late(struct device *dev, pm_message_t state, bool as
if (dev->power.syscore || dev->power.direct_complete) if (dev->power.syscore || dev->power.direct_complete)
goto Complete; goto Complete;
if (dev->pm_domain) { callback = dpm_subsys_suspend_late_cb(dev, state, &info);
info = "late power domain ";
callback = pm_late_early_op(&dev->pm_domain->ops, state);
} else if (dev->type && dev->type->pm) {
info = "late type ";
callback = pm_late_early_op(dev->type->pm, state);
} else if (dev->class && dev->class->pm) {
info = "late class ";
callback = pm_late_early_op(dev->class->pm, state);
} else if (dev->bus && dev->bus->pm) {
info = "late bus ";
callback = pm_late_early_op(dev->bus->pm, state);
}
if (!callback && dev->driver && dev->driver->pm) { if (!callback && dev->driver && dev->driver->pm) {
info = "late driver "; info = "late driver ";