diff options
Diffstat (limited to 'drivers/base/power/qos.c')
-rw-r--r-- | drivers/base/power/qos.c | 321 |
1 files changed, 256 insertions, 65 deletions
diff --git a/drivers/base/power/qos.c b/drivers/base/power/qos.c index fbbd4ed2edf2..ff46387f5308 100644 --- a/drivers/base/power/qos.c +++ b/drivers/base/power/qos.c | |||
@@ -40,6 +40,7 @@ | |||
40 | #include <linux/device.h> | 40 | #include <linux/device.h> |
41 | #include <linux/mutex.h> | 41 | #include <linux/mutex.h> |
42 | #include <linux/export.h> | 42 | #include <linux/export.h> |
43 | #include <linux/pm_runtime.h> | ||
43 | 44 | ||
44 | #include "power.h" | 45 | #include "power.h" |
45 | 46 | ||
@@ -48,6 +49,50 @@ static DEFINE_MUTEX(dev_pm_qos_mtx); | |||
48 | static BLOCKING_NOTIFIER_HEAD(dev_pm_notifiers); | 49 | static BLOCKING_NOTIFIER_HEAD(dev_pm_notifiers); |
49 | 50 | ||
50 | /** | 51 | /** |
52 | * __dev_pm_qos_flags - Check PM QoS flags for a given device. | ||
53 | * @dev: Device to check the PM QoS flags for. | ||
54 | * @mask: Flags to check against. | ||
55 | * | ||
56 | * This routine must be called with dev->power.lock held. | ||
57 | */ | ||
58 | enum pm_qos_flags_status __dev_pm_qos_flags(struct device *dev, s32 mask) | ||
59 | { | ||
60 | struct dev_pm_qos *qos = dev->power.qos; | ||
61 | struct pm_qos_flags *pqf; | ||
62 | s32 val; | ||
63 | |||
64 | if (!qos) | ||
65 | return PM_QOS_FLAGS_UNDEFINED; | ||
66 | |||
67 | pqf = &qos->flags; | ||
68 | if (list_empty(&pqf->list)) | ||
69 | return PM_QOS_FLAGS_UNDEFINED; | ||
70 | |||
71 | val = pqf->effective_flags & mask; | ||
72 | if (val) | ||
73 | return (val == mask) ? PM_QOS_FLAGS_ALL : PM_QOS_FLAGS_SOME; | ||
74 | |||
75 | return PM_QOS_FLAGS_NONE; | ||
76 | } | ||
77 | |||
78 | /** | ||
79 | * dev_pm_qos_flags - Check PM QoS flags for a given device (locked). | ||
80 | * @dev: Device to check the PM QoS flags for. | ||
81 | * @mask: Flags to check against. | ||
82 | */ | ||
83 | enum pm_qos_flags_status dev_pm_qos_flags(struct device *dev, s32 mask) | ||
84 | { | ||
85 | unsigned long irqflags; | ||
86 | enum pm_qos_flags_status ret; | ||
87 | |||
88 | spin_lock_irqsave(&dev->power.lock, irqflags); | ||
89 | ret = __dev_pm_qos_flags(dev, mask); | ||
90 | spin_unlock_irqrestore(&dev->power.lock, irqflags); | ||
91 | |||
92 | return ret; | ||
93 | } | ||
94 | |||
95 | /** | ||
51 | * __dev_pm_qos_read_value - Get PM QoS constraint for a given device. | 96 | * __dev_pm_qos_read_value - Get PM QoS constraint for a given device. |
52 | * @dev: Device to get the PM QoS constraint value for. | 97 | * @dev: Device to get the PM QoS constraint value for. |
53 | * | 98 | * |
@@ -55,9 +100,7 @@ static BLOCKING_NOTIFIER_HEAD(dev_pm_notifiers); | |||
55 | */ | 100 | */ |
56 | s32 __dev_pm_qos_read_value(struct device *dev) | 101 | s32 __dev_pm_qos_read_value(struct device *dev) |
57 | { | 102 | { |
58 | struct pm_qos_constraints *c = dev->power.constraints; | 103 | return dev->power.qos ? pm_qos_read_value(&dev->power.qos->latency) : 0; |
59 | |||
60 | return c ? pm_qos_read_value(c) : 0; | ||
61 | } | 104 | } |
62 | 105 | ||
63 | /** | 106 | /** |
@@ -76,30 +119,39 @@ s32 dev_pm_qos_read_value(struct device *dev) | |||
76 | return ret; | 119 | return ret; |
77 | } | 120 | } |
78 | 121 | ||
79 | /* | 122 | /** |
80 | * apply_constraint | 123 | * apply_constraint - Add/modify/remove device PM QoS request. |
81 | * @req: constraint request to apply | 124 | * @req: Constraint request to apply |
82 | * @action: action to perform add/update/remove, of type enum pm_qos_req_action | 125 | * @action: Action to perform (add/update/remove). |
83 | * @value: defines the qos request | 126 | * @value: Value to assign to the QoS request. |
84 | * | 127 | * |
85 | * Internal function to update the constraints list using the PM QoS core | 128 | * Internal function to update the constraints list using the PM QoS core |
86 | * code and if needed call the per-device and the global notification | 129 | * code and if needed call the per-device and the global notification |
87 | * callbacks | 130 | * callbacks |
88 | */ | 131 | */ |
89 | static int apply_constraint(struct dev_pm_qos_request *req, | 132 | static int apply_constraint(struct dev_pm_qos_request *req, |
90 | enum pm_qos_req_action action, int value) | 133 | enum pm_qos_req_action action, s32 value) |
91 | { | 134 | { |
92 | int ret, curr_value; | 135 | struct dev_pm_qos *qos = req->dev->power.qos; |
93 | 136 | int ret; | |
94 | ret = pm_qos_update_target(req->dev->power.constraints, | ||
95 | &req->node, action, value); | ||
96 | 137 | ||
97 | if (ret) { | 138 | switch(req->type) { |
98 | /* Call the global callbacks if needed */ | 139 | case DEV_PM_QOS_LATENCY: |
99 | curr_value = pm_qos_read_value(req->dev->power.constraints); | 140 | ret = pm_qos_update_target(&qos->latency, &req->data.pnode, |
100 | blocking_notifier_call_chain(&dev_pm_notifiers, | 141 | action, value); |
101 | (unsigned long)curr_value, | 142 | if (ret) { |
102 | req); | 143 | value = pm_qos_read_value(&qos->latency); |
144 | blocking_notifier_call_chain(&dev_pm_notifiers, | ||
145 | (unsigned long)value, | ||
146 | req); | ||
147 | } | ||
148 | break; | ||
149 | case DEV_PM_QOS_FLAGS: | ||
150 | ret = pm_qos_update_flags(&qos->flags, &req->data.flr, | ||
151 | action, value); | ||
152 | break; | ||
153 | default: | ||
154 | ret = -EINVAL; | ||
103 | } | 155 | } |
104 | 156 | ||
105 | return ret; | 157 | return ret; |
@@ -114,28 +166,32 @@ static int apply_constraint(struct dev_pm_qos_request *req, | |||
114 | */ | 166 | */ |
115 | static int dev_pm_qos_constraints_allocate(struct device *dev) | 167 | static int dev_pm_qos_constraints_allocate(struct device *dev) |
116 | { | 168 | { |
169 | struct dev_pm_qos *qos; | ||
117 | struct pm_qos_constraints *c; | 170 | struct pm_qos_constraints *c; |
118 | struct blocking_notifier_head *n; | 171 | struct blocking_notifier_head *n; |
119 | 172 | ||
120 | c = kzalloc(sizeof(*c), GFP_KERNEL); | 173 | qos = kzalloc(sizeof(*qos), GFP_KERNEL); |
121 | if (!c) | 174 | if (!qos) |
122 | return -ENOMEM; | 175 | return -ENOMEM; |
123 | 176 | ||
124 | n = kzalloc(sizeof(*n), GFP_KERNEL); | 177 | n = kzalloc(sizeof(*n), GFP_KERNEL); |
125 | if (!n) { | 178 | if (!n) { |
126 | kfree(c); | 179 | kfree(qos); |
127 | return -ENOMEM; | 180 | return -ENOMEM; |
128 | } | 181 | } |
129 | BLOCKING_INIT_NOTIFIER_HEAD(n); | 182 | BLOCKING_INIT_NOTIFIER_HEAD(n); |
130 | 183 | ||
184 | c = &qos->latency; | ||
131 | plist_head_init(&c->list); | 185 | plist_head_init(&c->list); |
132 | c->target_value = PM_QOS_DEV_LAT_DEFAULT_VALUE; | 186 | c->target_value = PM_QOS_DEV_LAT_DEFAULT_VALUE; |
133 | c->default_value = PM_QOS_DEV_LAT_DEFAULT_VALUE; | 187 | c->default_value = PM_QOS_DEV_LAT_DEFAULT_VALUE; |
134 | c->type = PM_QOS_MIN; | 188 | c->type = PM_QOS_MIN; |
135 | c->notifiers = n; | 189 | c->notifiers = n; |
136 | 190 | ||
191 | INIT_LIST_HEAD(&qos->flags.list); | ||
192 | |||
137 | spin_lock_irq(&dev->power.lock); | 193 | spin_lock_irq(&dev->power.lock); |
138 | dev->power.constraints = c; | 194 | dev->power.qos = qos; |
139 | spin_unlock_irq(&dev->power.lock); | 195 | spin_unlock_irq(&dev->power.lock); |
140 | 196 | ||
141 | return 0; | 197 | return 0; |
@@ -151,7 +207,7 @@ static int dev_pm_qos_constraints_allocate(struct device *dev) | |||
151 | void dev_pm_qos_constraints_init(struct device *dev) | 207 | void dev_pm_qos_constraints_init(struct device *dev) |
152 | { | 208 | { |
153 | mutex_lock(&dev_pm_qos_mtx); | 209 | mutex_lock(&dev_pm_qos_mtx); |
154 | dev->power.constraints = NULL; | 210 | dev->power.qos = NULL; |
155 | dev->power.power_state = PMSG_ON; | 211 | dev->power.power_state = PMSG_ON; |
156 | mutex_unlock(&dev_pm_qos_mtx); | 212 | mutex_unlock(&dev_pm_qos_mtx); |
157 | } | 213 | } |
@@ -164,24 +220,28 @@ void dev_pm_qos_constraints_init(struct device *dev) | |||
164 | */ | 220 | */ |
165 | void dev_pm_qos_constraints_destroy(struct device *dev) | 221 | void dev_pm_qos_constraints_destroy(struct device *dev) |
166 | { | 222 | { |
223 | struct dev_pm_qos *qos; | ||
167 | struct dev_pm_qos_request *req, *tmp; | 224 | struct dev_pm_qos_request *req, *tmp; |
168 | struct pm_qos_constraints *c; | 225 | struct pm_qos_constraints *c; |
226 | struct pm_qos_flags *f; | ||
169 | 227 | ||
170 | /* | 228 | /* |
171 | * If the device's PM QoS resume latency limit has been exposed to user | 229 | * If the device's PM QoS resume latency limit or PM QoS flags have been |
172 | * space, it has to be hidden at this point. | 230 | * exposed to user space, they have to be hidden at this point. |
173 | */ | 231 | */ |
174 | dev_pm_qos_hide_latency_limit(dev); | 232 | dev_pm_qos_hide_latency_limit(dev); |
233 | dev_pm_qos_hide_flags(dev); | ||
175 | 234 | ||
176 | mutex_lock(&dev_pm_qos_mtx); | 235 | mutex_lock(&dev_pm_qos_mtx); |
177 | 236 | ||
178 | dev->power.power_state = PMSG_INVALID; | 237 | dev->power.power_state = PMSG_INVALID; |
179 | c = dev->power.constraints; | 238 | qos = dev->power.qos; |
180 | if (!c) | 239 | if (!qos) |
181 | goto out; | 240 | goto out; |
182 | 241 | ||
183 | /* Flush the constraints list for the device */ | 242 | /* Flush the constraints lists for the device. */ |
184 | plist_for_each_entry_safe(req, tmp, &c->list, node) { | 243 | c = &qos->latency; |
244 | plist_for_each_entry_safe(req, tmp, &c->list, data.pnode) { | ||
185 | /* | 245 | /* |
186 | * Update constraints list and call the notification | 246 | * Update constraints list and call the notification |
187 | * callbacks if needed | 247 | * callbacks if needed |
@@ -189,13 +249,18 @@ void dev_pm_qos_constraints_destroy(struct device *dev) | |||
189 | apply_constraint(req, PM_QOS_REMOVE_REQ, PM_QOS_DEFAULT_VALUE); | 249 | apply_constraint(req, PM_QOS_REMOVE_REQ, PM_QOS_DEFAULT_VALUE); |
190 | memset(req, 0, sizeof(*req)); | 250 | memset(req, 0, sizeof(*req)); |
191 | } | 251 | } |
252 | f = &qos->flags; | ||
253 | list_for_each_entry_safe(req, tmp, &f->list, data.flr.node) { | ||
254 | apply_constraint(req, PM_QOS_REMOVE_REQ, PM_QOS_DEFAULT_VALUE); | ||
255 | memset(req, 0, sizeof(*req)); | ||
256 | } | ||
192 | 257 | ||
193 | spin_lock_irq(&dev->power.lock); | 258 | spin_lock_irq(&dev->power.lock); |
194 | dev->power.constraints = NULL; | 259 | dev->power.qos = NULL; |
195 | spin_unlock_irq(&dev->power.lock); | 260 | spin_unlock_irq(&dev->power.lock); |
196 | 261 | ||
197 | kfree(c->notifiers); | 262 | kfree(c->notifiers); |
198 | kfree(c); | 263 | kfree(qos); |
199 | 264 | ||
200 | out: | 265 | out: |
201 | mutex_unlock(&dev_pm_qos_mtx); | 266 | mutex_unlock(&dev_pm_qos_mtx); |
@@ -205,6 +270,7 @@ void dev_pm_qos_constraints_destroy(struct device *dev) | |||
205 | * dev_pm_qos_add_request - inserts new qos request into the list | 270 | * dev_pm_qos_add_request - inserts new qos request into the list |
206 | * @dev: target device for the constraint | 271 | * @dev: target device for the constraint |
207 | * @req: pointer to a preallocated handle | 272 | * @req: pointer to a preallocated handle |
273 | * @type: type of the request | ||
208 | * @value: defines the qos request | 274 | * @value: defines the qos request |
209 | * | 275 | * |
210 | * This function inserts a new entry in the device constraints list of | 276 | * This function inserts a new entry in the device constraints list of |
@@ -218,9 +284,12 @@ void dev_pm_qos_constraints_destroy(struct device *dev) | |||
218 | * -EINVAL in case of wrong parameters, -ENOMEM if there's not enough memory | 284 | * -EINVAL in case of wrong parameters, -ENOMEM if there's not enough memory |
219 | * to allocate for data structures, -ENODEV if the device has just been removed | 285 | * to allocate for data structures, -ENODEV if the device has just been removed |
220 | * from the system. | 286 | * from the system. |
287 | * | ||
288 | * Callers should ensure that the target device is not RPM_SUSPENDED before | ||
289 | * using this function for requests of type DEV_PM_QOS_FLAGS. | ||
221 | */ | 290 | */ |
222 | int dev_pm_qos_add_request(struct device *dev, struct dev_pm_qos_request *req, | 291 | int dev_pm_qos_add_request(struct device *dev, struct dev_pm_qos_request *req, |
223 | s32 value) | 292 | enum dev_pm_qos_req_type type, s32 value) |
224 | { | 293 | { |
225 | int ret = 0; | 294 | int ret = 0; |
226 | 295 | ||
@@ -235,7 +304,7 @@ int dev_pm_qos_add_request(struct device *dev, struct dev_pm_qos_request *req, | |||
235 | 304 | ||
236 | mutex_lock(&dev_pm_qos_mtx); | 305 | mutex_lock(&dev_pm_qos_mtx); |
237 | 306 | ||
238 | if (!dev->power.constraints) { | 307 | if (!dev->power.qos) { |
239 | if (dev->power.power_state.event == PM_EVENT_INVALID) { | 308 | if (dev->power.power_state.event == PM_EVENT_INVALID) { |
240 | /* The device has been removed from the system. */ | 309 | /* The device has been removed from the system. */ |
241 | req->dev = NULL; | 310 | req->dev = NULL; |
@@ -251,8 +320,10 @@ int dev_pm_qos_add_request(struct device *dev, struct dev_pm_qos_request *req, | |||
251 | } | 320 | } |
252 | } | 321 | } |
253 | 322 | ||
254 | if (!ret) | 323 | if (!ret) { |
324 | req->type = type; | ||
255 | ret = apply_constraint(req, PM_QOS_ADD_REQ, value); | 325 | ret = apply_constraint(req, PM_QOS_ADD_REQ, value); |
326 | } | ||
256 | 327 | ||
257 | out: | 328 | out: |
258 | mutex_unlock(&dev_pm_qos_mtx); | 329 | mutex_unlock(&dev_pm_qos_mtx); |
@@ -262,6 +333,37 @@ int dev_pm_qos_add_request(struct device *dev, struct dev_pm_qos_request *req, | |||
262 | EXPORT_SYMBOL_GPL(dev_pm_qos_add_request); | 333 | EXPORT_SYMBOL_GPL(dev_pm_qos_add_request); |
263 | 334 | ||
264 | /** | 335 | /** |
336 | * __dev_pm_qos_update_request - Modify an existing device PM QoS request. | ||
337 | * @req : PM QoS request to modify. | ||
338 | * @new_value: New value to request. | ||
339 | */ | ||
340 | static int __dev_pm_qos_update_request(struct dev_pm_qos_request *req, | ||
341 | s32 new_value) | ||
342 | { | ||
343 | s32 curr_value; | ||
344 | int ret = 0; | ||
345 | |||
346 | if (!req->dev->power.qos) | ||
347 | return -ENODEV; | ||
348 | |||
349 | switch(req->type) { | ||
350 | case DEV_PM_QOS_LATENCY: | ||
351 | curr_value = req->data.pnode.prio; | ||
352 | break; | ||
353 | case DEV_PM_QOS_FLAGS: | ||
354 | curr_value = req->data.flr.flags; | ||
355 | break; | ||
356 | default: | ||
357 | return -EINVAL; | ||
358 | } | ||
359 | |||
360 | if (curr_value != new_value) | ||
361 | ret = apply_constraint(req, PM_QOS_UPDATE_REQ, new_value); | ||
362 | |||
363 | return ret; | ||
364 | } | ||
365 | |||
366 | /** | ||
265 | * dev_pm_qos_update_request - modifies an existing qos request | 367 | * dev_pm_qos_update_request - modifies an existing qos request |
266 | * @req : handle to list element holding a dev_pm_qos request to use | 368 | * @req : handle to list element holding a dev_pm_qos request to use |
267 | * @new_value: defines the qos request | 369 | * @new_value: defines the qos request |
@@ -275,11 +377,13 @@ EXPORT_SYMBOL_GPL(dev_pm_qos_add_request); | |||
275 | * 0 if the aggregated constraint value has not changed, | 377 | * 0 if the aggregated constraint value has not changed, |
276 | * -EINVAL in case of wrong parameters, -ENODEV if the device has been | 378 | * -EINVAL in case of wrong parameters, -ENODEV if the device has been |
277 | * removed from the system | 379 | * removed from the system |
380 | * | ||
381 | * Callers should ensure that the target device is not RPM_SUSPENDED before | ||
382 | * using this function for requests of type DEV_PM_QOS_FLAGS. | ||
278 | */ | 383 | */ |
279 | int dev_pm_qos_update_request(struct dev_pm_qos_request *req, | 384 | int dev_pm_qos_update_request(struct dev_pm_qos_request *req, s32 new_value) |
280 | s32 new_value) | ||
281 | { | 385 | { |
282 | int ret = 0; | 386 | int ret; |
283 | 387 | ||
284 | if (!req) /*guard against callers passing in null */ | 388 | if (!req) /*guard against callers passing in null */ |
285 | return -EINVAL; | 389 | return -EINVAL; |
@@ -289,17 +393,9 @@ int dev_pm_qos_update_request(struct dev_pm_qos_request *req, | |||
289 | return -EINVAL; | 393 | return -EINVAL; |
290 | 394 | ||
291 | mutex_lock(&dev_pm_qos_mtx); | 395 | mutex_lock(&dev_pm_qos_mtx); |
292 | 396 | ret = __dev_pm_qos_update_request(req, new_value); | |
293 | if (req->dev->power.constraints) { | ||
294 | if (new_value != req->node.prio) | ||
295 | ret = apply_constraint(req, PM_QOS_UPDATE_REQ, | ||
296 | new_value); | ||
297 | } else { | ||
298 | /* Return if the device has been removed */ | ||
299 | ret = -ENODEV; | ||
300 | } | ||
301 | |||
302 | mutex_unlock(&dev_pm_qos_mtx); | 397 | mutex_unlock(&dev_pm_qos_mtx); |
398 | |||
303 | return ret; | 399 | return ret; |
304 | } | 400 | } |
305 | EXPORT_SYMBOL_GPL(dev_pm_qos_update_request); | 401 | EXPORT_SYMBOL_GPL(dev_pm_qos_update_request); |
@@ -315,6 +411,9 @@ EXPORT_SYMBOL_GPL(dev_pm_qos_update_request); | |||
315 | * 0 if the aggregated constraint value has not changed, | 411 | * 0 if the aggregated constraint value has not changed, |
316 | * -EINVAL in case of wrong parameters, -ENODEV if the device has been | 412 | * -EINVAL in case of wrong parameters, -ENODEV if the device has been |
317 | * removed from the system | 413 | * removed from the system |
414 | * | ||
415 | * Callers should ensure that the target device is not RPM_SUSPENDED before | ||
416 | * using this function for requests of type DEV_PM_QOS_FLAGS. | ||
318 | */ | 417 | */ |
319 | int dev_pm_qos_remove_request(struct dev_pm_qos_request *req) | 418 | int dev_pm_qos_remove_request(struct dev_pm_qos_request *req) |
320 | { | 419 | { |
@@ -329,7 +428,7 @@ int dev_pm_qos_remove_request(struct dev_pm_qos_request *req) | |||
329 | 428 | ||
330 | mutex_lock(&dev_pm_qos_mtx); | 429 | mutex_lock(&dev_pm_qos_mtx); |
331 | 430 | ||
332 | if (req->dev->power.constraints) { | 431 | if (req->dev->power.qos) { |
333 | ret = apply_constraint(req, PM_QOS_REMOVE_REQ, | 432 | ret = apply_constraint(req, PM_QOS_REMOVE_REQ, |
334 | PM_QOS_DEFAULT_VALUE); | 433 | PM_QOS_DEFAULT_VALUE); |
335 | memset(req, 0, sizeof(*req)); | 434 | memset(req, 0, sizeof(*req)); |
@@ -362,13 +461,13 @@ int dev_pm_qos_add_notifier(struct device *dev, struct notifier_block *notifier) | |||
362 | 461 | ||
363 | mutex_lock(&dev_pm_qos_mtx); | 462 | mutex_lock(&dev_pm_qos_mtx); |
364 | 463 | ||
365 | if (!dev->power.constraints) | 464 | if (!dev->power.qos) |
366 | ret = dev->power.power_state.event != PM_EVENT_INVALID ? | 465 | ret = dev->power.power_state.event != PM_EVENT_INVALID ? |
367 | dev_pm_qos_constraints_allocate(dev) : -ENODEV; | 466 | dev_pm_qos_constraints_allocate(dev) : -ENODEV; |
368 | 467 | ||
369 | if (!ret) | 468 | if (!ret) |
370 | ret = blocking_notifier_chain_register( | 469 | ret = blocking_notifier_chain_register( |
371 | dev->power.constraints->notifiers, notifier); | 470 | dev->power.qos->latency.notifiers, notifier); |
372 | 471 | ||
373 | mutex_unlock(&dev_pm_qos_mtx); | 472 | mutex_unlock(&dev_pm_qos_mtx); |
374 | return ret; | 473 | return ret; |
@@ -393,9 +492,9 @@ int dev_pm_qos_remove_notifier(struct device *dev, | |||
393 | mutex_lock(&dev_pm_qos_mtx); | 492 | mutex_lock(&dev_pm_qos_mtx); |
394 | 493 | ||
395 | /* Silently return if the constraints object is not present. */ | 494 | /* Silently return if the constraints object is not present. */ |
396 | if (dev->power.constraints) | 495 | if (dev->power.qos) |
397 | retval = blocking_notifier_chain_unregister( | 496 | retval = blocking_notifier_chain_unregister( |
398 | dev->power.constraints->notifiers, | 497 | dev->power.qos->latency.notifiers, |
399 | notifier); | 498 | notifier); |
400 | 499 | ||
401 | mutex_unlock(&dev_pm_qos_mtx); | 500 | mutex_unlock(&dev_pm_qos_mtx); |
@@ -449,7 +548,8 @@ int dev_pm_qos_add_ancestor_request(struct device *dev, | |||
449 | ancestor = ancestor->parent; | 548 | ancestor = ancestor->parent; |
450 | 549 | ||
451 | if (ancestor) | 550 | if (ancestor) |
452 | error = dev_pm_qos_add_request(ancestor, req, value); | 551 | error = dev_pm_qos_add_request(ancestor, req, |
552 | DEV_PM_QOS_LATENCY, value); | ||
453 | 553 | ||
454 | if (error < 0) | 554 | if (error < 0) |
455 | req->dev = NULL; | 555 | req->dev = NULL; |
@@ -459,10 +559,19 @@ int dev_pm_qos_add_ancestor_request(struct device *dev, | |||
459 | EXPORT_SYMBOL_GPL(dev_pm_qos_add_ancestor_request); | 559 | EXPORT_SYMBOL_GPL(dev_pm_qos_add_ancestor_request); |
460 | 560 | ||
461 | #ifdef CONFIG_PM_RUNTIME | 561 | #ifdef CONFIG_PM_RUNTIME |
462 | static void __dev_pm_qos_drop_user_request(struct device *dev) | 562 | static void __dev_pm_qos_drop_user_request(struct device *dev, |
563 | enum dev_pm_qos_req_type type) | ||
463 | { | 564 | { |
464 | dev_pm_qos_remove_request(dev->power.pq_req); | 565 | switch(type) { |
465 | dev->power.pq_req = NULL; | 566 | case DEV_PM_QOS_LATENCY: |
567 | dev_pm_qos_remove_request(dev->power.qos->latency_req); | ||
568 | dev->power.qos->latency_req = NULL; | ||
569 | break; | ||
570 | case DEV_PM_QOS_FLAGS: | ||
571 | dev_pm_qos_remove_request(dev->power.qos->flags_req); | ||
572 | dev->power.qos->flags_req = NULL; | ||
573 | break; | ||
574 | } | ||
466 | } | 575 | } |
467 | 576 | ||
468 | /** | 577 | /** |
@@ -478,21 +587,21 @@ int dev_pm_qos_expose_latency_limit(struct device *dev, s32 value) | |||
478 | if (!device_is_registered(dev) || value < 0) | 587 | if (!device_is_registered(dev) || value < 0) |
479 | return -EINVAL; | 588 | return -EINVAL; |
480 | 589 | ||
481 | if (dev->power.pq_req) | 590 | if (dev->power.qos && dev->power.qos->latency_req) |
482 | return -EEXIST; | 591 | return -EEXIST; |
483 | 592 | ||
484 | req = kzalloc(sizeof(*req), GFP_KERNEL); | 593 | req = kzalloc(sizeof(*req), GFP_KERNEL); |
485 | if (!req) | 594 | if (!req) |
486 | return -ENOMEM; | 595 | return -ENOMEM; |
487 | 596 | ||
488 | ret = dev_pm_qos_add_request(dev, req, value); | 597 | ret = dev_pm_qos_add_request(dev, req, DEV_PM_QOS_LATENCY, value); |
489 | if (ret < 0) | 598 | if (ret < 0) |
490 | return ret; | 599 | return ret; |
491 | 600 | ||
492 | dev->power.pq_req = req; | 601 | dev->power.qos->latency_req = req; |
493 | ret = pm_qos_sysfs_add(dev); | 602 | ret = pm_qos_sysfs_add_latency(dev); |
494 | if (ret) | 603 | if (ret) |
495 | __dev_pm_qos_drop_user_request(dev); | 604 | __dev_pm_qos_drop_user_request(dev, DEV_PM_QOS_LATENCY); |
496 | 605 | ||
497 | return ret; | 606 | return ret; |
498 | } | 607 | } |
@@ -504,10 +613,92 @@ EXPORT_SYMBOL_GPL(dev_pm_qos_expose_latency_limit); | |||
504 | */ | 613 | */ |
505 | void dev_pm_qos_hide_latency_limit(struct device *dev) | 614 | void dev_pm_qos_hide_latency_limit(struct device *dev) |
506 | { | 615 | { |
507 | if (dev->power.pq_req) { | 616 | if (dev->power.qos && dev->power.qos->latency_req) { |
508 | pm_qos_sysfs_remove(dev); | 617 | pm_qos_sysfs_remove_latency(dev); |
509 | __dev_pm_qos_drop_user_request(dev); | 618 | __dev_pm_qos_drop_user_request(dev, DEV_PM_QOS_LATENCY); |
510 | } | 619 | } |
511 | } | 620 | } |
512 | EXPORT_SYMBOL_GPL(dev_pm_qos_hide_latency_limit); | 621 | EXPORT_SYMBOL_GPL(dev_pm_qos_hide_latency_limit); |
622 | |||
623 | /** | ||
624 | * dev_pm_qos_expose_flags - Expose PM QoS flags of a device to user space. | ||
625 | * @dev: Device whose PM QoS flags are to be exposed to user space. | ||
626 | * @val: Initial values of the flags. | ||
627 | */ | ||
628 | int dev_pm_qos_expose_flags(struct device *dev, s32 val) | ||
629 | { | ||
630 | struct dev_pm_qos_request *req; | ||
631 | int ret; | ||
632 | |||
633 | if (!device_is_registered(dev)) | ||
634 | return -EINVAL; | ||
635 | |||
636 | if (dev->power.qos && dev->power.qos->flags_req) | ||
637 | return -EEXIST; | ||
638 | |||
639 | req = kzalloc(sizeof(*req), GFP_KERNEL); | ||
640 | if (!req) | ||
641 | return -ENOMEM; | ||
642 | |||
643 | pm_runtime_get_sync(dev); | ||
644 | ret = dev_pm_qos_add_request(dev, req, DEV_PM_QOS_FLAGS, val); | ||
645 | if (ret < 0) | ||
646 | goto fail; | ||
647 | |||
648 | dev->power.qos->flags_req = req; | ||
649 | ret = pm_qos_sysfs_add_flags(dev); | ||
650 | if (ret) | ||
651 | __dev_pm_qos_drop_user_request(dev, DEV_PM_QOS_FLAGS); | ||
652 | |||
653 | fail: | ||
654 | pm_runtime_put(dev); | ||
655 | return ret; | ||
656 | } | ||
657 | EXPORT_SYMBOL_GPL(dev_pm_qos_expose_flags); | ||
658 | |||
659 | /** | ||
660 | * dev_pm_qos_hide_flags - Hide PM QoS flags of a device from user space. | ||
661 | * @dev: Device whose PM QoS flags are to be hidden from user space. | ||
662 | */ | ||
663 | void dev_pm_qos_hide_flags(struct device *dev) | ||
664 | { | ||
665 | if (dev->power.qos && dev->power.qos->flags_req) { | ||
666 | pm_qos_sysfs_remove_flags(dev); | ||
667 | pm_runtime_get_sync(dev); | ||
668 | __dev_pm_qos_drop_user_request(dev, DEV_PM_QOS_FLAGS); | ||
669 | pm_runtime_put(dev); | ||
670 | } | ||
671 | } | ||
672 | EXPORT_SYMBOL_GPL(dev_pm_qos_hide_flags); | ||
673 | |||
674 | /** | ||
675 | * dev_pm_qos_update_flags - Update PM QoS flags request owned by user space. | ||
676 | * @dev: Device to update the PM QoS flags request for. | ||
677 | * @mask: Flags to set/clear. | ||
678 | * @set: Whether to set or clear the flags (true means set). | ||
679 | */ | ||
680 | int dev_pm_qos_update_flags(struct device *dev, s32 mask, bool set) | ||
681 | { | ||
682 | s32 value; | ||
683 | int ret; | ||
684 | |||
685 | if (!dev->power.qos || !dev->power.qos->flags_req) | ||
686 | return -EINVAL; | ||
687 | |||
688 | pm_runtime_get_sync(dev); | ||
689 | mutex_lock(&dev_pm_qos_mtx); | ||
690 | |||
691 | value = dev_pm_qos_requested_flags(dev); | ||
692 | if (set) | ||
693 | value |= mask; | ||
694 | else | ||
695 | value &= ~mask; | ||
696 | |||
697 | ret = __dev_pm_qos_update_request(dev->power.qos->flags_req, value); | ||
698 | |||
699 | mutex_unlock(&dev_pm_qos_mtx); | ||
700 | pm_runtime_put(dev); | ||
701 | |||
702 | return ret; | ||
703 | } | ||
513 | #endif /* CONFIG_PM_RUNTIME */ | 704 | #endif /* CONFIG_PM_RUNTIME */ |