Loading drivers/base/power/qos.c +16 −8 Original line number Diff line number Diff line Loading @@ -47,21 +47,29 @@ static DEFINE_MUTEX(dev_pm_qos_mtx); static BLOCKING_NOTIFIER_HEAD(dev_pm_notifiers); /** * dev_pm_qos_read_value - Get PM QoS constraint for a given device. * __dev_pm_qos_read_value - Get PM QoS constraint for a given device. * @dev: Device to get the PM QoS constraint value for. * * This routine must be called with dev->power.lock held. */ s32 __dev_pm_qos_read_value(struct device *dev) { struct pm_qos_constraints *c = dev->power.constraints; return c ? pm_qos_read_value(c) : 0; } /** * dev_pm_qos_read_value - Get PM QoS constraint for a given device (locked). * @dev: Device to get the PM QoS constraint value for. */ s32 dev_pm_qos_read_value(struct device *dev) { struct pm_qos_constraints *c; unsigned long flags; s32 ret = 0; s32 ret; spin_lock_irqsave(&dev->power.lock, flags); c = dev->power.constraints; if (c) ret = pm_qos_read_value(c); ret = __dev_pm_qos_read_value(dev); spin_unlock_irqrestore(&dev->power.lock, flags); return ret; Loading drivers/base/power/runtime.c +128 −20 Original line number Diff line number Diff line Loading @@ -282,6 +282,47 @@ static int rpm_callback(int (*cb)(struct device *), struct device *dev) 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. Loading @@ -308,6 +349,7 @@ static int rpm_suspend(struct device *dev, int rpmflags) { int (*callback)(struct device *); struct device *parent = NULL; struct rpm_qos_data qos; int retval; trace_rpm_suspend(dev, rpmflags); Loading Loading @@ -403,8 +445,38 @@ static int rpm_suspend(struct device *dev, int rpmflags) 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) Loading @@ -420,27 +492,9 @@ static int rpm_suspend(struct device *dev, int rpmflags) callback = dev->driver->pm->runtime_suspend; retval = rpm_callback(callback, dev); if (retval) { __update_runtime_status(dev, RPM_ACTIVE); dev->power.deferred_resume = false; if (retval == -EAGAIN || retval == -EBUSY) { dev->power.runtime_error = 0; if (retval) goto fail; /* * If the callback routine failed an autosuspend, and * if the last_busy time has been updated so that there * is a new autosuspend expiration time, automatically * reschedule another autosuspend. */ if ((rpmflags & RPM_AUTO) && pm_runtime_autosuspend_expiration(dev) != 0) goto repeat; } else { pm_runtime_cancel_pending(dev); } wake_up_all(&dev->power.wait_queue); goto out; } no_callback: __update_runtime_status(dev, RPM_SUSPENDED); pm_runtime_deactivate_timer(dev); Loading Loading @@ -472,6 +526,29 @@ static int rpm_suspend(struct device *dev, int rpmflags) trace_rpm_return_int(dev, _THIS_IP_, retval); return retval; fail: __update_runtime_status(dev, RPM_ACTIVE); dev->power.suspend_time = ktime_set(0, 0); dev->power.max_time_suspended_ns = -1; dev->power.deferred_resume = false; if (retval == -EAGAIN || retval == -EBUSY) { dev->power.runtime_error = 0; /* * If the callback routine failed an autosuspend, and * if the last_busy time has been updated so that there * is a new autosuspend expiration time, automatically * reschedule another autosuspend. */ if ((rpmflags & RPM_AUTO) && pm_runtime_autosuspend_expiration(dev) != 0) goto repeat; } else { pm_runtime_cancel_pending(dev); } wake_up_all(&dev->power.wait_queue); goto out; } /** Loading Loading @@ -626,6 +703,9 @@ static int rpm_resume(struct device *dev, int rpmflags) if (dev->power.no_callbacks) goto no_callback; /* Assume success. */ dev->power.suspend_time = ktime_set(0, 0); dev->power.max_time_suspended_ns = -1; __update_runtime_status(dev, RPM_RESUMING); if (dev->pm_domain) Loading Loading @@ -1288,6 +1368,9 @@ void pm_runtime_init(struct device *dev) setup_timer(&dev->power.suspend_timer, pm_suspend_timer_fn, (unsigned long)dev); dev->power.suspend_time = ktime_set(0, 0); dev->power.max_time_suspended_ns = -1; init_waitqueue_head(&dev->power.wait_queue); } Loading @@ -1305,3 +1388,28 @@ void pm_runtime_remove(struct device *dev) if (dev->power.irq_safe && dev->parent) pm_runtime_put_sync(dev->parent); } /** * pm_runtime_update_max_time_suspended - Update device's suspend time data. * @dev: Device to handle. * @delta_ns: Value to subtract from the device's max_time_suspended_ns field. * * Update the device's power.max_time_suspended_ns field by subtracting * @delta_ns from it. The resulting value of power.max_time_suspended_ns is * never negative. */ void pm_runtime_update_max_time_suspended(struct device *dev, s64 delta_ns) { unsigned long flags; spin_lock_irqsave(&dev->power.lock, flags); if (delta_ns > 0 && dev->power.max_time_suspended_ns > 0) { if (dev->power.max_time_suspended_ns > delta_ns) dev->power.max_time_suspended_ns -= delta_ns; else dev->power.max_time_suspended_ns = 0; } spin_unlock_irqrestore(&dev->power.lock, flags); } include/linux/pm.h +2 −0 Original line number Diff line number Diff line Loading @@ -508,6 +508,8 @@ struct dev_pm_info { unsigned long active_jiffies; unsigned long suspended_jiffies; unsigned long accounting_timestamp; ktime_t suspend_time; s64 max_time_suspended_ns; #endif struct pm_subsys_data *subsys_data; /* Owned by the subsystem. */ struct pm_qos_constraints *constraints; Loading include/linux/pm_qos.h +3 −0 Original line number Diff line number Diff line Loading @@ -78,6 +78,7 @@ int pm_qos_remove_notifier(int pm_qos_class, struct notifier_block *notifier); int pm_qos_request_active(struct pm_qos_request *req); s32 pm_qos_read_value(struct pm_qos_constraints *c); s32 __dev_pm_qos_read_value(struct device *dev); s32 dev_pm_qos_read_value(struct device *dev); int dev_pm_qos_add_request(struct device *dev, struct dev_pm_qos_request *req, s32 value); Loading Loading @@ -119,6 +120,8 @@ static inline int pm_qos_request_active(struct pm_qos_request *req) static inline s32 pm_qos_read_value(struct pm_qos_constraints *c) { return 0; } static inline s32 __dev_pm_qos_read_value(struct device *dev) { return 0; } static inline s32 dev_pm_qos_read_value(struct device *dev) { return 0; } static inline int dev_pm_qos_add_request(struct device *dev, Loading include/linux/pm_runtime.h +5 −0 Original line number Diff line number Diff line Loading @@ -45,6 +45,8 @@ extern void pm_runtime_irq_safe(struct device *dev); extern void __pm_runtime_use_autosuspend(struct device *dev, bool use); extern void pm_runtime_set_autosuspend_delay(struct device *dev, int delay); extern unsigned long pm_runtime_autosuspend_expiration(struct device *dev); extern void pm_runtime_update_max_time_suspended(struct device *dev, s64 delta_ns); static inline bool pm_children_suspended(struct device *dev) { Loading Loading @@ -148,6 +150,9 @@ static inline void pm_runtime_set_autosuspend_delay(struct device *dev, static inline unsigned long pm_runtime_autosuspend_expiration( struct device *dev) { return 0; } static inline void pm_runtime_update_max_time_suspended(struct device *dev, s64 delta_ns) {} #endif /* !CONFIG_PM_RUNTIME */ static inline int pm_runtime_idle(struct device *dev) Loading Loading
drivers/base/power/qos.c +16 −8 Original line number Diff line number Diff line Loading @@ -47,21 +47,29 @@ static DEFINE_MUTEX(dev_pm_qos_mtx); static BLOCKING_NOTIFIER_HEAD(dev_pm_notifiers); /** * dev_pm_qos_read_value - Get PM QoS constraint for a given device. * __dev_pm_qos_read_value - Get PM QoS constraint for a given device. * @dev: Device to get the PM QoS constraint value for. * * This routine must be called with dev->power.lock held. */ s32 __dev_pm_qos_read_value(struct device *dev) { struct pm_qos_constraints *c = dev->power.constraints; return c ? pm_qos_read_value(c) : 0; } /** * dev_pm_qos_read_value - Get PM QoS constraint for a given device (locked). * @dev: Device to get the PM QoS constraint value for. */ s32 dev_pm_qos_read_value(struct device *dev) { struct pm_qos_constraints *c; unsigned long flags; s32 ret = 0; s32 ret; spin_lock_irqsave(&dev->power.lock, flags); c = dev->power.constraints; if (c) ret = pm_qos_read_value(c); ret = __dev_pm_qos_read_value(dev); spin_unlock_irqrestore(&dev->power.lock, flags); return ret; Loading
drivers/base/power/runtime.c +128 −20 Original line number Diff line number Diff line Loading @@ -282,6 +282,47 @@ static int rpm_callback(int (*cb)(struct device *), struct device *dev) 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. Loading @@ -308,6 +349,7 @@ static int rpm_suspend(struct device *dev, int rpmflags) { int (*callback)(struct device *); struct device *parent = NULL; struct rpm_qos_data qos; int retval; trace_rpm_suspend(dev, rpmflags); Loading Loading @@ -403,8 +445,38 @@ static int rpm_suspend(struct device *dev, int rpmflags) 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) Loading @@ -420,27 +492,9 @@ static int rpm_suspend(struct device *dev, int rpmflags) callback = dev->driver->pm->runtime_suspend; retval = rpm_callback(callback, dev); if (retval) { __update_runtime_status(dev, RPM_ACTIVE); dev->power.deferred_resume = false; if (retval == -EAGAIN || retval == -EBUSY) { dev->power.runtime_error = 0; if (retval) goto fail; /* * If the callback routine failed an autosuspend, and * if the last_busy time has been updated so that there * is a new autosuspend expiration time, automatically * reschedule another autosuspend. */ if ((rpmflags & RPM_AUTO) && pm_runtime_autosuspend_expiration(dev) != 0) goto repeat; } else { pm_runtime_cancel_pending(dev); } wake_up_all(&dev->power.wait_queue); goto out; } no_callback: __update_runtime_status(dev, RPM_SUSPENDED); pm_runtime_deactivate_timer(dev); Loading Loading @@ -472,6 +526,29 @@ static int rpm_suspend(struct device *dev, int rpmflags) trace_rpm_return_int(dev, _THIS_IP_, retval); return retval; fail: __update_runtime_status(dev, RPM_ACTIVE); dev->power.suspend_time = ktime_set(0, 0); dev->power.max_time_suspended_ns = -1; dev->power.deferred_resume = false; if (retval == -EAGAIN || retval == -EBUSY) { dev->power.runtime_error = 0; /* * If the callback routine failed an autosuspend, and * if the last_busy time has been updated so that there * is a new autosuspend expiration time, automatically * reschedule another autosuspend. */ if ((rpmflags & RPM_AUTO) && pm_runtime_autosuspend_expiration(dev) != 0) goto repeat; } else { pm_runtime_cancel_pending(dev); } wake_up_all(&dev->power.wait_queue); goto out; } /** Loading Loading @@ -626,6 +703,9 @@ static int rpm_resume(struct device *dev, int rpmflags) if (dev->power.no_callbacks) goto no_callback; /* Assume success. */ dev->power.suspend_time = ktime_set(0, 0); dev->power.max_time_suspended_ns = -1; __update_runtime_status(dev, RPM_RESUMING); if (dev->pm_domain) Loading Loading @@ -1288,6 +1368,9 @@ void pm_runtime_init(struct device *dev) setup_timer(&dev->power.suspend_timer, pm_suspend_timer_fn, (unsigned long)dev); dev->power.suspend_time = ktime_set(0, 0); dev->power.max_time_suspended_ns = -1; init_waitqueue_head(&dev->power.wait_queue); } Loading @@ -1305,3 +1388,28 @@ void pm_runtime_remove(struct device *dev) if (dev->power.irq_safe && dev->parent) pm_runtime_put_sync(dev->parent); } /** * pm_runtime_update_max_time_suspended - Update device's suspend time data. * @dev: Device to handle. * @delta_ns: Value to subtract from the device's max_time_suspended_ns field. * * Update the device's power.max_time_suspended_ns field by subtracting * @delta_ns from it. The resulting value of power.max_time_suspended_ns is * never negative. */ void pm_runtime_update_max_time_suspended(struct device *dev, s64 delta_ns) { unsigned long flags; spin_lock_irqsave(&dev->power.lock, flags); if (delta_ns > 0 && dev->power.max_time_suspended_ns > 0) { if (dev->power.max_time_suspended_ns > delta_ns) dev->power.max_time_suspended_ns -= delta_ns; else dev->power.max_time_suspended_ns = 0; } spin_unlock_irqrestore(&dev->power.lock, flags); }
include/linux/pm.h +2 −0 Original line number Diff line number Diff line Loading @@ -508,6 +508,8 @@ struct dev_pm_info { unsigned long active_jiffies; unsigned long suspended_jiffies; unsigned long accounting_timestamp; ktime_t suspend_time; s64 max_time_suspended_ns; #endif struct pm_subsys_data *subsys_data; /* Owned by the subsystem. */ struct pm_qos_constraints *constraints; Loading
include/linux/pm_qos.h +3 −0 Original line number Diff line number Diff line Loading @@ -78,6 +78,7 @@ int pm_qos_remove_notifier(int pm_qos_class, struct notifier_block *notifier); int pm_qos_request_active(struct pm_qos_request *req); s32 pm_qos_read_value(struct pm_qos_constraints *c); s32 __dev_pm_qos_read_value(struct device *dev); s32 dev_pm_qos_read_value(struct device *dev); int dev_pm_qos_add_request(struct device *dev, struct dev_pm_qos_request *req, s32 value); Loading Loading @@ -119,6 +120,8 @@ static inline int pm_qos_request_active(struct pm_qos_request *req) static inline s32 pm_qos_read_value(struct pm_qos_constraints *c) { return 0; } static inline s32 __dev_pm_qos_read_value(struct device *dev) { return 0; } static inline s32 dev_pm_qos_read_value(struct device *dev) { return 0; } static inline int dev_pm_qos_add_request(struct device *dev, Loading
include/linux/pm_runtime.h +5 −0 Original line number Diff line number Diff line Loading @@ -45,6 +45,8 @@ extern void pm_runtime_irq_safe(struct device *dev); extern void __pm_runtime_use_autosuspend(struct device *dev, bool use); extern void pm_runtime_set_autosuspend_delay(struct device *dev, int delay); extern unsigned long pm_runtime_autosuspend_expiration(struct device *dev); extern void pm_runtime_update_max_time_suspended(struct device *dev, s64 delta_ns); static inline bool pm_children_suspended(struct device *dev) { Loading Loading @@ -148,6 +150,9 @@ static inline void pm_runtime_set_autosuspend_delay(struct device *dev, static inline unsigned long pm_runtime_autosuspend_expiration( struct device *dev) { return 0; } static inline void pm_runtime_update_max_time_suspended(struct device *dev, s64 delta_ns) {} #endif /* !CONFIG_PM_RUNTIME */ static inline int pm_runtime_idle(struct device *dev) Loading