diff options
Diffstat (limited to 'drivers/base/power/main.c')
-rw-r--r-- | drivers/base/power/main.c | 191 |
1 files changed, 105 insertions, 86 deletions
diff --git a/drivers/base/power/main.c b/drivers/base/power/main.c index 1b1a786b7dec..e0dc4071e088 100644 --- a/drivers/base/power/main.c +++ b/drivers/base/power/main.c @@ -21,6 +21,7 @@ #include <linux/kallsyms.h> #include <linux/mutex.h> #include <linux/pm.h> +#include <linux/pm_runtime.h> #include <linux/resume-trace.h> #include <linux/rwsem.h> #include <linux/interrupt.h> @@ -49,7 +50,17 @@ static DEFINE_MUTEX(dpm_list_mtx); static bool transition_started; /** - * device_pm_lock - lock the list of active devices used by the PM core + * device_pm_init - Initialize the PM-related part of a device object. + * @dev: Device object being initialized. + */ +void device_pm_init(struct device *dev) +{ + dev->power.status = DPM_ON; + pm_runtime_init(dev); +} + +/** + * device_pm_lock - Lock the list of active devices used by the PM core. */ void device_pm_lock(void) { @@ -57,7 +68,7 @@ void device_pm_lock(void) } /** - * device_pm_unlock - unlock the list of active devices used by the PM core + * device_pm_unlock - Unlock the list of active devices used by the PM core. */ void device_pm_unlock(void) { @@ -65,8 +76,8 @@ void device_pm_unlock(void) } /** - * device_pm_add - add a device to the list of active devices - * @dev: Device to be added to the list + * device_pm_add - Add a device to the PM core's list of active devices. + * @dev: Device to add to the list. */ void device_pm_add(struct device *dev) { @@ -92,10 +103,8 @@ void device_pm_add(struct device *dev) } /** - * device_pm_remove - remove a device from the list of active devices - * @dev: Device to be removed from the list - * - * This function also removes the device's PM-related sysfs attributes. + * device_pm_remove - Remove a device from the PM core's list of active devices. + * @dev: Device to be removed from the list. */ void device_pm_remove(struct device *dev) { @@ -105,12 +114,13 @@ void device_pm_remove(struct device *dev) mutex_lock(&dpm_list_mtx); list_del_init(&dev->power.entry); mutex_unlock(&dpm_list_mtx); + pm_runtime_remove(dev); } /** - * device_pm_move_before - move device in dpm_list - * @deva: Device to move in dpm_list - * @devb: Device @deva should come before + * device_pm_move_before - Move device in the PM core's list of active devices. + * @deva: Device to move in dpm_list. + * @devb: Device @deva should come before. */ void device_pm_move_before(struct device *deva, struct device *devb) { @@ -124,9 +134,9 @@ void device_pm_move_before(struct device *deva, struct device *devb) } /** - * device_pm_move_after - move device in dpm_list - * @deva: Device to move in dpm_list - * @devb: Device @deva should come after + * device_pm_move_after - Move device in the PM core's list of active devices. + * @deva: Device to move in dpm_list. + * @devb: Device @deva should come after. */ void device_pm_move_after(struct device *deva, struct device *devb) { @@ -140,8 +150,8 @@ void device_pm_move_after(struct device *deva, struct device *devb) } /** - * device_pm_move_last - move device to end of dpm_list - * @dev: Device to move in dpm_list + * device_pm_move_last - Move device to end of the PM core's list of devices. + * @dev: Device to move in dpm_list. */ void device_pm_move_last(struct device *dev) { @@ -152,10 +162,10 @@ void device_pm_move_last(struct device *dev) } /** - * pm_op - execute the PM operation appropiate for given PM event - * @dev: Device. - * @ops: PM operations to choose from. - * @state: PM transition of the system being carried out. + * pm_op - Execute the PM operation appropriate for given PM event. + * @dev: Device to handle. + * @ops: PM operations to choose from. + * @state: PM transition of the system being carried out. */ static int pm_op(struct device *dev, const struct dev_pm_ops *ops, @@ -213,13 +223,13 @@ static int pm_op(struct device *dev, } /** - * pm_noirq_op - execute the PM operation appropiate for given PM event - * @dev: Device. - * @ops: PM operations to choose from. - * @state: PM transition of the system being carried out. + * pm_noirq_op - Execute the PM operation appropriate for given PM event. + * @dev: Device to handle. + * @ops: PM operations to choose from. + * @state: PM transition of the system being carried out. * - * The operation is executed with interrupts disabled by the only remaining - * functional CPU in the system. + * The driver of @dev will not receive interrupts while this function is being + * executed. */ static int pm_noirq_op(struct device *dev, const struct dev_pm_ops *ops, @@ -317,11 +327,12 @@ static void pm_dev_err(struct device *dev, pm_message_t state, char *info, /*------------------------- Resume routines -------------------------*/ /** - * device_resume_noirq - Power on one device (early resume). - * @dev: Device. - * @state: PM transition of the system being carried out. + * device_resume_noirq - Execute an "early resume" callback for given device. + * @dev: Device to handle. + * @state: PM transition of the system being carried out. * - * Must be called with interrupts disabled. + * 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) { @@ -343,20 +354,18 @@ static int device_resume_noirq(struct device *dev, pm_message_t state) } /** - * dpm_resume_noirq - Power on all regular (non-sysdev) devices. - * @state: PM transition of the system being carried out. - * - * Call the "noirq" resume handlers for all devices marked as - * DPM_OFF_IRQ and enable device drivers to receive interrupts. + * dpm_resume_noirq - Execute "early resume" callbacks for non-sysdev devices. + * @state: PM transition of the system being carried out. * - * Must be called under dpm_list_mtx. Device drivers should not receive - * interrupts while it's being executed. + * Call the "noirq" resume handlers for all devices marked as DPM_OFF_IRQ and + * enable device drivers to receive interrupts. */ void dpm_resume_noirq(pm_message_t state) { struct device *dev; mutex_lock(&dpm_list_mtx); + transition_started = false; list_for_each_entry(dev, &dpm_list, power.entry) if (dev->power.status > DPM_OFF) { int error; @@ -372,9 +381,9 @@ void dpm_resume_noirq(pm_message_t state) EXPORT_SYMBOL_GPL(dpm_resume_noirq); /** - * device_resume - Restore state for one device. - * @dev: Device. - * @state: PM transition of the system being carried out. + * device_resume - Execute "resume" callbacks for given device. + * @dev: Device to handle. + * @state: PM transition of the system being carried out. */ static int device_resume(struct device *dev, pm_message_t state) { @@ -423,11 +432,11 @@ static int device_resume(struct device *dev, pm_message_t state) } /** - * dpm_resume - Resume every device. - * @state: PM transition of the system being carried out. + * dpm_resume - Execute "resume" callbacks for non-sysdev devices. + * @state: PM transition of the system being carried out. * - * Execute the appropriate "resume" callback for all devices the status of - * which indicates that they are inactive. + * Execute the appropriate "resume" callback for all devices whose status + * indicates that they are suspended. */ static void dpm_resume(pm_message_t state) { @@ -435,7 +444,6 @@ static void dpm_resume(pm_message_t state) INIT_LIST_HEAD(&list); mutex_lock(&dpm_list_mtx); - transition_started = false; while (!list_empty(&dpm_list)) { struct device *dev = to_device(dpm_list.next); @@ -464,9 +472,9 @@ static void dpm_resume(pm_message_t state) } /** - * device_complete - Complete a PM transition for given device - * @dev: Device. - * @state: PM transition of the system being carried out. + * device_complete - Complete a PM transition for given device. + * @dev: Device to handle. + * @state: PM transition of the system being carried out. */ static void device_complete(struct device *dev, pm_message_t state) { @@ -491,11 +499,11 @@ static void device_complete(struct device *dev, pm_message_t state) } /** - * dpm_complete - Complete a PM transition for all devices. - * @state: PM transition of the system being carried out. + * dpm_complete - Complete a PM transition for all non-sysdev devices. + * @state: PM transition of the system being carried out. * - * Execute the ->complete() callbacks for all devices that are not marked - * as DPM_ON. + * Execute the ->complete() callbacks for all devices whose PM status is not + * DPM_ON (this allows new devices to be registered). */ static void dpm_complete(pm_message_t state) { @@ -512,6 +520,7 @@ static void dpm_complete(pm_message_t state) mutex_unlock(&dpm_list_mtx); device_complete(dev, state); + pm_runtime_put_noidle(dev); mutex_lock(&dpm_list_mtx); } @@ -524,11 +533,11 @@ static void dpm_complete(pm_message_t state) } /** - * dpm_resume_end - Restore state of each device in system. - * @state: PM transition of the system being carried out. + * dpm_resume_end - Execute "resume" callbacks and complete system transition. + * @state: PM transition of the system being carried out. * - * Resume all the devices, unlock them all, and allow new - * devices to be registered once again. + * Execute "resume" callbacks for all devices and complete the PM transition of + * the system. */ void dpm_resume_end(pm_message_t state) { @@ -542,9 +551,11 @@ EXPORT_SYMBOL_GPL(dpm_resume_end); /*------------------------- Suspend routines -------------------------*/ /** - * resume_event - return a PM message representing the resume event - * corresponding to given sleep state. - * @sleep_state: PM message representing a sleep state. + * resume_event - Return a "resume" message for given "suspend" sleep state. + * @sleep_state: PM message representing a sleep state. + * + * Return a PM message representing the resume event corresponding to given + * sleep state. */ static pm_message_t resume_event(pm_message_t sleep_state) { @@ -561,11 +572,12 @@ static pm_message_t resume_event(pm_message_t sleep_state) } /** - * device_suspend_noirq - Shut down one device (late suspend). - * @dev: Device. - * @state: PM transition of the system being carried out. + * device_suspend_noirq - Execute a "late suspend" callback for given device. + * @dev: Device to handle. + * @state: PM transition of the system being carried out. * - * This is called with interrupts off and only a single CPU running. + * The driver of @dev will not receive interrupts while this function is being + * executed. */ static int device_suspend_noirq(struct device *dev, pm_message_t state) { @@ -582,13 +594,11 @@ static int device_suspend_noirq(struct device *dev, pm_message_t state) } /** - * dpm_suspend_noirq - Power down all regular (non-sysdev) devices. - * @state: PM transition of the system being carried out. - * - * Prevent device drivers from receiving interrupts and call the "noirq" - * suspend handlers. + * dpm_suspend_noirq - Execute "late suspend" callbacks for non-sysdev devices. + * @state: PM transition of the system being carried out. * - * Must be called under dpm_list_mtx. + * Prevent device drivers from receiving interrupts and call the "noirq" suspend + * handlers for all non-sysdev devices. */ int dpm_suspend_noirq(pm_message_t state) { @@ -613,9 +623,9 @@ int dpm_suspend_noirq(pm_message_t state) EXPORT_SYMBOL_GPL(dpm_suspend_noirq); /** - * device_suspend - Save state of one device. - * @dev: Device. - * @state: PM transition of the system being carried out. + * device_suspend - Execute "suspend" callbacks for given device. + * @dev: Device to handle. + * @state: PM transition of the system being carried out. */ static int device_suspend(struct device *dev, pm_message_t state) { @@ -662,10 +672,8 @@ static int device_suspend(struct device *dev, pm_message_t state) } /** - * dpm_suspend - Suspend every device. - * @state: PM transition of the system being carried out. - * - * Execute the appropriate "suspend" callbacks for all devices. + * dpm_suspend - Execute "suspend" callbacks for all non-sysdev devices. + * @state: PM transition of the system being carried out. */ static int dpm_suspend(pm_message_t state) { @@ -699,9 +707,12 @@ static int dpm_suspend(pm_message_t state) } /** - * device_prepare - Execute the ->prepare() callback(s) for given device. - * @dev: Device. - * @state: PM transition of the system being carried out. + * device_prepare - Prepare a device for system power transition. + * @dev: Device to handle. + * @state: PM transition of the system being carried out. + * + * Execute the ->prepare() callback(s) for given device. No new children of the + * device may be registered after this function has returned. */ static int device_prepare(struct device *dev, pm_message_t state) { @@ -737,10 +748,10 @@ static int device_prepare(struct device *dev, pm_message_t state) } /** - * dpm_prepare - Prepare all devices for a PM transition. - * @state: PM transition of the system being carried out. + * dpm_prepare - Prepare all non-sysdev devices for a system PM transition. + * @state: PM transition of the system being carried out. * - * Execute the ->prepare() callback for all devices. + * Execute the ->prepare() callback(s) for all devices. */ static int dpm_prepare(pm_message_t state) { @@ -757,7 +768,14 @@ static int dpm_prepare(pm_message_t state) dev->power.status = DPM_PREPARING; mutex_unlock(&dpm_list_mtx); - error = device_prepare(dev, state); + pm_runtime_get_noresume(dev); + if (pm_runtime_barrier(dev) && device_may_wakeup(dev)) { + /* Wake-up requested during system sleep transition. */ + pm_runtime_put_noidle(dev); + error = -EBUSY; + } else { + error = device_prepare(dev, state); + } mutex_lock(&dpm_list_mtx); if (error) { @@ -784,10 +802,11 @@ static int dpm_prepare(pm_message_t state) } /** - * dpm_suspend_start - Save state and stop all devices in system. - * @state: PM transition of the system being carried out. + * dpm_suspend_start - Prepare devices for PM transition and suspend them. + * @state: PM transition of the system being carried out. * - * Prepare and suspend all devices. + * Prepare all non-sysdev devices for system PM transition and execute "suspend" + * callbacks for them. */ int dpm_suspend_start(pm_message_t state) { |