diff options
Diffstat (limited to 'drivers/base/power/qos.c')
-rw-r--r-- | drivers/base/power/qos.c | 89 |
1 files changed, 75 insertions, 14 deletions
diff --git a/drivers/base/power/qos.c b/drivers/base/power/qos.c index cc4c541398aa..8d0b81151c14 100644 --- a/drivers/base/power/qos.c +++ b/drivers/base/power/qos.c | |||
@@ -17,6 +17,12 @@ | |||
17 | * | 17 | * |
18 | * This QoS design is best effort based. Dependents register their QoS needs. | 18 | * This QoS design is best effort based. Dependents register their QoS needs. |
19 | * Watchers register to keep track of the current QoS needs of the system. | 19 | * Watchers register to keep track of the current QoS needs of the system. |
20 | * Watchers can register different types of notification callbacks: | ||
21 | * . a per-device notification callback using the dev_pm_qos_*_notifier API. | ||
22 | * The notification chain data is stored in the per-device constraint | ||
23 | * data struct. | ||
24 | * . a system-wide notification callback using the dev_pm_qos_*_global_notifier | ||
25 | * API. The notification chain data is stored in a static variable. | ||
20 | * | 26 | * |
21 | * Note about the per-device constraint data struct allocation: | 27 | * Note about the per-device constraint data struct allocation: |
22 | * . The per-device constraints data struct ptr is tored into the device | 28 | * . The per-device constraints data struct ptr is tored into the device |
@@ -45,6 +51,36 @@ | |||
45 | 51 | ||
46 | 52 | ||
47 | static DEFINE_MUTEX(dev_pm_qos_mtx); | 53 | static DEFINE_MUTEX(dev_pm_qos_mtx); |
54 | static BLOCKING_NOTIFIER_HEAD(dev_pm_notifiers); | ||
55 | |||
56 | /* | ||
57 | * apply_constraint | ||
58 | * @req: constraint request to apply | ||
59 | * @action: action to perform add/update/remove, of type enum pm_qos_req_action | ||
60 | * @value: defines the qos request | ||
61 | * | ||
62 | * Internal function to update the constraints list using the PM QoS core | ||
63 | * code and if needed call the per-device and the global notification | ||
64 | * callbacks | ||
65 | */ | ||
66 | static int apply_constraint(struct dev_pm_qos_request *req, | ||
67 | enum pm_qos_req_action action, int value) | ||
68 | { | ||
69 | int ret, curr_value; | ||
70 | |||
71 | ret = pm_qos_update_target(req->dev->power.constraints, | ||
72 | &req->node, action, value); | ||
73 | |||
74 | if (ret) { | ||
75 | /* Call the global callbacks if needed */ | ||
76 | curr_value = pm_qos_read_value(req->dev->power.constraints); | ||
77 | blocking_notifier_call_chain(&dev_pm_notifiers, | ||
78 | (unsigned long)curr_value, | ||
79 | req); | ||
80 | } | ||
81 | |||
82 | return ret; | ||
83 | } | ||
48 | 84 | ||
49 | /* | 85 | /* |
50 | * dev_pm_qos_constraints_allocate | 86 | * dev_pm_qos_constraints_allocate |
@@ -111,12 +147,11 @@ void dev_pm_qos_constraints_destroy(struct device *dev) | |||
111 | &dev->power.constraints->list, | 147 | &dev->power.constraints->list, |
112 | node) { | 148 | node) { |
113 | /* | 149 | /* |
114 | * Update constraints list and call the per-device | 150 | * Update constraints list and call the notification |
115 | * callbacks if needed | 151 | * callbacks if needed |
116 | */ | 152 | */ |
117 | pm_qos_update_target(req->dev->power.constraints, | 153 | apply_constraint(req, PM_QOS_REMOVE_REQ, |
118 | &req->node, PM_QOS_REMOVE_REQ, | 154 | PM_QOS_DEFAULT_VALUE); |
119 | PM_QOS_DEFAULT_VALUE); | ||
120 | memset(req, 0, sizeof(*req)); | 155 | memset(req, 0, sizeof(*req)); |
121 | } | 156 | } |
122 | 157 | ||
@@ -147,7 +182,7 @@ void dev_pm_qos_constraints_destroy(struct device *dev) | |||
147 | * removed from the system | 182 | * removed from the system |
148 | */ | 183 | */ |
149 | int dev_pm_qos_add_request(struct device *dev, struct dev_pm_qos_request *req, | 184 | int dev_pm_qos_add_request(struct device *dev, struct dev_pm_qos_request *req, |
150 | s32 value) | 185 | s32 value) |
151 | { | 186 | { |
152 | int ret = 0; | 187 | int ret = 0; |
153 | 188 | ||
@@ -178,8 +213,7 @@ int dev_pm_qos_add_request(struct device *dev, struct dev_pm_qos_request *req, | |||
178 | ret = dev_pm_qos_constraints_allocate(dev); | 213 | ret = dev_pm_qos_constraints_allocate(dev); |
179 | 214 | ||
180 | if (!ret) | 215 | if (!ret) |
181 | ret = pm_qos_update_target(dev->power.constraints, &req->node, | 216 | ret = apply_constraint(req, PM_QOS_ADD_REQ, value); |
182 | PM_QOS_ADD_REQ, value); | ||
183 | 217 | ||
184 | out: | 218 | out: |
185 | mutex_unlock(&dev_pm_qos_mtx); | 219 | mutex_unlock(&dev_pm_qos_mtx); |
@@ -220,10 +254,8 @@ int dev_pm_qos_update_request(struct dev_pm_qos_request *req, | |||
220 | 254 | ||
221 | if (req->dev->power.constraints_state == DEV_PM_QOS_ALLOCATED) { | 255 | if (req->dev->power.constraints_state == DEV_PM_QOS_ALLOCATED) { |
222 | if (new_value != req->node.prio) | 256 | if (new_value != req->node.prio) |
223 | ret = pm_qos_update_target(req->dev->power.constraints, | 257 | ret = apply_constraint(req, PM_QOS_UPDATE_REQ, |
224 | &req->node, | 258 | new_value); |
225 | PM_QOS_UPDATE_REQ, | ||
226 | new_value); | ||
227 | } else { | 259 | } else { |
228 | /* Return if the device has been removed */ | 260 | /* Return if the device has been removed */ |
229 | ret = -ENODEV; | 261 | ret = -ENODEV; |
@@ -262,9 +294,8 @@ int dev_pm_qos_remove_request(struct dev_pm_qos_request *req) | |||
262 | mutex_lock(&dev_pm_qos_mtx); | 294 | mutex_lock(&dev_pm_qos_mtx); |
263 | 295 | ||
264 | if (req->dev->power.constraints_state == DEV_PM_QOS_ALLOCATED) { | 296 | if (req->dev->power.constraints_state == DEV_PM_QOS_ALLOCATED) { |
265 | ret = pm_qos_update_target(req->dev->power.constraints, | 297 | ret = apply_constraint(req, PM_QOS_REMOVE_REQ, |
266 | &req->node, PM_QOS_REMOVE_REQ, | 298 | PM_QOS_DEFAULT_VALUE); |
267 | PM_QOS_DEFAULT_VALUE); | ||
268 | memset(req, 0, sizeof(*req)); | 299 | memset(req, 0, sizeof(*req)); |
269 | } else { | 300 | } else { |
270 | /* Return if the device has been removed */ | 301 | /* Return if the device has been removed */ |
@@ -336,3 +367,33 @@ out: | |||
336 | return retval; | 367 | return retval; |
337 | } | 368 | } |
338 | EXPORT_SYMBOL_GPL(dev_pm_qos_remove_notifier); | 369 | EXPORT_SYMBOL_GPL(dev_pm_qos_remove_notifier); |
370 | |||
371 | /** | ||
372 | * dev_pm_qos_add_global_notifier - sets notification entry for changes to | ||
373 | * target value of the PM QoS constraints for any device | ||
374 | * | ||
375 | * @notifier: notifier block managed by caller. | ||
376 | * | ||
377 | * Will register the notifier into a notification chain that gets called | ||
378 | * upon changes to the target value for any device. | ||
379 | */ | ||
380 | int dev_pm_qos_add_global_notifier(struct notifier_block *notifier) | ||
381 | { | ||
382 | return blocking_notifier_chain_register(&dev_pm_notifiers, notifier); | ||
383 | } | ||
384 | EXPORT_SYMBOL_GPL(dev_pm_qos_add_global_notifier); | ||
385 | |||
386 | /** | ||
387 | * dev_pm_qos_remove_global_notifier - deletes notification for changes to | ||
388 | * target value of PM QoS constraints for any device | ||
389 | * | ||
390 | * @notifier: notifier block to be removed. | ||
391 | * | ||
392 | * Will remove the notifier from the notification chain that gets called | ||
393 | * upon changes to the target value for any device. | ||
394 | */ | ||
395 | int dev_pm_qos_remove_global_notifier(struct notifier_block *notifier) | ||
396 | { | ||
397 | return blocking_notifier_chain_unregister(&dev_pm_notifiers, notifier); | ||
398 | } | ||
399 | EXPORT_SYMBOL_GPL(dev_pm_qos_remove_global_notifier); | ||