diff --git a/Documentation/power/runtime_pm.txt b/Documentation/power/runtime_pm.txt index 1f054046bc76..0e856088db7c 100644 --- a/Documentation/power/runtime_pm.txt +++ b/Documentation/power/runtime_pm.txt @@ -43,13 +43,18 @@ struct dev_pm_ops { ... }; -The ->runtime_suspend(), ->runtime_resume() and ->runtime_idle() callbacks are -executed by the PM core for either the device type, or the class (if the device -type's struct dev_pm_ops object does not exist), or the bus type (if the -device type's and class' struct dev_pm_ops objects do not exist) of the given -device (this allows device types to override callbacks provided by bus types or -classes if necessary). The bus type, device type and class callbacks are -referred to as subsystem-level callbacks in what follows. +The ->runtime_suspend(), ->runtime_resume() and ->runtime_idle() callbacks +are executed by the PM core for either the power domain, or the device type +(if the device power domain's struct dev_pm_ops does not exist), or the class +(if the device power domain's and type's struct dev_pm_ops object does not +exist), or the bus type (if the device power domain's, type's and class' +struct dev_pm_ops objects do not exist) of the given device, so the priority +order of callbacks from high to low is that power domain callbacks, device +type callbacks, class callbacks and bus type callbacks, and the high priority +one will take precedence over low priority one. The bus type, device type and +class callbacks are referred to as subsystem-level callbacks in what follows, +and generally speaking, the power domain callbacks are used for representing +power domains within a SoC. By default, the callbacks are always invoked in process context with interrupts enabled. However, subsystems can use the pm_runtime_irq_safe() helper function diff --git a/drivers/base/power/runtime.c b/drivers/base/power/runtime.c index 7a6fb5e34a0e..6bb3aafa85ed 100644 --- a/drivers/base/power/runtime.c +++ b/drivers/base/power/runtime.c @@ -286,14 +286,16 @@ static int rpm_callback(int (*cb)(struct device *), struct device *dev) * @dev: Device to suspend. * @rpmflags: Flag bits. * - * Check if the device's runtime PM status allows it to be suspended. If - * another suspend has been started earlier, either return immediately or wait - * for it to finish, depending on the RPM_NOWAIT and RPM_ASYNC flags. Cancel a - * pending idle notification. If the RPM_ASYNC flag is set then queue a - * suspend request; otherwise run the ->runtime_suspend() callback directly. - * If a deferred resume was requested while the callback was running then carry - * it out; otherwise send an idle notification for the device (if the suspend - * failed) or for its parent (if the suspend succeeded). + * Check if the device's runtime PM status allows it to be suspended. + * Cancel a pending idle notification, autosuspend or suspend. If + * another suspend has been started earlier, either return immediately + * or wait for it to finish, depending on the RPM_NOWAIT and RPM_ASYNC + * flags. If the RPM_ASYNC flag is set then queue a suspend request; + * otherwise run the ->runtime_suspend() callback directly. When + * ->runtime_suspend succeeded, if a deferred resume was requested while + * the callback was running then carry it out, otherwise send an idle + * notification for its parent (if the suspend succeeded and both + * ignore_children of parent->power and irq_safe of dev->power are not set). * * This function must be called under dev->power.lock with interrupts disabled. */ @@ -418,15 +420,16 @@ static int rpm_suspend(struct device *dev, int rpmflags) dev->power.runtime_error = 0; else pm_runtime_cancel_pending(dev); - } else { + wake_up_all(&dev->power.wait_queue); + goto out; + } no_callback: - __update_runtime_status(dev, RPM_SUSPENDED); - pm_runtime_deactivate_timer(dev); + __update_runtime_status(dev, RPM_SUSPENDED); + pm_runtime_deactivate_timer(dev); - if (dev->parent) { - parent = dev->parent; - atomic_add_unless(&parent->power.child_count, -1, 0); - } + if (dev->parent) { + parent = dev->parent; + atomic_add_unless(&parent->power.child_count, -1, 0); } wake_up_all(&dev->power.wait_queue);