/* * drivers/base/power/runtime.c - Helper functions for device runtime PM * * Copyright (c) 2009 Rafael J. Wysocki <rjw@sisk.pl>, Novell Inc. * Copyright (C) 2010 Alan Stern <stern@rowland.harvard.edu> * * This file is released under the GPLv2. */ #include <linux/sched.h>#include <linux/export.h>#include <linux/pm_runtime.h>#include <trace/events/rpm.h>#include "power.h" static int rpm_resume(struct device *dev, int rpmflags);static int rpm_suspend(struct device *dev, int rpmflags); /** * update_pm_runtime_accounting - Update the time accounting of power states * @dev: Device to update the accounting for * * In order to be able to have time accounting of the various power states * (as used by programs such as PowerTOP to show the effectiveness of runtime * PM), we need to track the time spent in each state. * update_pm_runtime_accounting must be called each time before the * runtime_status field is updated, to account the time in the old state * correctly. */void update_pm_runtime_accounting(struct device *dev){ unsigned long now = jiffies; unsigned long delta; delta = now - dev->power.accounting_timestamp; dev->power.accounting_timestamp = now; if (dev->power.disable_depth > 0) return; if (dev->power.runtime_status == RPM_SUSPENDED) dev->power.suspended_jiffies += delta; else dev->power.active_jiffies += delta;} static void __update_runtime_status(struct device *dev, enum rpm_status status){ update_pm_runtime_accounting(dev); dev->power.runtime_status = status;} /** * pm_runtime_deactivate_timer - Deactivate given device's suspend timer. * @dev: Device to handle. */static void pm_runtime_deactivate_timer(struct device *dev){ if (dev->power.timer_expires > 0) { del_timer(&dev->power.suspend_timer); dev->power.timer_expires = 0; }} /** * pm_runtime_cancel_pending - Deactivate suspend timer and cancel requests. * @dev: Device to handle. */static void pm_runtime_cancel_pending(struct device *dev){ pm_runtime_deactivate_timer(dev); /* * In case there's a request pending, make sure its work function will * return without doing anything. */ dev->power.request = RPM_REQ_NONE;} /* * pm_runtime_autosuspend_expiration - Get a device's autosuspend-delay expiration time. * @dev: Device to handle. * * Compute the autosuspend-delay expiration time based on the device's * power.last_busy time. If the delay has already expired or is disabled * (negative) or the power.use_autosuspend flag isn't set, return 0. * Otherwise return the expiration time in jiffies (adjusted to be nonzero). * * This function may be called either with or without dev->power.lock held. * Either way it can be racy, since power.last_busy may be updated at any time. */unsigned long pm_runtime_autosuspend_expiration(struct device *dev){ int autosuspend_delay; long elapsed; unsigned long last_busy; unsigned long expires = 0; if (!dev->power.use_autosuspend) goto out; autosuspend_delay = ACCESS_ONCE(dev->power.autosuspend_delay); if (autosuspend_delay < 0) goto out; last_busy = ACCESS_ONCE(dev->power.last_busy); elapsed = jiffies - last_busy; if (elapsed < 0) goto out; /* jiffies has wrapped around. */ /* * If the autosuspend_delay is >= 1 second, align the timer by rounding * up to the nearest second. */ expires = last_busy + msecs_to_jiffies(autosuspend_delay); if (autosuspend_delay >= 1000) expires = round_jiffies(expires); expires += !expires; if (elapsed >= expires - last_busy) expires = 0; /* Already expired. */ out: return expires;}EXPORT_SYMBOL_GPL(pm_runtime_autosuspend_expiration); /** * rpm_check_suspend_allowed - Test whether a device may be suspended. * @dev: Device to test. */static int rpm_check_suspend_allowed(struct device *dev){ int retval = 0; if (dev->power.runtime_error) retval = -EINVAL; else if (dev->power.disable_depth > 0) retval = -EACCES; else if (atomic_read(&dev->power.usage_count) > 0) retval = -EAGAIN; else if (!pm_children_suspended(dev)) retval = -EBUSY; /* Pending resume requests take precedence over suspends. */ else if ((dev->power.deferred_resume && dev->power.runtime_status == RPM_SUSPENDING) || (dev->power.request_pending && dev->power.request == RPM_REQ_RESUME)) retval = -EAGAIN; else if (dev->power.runtime_status == RPM_SUSPENDED) retval = 1; return retval;} /** * __rpm_callback - Run a given runtime PM callback for a given device. * @cb: Runtime PM callback to run. * @dev: Device to run the callback for. */static int __rpm_callback(int (*cb)(struct device *), struct device *dev) __releases(&dev->power.lock) __acquires(&dev->power.lock){ int retval; if (dev->power.irq_safe) spin_unlock(&dev->power.lock); else spin_unlock_irq(&dev->power.lock); retval = cb(dev); if (dev->power.irq_safe) spin_lock(&dev->power.lock); else spin_lock_irq(&dev->power.lock); return retval;} /** * rpm_idle - Notify device bus type if the device can be suspended. * @dev: Device to notify the bus type about. * @rpmflags: Flag bits. * * 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. * * This function must be called under dev->power.lock with interrupts disabled. */static int rpm_idle(struct device *dev, int rpmflags){ int (*callback)(struct device *); int retval; trace_rpm_idle(dev, rpmflags); retval = rpm_check_suspend_allowed(dev); if (retval < 0) ; /* Conditions are wrong. */ /* Idle notifications are allowed only in the RPM_ACTIVE state. */ else if (dev->power.runtime_status != RPM_ACTIVE) retval = -EAGAIN; /* * Any pending request other than an idle notification takes * precedence over us, except that the timer may be running. */ else if (dev->power.request_pending && dev->power.request > RPM_REQ_IDLE) retval = -EAGAIN; /* Act as though RPM_NOWAIT is always set. */ else if (dev->power.idle_notification) retval = -EINPROGRESS; if (retval) goto out; /* Pending requests need to be canceled. */ dev->power.request = RPM_REQ_NONE; if (dev->power.no_callbacks) { /* Assume ->runtime_idle() callback would have suspended. */ retval = rpm_suspend(dev, rpmflags); goto out; } /* Carry out an asynchronous or a synchronous idle notification. */ if (rpmflags & RPM_ASYNC) { dev->power.request = RPM_REQ_IDLE; if (!dev->power.request_pending) { dev->power.request_pending = true; queue_work(pm_wq, &dev->power.work); } goto out; } 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; if (callback) __rpm_callback(callback, dev); dev->power.idle_notification = false; wake_up_all(&dev->power.wait_queue); out: trace_rpm_return_int(dev, _THIS_IP_, retval); return retval;} /** * rpm_callback - Run a given runtime PM callback for a given device. * @cb: Runtime PM callback to run. * @dev: Device to run the callback for. */static int rpm_callback(int (*cb)(struct device *), struct device *dev){ int retval; if (!cb) return -ENOSYS; retval = __rpm_callback(cb, dev); dev->power.runtime_error = retval; return retval != -EACCES ? retval : -EIO;} struct rpm_qos_data { ktime_t time_now; s64 constraint_ns;}; /** * rpm_update_qos_constraint - Update a given PM QoS constraint data. * @dev: Device whose timing data to use. * @data: PM QoS constraint data to update. * * Use the suspend timing data of @dev to update PM QoS constraint data pointed * to by @data. */static int rpm_update_qos_constraint(struct device *dev, void *data){ struct rpm_qos_data *qos = data; unsigned long flags; s64 delta_ns; int ret = 0; spin_lock_irqsave(&dev->power.lock, flags); if (dev->power.max_time_suspended_ns < 0) goto out; delta_ns = dev->power.max_time_suspended_ns - ktime_to_ns(ktime_sub(qos->time_now, dev->power.suspend_time)); if (delta_ns <= 0) { ret = -EBUSY; goto out; } if (qos->constraint_ns > delta_ns || qos->constraint_ns == 0) qos->constraint_ns = delta_ns; out: spin_unlock_irqrestore(&dev->power.lock, flags); return ret;} /** * rpm_suspend - Carry out runtime suspend of given device. * @dev: Device to suspend. * @rpmflags: Flag bits. * * 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). * If ->runtime_suspend failed with -EAGAIN or -EBUSY, and if the RPM_AUTO * flag is set and the next autosuspend-delay expiration time is in the * future, schedule another autosuspend attempt. * * This function must be called under dev->power.lock with interrupts disabled. */static int rpm_suspend(struct device *dev, int rpmflags) __releases(&dev->power.lock) __acquires(&dev->power.lock){ int (*callback)(struct device *); struct device *parent = NULL; struct rpm_qos_data qos; int retval; trace_rpm_suspend(dev, rpmflags); repeat: retval = rpm_check_suspend_allowed(dev); if (retval < 0) ; /* Conditions are wrong. */ /* Synchronous suspends are not allowed in the RPM_RESUMING state. */ else if (dev->power.runtime_status == RPM_RESUMING && !(rpmflags & RPM_ASYNC)) retval = -EAGAIN; if (retval) goto out; /* If the autosuspend_delay time hasn't expired yet, reschedule. */ if ((rpmflags & RPM_AUTO) && dev->power.runtime_status != RPM_SUSPENDING) { unsigned long expires = pm_runtime_autosuspend_expiration(dev); if (expires != 0) { /* Pending requests need to be canceled. */ dev->power.request = RPM_REQ_NONE; /* * Optimization: If the timer is already running and is * set to expire at or before the autosuspend delay, * avoid the overhead of resetting it. Just let it * expire; pm_suspend_timer_fn() will take care of the * rest. */ if (!(dev->power.timer_expires && time_before_eq( dev->power.timer_expires, expires))) { dev->power.timer_expires = expires; mod_timer(&dev->power.suspend_timer, expires); } dev->power.timer_autosuspends = 1; goto out; } } /* Other scheduled or pending requests need to be canceled. */ pm_runtime_cancel_pending(dev); if (dev->power.runtime_status == RPM_SUSPENDING) { DEFINE_WAIT(wait); if (rpmflags & (RPM_ASYNC | RPM_NOWAIT)) { retval = -EINPROGRESS; goto out; } if (dev->power.irq_safe) { spin_unlock(&dev->power.lock); cpu_relax(); spin_lock(&dev->power.lock); goto repeat; } /* Wait for the other suspend running in parallel with us. */ for (;;) { prepare_to_wait(&dev->power.wait_queue, &wait, TASK_UNINTERRUPTIBLE); if (dev->power.runtime_status != RPM_SUSPENDING) break; spin_unlock_irq(&dev->power.lock); schedule(); spin_lock_irq(&dev->power.lock); } finish_wait(&dev->power.wait_queue, &wait); goto repeat; } dev->power.deferred_resume = false; if (dev->power.no_callbacks) goto no_callback; /* Assume success. */ /* Carry out an asynchronous or a synchronous suspend. */ if (rpmflags & RPM_ASYNC) { dev->power.request = (rpmflags & RPM_AUTO) ? RPM_REQ_AUTOSUSPEND : RPM_REQ_SUSPEND; if (!dev->power.request_pending) { dev->power.request_pending = true; queue_work(pm_wq, &dev->power.work); } goto out; } qos.constraint_ns = __dev_pm_qos_read_value(dev); if (qos.constraint_ns < 0) { /* Negative constraint means "never suspend". */ retval = -EPERM; goto out; } qos.constraint_ns *= NSEC_PER_USEC; qos.time_now = ktime_get(); __update_runtime_status(dev, RPM_SUSPENDING); if (!dev->power.ignore_children) { if (dev->power.irq_safe) spin_unlock(&dev->power.lock); else spin_unlock_irq(&dev->power.lock); retval = device_for_each_child(dev, &qos, rpm_update_qos_constraint); if (dev->power.irq_safe) spin_lock(&dev->power.lock); else spin_lock_irq(&dev->power.lock); if (retval) goto fail; } dev->power.suspend_time = qos.time_now; dev->power.max_time_suspended_ns = qos.constraint_ns ? : -1; 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; retval = rpm_callback(callback, dev); if (retval) goto fail; no_callback: __update_runtime_status(dev, RPM_SUSPENDED); pm_runtime_deactivate_timer(dev);Lines 1–500 of 1416. Load more
/* * drivers/base/power/runtime.c - Helper functions for device runtime PM * * Copyright (c) 2009 Rafael J. Wysocki <rjw@sisk.pl>, Novell Inc. * Copyright (C) 2010 Alan Stern <stern@rowland.harvard.edu> * * This file is released under the GPLv2. */ #include <linux/sched.h> #include <linux/export.h> #include <linux/pm_runtime.h> #include <trace/events/rpm.h> #include "power.h" static int rpm_resume(struct device *dev, int rpmflags); static int rpm_suspend(struct device *dev, int rpmflags); /** * update_pm_runtime_accounting - Update the time accounting of power states * @dev: Device to update the accounting for * * In order to be able to have time accounting of the various power states * (as used by programs such as PowerTOP to show the effectiveness of runtime * PM), we need to track the time spent in each state. * update_pm_runtime_accounting must be called each time before the * runtime_status field is updated, to account the time in the old state * correctly. */ void update_pm_runtime_accounting(struct device *dev) { unsigned long now = jiffies; unsigned long delta; delta = now - dev->power.accounting_timestamp; dev->power.accounting_timestamp = now; if (dev->power.disable_depth > 0) return; if (dev->power.runtime_status == RPM_SUSPENDED) dev->power.suspended_jiffies += delta; else dev->power.active_jiffies += delta; } static void __update_runtime_status(struct device *dev, enum rpm_status status) { update_pm_runtime_accounting(dev); dev->power.runtime_status = status; } /** * pm_runtime_deactivate_timer - Deactivate given device's suspend timer. * @dev: Device to handle. */ static void pm_runtime_deactivate_timer(struct device *dev) { if (dev->power.timer_expires > 0) { del_timer(&dev->power.suspend_timer); dev->power.timer_expires = 0; } } /** * pm_runtime_cancel_pending - Deactivate suspend timer and cancel requests. * @dev: Device to handle. */ static void pm_runtime_cancel_pending(struct device *dev) { pm_runtime_deactivate_timer(dev); /* * In case there's a request pending, make sure its work function will * return without doing anything. */ dev->power.request = RPM_REQ_NONE; } /* * pm_runtime_autosuspend_expiration - Get a device's autosuspend-delay expiration time. * @dev: Device to handle. * * Compute the autosuspend-delay expiration time based on the device's * power.last_busy time. If the delay has already expired or is disabled * (negative) or the power.use_autosuspend flag isn't set, return 0. * Otherwise return the expiration time in jiffies (adjusted to be nonzero). * * This function may be called either with or without dev->power.lock held. * Either way it can be racy, since power.last_busy may be updated at any time. */ unsigned long pm_runtime_autosuspend_expiration(struct device *dev) { int autosuspend_delay; long elapsed; unsigned long last_busy; unsigned long expires = 0; if (!dev->power.use_autosuspend) goto out; autosuspend_delay = ACCESS_ONCE(dev->power.autosuspend_delay); if (autosuspend_delay < 0) goto out; last_busy = ACCESS_ONCE(dev->power.last_busy); elapsed = jiffies - last_busy; if (elapsed < 0) goto out; /* jiffies has wrapped around. */ /* * If the autosuspend_delay is >= 1 second, align the timer by rounding * up to the nearest second. */ expires = last_busy + msecs_to_jiffies(autosuspend_delay); if (autosuspend_delay >= 1000) expires = round_jiffies(expires); expires += !expires; if (elapsed >= expires - last_busy) expires = 0; /* Already expired. */ out: return expires; } EXPORT_SYMBOL_GPL(pm_runtime_autosuspend_expiration); /** * rpm_check_suspend_allowed - Test whether a device may be suspended. * @dev: Device to test. */ static int rpm_check_suspend_allowed(struct device *dev) { int retval = 0; if (dev->power.runtime_error) retval = -EINVAL; else if (dev->power.disable_depth > 0) retval = -EACCES; else if (atomic_read(&dev->power.usage_count) > 0) retval = -EAGAIN; else if (!pm_children_suspended(dev)) retval = -EBUSY; /* Pending resume requests take precedence over suspends. */ else if ((dev->power.deferred_resume && dev->power.runtime_status == RPM_SUSPENDING) || (dev->power.request_pending && dev->power.request == RPM_REQ_RESUME)) retval = -EAGAIN; else if (dev->power.runtime_status == RPM_SUSPENDED) retval = 1; return retval; } /** * __rpm_callback - Run a given runtime PM callback for a given device. * @cb: Runtime PM callback to run. * @dev: Device to run the callback for. */ static int __rpm_callback(int (*cb)(struct device *), struct device *dev) __releases(&dev->power.lock) __acquires(&dev->power.lock) { int retval; if (dev->power.irq_safe) spin_unlock(&dev->power.lock); else spin_unlock_irq(&dev->power.lock); retval = cb(dev); if (dev->power.irq_safe) spin_lock(&dev->power.lock); else spin_lock_irq(&dev->power.lock); return retval; } /** * rpm_idle - Notify device bus type if the device can be suspended. * @dev: Device to notify the bus type about. * @rpmflags: Flag bits. * * 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. * * This function must be called under dev->power.lock with interrupts disabled. */ static int rpm_idle(struct device *dev, int rpmflags) { int (*callback)(struct device *); int retval; trace_rpm_idle(dev, rpmflags); retval = rpm_check_suspend_allowed(dev); if (retval < 0) ; /* Conditions are wrong. */ /* Idle notifications are allowed only in the RPM_ACTIVE state. */ else if (dev->power.runtime_status != RPM_ACTIVE) retval = -EAGAIN; /* * Any pending request other than an idle notification takes * precedence over us, except that the timer may be running. */ else if (dev->power.request_pending && dev->power.request > RPM_REQ_IDLE) retval = -EAGAIN; /* Act as though RPM_NOWAIT is always set. */ else if (dev->power.idle_notification) retval = -EINPROGRESS; if (retval) goto out; /* Pending requests need to be canceled. */ dev->power.request = RPM_REQ_NONE; if (dev->power.no_callbacks) { /* Assume ->runtime_idle() callback would have suspended. */ retval = rpm_suspend(dev, rpmflags); goto out; } /* Carry out an asynchronous or a synchronous idle notification. */ if (rpmflags & RPM_ASYNC) { dev->power.request = RPM_REQ_IDLE; if (!dev->power.request_pending) { dev->power.request_pending = true; queue_work(pm_wq, &dev->power.work); } goto out; } 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; if (callback) __rpm_callback(callback, dev); dev->power.idle_notification = false; wake_up_all(&dev->power.wait_queue); out: trace_rpm_return_int(dev, _THIS_IP_, retval); return retval; } /** * rpm_callback - Run a given runtime PM callback for a given device. * @cb: Runtime PM callback to run. * @dev: Device to run the callback for. */ static int rpm_callback(int (*cb)(struct device *), struct device *dev) { int retval; if (!cb) return -ENOSYS; retval = __rpm_callback(cb, dev); dev->power.runtime_error = retval; return retval != -EACCES ? retval : -EIO; } struct rpm_qos_data { ktime_t time_now; s64 constraint_ns; }; /** * rpm_update_qos_constraint - Update a given PM QoS constraint data. * @dev: Device whose timing data to use. * @data: PM QoS constraint data to update. * * Use the suspend timing data of @dev to update PM QoS constraint data pointed * to by @data. */ static int rpm_update_qos_constraint(struct device *dev, void *data) { struct rpm_qos_data *qos = data; unsigned long flags; s64 delta_ns; int ret = 0; spin_lock_irqsave(&dev->power.lock, flags); if (dev->power.max_time_suspended_ns < 0) goto out; delta_ns = dev->power.max_time_suspended_ns - ktime_to_ns(ktime_sub(qos->time_now, dev->power.suspend_time)); if (delta_ns <= 0) { ret = -EBUSY; goto out; } if (qos->constraint_ns > delta_ns || qos->constraint_ns == 0) qos->constraint_ns = delta_ns; out: spin_unlock_irqrestore(&dev->power.lock, flags); return ret; } /** * rpm_suspend - Carry out runtime suspend of given device. * @dev: Device to suspend. * @rpmflags: Flag bits. * * 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). * If ->runtime_suspend failed with -EAGAIN or -EBUSY, and if the RPM_AUTO * flag is set and the next autosuspend-delay expiration time is in the * future, schedule another autosuspend attempt. * * This function must be called under dev->power.lock with interrupts disabled. */ static int rpm_suspend(struct device *dev, int rpmflags) __releases(&dev->power.lock) __acquires(&dev->power.lock) { int (*callback)(struct device *); struct device *parent = NULL; struct rpm_qos_data qos; int retval; trace_rpm_suspend(dev, rpmflags); repeat: retval = rpm_check_suspend_allowed(dev); if (retval < 0) ; /* Conditions are wrong. */ /* Synchronous suspends are not allowed in the RPM_RESUMING state. */ else if (dev->power.runtime_status == RPM_RESUMING && !(rpmflags & RPM_ASYNC)) retval = -EAGAIN; if (retval) goto out; /* If the autosuspend_delay time hasn't expired yet, reschedule. */ if ((rpmflags & RPM_AUTO) && dev->power.runtime_status != RPM_SUSPENDING) { unsigned long expires = pm_runtime_autosuspend_expiration(dev); if (expires != 0) { /* Pending requests need to be canceled. */ dev->power.request = RPM_REQ_NONE; /* * Optimization: If the timer is already running and is * set to expire at or before the autosuspend delay, * avoid the overhead of resetting it. Just let it * expire; pm_suspend_timer_fn() will take care of the * rest. */ if (!(dev->power.timer_expires && time_before_eq( dev->power.timer_expires, expires))) { dev->power.timer_expires = expires; mod_timer(&dev->power.suspend_timer, expires); } dev->power.timer_autosuspends = 1; goto out; } } /* Other scheduled or pending requests need to be canceled. */ pm_runtime_cancel_pending(dev); if (dev->power.runtime_status == RPM_SUSPENDING) { DEFINE_WAIT(wait); if (rpmflags & (RPM_ASYNC | RPM_NOWAIT)) { retval = -EINPROGRESS; goto out; } if (dev->power.irq_safe) { spin_unlock(&dev->power.lock); cpu_relax(); spin_lock(&dev->power.lock); goto repeat; } /* Wait for the other suspend running in parallel with us. */ for (;;) { prepare_to_wait(&dev->power.wait_queue, &wait, TASK_UNINTERRUPTIBLE); if (dev->power.runtime_status != RPM_SUSPENDING) break; spin_unlock_irq(&dev->power.lock); schedule(); spin_lock_irq(&dev->power.lock); } finish_wait(&dev->power.wait_queue, &wait); goto repeat; } dev->power.deferred_resume = false; if (dev->power.no_callbacks) goto no_callback; /* Assume success. */ /* Carry out an asynchronous or a synchronous suspend. */ if (rpmflags & RPM_ASYNC) { dev->power.request = (rpmflags & RPM_AUTO) ? RPM_REQ_AUTOSUSPEND : RPM_REQ_SUSPEND; if (!dev->power.request_pending) { dev->power.request_pending = true; queue_work(pm_wq, &dev->power.work); } goto out; } qos.constraint_ns = __dev_pm_qos_read_value(dev); if (qos.constraint_ns < 0) { /* Negative constraint means "never suspend". */ retval = -EPERM; goto out; } qos.constraint_ns *= NSEC_PER_USEC; qos.time_now = ktime_get(); __update_runtime_status(dev, RPM_SUSPENDING); if (!dev->power.ignore_children) { if (dev->power.irq_safe) spin_unlock(&dev->power.lock); else spin_unlock_irq(&dev->power.lock); retval = device_for_each_child(dev, &qos, rpm_update_qos_constraint); if (dev->power.irq_safe) spin_lock(&dev->power.lock); else spin_lock_irq(&dev->power.lock); if (retval) goto fail; } dev->power.suspend_time = qos.time_now; dev->power.max_time_suspended_ns = qos.constraint_ns ? : -1; 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; retval = rpm_callback(callback, dev); if (retval) goto fail; no_callback: __update_runtime_status(dev, RPM_SUSPENDED); pm_runtime_deactivate_timer(dev);