Merge branch 'pm-runtime' into pm-for-linus
* pm-runtime: PM / Runtime: Use device PM QoS constraints (v2)
This commit is contained in:
commit
0015afaa1f
|
@ -47,21 +47,29 @@ static DEFINE_MUTEX(dev_pm_qos_mtx);
|
||||||
static BLOCKING_NOTIFIER_HEAD(dev_pm_notifiers);
|
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.
|
* @dev: Device to get the PM QoS constraint value for.
|
||||||
*/
|
*/
|
||||||
s32 dev_pm_qos_read_value(struct device *dev)
|
s32 dev_pm_qos_read_value(struct device *dev)
|
||||||
{
|
{
|
||||||
struct pm_qos_constraints *c;
|
|
||||||
unsigned long flags;
|
unsigned long flags;
|
||||||
s32 ret = 0;
|
s32 ret;
|
||||||
|
|
||||||
spin_lock_irqsave(&dev->power.lock, flags);
|
spin_lock_irqsave(&dev->power.lock, flags);
|
||||||
|
ret = __dev_pm_qos_read_value(dev);
|
||||||
c = dev->power.constraints;
|
|
||||||
if (c)
|
|
||||||
ret = pm_qos_read_value(c);
|
|
||||||
|
|
||||||
spin_unlock_irqrestore(&dev->power.lock, flags);
|
spin_unlock_irqrestore(&dev->power.lock, flags);
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
|
|
|
@ -282,6 +282,47 @@ static int rpm_callback(int (*cb)(struct device *), struct device *dev)
|
||||||
return retval != -EACCES ? retval : -EIO;
|
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.
|
* rpm_suspend - Carry out runtime suspend of given device.
|
||||||
* @dev: Device to suspend.
|
* @dev: Device to suspend.
|
||||||
|
@ -308,6 +349,7 @@ static int rpm_suspend(struct device *dev, int rpmflags)
|
||||||
{
|
{
|
||||||
int (*callback)(struct device *);
|
int (*callback)(struct device *);
|
||||||
struct device *parent = NULL;
|
struct device *parent = NULL;
|
||||||
|
struct rpm_qos_data qos;
|
||||||
int retval;
|
int retval;
|
||||||
|
|
||||||
trace_rpm_suspend(dev, rpmflags);
|
trace_rpm_suspend(dev, rpmflags);
|
||||||
|
@ -403,8 +445,38 @@ static int rpm_suspend(struct device *dev, int rpmflags)
|
||||||
goto out;
|
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);
|
__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)
|
if (dev->pm_domain)
|
||||||
callback = dev->pm_domain->ops.runtime_suspend;
|
callback = dev->pm_domain->ops.runtime_suspend;
|
||||||
else if (dev->type && dev->type->pm)
|
else if (dev->type && dev->type->pm)
|
||||||
|
@ -420,27 +492,9 @@ static int rpm_suspend(struct device *dev, int rpmflags)
|
||||||
callback = dev->driver->pm->runtime_suspend;
|
callback = dev->driver->pm->runtime_suspend;
|
||||||
|
|
||||||
retval = rpm_callback(callback, dev);
|
retval = rpm_callback(callback, dev);
|
||||||
if (retval) {
|
if (retval)
|
||||||
__update_runtime_status(dev, RPM_ACTIVE);
|
goto fail;
|
||||||
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;
|
|
||||||
}
|
|
||||||
no_callback:
|
no_callback:
|
||||||
__update_runtime_status(dev, RPM_SUSPENDED);
|
__update_runtime_status(dev, RPM_SUSPENDED);
|
||||||
pm_runtime_deactivate_timer(dev);
|
pm_runtime_deactivate_timer(dev);
|
||||||
|
@ -472,6 +526,29 @@ static int rpm_suspend(struct device *dev, int rpmflags)
|
||||||
trace_rpm_return_int(dev, _THIS_IP_, retval);
|
trace_rpm_return_int(dev, _THIS_IP_, retval);
|
||||||
|
|
||||||
return 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;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -626,6 +703,9 @@ static int rpm_resume(struct device *dev, int rpmflags)
|
||||||
if (dev->power.no_callbacks)
|
if (dev->power.no_callbacks)
|
||||||
goto no_callback; /* Assume success. */
|
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);
|
__update_runtime_status(dev, RPM_RESUMING);
|
||||||
|
|
||||||
if (dev->pm_domain)
|
if (dev->pm_domain)
|
||||||
|
@ -1288,6 +1368,9 @@ void pm_runtime_init(struct device *dev)
|
||||||
setup_timer(&dev->power.suspend_timer, pm_suspend_timer_fn,
|
setup_timer(&dev->power.suspend_timer, pm_suspend_timer_fn,
|
||||||
(unsigned long)dev);
|
(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);
|
init_waitqueue_head(&dev->power.wait_queue);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1305,3 +1388,28 @@ void pm_runtime_remove(struct device *dev)
|
||||||
if (dev->power.irq_safe && dev->parent)
|
if (dev->power.irq_safe && dev->parent)
|
||||||
pm_runtime_put_sync(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);
|
||||||
|
}
|
||||||
|
|
|
@ -508,6 +508,8 @@ struct dev_pm_info {
|
||||||
unsigned long active_jiffies;
|
unsigned long active_jiffies;
|
||||||
unsigned long suspended_jiffies;
|
unsigned long suspended_jiffies;
|
||||||
unsigned long accounting_timestamp;
|
unsigned long accounting_timestamp;
|
||||||
|
ktime_t suspend_time;
|
||||||
|
s64 max_time_suspended_ns;
|
||||||
#endif
|
#endif
|
||||||
struct pm_subsys_data *subsys_data; /* Owned by the subsystem. */
|
struct pm_subsys_data *subsys_data; /* Owned by the subsystem. */
|
||||||
struct pm_qos_constraints *constraints;
|
struct pm_qos_constraints *constraints;
|
||||||
|
|
|
@ -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);
|
int pm_qos_request_active(struct pm_qos_request *req);
|
||||||
s32 pm_qos_read_value(struct pm_qos_constraints *c);
|
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);
|
s32 dev_pm_qos_read_value(struct device *dev);
|
||||||
int dev_pm_qos_add_request(struct device *dev, struct dev_pm_qos_request *req,
|
int dev_pm_qos_add_request(struct device *dev, struct dev_pm_qos_request *req,
|
||||||
s32 value);
|
s32 value);
|
||||||
|
@ -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)
|
static inline s32 pm_qos_read_value(struct pm_qos_constraints *c)
|
||||||
{ return 0; }
|
{ 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)
|
static inline s32 dev_pm_qos_read_value(struct device *dev)
|
||||||
{ return 0; }
|
{ return 0; }
|
||||||
static inline int dev_pm_qos_add_request(struct device *dev,
|
static inline int dev_pm_qos_add_request(struct device *dev,
|
||||||
|
|
|
@ -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_use_autosuspend(struct device *dev, bool use);
|
||||||
extern void pm_runtime_set_autosuspend_delay(struct device *dev, int delay);
|
extern void pm_runtime_set_autosuspend_delay(struct device *dev, int delay);
|
||||||
extern unsigned long pm_runtime_autosuspend_expiration(struct device *dev);
|
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)
|
static inline bool pm_children_suspended(struct device *dev)
|
||||||
{
|
{
|
||||||
|
@ -148,6 +150,9 @@ static inline void pm_runtime_set_autosuspend_delay(struct device *dev,
|
||||||
static inline unsigned long pm_runtime_autosuspend_expiration(
|
static inline unsigned long pm_runtime_autosuspend_expiration(
|
||||||
struct device *dev) { return 0; }
|
struct device *dev) { return 0; }
|
||||||
|
|
||||||
|
static inline void pm_runtime_update_max_time_suspended(struct device *dev,
|
||||||
|
s64 delta_ns) {}
|
||||||
|
|
||||||
#endif /* !CONFIG_PM_RUNTIME */
|
#endif /* !CONFIG_PM_RUNTIME */
|
||||||
|
|
||||||
static inline int pm_runtime_idle(struct device *dev)
|
static inline int pm_runtime_idle(struct device *dev)
|
||||||
|
|
Reference in New Issue