From 5f986c590fcf4284924fcda991cf14ab32bff49f Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Tue, 23 Oct 2012 01:07:27 +0200 Subject: PM / QoS: Prepare device structure for adding more constraint types Currently struct dev_pm_info contains only one PM QoS constraints pointer reserved for latency requirements. Since one more device constraints type (i.e. flags) will be necessary, introduce a new structure, struct dev_pm_qos, that eventually will contain all of the available device PM QoS constraints and replace the "constraints" pointer in struct dev_pm_info with a pointer to the new structure called "qos". Signed-off-by: Rafael J. Wysocki Reviewed-by: Jean Pihet --- drivers/base/power/qos.c | 42 ++++++++++++++++++++++-------------------- 1 file changed, 22 insertions(+), 20 deletions(-) (limited to 'drivers/base') diff --git a/drivers/base/power/qos.c b/drivers/base/power/qos.c index 74a67e0019a..40ff1b02a7c 100644 --- a/drivers/base/power/qos.c +++ b/drivers/base/power/qos.c @@ -55,9 +55,7 @@ static BLOCKING_NOTIFIER_HEAD(dev_pm_notifiers); */ 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; + return dev->power.qos ? pm_qos_read_value(&dev->power.qos->latency) : 0; } /** @@ -91,12 +89,12 @@ static int apply_constraint(struct dev_pm_qos_request *req, { int ret, curr_value; - ret = pm_qos_update_target(req->dev->power.constraints, + ret = pm_qos_update_target(&req->dev->power.qos->latency, &req->node, action, value); if (ret) { /* Call the global callbacks if needed */ - curr_value = pm_qos_read_value(req->dev->power.constraints); + curr_value = pm_qos_read_value(&req->dev->power.qos->latency); blocking_notifier_call_chain(&dev_pm_notifiers, (unsigned long)curr_value, req); @@ -114,20 +112,22 @@ static int apply_constraint(struct dev_pm_qos_request *req, */ static int dev_pm_qos_constraints_allocate(struct device *dev) { + struct dev_pm_qos *qos; struct pm_qos_constraints *c; struct blocking_notifier_head *n; - c = kzalloc(sizeof(*c), GFP_KERNEL); - if (!c) + qos = kzalloc(sizeof(*qos), GFP_KERNEL); + if (!qos) return -ENOMEM; n = kzalloc(sizeof(*n), GFP_KERNEL); if (!n) { - kfree(c); + kfree(qos); return -ENOMEM; } BLOCKING_INIT_NOTIFIER_HEAD(n); + c = &qos->latency; plist_head_init(&c->list); c->target_value = PM_QOS_DEV_LAT_DEFAULT_VALUE; c->default_value = PM_QOS_DEV_LAT_DEFAULT_VALUE; @@ -135,7 +135,7 @@ static int dev_pm_qos_constraints_allocate(struct device *dev) c->notifiers = n; spin_lock_irq(&dev->power.lock); - dev->power.constraints = c; + dev->power.qos = qos; spin_unlock_irq(&dev->power.lock); return 0; @@ -151,7 +151,7 @@ static int dev_pm_qos_constraints_allocate(struct device *dev) void dev_pm_qos_constraints_init(struct device *dev) { mutex_lock(&dev_pm_qos_mtx); - dev->power.constraints = NULL; + dev->power.qos = NULL; dev->power.power_state = PMSG_ON; mutex_unlock(&dev_pm_qos_mtx); } @@ -164,6 +164,7 @@ void dev_pm_qos_constraints_init(struct device *dev) */ void dev_pm_qos_constraints_destroy(struct device *dev) { + struct dev_pm_qos *qos; struct dev_pm_qos_request *req, *tmp; struct pm_qos_constraints *c; @@ -176,10 +177,11 @@ void dev_pm_qos_constraints_destroy(struct device *dev) mutex_lock(&dev_pm_qos_mtx); dev->power.power_state = PMSG_INVALID; - c = dev->power.constraints; - if (!c) + qos = dev->power.qos; + if (!qos) goto out; + c = &qos->latency; /* Flush the constraints list for the device */ plist_for_each_entry_safe(req, tmp, &c->list, node) { /* @@ -191,7 +193,7 @@ void dev_pm_qos_constraints_destroy(struct device *dev) } spin_lock_irq(&dev->power.lock); - dev->power.constraints = NULL; + dev->power.qos = NULL; spin_unlock_irq(&dev->power.lock); kfree(c->notifiers); @@ -235,7 +237,7 @@ int dev_pm_qos_add_request(struct device *dev, struct dev_pm_qos_request *req, mutex_lock(&dev_pm_qos_mtx); - if (!dev->power.constraints) { + if (!dev->power.qos) { if (dev->power.power_state.event == PM_EVENT_INVALID) { /* The device has been removed from the system. */ req->dev = NULL; @@ -290,7 +292,7 @@ int dev_pm_qos_update_request(struct dev_pm_qos_request *req, mutex_lock(&dev_pm_qos_mtx); - if (req->dev->power.constraints) { + if (req->dev->power.qos) { if (new_value != req->node.prio) ret = apply_constraint(req, PM_QOS_UPDATE_REQ, new_value); @@ -329,7 +331,7 @@ int dev_pm_qos_remove_request(struct dev_pm_qos_request *req) mutex_lock(&dev_pm_qos_mtx); - if (req->dev->power.constraints) { + if (req->dev->power.qos) { ret = apply_constraint(req, PM_QOS_REMOVE_REQ, PM_QOS_DEFAULT_VALUE); memset(req, 0, sizeof(*req)); @@ -362,13 +364,13 @@ int dev_pm_qos_add_notifier(struct device *dev, struct notifier_block *notifier) mutex_lock(&dev_pm_qos_mtx); - if (!dev->power.constraints) + if (!dev->power.qos) ret = dev->power.power_state.event != PM_EVENT_INVALID ? dev_pm_qos_constraints_allocate(dev) : -ENODEV; if (!ret) ret = blocking_notifier_chain_register( - dev->power.constraints->notifiers, notifier); + dev->power.qos->latency.notifiers, notifier); mutex_unlock(&dev_pm_qos_mtx); return ret; @@ -393,9 +395,9 @@ int dev_pm_qos_remove_notifier(struct device *dev, mutex_lock(&dev_pm_qos_mtx); /* Silently return if the constraints object is not present. */ - if (dev->power.constraints) + if (dev->power.qos) retval = blocking_notifier_chain_unregister( - dev->power.constraints->notifiers, + dev->power.qos->latency.notifiers, notifier); mutex_unlock(&dev_pm_qos_mtx); -- cgit v1.2.3 From 021c870ba4ab4bc9a23d5db4e324f50f26d8ab24 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Tue, 23 Oct 2012 01:09:00 +0200 Subject: PM / QoS: Prepare struct dev_pm_qos_request for more request types The subsequent patches will use struct dev_pm_qos_request for representing both latency requests and flags requests. To make that easier, put the node member of struct dev_pm_qos_request (under the name "pnode") into a union called "data" that will represent the request's value and list node depending on its type. Signed-off-by: Rafael J. Wysocki Reviewed-by: Jean Pihet Reviewed-by: mark gross --- drivers/base/power/qos.c | 6 +++--- drivers/base/power/sysfs.c | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) (limited to 'drivers/base') diff --git a/drivers/base/power/qos.c b/drivers/base/power/qos.c index 40ff1b02a7c..96d27b821bb 100644 --- a/drivers/base/power/qos.c +++ b/drivers/base/power/qos.c @@ -90,7 +90,7 @@ static int apply_constraint(struct dev_pm_qos_request *req, int ret, curr_value; ret = pm_qos_update_target(&req->dev->power.qos->latency, - &req->node, action, value); + &req->data.pnode, action, value); if (ret) { /* Call the global callbacks if needed */ @@ -183,7 +183,7 @@ void dev_pm_qos_constraints_destroy(struct device *dev) c = &qos->latency; /* Flush the constraints list for the device */ - plist_for_each_entry_safe(req, tmp, &c->list, node) { + plist_for_each_entry_safe(req, tmp, &c->list, data.pnode) { /* * Update constraints list and call the notification * callbacks if needed @@ -293,7 +293,7 @@ int dev_pm_qos_update_request(struct dev_pm_qos_request *req, mutex_lock(&dev_pm_qos_mtx); if (req->dev->power.qos) { - if (new_value != req->node.prio) + if (new_value != req->data.pnode.prio) ret = apply_constraint(req, PM_QOS_UPDATE_REQ, new_value); } else { diff --git a/drivers/base/power/sysfs.c b/drivers/base/power/sysfs.c index b91dc6f1e91..54c61ffa204 100644 --- a/drivers/base/power/sysfs.c +++ b/drivers/base/power/sysfs.c @@ -221,7 +221,7 @@ static DEVICE_ATTR(autosuspend_delay_ms, 0644, autosuspend_delay_ms_show, static ssize_t pm_qos_latency_show(struct device *dev, struct device_attribute *attr, char *buf) { - return sprintf(buf, "%d\n", dev->power.pq_req->node.prio); + return sprintf(buf, "%d\n", dev->power.pq_req->data.pnode.prio); } static ssize_t pm_qos_latency_store(struct device *dev, -- cgit v1.2.3 From ae0fb4b72c8db7e6c4ef32bc58a43a759ad414b9 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Tue, 23 Oct 2012 01:09:12 +0200 Subject: PM / QoS: Introduce PM QoS device flags support Modify the device PM QoS core code to support PM QoS flags requests. First, add a new field of type struct pm_qos_flags called "flags" to struct dev_pm_qos for representing the list of PM QoS flags requests for the given device. Accordingly, add a new "type" field to struct dev_pm_qos_request (along with an enum for representing request types) and a new member called "flr" to its data union for representig flags requests. Second, modify dev_pm_qos_add_request(), dev_pm_qos_update_request(), the internal routine apply_constraint() used by them and their existing callers to cover flags requests as well as latency requests. In particular, dev_pm_qos_add_request() gets a new argument called "type" for specifying the type of a request to be added. Finally, introduce two routines, __dev_pm_qos_flags() and dev_pm_qos_flags(), allowing their callers to check which PM QoS flags have been requested for the given device (the caller is supposed to pass the mask of flags to check as the routine's second argument and examine its return value for the result). Signed-off-by: Rafael J. Wysocki Reviewed-by: Jean Pihet Reviewed-by: mark gross --- drivers/base/power/qos.c | 124 +++++++++++++++++++++++++++++++++++++---------- 1 file changed, 98 insertions(+), 26 deletions(-) (limited to 'drivers/base') diff --git a/drivers/base/power/qos.c b/drivers/base/power/qos.c index 96d27b821bb..3c66f75d14b 100644 --- a/drivers/base/power/qos.c +++ b/drivers/base/power/qos.c @@ -47,6 +47,50 @@ static DEFINE_MUTEX(dev_pm_qos_mtx); static BLOCKING_NOTIFIER_HEAD(dev_pm_notifiers); +/** + * __dev_pm_qos_flags - Check PM QoS flags for a given device. + * @dev: Device to check the PM QoS flags for. + * @mask: Flags to check against. + * + * This routine must be called with dev->power.lock held. + */ +enum pm_qos_flags_status __dev_pm_qos_flags(struct device *dev, s32 mask) +{ + struct dev_pm_qos *qos = dev->power.qos; + struct pm_qos_flags *pqf; + s32 val; + + if (!qos) + return PM_QOS_FLAGS_UNDEFINED; + + pqf = &qos->flags; + if (list_empty(&pqf->list)) + return PM_QOS_FLAGS_UNDEFINED; + + val = pqf->effective_flags & mask; + if (val) + return (val == mask) ? PM_QOS_FLAGS_ALL : PM_QOS_FLAGS_SOME; + + return PM_QOS_FLAGS_NONE; +} + +/** + * dev_pm_qos_flags - Check PM QoS flags for a given device (locked). + * @dev: Device to check the PM QoS flags for. + * @mask: Flags to check against. + */ +enum pm_qos_flags_status dev_pm_qos_flags(struct device *dev, s32 mask) +{ + unsigned long irqflags; + enum pm_qos_flags_status ret; + + spin_lock_irqsave(&dev->power.lock, irqflags); + ret = __dev_pm_qos_flags(dev, mask); + spin_unlock_irqrestore(&dev->power.lock, irqflags); + + return ret; +} + /** * __dev_pm_qos_read_value - Get PM QoS constraint for a given device. * @dev: Device to get the PM QoS constraint value for. @@ -74,30 +118,39 @@ s32 dev_pm_qos_read_value(struct device *dev) return ret; } -/* - * apply_constraint - * @req: constraint request to apply - * @action: action to perform add/update/remove, of type enum pm_qos_req_action - * @value: defines the qos request +/** + * apply_constraint - Add/modify/remove device PM QoS request. + * @req: Constraint request to apply + * @action: Action to perform (add/update/remove). + * @value: Value to assign to the QoS request. * * Internal function to update the constraints list using the PM QoS core * code and if needed call the per-device and the global notification * callbacks */ static int apply_constraint(struct dev_pm_qos_request *req, - enum pm_qos_req_action action, int value) + enum pm_qos_req_action action, s32 value) { - int ret, curr_value; - - ret = pm_qos_update_target(&req->dev->power.qos->latency, - &req->data.pnode, action, value); + struct dev_pm_qos *qos = req->dev->power.qos; + int ret; - if (ret) { - /* Call the global callbacks if needed */ - curr_value = pm_qos_read_value(&req->dev->power.qos->latency); - blocking_notifier_call_chain(&dev_pm_notifiers, - (unsigned long)curr_value, - req); + switch(req->type) { + case DEV_PM_QOS_LATENCY: + ret = pm_qos_update_target(&qos->latency, &req->data.pnode, + action, value); + if (ret) { + value = pm_qos_read_value(&qos->latency); + blocking_notifier_call_chain(&dev_pm_notifiers, + (unsigned long)value, + req); + } + break; + case DEV_PM_QOS_FLAGS: + ret = pm_qos_update_flags(&qos->flags, &req->data.flr, + action, value); + break; + default: + ret = -EINVAL; } return ret; @@ -134,6 +187,8 @@ static int dev_pm_qos_constraints_allocate(struct device *dev) c->type = PM_QOS_MIN; c->notifiers = n; + INIT_LIST_HEAD(&qos->flags.list); + spin_lock_irq(&dev->power.lock); dev->power.qos = qos; spin_unlock_irq(&dev->power.lock); @@ -207,6 +262,7 @@ void dev_pm_qos_constraints_destroy(struct device *dev) * dev_pm_qos_add_request - inserts new qos request into the list * @dev: target device for the constraint * @req: pointer to a preallocated handle + * @type: type of the request * @value: defines the qos request * * This function inserts a new entry in the device constraints list of @@ -222,7 +278,7 @@ void dev_pm_qos_constraints_destroy(struct device *dev) * from the system. */ int dev_pm_qos_add_request(struct device *dev, struct dev_pm_qos_request *req, - s32 value) + enum dev_pm_qos_req_type type, s32 value) { int ret = 0; @@ -253,8 +309,10 @@ int dev_pm_qos_add_request(struct device *dev, struct dev_pm_qos_request *req, } } - if (!ret) + if (!ret) { + req->type = type; ret = apply_constraint(req, PM_QOS_ADD_REQ, value); + } out: mutex_unlock(&dev_pm_qos_mtx); @@ -281,6 +339,7 @@ EXPORT_SYMBOL_GPL(dev_pm_qos_add_request); int dev_pm_qos_update_request(struct dev_pm_qos_request *req, s32 new_value) { + s32 curr_value; int ret = 0; if (!req) /*guard against callers passing in null */ @@ -292,15 +351,27 @@ int dev_pm_qos_update_request(struct dev_pm_qos_request *req, mutex_lock(&dev_pm_qos_mtx); - if (req->dev->power.qos) { - if (new_value != req->data.pnode.prio) - ret = apply_constraint(req, PM_QOS_UPDATE_REQ, - new_value); - } else { - /* Return if the device has been removed */ + if (!req->dev->power.qos) { ret = -ENODEV; + goto out; } + switch(req->type) { + case DEV_PM_QOS_LATENCY: + curr_value = req->data.pnode.prio; + break; + case DEV_PM_QOS_FLAGS: + curr_value = req->data.flr.flags; + break; + default: + ret = -EINVAL; + goto out; + } + + if (curr_value != new_value) + ret = apply_constraint(req, PM_QOS_UPDATE_REQ, new_value); + + out: mutex_unlock(&dev_pm_qos_mtx); return ret; } @@ -451,7 +522,8 @@ int dev_pm_qos_add_ancestor_request(struct device *dev, ancestor = ancestor->parent; if (ancestor) - error = dev_pm_qos_add_request(ancestor, req, value); + error = dev_pm_qos_add_request(ancestor, req, + DEV_PM_QOS_LATENCY, value); if (error) req->dev = NULL; @@ -487,7 +559,7 @@ int dev_pm_qos_expose_latency_limit(struct device *dev, s32 value) if (!req) return -ENOMEM; - ret = dev_pm_qos_add_request(dev, req, value); + ret = dev_pm_qos_add_request(dev, req, DEV_PM_QOS_LATENCY, value); if (ret < 0) return ret; -- cgit v1.2.3 From e39473d0b9448e770f49b0b15e514be884264438 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Wed, 24 Oct 2012 02:08:18 +0200 Subject: PM / QoS: Make it possible to expose PM QoS device flags to user space Define two device PM QoS flags, PM_QOS_FLAG_NO_POWER_OFF and PM_QOS_FLAG_REMOTE_WAKEUP, and introduce routines dev_pm_qos_expose_flags() and dev_pm_qos_hide_flags() allowing the caller to expose those two flags to user space or to hide them from it, respectively. After the flags have been exposed, user space will see two additional sysfs attributes, pm_qos_no_power_off and pm_qos_remote_wakeup, under the device's /sys/devices/.../power/ directory. Then, writing 1 to one of them will update the PM QoS flags request owned by user space so that the corresponding flag is requested to be set. In turn, writing 0 to one of them will cause the corresponding flag in the user space's request to be cleared (however, the owners of the other PM QoS flags requests for the same device may still request the flag to be set and it may be effectively set even if user space doesn't request that). Signed-off-by: Rafael J. Wysocki Reviewed-by: Jean Pihet Acked-by: mark gross --- drivers/base/power/power.h | 6 +- drivers/base/power/qos.c | 168 +++++++++++++++++++++++++++++++++++---------- drivers/base/power/sysfs.c | 94 ++++++++++++++++++++++--- 3 files changed, 221 insertions(+), 47 deletions(-) (limited to 'drivers/base') diff --git a/drivers/base/power/power.h b/drivers/base/power/power.h index 0dbfdf4419a..b16686a0a5a 100644 --- a/drivers/base/power/power.h +++ b/drivers/base/power/power.h @@ -93,8 +93,10 @@ extern void dpm_sysfs_remove(struct device *dev); extern void rpm_sysfs_remove(struct device *dev); extern int wakeup_sysfs_add(struct device *dev); extern void wakeup_sysfs_remove(struct device *dev); -extern int pm_qos_sysfs_add(struct device *dev); -extern void pm_qos_sysfs_remove(struct device *dev); +extern int pm_qos_sysfs_add_latency(struct device *dev); +extern void pm_qos_sysfs_remove_latency(struct device *dev); +extern int pm_qos_sysfs_add_flags(struct device *dev); +extern void pm_qos_sysfs_remove_flags(struct device *dev); #else /* CONFIG_PM */ diff --git a/drivers/base/power/qos.c b/drivers/base/power/qos.c index 3c66f75d14b..167834dcc82 100644 --- a/drivers/base/power/qos.c +++ b/drivers/base/power/qos.c @@ -40,6 +40,7 @@ #include #include #include +#include #include "power.h" @@ -321,6 +322,37 @@ int dev_pm_qos_add_request(struct device *dev, struct dev_pm_qos_request *req, } EXPORT_SYMBOL_GPL(dev_pm_qos_add_request); +/** + * __dev_pm_qos_update_request - Modify an existing device PM QoS request. + * @req : PM QoS request to modify. + * @new_value: New value to request. + */ +static int __dev_pm_qos_update_request(struct dev_pm_qos_request *req, + s32 new_value) +{ + s32 curr_value; + int ret = 0; + + if (!req->dev->power.qos) + return -ENODEV; + + switch(req->type) { + case DEV_PM_QOS_LATENCY: + curr_value = req->data.pnode.prio; + break; + case DEV_PM_QOS_FLAGS: + curr_value = req->data.flr.flags; + break; + default: + return -EINVAL; + } + + if (curr_value != new_value) + ret = apply_constraint(req, PM_QOS_UPDATE_REQ, new_value); + + return ret; +} + /** * dev_pm_qos_update_request - modifies an existing qos request * @req : handle to list element holding a dev_pm_qos request to use @@ -336,11 +368,9 @@ EXPORT_SYMBOL_GPL(dev_pm_qos_add_request); * -EINVAL in case of wrong parameters, -ENODEV if the device has been * removed from the system */ -int dev_pm_qos_update_request(struct dev_pm_qos_request *req, - s32 new_value) +int dev_pm_qos_update_request(struct dev_pm_qos_request *req, s32 new_value) { - s32 curr_value; - int ret = 0; + int ret; if (!req) /*guard against callers passing in null */ return -EINVAL; @@ -350,29 +380,9 @@ int dev_pm_qos_update_request(struct dev_pm_qos_request *req, return -EINVAL; mutex_lock(&dev_pm_qos_mtx); - - if (!req->dev->power.qos) { - ret = -ENODEV; - goto out; - } - - switch(req->type) { - case DEV_PM_QOS_LATENCY: - curr_value = req->data.pnode.prio; - break; - case DEV_PM_QOS_FLAGS: - curr_value = req->data.flr.flags; - break; - default: - ret = -EINVAL; - goto out; - } - - if (curr_value != new_value) - ret = apply_constraint(req, PM_QOS_UPDATE_REQ, new_value); - - out: + __dev_pm_qos_update_request(req, new_value); mutex_unlock(&dev_pm_qos_mtx); + return ret; } EXPORT_SYMBOL_GPL(dev_pm_qos_update_request); @@ -533,10 +543,19 @@ int dev_pm_qos_add_ancestor_request(struct device *dev, EXPORT_SYMBOL_GPL(dev_pm_qos_add_ancestor_request); #ifdef CONFIG_PM_RUNTIME -static void __dev_pm_qos_drop_user_request(struct device *dev) +static void __dev_pm_qos_drop_user_request(struct device *dev, + enum dev_pm_qos_req_type type) { - dev_pm_qos_remove_request(dev->power.pq_req); - dev->power.pq_req = NULL; + switch(type) { + case DEV_PM_QOS_LATENCY: + dev_pm_qos_remove_request(dev->power.qos->latency_req); + dev->power.qos->latency_req = NULL; + break; + case DEV_PM_QOS_FLAGS: + dev_pm_qos_remove_request(dev->power.qos->flags_req); + dev->power.qos->flags_req = NULL; + break; + } } /** @@ -552,7 +571,7 @@ int dev_pm_qos_expose_latency_limit(struct device *dev, s32 value) if (!device_is_registered(dev) || value < 0) return -EINVAL; - if (dev->power.pq_req) + if (dev->power.qos && dev->power.qos->latency_req) return -EEXIST; req = kzalloc(sizeof(*req), GFP_KERNEL); @@ -563,10 +582,10 @@ int dev_pm_qos_expose_latency_limit(struct device *dev, s32 value) if (ret < 0) return ret; - dev->power.pq_req = req; - ret = pm_qos_sysfs_add(dev); + dev->power.qos->latency_req = req; + ret = pm_qos_sysfs_add_latency(dev); if (ret) - __dev_pm_qos_drop_user_request(dev); + __dev_pm_qos_drop_user_request(dev, DEV_PM_QOS_LATENCY); return ret; } @@ -578,10 +597,87 @@ EXPORT_SYMBOL_GPL(dev_pm_qos_expose_latency_limit); */ void dev_pm_qos_hide_latency_limit(struct device *dev) { - if (dev->power.pq_req) { - pm_qos_sysfs_remove(dev); - __dev_pm_qos_drop_user_request(dev); + if (dev->power.qos && dev->power.qos->latency_req) { + pm_qos_sysfs_remove_latency(dev); + __dev_pm_qos_drop_user_request(dev, DEV_PM_QOS_LATENCY); } } EXPORT_SYMBOL_GPL(dev_pm_qos_hide_latency_limit); + +/** + * dev_pm_qos_expose_flags - Expose PM QoS flags of a device to user space. + * @dev: Device whose PM QoS flags are to be exposed to user space. + * @val: Initial values of the flags. + */ +int dev_pm_qos_expose_flags(struct device *dev, s32 val) +{ + struct dev_pm_qos_request *req; + int ret; + + if (!device_is_registered(dev)) + return -EINVAL; + + if (dev->power.qos && dev->power.qos->flags_req) + return -EEXIST; + + req = kzalloc(sizeof(*req), GFP_KERNEL); + if (!req) + return -ENOMEM; + + ret = dev_pm_qos_add_request(dev, req, DEV_PM_QOS_FLAGS, val); + if (ret < 0) + return ret; + + dev->power.qos->flags_req = req; + ret = pm_qos_sysfs_add_flags(dev); + if (ret) + __dev_pm_qos_drop_user_request(dev, DEV_PM_QOS_FLAGS); + + return ret; +} +EXPORT_SYMBOL_GPL(dev_pm_qos_expose_flags); + +/** + * dev_pm_qos_hide_flags - Hide PM QoS flags of a device from user space. + * @dev: Device whose PM QoS flags are to be hidden from user space. + */ +void dev_pm_qos_hide_flags(struct device *dev) +{ + if (dev->power.qos && dev->power.qos->flags_req) { + pm_qos_sysfs_remove_flags(dev); + __dev_pm_qos_drop_user_request(dev, DEV_PM_QOS_FLAGS); + } +} +EXPORT_SYMBOL_GPL(dev_pm_qos_hide_flags); + +/** + * dev_pm_qos_update_flags - Update PM QoS flags request owned by user space. + * @dev: Device to update the PM QoS flags request for. + * @mask: Flags to set/clear. + * @set: Whether to set or clear the flags (true means set). + */ +int dev_pm_qos_update_flags(struct device *dev, s32 mask, bool set) +{ + s32 value; + int ret; + + if (!dev->power.qos || !dev->power.qos->flags_req) + return -EINVAL; + + pm_runtime_get_sync(dev); + mutex_lock(&dev_pm_qos_mtx); + + value = dev_pm_qos_requested_flags(dev); + if (set) + value |= mask; + else + value &= ~mask; + + ret = __dev_pm_qos_update_request(dev->power.qos->flags_req, value); + + mutex_unlock(&dev_pm_qos_mtx); + pm_runtime_put(dev); + + return ret; +} #endif /* CONFIG_PM_RUNTIME */ diff --git a/drivers/base/power/sysfs.c b/drivers/base/power/sysfs.c index 54c61ffa204..50d16e3cb0a 100644 --- a/drivers/base/power/sysfs.c +++ b/drivers/base/power/sysfs.c @@ -221,7 +221,7 @@ static DEVICE_ATTR(autosuspend_delay_ms, 0644, autosuspend_delay_ms_show, static ssize_t pm_qos_latency_show(struct device *dev, struct device_attribute *attr, char *buf) { - return sprintf(buf, "%d\n", dev->power.pq_req->data.pnode.prio); + return sprintf(buf, "%d\n", dev_pm_qos_requested_latency(dev)); } static ssize_t pm_qos_latency_store(struct device *dev, @@ -237,12 +237,66 @@ static ssize_t pm_qos_latency_store(struct device *dev, if (value < 0) return -EINVAL; - ret = dev_pm_qos_update_request(dev->power.pq_req, value); + ret = dev_pm_qos_update_request(dev->power.qos->latency_req, value); return ret < 0 ? ret : n; } static DEVICE_ATTR(pm_qos_resume_latency_us, 0644, pm_qos_latency_show, pm_qos_latency_store); + +static ssize_t pm_qos_no_power_off_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + return sprintf(buf, "%d\n", !!(dev_pm_qos_requested_flags(dev) + & PM_QOS_FLAG_NO_POWER_OFF)); +} + +static ssize_t pm_qos_no_power_off_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t n) +{ + int ret; + + if (kstrtoint(buf, 0, &ret)) + return -EINVAL; + + if (ret != 0 && ret != 1) + return -EINVAL; + + ret = dev_pm_qos_update_flags(dev, PM_QOS_FLAG_NO_POWER_OFF, ret); + return ret < 0 ? ret : n; +} + +static DEVICE_ATTR(pm_qos_no_power_off, 0644, + pm_qos_no_power_off_show, pm_qos_no_power_off_store); + +static ssize_t pm_qos_remote_wakeup_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + return sprintf(buf, "%d\n", !!(dev_pm_qos_requested_flags(dev) + & PM_QOS_FLAG_REMOTE_WAKEUP)); +} + +static ssize_t pm_qos_remote_wakeup_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t n) +{ + int ret; + + if (kstrtoint(buf, 0, &ret)) + return -EINVAL; + + if (ret != 0 && ret != 1) + return -EINVAL; + + ret = dev_pm_qos_update_flags(dev, PM_QOS_FLAG_REMOTE_WAKEUP, ret); + return ret < 0 ? ret : n; +} + +static DEVICE_ATTR(pm_qos_remote_wakeup, 0644, + pm_qos_remote_wakeup_show, pm_qos_remote_wakeup_store); #endif /* CONFIG_PM_RUNTIME */ #ifdef CONFIG_PM_SLEEP @@ -564,15 +618,27 @@ static struct attribute_group pm_runtime_attr_group = { .attrs = runtime_attrs, }; -static struct attribute *pm_qos_attrs[] = { +static struct attribute *pm_qos_latency_attrs[] = { #ifdef CONFIG_PM_RUNTIME &dev_attr_pm_qos_resume_latency_us.attr, #endif /* CONFIG_PM_RUNTIME */ NULL, }; -static struct attribute_group pm_qos_attr_group = { +static struct attribute_group pm_qos_latency_attr_group = { .name = power_group_name, - .attrs = pm_qos_attrs, + .attrs = pm_qos_latency_attrs, +}; + +static struct attribute *pm_qos_flags_attrs[] = { +#ifdef CONFIG_PM_RUNTIME + &dev_attr_pm_qos_no_power_off.attr, + &dev_attr_pm_qos_remote_wakeup.attr, +#endif /* CONFIG_PM_RUNTIME */ + NULL, +}; +static struct attribute_group pm_qos_flags_attr_group = { + .name = power_group_name, + .attrs = pm_qos_flags_attrs, }; int dpm_sysfs_add(struct device *dev) @@ -615,14 +681,24 @@ void wakeup_sysfs_remove(struct device *dev) sysfs_unmerge_group(&dev->kobj, &pm_wakeup_attr_group); } -int pm_qos_sysfs_add(struct device *dev) +int pm_qos_sysfs_add_latency(struct device *dev) +{ + return sysfs_merge_group(&dev->kobj, &pm_qos_latency_attr_group); +} + +void pm_qos_sysfs_remove_latency(struct device *dev) +{ + sysfs_unmerge_group(&dev->kobj, &pm_qos_latency_attr_group); +} + +int pm_qos_sysfs_add_flags(struct device *dev) { - return sysfs_merge_group(&dev->kobj, &pm_qos_attr_group); + return sysfs_merge_group(&dev->kobj, &pm_qos_flags_attr_group); } -void pm_qos_sysfs_remove(struct device *dev) +void pm_qos_sysfs_remove_flags(struct device *dev) { - sysfs_unmerge_group(&dev->kobj, &pm_qos_attr_group); + sysfs_unmerge_group(&dev->kobj, &pm_qos_flags_attr_group); } void rpm_sysfs_remove(struct device *dev) -- cgit v1.2.3 From 34b1f76275a2cb8c1ce8e00095d200552b235122 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Wed, 24 Oct 2012 02:08:30 +0200 Subject: PM / Domains: Check device PM QoS flags in pm_genpd_poweroff() Make the generic PM domains pm_genpd_poweroff() function take device PM QoS flags into account when deciding whether or not to remove power from the domain. After this change the routine will return -EBUSY without executing the domain's .power_off() callback if there is at least one PM QoS flags request for at least one device in the domain and at least of those request has at least one of the NO_POWER_OFF and REMOTE_WAKEUP flags set. Signed-off-by: Rafael J. Wysocki Reviewed-by: Jean Pihet Reviewed-by: mark gross --- drivers/base/power/domain.c | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) (limited to 'drivers/base') diff --git a/drivers/base/power/domain.c b/drivers/base/power/domain.c index c22b869245d..e2cf392d99d 100644 --- a/drivers/base/power/domain.c +++ b/drivers/base/power/domain.c @@ -470,10 +470,19 @@ static int pm_genpd_poweroff(struct generic_pm_domain *genpd) return -EBUSY; not_suspended = 0; - list_for_each_entry(pdd, &genpd->dev_list, list_node) + list_for_each_entry(pdd, &genpd->dev_list, list_node) { + enum pm_qos_flags_status stat; + + stat = dev_pm_qos_flags(pdd->dev, + PM_QOS_FLAG_NO_POWER_OFF + | PM_QOS_FLAG_REMOTE_WAKEUP); + if (stat > PM_QOS_FLAGS_NONE) + return -EBUSY; + if (pdd->dev->driver && (!pm_runtime_suspended(pdd->dev) || pdd->dev->power.irq_safe)) not_suspended++; + } if (not_suspended > genpd->in_progress) return -EBUSY; -- cgit v1.2.3 From f9652875dcd49d400b775aecc8e0bf76e405b70a Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Tue, 30 Oct 2012 20:00:30 +0100 Subject: PM / QoS: Fix the return value of dev_pm_qos_update_request() Commit e39473d (PM / QoS: Make it possible to expose PM QoS device flags to user space) introduced __dev_pm_qos_update_request() to be called internally by dev_pm_qos_update_request(), but forgot to make the latter actually use the return value of the former. Fix this mistake. Signed-off-by: Rafael J. Wysocki --- drivers/base/power/qos.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/base') diff --git a/drivers/base/power/qos.c b/drivers/base/power/qos.c index 167834dcc82..3a7687ae5a4 100644 --- a/drivers/base/power/qos.c +++ b/drivers/base/power/qos.c @@ -380,7 +380,7 @@ int dev_pm_qos_update_request(struct dev_pm_qos_request *req, s32 new_value) return -EINVAL; mutex_lock(&dev_pm_qos_mtx); - __dev_pm_qos_update_request(req, new_value); + ret = __dev_pm_qos_update_request(req, new_value); mutex_unlock(&dev_pm_qos_mtx); return ret; -- cgit v1.2.3 From 9eaee2cdcf9ead20f234b15ed26f82a96a4fa8fb Mon Sep 17 00:00:00 2001 From: "Lan,Tianyu" Date: Thu, 1 Nov 2012 22:45:30 +0100 Subject: PM / QoS: Fix a free error in the dev_pm_qos_constraints_destroy() Free a wrong point to struct dev_pm_qos->latency which suppose to be the point to struct dev_pm_qos. The patch is to fix the issue. Signed-off-by: Lan Tianyu Signed-off-by: Rafael J. Wysocki --- drivers/base/power/qos.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/base') diff --git a/drivers/base/power/qos.c b/drivers/base/power/qos.c index 3a7687ae5a4..31d3f4842b9 100644 --- a/drivers/base/power/qos.c +++ b/drivers/base/power/qos.c @@ -253,7 +253,7 @@ void dev_pm_qos_constraints_destroy(struct device *dev) spin_unlock_irq(&dev->power.lock); kfree(c->notifiers); - kfree(c); + kfree(qos); out: mutex_unlock(&dev_pm_qos_mtx); -- cgit v1.2.3 From 436ede8942ab43474182c6454f420d71f7bb1163 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Fri, 2 Nov 2012 13:10:09 +0100 Subject: PM / QoS: Document request manipulation requirement for flags In fact, the callers of dev_pm_qos_add_request(), dev_pm_qos_update_request() and dev_pm_qos_remove_request() for requests of type DEV_PM_QOS_FLAGS need to ensure that the target device is not RPM_SUSPENDED before using any of these functions (or be prepared for the new PM QoS flags to take effect after the device has been resumed). Document this in their kerneldoc comments. Signed-off-by: Rafael J. Wysocki --- drivers/base/power/qos.c | 9 +++++++++ 1 file changed, 9 insertions(+) (limited to 'drivers/base') diff --git a/drivers/base/power/qos.c b/drivers/base/power/qos.c index 31d3f4842b9..081db2d25da 100644 --- a/drivers/base/power/qos.c +++ b/drivers/base/power/qos.c @@ -277,6 +277,9 @@ void dev_pm_qos_constraints_destroy(struct device *dev) * -EINVAL in case of wrong parameters, -ENOMEM if there's not enough memory * to allocate for data structures, -ENODEV if the device has just been removed * from the system. + * + * Callers should ensure that the target device is not RPM_SUSPENDED before + * using this function for requests of type DEV_PM_QOS_FLAGS. */ int dev_pm_qos_add_request(struct device *dev, struct dev_pm_qos_request *req, enum dev_pm_qos_req_type type, s32 value) @@ -367,6 +370,9 @@ static int __dev_pm_qos_update_request(struct dev_pm_qos_request *req, * 0 if the aggregated constraint value has not changed, * -EINVAL in case of wrong parameters, -ENODEV if the device has been * removed from the system + * + * Callers should ensure that the target device is not RPM_SUSPENDED before + * using this function for requests of type DEV_PM_QOS_FLAGS. */ int dev_pm_qos_update_request(struct dev_pm_qos_request *req, s32 new_value) { @@ -398,6 +404,9 @@ EXPORT_SYMBOL_GPL(dev_pm_qos_update_request); * 0 if the aggregated constraint value has not changed, * -EINVAL in case of wrong parameters, -ENODEV if the device has been * removed from the system + * + * Callers should ensure that the target device is not RPM_SUSPENDED before + * using this function for requests of type DEV_PM_QOS_FLAGS. */ int dev_pm_qos_remove_request(struct dev_pm_qos_request *req) { -- cgit v1.2.3 From 7e4d68443a80574392d1027ff34992ab945934a6 Mon Sep 17 00:00:00 2001 From: Lan Tianyu Date: Thu, 8 Nov 2012 11:14:08 +0800 Subject: PM / QoS: Resume device before exposing/hiding PM QoS flags Since dev_pm_qos_add_request(), dev_pm_qos_update_request() and dev_pm_qos_remove_request() for PM QoS flags should not be invoked when device in RPM_SUSPENDED, add pm_runtime_get_sync() and pm_runtime_put() around these functions in dev_pm_qos_expose_flags() and dev_pm_qos_hide_flags(). [rjw: Modified the subject and changelog to better reflect the code changes made.] Signed-off-by: Lan Tianyu Signed-off-by: Rafael J. Wysocki --- drivers/base/power/qos.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) (limited to 'drivers/base') diff --git a/drivers/base/power/qos.c b/drivers/base/power/qos.c index 081db2d25da..fdc3894bc33 100644 --- a/drivers/base/power/qos.c +++ b/drivers/base/power/qos.c @@ -633,15 +633,18 @@ int dev_pm_qos_expose_flags(struct device *dev, s32 val) if (!req) return -ENOMEM; + pm_runtime_get_sync(dev); ret = dev_pm_qos_add_request(dev, req, DEV_PM_QOS_FLAGS, val); if (ret < 0) - return ret; + goto fail; dev->power.qos->flags_req = req; ret = pm_qos_sysfs_add_flags(dev); if (ret) __dev_pm_qos_drop_user_request(dev, DEV_PM_QOS_FLAGS); +fail: + pm_runtime_put(dev); return ret; } EXPORT_SYMBOL_GPL(dev_pm_qos_expose_flags); @@ -654,7 +657,9 @@ void dev_pm_qos_hide_flags(struct device *dev) { if (dev->power.qos && dev->power.qos->flags_req) { pm_qos_sysfs_remove_flags(dev); + pm_runtime_get_sync(dev); __dev_pm_qos_drop_user_request(dev, DEV_PM_QOS_FLAGS); + pm_runtime_put(dev); } } EXPORT_SYMBOL_GPL(dev_pm_qos_hide_flags); -- cgit v1.2.3 From 35546bd477146b75ae2a9ff2cb9bfcdb0f701015 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Sat, 24 Nov 2012 10:10:51 +0100 Subject: PM / QoS: Handle device PM QoS flags while removing constraints PM QoS flags have to be handled by dev_pm_qos_constraints_destroy() in the same way as PM QoS resume latency constraints. That is, if they have been exposed to user space, they have to be hidden from it and the list of flags requests has to be flushed before destroying the device's PM QoS object. Make that happen. Signed-off-by: Rafael J. Wysocki --- drivers/base/power/qos.c | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) (limited to 'drivers/base') diff --git a/drivers/base/power/qos.c b/drivers/base/power/qos.c index fdc3894bc33..f03f1ba28c7 100644 --- a/drivers/base/power/qos.c +++ b/drivers/base/power/qos.c @@ -223,12 +223,14 @@ void dev_pm_qos_constraints_destroy(struct device *dev) struct dev_pm_qos *qos; struct dev_pm_qos_request *req, *tmp; struct pm_qos_constraints *c; + struct pm_qos_flags *f; /* - * If the device's PM QoS resume latency limit has been exposed to user - * space, it has to be hidden at this point. + * If the device's PM QoS resume latency limit or PM QoS flags have been + * exposed to user space, they have to be hidden at this point. */ dev_pm_qos_hide_latency_limit(dev); + dev_pm_qos_hide_flags(dev); mutex_lock(&dev_pm_qos_mtx); @@ -237,8 +239,8 @@ void dev_pm_qos_constraints_destroy(struct device *dev) if (!qos) goto out; + /* Flush the constraints lists for the device. */ c = &qos->latency; - /* Flush the constraints list for the device */ plist_for_each_entry_safe(req, tmp, &c->list, data.pnode) { /* * Update constraints list and call the notification @@ -247,6 +249,11 @@ void dev_pm_qos_constraints_destroy(struct device *dev) apply_constraint(req, PM_QOS_REMOVE_REQ, PM_QOS_DEFAULT_VALUE); memset(req, 0, sizeof(*req)); } + f = &qos->flags; + list_for_each_entry_safe(req, tmp, &f->list, data.flr.node) { + apply_constraint(req, PM_QOS_REMOVE_REQ, PM_QOS_DEFAULT_VALUE); + memset(req, 0, sizeof(*req)); + } spin_lock_irq(&dev->power.lock); dev->power.qos = NULL; -- cgit v1.2.3