Root/
1 | /* |
2 | * Devices PM QoS constraints management |
3 | * |
4 | * Copyright (C) 2011 Texas Instruments, Inc. |
5 | * |
6 | * This program is free software; you can redistribute it and/or modify |
7 | * it under the terms of the GNU General Public License version 2 as |
8 | * published by the Free Software Foundation. |
9 | * |
10 | * |
11 | * This module exposes the interface to kernel space for specifying |
12 | * per-device PM QoS dependencies. It provides infrastructure for registration |
13 | * of: |
14 | * |
15 | * Dependents on a QoS value : register requests |
16 | * Watchers of QoS value : get notified when target QoS value changes |
17 | * |
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. |
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. |
26 | * |
27 | * Note about the per-device constraint data struct allocation: |
28 | * . The per-device constraints data struct ptr is tored into the device |
29 | * dev_pm_info. |
30 | * . To minimize the data usage by the per-device constraints, the data struct |
31 | * is only allocated at the first call to dev_pm_qos_add_request. |
32 | * . The data is later free'd when the device is removed from the system. |
33 | * . A global mutex protects the constraints users from the data being |
34 | * allocated and free'd. |
35 | */ |
36 | |
37 | #include <linux/pm_qos.h> |
38 | #include <linux/spinlock.h> |
39 | #include <linux/slab.h> |
40 | #include <linux/device.h> |
41 | #include <linux/mutex.h> |
42 | #include <linux/export.h> |
43 | #include <linux/pm_runtime.h> |
44 | #include <linux/err.h> |
45 | |
46 | #include "power.h" |
47 | |
48 | static DEFINE_MUTEX(dev_pm_qos_mtx); |
49 | static DEFINE_MUTEX(dev_pm_qos_sysfs_mtx); |
50 | |
51 | static BLOCKING_NOTIFIER_HEAD(dev_pm_notifiers); |
52 | |
53 | /** |
54 | * __dev_pm_qos_flags - Check PM QoS flags for a given device. |
55 | * @dev: Device to check the PM QoS flags for. |
56 | * @mask: Flags to check against. |
57 | * |
58 | * This routine must be called with dev->power.lock held. |
59 | */ |
60 | enum pm_qos_flags_status __dev_pm_qos_flags(struct device *dev, s32 mask) |
61 | { |
62 | struct dev_pm_qos *qos = dev->power.qos; |
63 | struct pm_qos_flags *pqf; |
64 | s32 val; |
65 | |
66 | if (IS_ERR_OR_NULL(qos)) |
67 | return PM_QOS_FLAGS_UNDEFINED; |
68 | |
69 | pqf = &qos->flags; |
70 | if (list_empty(&pqf->list)) |
71 | return PM_QOS_FLAGS_UNDEFINED; |
72 | |
73 | val = pqf->effective_flags & mask; |
74 | if (val) |
75 | return (val == mask) ? PM_QOS_FLAGS_ALL : PM_QOS_FLAGS_SOME; |
76 | |
77 | return PM_QOS_FLAGS_NONE; |
78 | } |
79 | |
80 | /** |
81 | * dev_pm_qos_flags - Check PM QoS flags for a given device (locked). |
82 | * @dev: Device to check the PM QoS flags for. |
83 | * @mask: Flags to check against. |
84 | */ |
85 | enum pm_qos_flags_status dev_pm_qos_flags(struct device *dev, s32 mask) |
86 | { |
87 | unsigned long irqflags; |
88 | enum pm_qos_flags_status ret; |
89 | |
90 | spin_lock_irqsave(&dev->power.lock, irqflags); |
91 | ret = __dev_pm_qos_flags(dev, mask); |
92 | spin_unlock_irqrestore(&dev->power.lock, irqflags); |
93 | |
94 | return ret; |
95 | } |
96 | EXPORT_SYMBOL_GPL(dev_pm_qos_flags); |
97 | |
98 | /** |
99 | * __dev_pm_qos_read_value - Get PM QoS constraint for a given device. |
100 | * @dev: Device to get the PM QoS constraint value for. |
101 | * |
102 | * This routine must be called with dev->power.lock held. |
103 | */ |
104 | s32 __dev_pm_qos_read_value(struct device *dev) |
105 | { |
106 | return IS_ERR_OR_NULL(dev->power.qos) ? |
107 | 0 : pm_qos_read_value(&dev->power.qos->latency); |
108 | } |
109 | |
110 | /** |
111 | * dev_pm_qos_read_value - Get PM QoS constraint for a given device (locked). |
112 | * @dev: Device to get the PM QoS constraint value for. |
113 | */ |
114 | s32 dev_pm_qos_read_value(struct device *dev) |
115 | { |
116 | unsigned long flags; |
117 | s32 ret; |
118 | |
119 | spin_lock_irqsave(&dev->power.lock, flags); |
120 | ret = __dev_pm_qos_read_value(dev); |
121 | spin_unlock_irqrestore(&dev->power.lock, flags); |
122 | |
123 | return ret; |
124 | } |
125 | |
126 | /** |
127 | * apply_constraint - Add/modify/remove device PM QoS request. |
128 | * @req: Constraint request to apply |
129 | * @action: Action to perform (add/update/remove). |
130 | * @value: Value to assign to the QoS request. |
131 | * |
132 | * Internal function to update the constraints list using the PM QoS core |
133 | * code and if needed call the per-device and the global notification |
134 | * callbacks |
135 | */ |
136 | static int apply_constraint(struct dev_pm_qos_request *req, |
137 | enum pm_qos_req_action action, s32 value) |
138 | { |
139 | struct dev_pm_qos *qos = req->dev->power.qos; |
140 | int ret; |
141 | |
142 | switch(req->type) { |
143 | case DEV_PM_QOS_LATENCY: |
144 | ret = pm_qos_update_target(&qos->latency, &req->data.pnode, |
145 | action, value); |
146 | if (ret) { |
147 | value = pm_qos_read_value(&qos->latency); |
148 | blocking_notifier_call_chain(&dev_pm_notifiers, |
149 | (unsigned long)value, |
150 | req); |
151 | } |
152 | break; |
153 | case DEV_PM_QOS_FLAGS: |
154 | ret = pm_qos_update_flags(&qos->flags, &req->data.flr, |
155 | action, value); |
156 | break; |
157 | default: |
158 | ret = -EINVAL; |
159 | } |
160 | |
161 | return ret; |
162 | } |
163 | |
164 | /* |
165 | * dev_pm_qos_constraints_allocate |
166 | * @dev: device to allocate data for |
167 | * |
168 | * Called at the first call to add_request, for constraint data allocation |
169 | * Must be called with the dev_pm_qos_mtx mutex held |
170 | */ |
171 | static int dev_pm_qos_constraints_allocate(struct device *dev) |
172 | { |
173 | struct dev_pm_qos *qos; |
174 | struct pm_qos_constraints *c; |
175 | struct blocking_notifier_head *n; |
176 | |
177 | qos = kzalloc(sizeof(*qos), GFP_KERNEL); |
178 | if (!qos) |
179 | return -ENOMEM; |
180 | |
181 | n = kzalloc(sizeof(*n), GFP_KERNEL); |
182 | if (!n) { |
183 | kfree(qos); |
184 | return -ENOMEM; |
185 | } |
186 | BLOCKING_INIT_NOTIFIER_HEAD(n); |
187 | |
188 | c = &qos->latency; |
189 | plist_head_init(&c->list); |
190 | c->target_value = PM_QOS_DEV_LAT_DEFAULT_VALUE; |
191 | c->default_value = PM_QOS_DEV_LAT_DEFAULT_VALUE; |
192 | c->type = PM_QOS_MIN; |
193 | c->notifiers = n; |
194 | |
195 | INIT_LIST_HEAD(&qos->flags.list); |
196 | |
197 | spin_lock_irq(&dev->power.lock); |
198 | dev->power.qos = qos; |
199 | spin_unlock_irq(&dev->power.lock); |
200 | |
201 | return 0; |
202 | } |
203 | |
204 | static void __dev_pm_qos_hide_latency_limit(struct device *dev); |
205 | static void __dev_pm_qos_hide_flags(struct device *dev); |
206 | |
207 | /** |
208 | * dev_pm_qos_constraints_destroy |
209 | * @dev: target device |
210 | * |
211 | * Called from the device PM subsystem on device removal under device_pm_lock(). |
212 | */ |
213 | void dev_pm_qos_constraints_destroy(struct device *dev) |
214 | { |
215 | struct dev_pm_qos *qos; |
216 | struct dev_pm_qos_request *req, *tmp; |
217 | struct pm_qos_constraints *c; |
218 | struct pm_qos_flags *f; |
219 | |
220 | mutex_lock(&dev_pm_qos_sysfs_mtx); |
221 | |
222 | /* |
223 | * If the device's PM QoS resume latency limit or PM QoS flags have been |
224 | * exposed to user space, they have to be hidden at this point. |
225 | */ |
226 | pm_qos_sysfs_remove_latency(dev); |
227 | pm_qos_sysfs_remove_flags(dev); |
228 | |
229 | mutex_lock(&dev_pm_qos_mtx); |
230 | |
231 | __dev_pm_qos_hide_latency_limit(dev); |
232 | __dev_pm_qos_hide_flags(dev); |
233 | |
234 | qos = dev->power.qos; |
235 | if (!qos) |
236 | goto out; |
237 | |
238 | /* Flush the constraints lists for the device. */ |
239 | c = &qos->latency; |
240 | plist_for_each_entry_safe(req, tmp, &c->list, data.pnode) { |
241 | /* |
242 | * Update constraints list and call the notification |
243 | * callbacks if needed |
244 | */ |
245 | apply_constraint(req, PM_QOS_REMOVE_REQ, PM_QOS_DEFAULT_VALUE); |
246 | memset(req, 0, sizeof(*req)); |
247 | } |
248 | f = &qos->flags; |
249 | list_for_each_entry_safe(req, tmp, &f->list, data.flr.node) { |
250 | apply_constraint(req, PM_QOS_REMOVE_REQ, PM_QOS_DEFAULT_VALUE); |
251 | memset(req, 0, sizeof(*req)); |
252 | } |
253 | |
254 | spin_lock_irq(&dev->power.lock); |
255 | dev->power.qos = ERR_PTR(-ENODEV); |
256 | spin_unlock_irq(&dev->power.lock); |
257 | |
258 | kfree(c->notifiers); |
259 | kfree(qos); |
260 | |
261 | out: |
262 | mutex_unlock(&dev_pm_qos_mtx); |
263 | |
264 | mutex_unlock(&dev_pm_qos_sysfs_mtx); |
265 | } |
266 | |
267 | /** |
268 | * dev_pm_qos_add_request - inserts new qos request into the list |
269 | * @dev: target device for the constraint |
270 | * @req: pointer to a preallocated handle |
271 | * @type: type of the request |
272 | * @value: defines the qos request |
273 | * |
274 | * This function inserts a new entry in the device constraints list of |
275 | * requested qos performance characteristics. It recomputes the aggregate |
276 | * QoS expectations of parameters and initializes the dev_pm_qos_request |
277 | * handle. Caller needs to save this handle for later use in updates and |
278 | * removal. |
279 | * |
280 | * Returns 1 if the aggregated constraint value has changed, |
281 | * 0 if the aggregated constraint value has not changed, |
282 | * -EINVAL in case of wrong parameters, -ENOMEM if there's not enough memory |
283 | * to allocate for data structures, -ENODEV if the device has just been removed |
284 | * from the system. |
285 | * |
286 | * Callers should ensure that the target device is not RPM_SUSPENDED before |
287 | * using this function for requests of type DEV_PM_QOS_FLAGS. |
288 | */ |
289 | int dev_pm_qos_add_request(struct device *dev, struct dev_pm_qos_request *req, |
290 | enum dev_pm_qos_req_type type, s32 value) |
291 | { |
292 | int ret = 0; |
293 | |
294 | if (!dev || !req) /*guard against callers passing in null */ |
295 | return -EINVAL; |
296 | |
297 | if (WARN(dev_pm_qos_request_active(req), |
298 | "%s() called for already added request\n", __func__)) |
299 | return -EINVAL; |
300 | |
301 | mutex_lock(&dev_pm_qos_mtx); |
302 | |
303 | if (IS_ERR(dev->power.qos)) |
304 | ret = -ENODEV; |
305 | else if (!dev->power.qos) |
306 | ret = dev_pm_qos_constraints_allocate(dev); |
307 | |
308 | if (!ret) { |
309 | req->dev = dev; |
310 | req->type = type; |
311 | ret = apply_constraint(req, PM_QOS_ADD_REQ, value); |
312 | } |
313 | |
314 | mutex_unlock(&dev_pm_qos_mtx); |
315 | |
316 | return ret; |
317 | } |
318 | EXPORT_SYMBOL_GPL(dev_pm_qos_add_request); |
319 | |
320 | /** |
321 | * __dev_pm_qos_update_request - Modify an existing device PM QoS request. |
322 | * @req : PM QoS request to modify. |
323 | * @new_value: New value to request. |
324 | */ |
325 | static int __dev_pm_qos_update_request(struct dev_pm_qos_request *req, |
326 | s32 new_value) |
327 | { |
328 | s32 curr_value; |
329 | int ret = 0; |
330 | |
331 | if (!req) /*guard against callers passing in null */ |
332 | return -EINVAL; |
333 | |
334 | if (WARN(!dev_pm_qos_request_active(req), |
335 | "%s() called for unknown object\n", __func__)) |
336 | return -EINVAL; |
337 | |
338 | if (IS_ERR_OR_NULL(req->dev->power.qos)) |
339 | return -ENODEV; |
340 | |
341 | switch(req->type) { |
342 | case DEV_PM_QOS_LATENCY: |
343 | curr_value = req->data.pnode.prio; |
344 | break; |
345 | case DEV_PM_QOS_FLAGS: |
346 | curr_value = req->data.flr.flags; |
347 | break; |
348 | default: |
349 | return -EINVAL; |
350 | } |
351 | |
352 | if (curr_value != new_value) |
353 | ret = apply_constraint(req, PM_QOS_UPDATE_REQ, new_value); |
354 | |
355 | return ret; |
356 | } |
357 | |
358 | /** |
359 | * dev_pm_qos_update_request - modifies an existing qos request |
360 | * @req : handle to list element holding a dev_pm_qos request to use |
361 | * @new_value: defines the qos request |
362 | * |
363 | * Updates an existing dev PM qos request along with updating the |
364 | * target value. |
365 | * |
366 | * Attempts are made to make this code callable on hot code paths. |
367 | * |
368 | * Returns 1 if the aggregated constraint value has changed, |
369 | * 0 if the aggregated constraint value has not changed, |
370 | * -EINVAL in case of wrong parameters, -ENODEV if the device has been |
371 | * removed from the system |
372 | * |
373 | * Callers should ensure that the target device is not RPM_SUSPENDED before |
374 | * using this function for requests of type DEV_PM_QOS_FLAGS. |
375 | */ |
376 | int dev_pm_qos_update_request(struct dev_pm_qos_request *req, s32 new_value) |
377 | { |
378 | int ret; |
379 | |
380 | mutex_lock(&dev_pm_qos_mtx); |
381 | ret = __dev_pm_qos_update_request(req, new_value); |
382 | mutex_unlock(&dev_pm_qos_mtx); |
383 | return ret; |
384 | } |
385 | EXPORT_SYMBOL_GPL(dev_pm_qos_update_request); |
386 | |
387 | static int __dev_pm_qos_remove_request(struct dev_pm_qos_request *req) |
388 | { |
389 | int ret; |
390 | |
391 | if (!req) /*guard against callers passing in null */ |
392 | return -EINVAL; |
393 | |
394 | if (WARN(!dev_pm_qos_request_active(req), |
395 | "%s() called for unknown object\n", __func__)) |
396 | return -EINVAL; |
397 | |
398 | if (IS_ERR_OR_NULL(req->dev->power.qos)) |
399 | return -ENODEV; |
400 | |
401 | ret = apply_constraint(req, PM_QOS_REMOVE_REQ, PM_QOS_DEFAULT_VALUE); |
402 | memset(req, 0, sizeof(*req)); |
403 | return ret; |
404 | } |
405 | |
406 | /** |
407 | * dev_pm_qos_remove_request - modifies an existing qos request |
408 | * @req: handle to request list element |
409 | * |
410 | * Will remove pm qos request from the list of constraints and |
411 | * recompute the current target value. Call this on slow code paths. |
412 | * |
413 | * Returns 1 if the aggregated constraint value has changed, |
414 | * 0 if the aggregated constraint value has not changed, |
415 | * -EINVAL in case of wrong parameters, -ENODEV if the device has been |
416 | * removed from the system |
417 | * |
418 | * Callers should ensure that the target device is not RPM_SUSPENDED before |
419 | * using this function for requests of type DEV_PM_QOS_FLAGS. |
420 | */ |
421 | int dev_pm_qos_remove_request(struct dev_pm_qos_request *req) |
422 | { |
423 | int ret; |
424 | |
425 | mutex_lock(&dev_pm_qos_mtx); |
426 | ret = __dev_pm_qos_remove_request(req); |
427 | mutex_unlock(&dev_pm_qos_mtx); |
428 | return ret; |
429 | } |
430 | EXPORT_SYMBOL_GPL(dev_pm_qos_remove_request); |
431 | |
432 | /** |
433 | * dev_pm_qos_add_notifier - sets notification entry for changes to target value |
434 | * of per-device PM QoS constraints |
435 | * |
436 | * @dev: target device for the constraint |
437 | * @notifier: notifier block managed by caller. |
438 | * |
439 | * Will register the notifier into a notification chain that gets called |
440 | * upon changes to the target value for the device. |
441 | * |
442 | * If the device's constraints object doesn't exist when this routine is called, |
443 | * it will be created (or error code will be returned if that fails). |
444 | */ |
445 | int dev_pm_qos_add_notifier(struct device *dev, struct notifier_block *notifier) |
446 | { |
447 | int ret = 0; |
448 | |
449 | mutex_lock(&dev_pm_qos_mtx); |
450 | |
451 | if (IS_ERR(dev->power.qos)) |
452 | ret = -ENODEV; |
453 | else if (!dev->power.qos) |
454 | ret = dev_pm_qos_constraints_allocate(dev); |
455 | |
456 | if (!ret) |
457 | ret = blocking_notifier_chain_register( |
458 | dev->power.qos->latency.notifiers, notifier); |
459 | |
460 | mutex_unlock(&dev_pm_qos_mtx); |
461 | return ret; |
462 | } |
463 | EXPORT_SYMBOL_GPL(dev_pm_qos_add_notifier); |
464 | |
465 | /** |
466 | * dev_pm_qos_remove_notifier - deletes notification for changes to target value |
467 | * of per-device PM QoS constraints |
468 | * |
469 | * @dev: target device for the constraint |
470 | * @notifier: notifier block to be removed. |
471 | * |
472 | * Will remove the notifier from the notification chain that gets called |
473 | * upon changes to the target value. |
474 | */ |
475 | int dev_pm_qos_remove_notifier(struct device *dev, |
476 | struct notifier_block *notifier) |
477 | { |
478 | int retval = 0; |
479 | |
480 | mutex_lock(&dev_pm_qos_mtx); |
481 | |
482 | /* Silently return if the constraints object is not present. */ |
483 | if (!IS_ERR_OR_NULL(dev->power.qos)) |
484 | retval = blocking_notifier_chain_unregister( |
485 | dev->power.qos->latency.notifiers, |
486 | notifier); |
487 | |
488 | mutex_unlock(&dev_pm_qos_mtx); |
489 | return retval; |
490 | } |
491 | EXPORT_SYMBOL_GPL(dev_pm_qos_remove_notifier); |
492 | |
493 | /** |
494 | * dev_pm_qos_add_global_notifier - sets notification entry for changes to |
495 | * target value of the PM QoS constraints for any device |
496 | * |
497 | * @notifier: notifier block managed by caller. |
498 | * |
499 | * Will register the notifier into a notification chain that gets called |
500 | * upon changes to the target value for any device. |
501 | */ |
502 | int dev_pm_qos_add_global_notifier(struct notifier_block *notifier) |
503 | { |
504 | return blocking_notifier_chain_register(&dev_pm_notifiers, notifier); |
505 | } |
506 | EXPORT_SYMBOL_GPL(dev_pm_qos_add_global_notifier); |
507 | |
508 | /** |
509 | * dev_pm_qos_remove_global_notifier - deletes notification for changes to |
510 | * target value of PM QoS constraints for any device |
511 | * |
512 | * @notifier: notifier block to be removed. |
513 | * |
514 | * Will remove the notifier from the notification chain that gets called |
515 | * upon changes to the target value for any device. |
516 | */ |
517 | int dev_pm_qos_remove_global_notifier(struct notifier_block *notifier) |
518 | { |
519 | return blocking_notifier_chain_unregister(&dev_pm_notifiers, notifier); |
520 | } |
521 | EXPORT_SYMBOL_GPL(dev_pm_qos_remove_global_notifier); |
522 | |
523 | /** |
524 | * dev_pm_qos_add_ancestor_request - Add PM QoS request for device's ancestor. |
525 | * @dev: Device whose ancestor to add the request for. |
526 | * @req: Pointer to the preallocated handle. |
527 | * @value: Constraint latency value. |
528 | */ |
529 | int dev_pm_qos_add_ancestor_request(struct device *dev, |
530 | struct dev_pm_qos_request *req, s32 value) |
531 | { |
532 | struct device *ancestor = dev->parent; |
533 | int ret = -ENODEV; |
534 | |
535 | while (ancestor && !ancestor->power.ignore_children) |
536 | ancestor = ancestor->parent; |
537 | |
538 | if (ancestor) |
539 | ret = dev_pm_qos_add_request(ancestor, req, |
540 | DEV_PM_QOS_LATENCY, value); |
541 | |
542 | if (ret < 0) |
543 | req->dev = NULL; |
544 | |
545 | return ret; |
546 | } |
547 | EXPORT_SYMBOL_GPL(dev_pm_qos_add_ancestor_request); |
548 | |
549 | #ifdef CONFIG_PM_RUNTIME |
550 | static void __dev_pm_qos_drop_user_request(struct device *dev, |
551 | enum dev_pm_qos_req_type type) |
552 | { |
553 | struct dev_pm_qos_request *req = NULL; |
554 | |
555 | switch(type) { |
556 | case DEV_PM_QOS_LATENCY: |
557 | req = dev->power.qos->latency_req; |
558 | dev->power.qos->latency_req = NULL; |
559 | break; |
560 | case DEV_PM_QOS_FLAGS: |
561 | req = dev->power.qos->flags_req; |
562 | dev->power.qos->flags_req = NULL; |
563 | break; |
564 | } |
565 | __dev_pm_qos_remove_request(req); |
566 | kfree(req); |
567 | } |
568 | |
569 | static void dev_pm_qos_drop_user_request(struct device *dev, |
570 | enum dev_pm_qos_req_type type) |
571 | { |
572 | mutex_lock(&dev_pm_qos_mtx); |
573 | __dev_pm_qos_drop_user_request(dev, type); |
574 | mutex_unlock(&dev_pm_qos_mtx); |
575 | } |
576 | |
577 | /** |
578 | * dev_pm_qos_expose_latency_limit - Expose PM QoS latency limit to user space. |
579 | * @dev: Device whose PM QoS latency limit is to be exposed to user space. |
580 | * @value: Initial value of the latency limit. |
581 | */ |
582 | int dev_pm_qos_expose_latency_limit(struct device *dev, s32 value) |
583 | { |
584 | struct dev_pm_qos_request *req; |
585 | int ret; |
586 | |
587 | if (!device_is_registered(dev) || value < 0) |
588 | return -EINVAL; |
589 | |
590 | req = kzalloc(sizeof(*req), GFP_KERNEL); |
591 | if (!req) |
592 | return -ENOMEM; |
593 | |
594 | ret = dev_pm_qos_add_request(dev, req, DEV_PM_QOS_LATENCY, value); |
595 | if (ret < 0) { |
596 | kfree(req); |
597 | return ret; |
598 | } |
599 | |
600 | mutex_lock(&dev_pm_qos_sysfs_mtx); |
601 | |
602 | mutex_lock(&dev_pm_qos_mtx); |
603 | |
604 | if (IS_ERR_OR_NULL(dev->power.qos)) |
605 | ret = -ENODEV; |
606 | else if (dev->power.qos->latency_req) |
607 | ret = -EEXIST; |
608 | |
609 | if (ret < 0) { |
610 | __dev_pm_qos_remove_request(req); |
611 | kfree(req); |
612 | mutex_unlock(&dev_pm_qos_mtx); |
613 | goto out; |
614 | } |
615 | dev->power.qos->latency_req = req; |
616 | |
617 | mutex_unlock(&dev_pm_qos_mtx); |
618 | |
619 | ret = pm_qos_sysfs_add_latency(dev); |
620 | if (ret) |
621 | dev_pm_qos_drop_user_request(dev, DEV_PM_QOS_LATENCY); |
622 | |
623 | out: |
624 | mutex_unlock(&dev_pm_qos_sysfs_mtx); |
625 | return ret; |
626 | } |
627 | EXPORT_SYMBOL_GPL(dev_pm_qos_expose_latency_limit); |
628 | |
629 | static void __dev_pm_qos_hide_latency_limit(struct device *dev) |
630 | { |
631 | if (!IS_ERR_OR_NULL(dev->power.qos) && dev->power.qos->latency_req) |
632 | __dev_pm_qos_drop_user_request(dev, DEV_PM_QOS_LATENCY); |
633 | } |
634 | |
635 | /** |
636 | * dev_pm_qos_hide_latency_limit - Hide PM QoS latency limit from user space. |
637 | * @dev: Device whose PM QoS latency limit is to be hidden from user space. |
638 | */ |
639 | void dev_pm_qos_hide_latency_limit(struct device *dev) |
640 | { |
641 | mutex_lock(&dev_pm_qos_sysfs_mtx); |
642 | |
643 | pm_qos_sysfs_remove_latency(dev); |
644 | |
645 | mutex_lock(&dev_pm_qos_mtx); |
646 | __dev_pm_qos_hide_latency_limit(dev); |
647 | mutex_unlock(&dev_pm_qos_mtx); |
648 | |
649 | mutex_unlock(&dev_pm_qos_sysfs_mtx); |
650 | } |
651 | EXPORT_SYMBOL_GPL(dev_pm_qos_hide_latency_limit); |
652 | |
653 | /** |
654 | * dev_pm_qos_expose_flags - Expose PM QoS flags of a device to user space. |
655 | * @dev: Device whose PM QoS flags are to be exposed to user space. |
656 | * @val: Initial values of the flags. |
657 | */ |
658 | int dev_pm_qos_expose_flags(struct device *dev, s32 val) |
659 | { |
660 | struct dev_pm_qos_request *req; |
661 | int ret; |
662 | |
663 | if (!device_is_registered(dev)) |
664 | return -EINVAL; |
665 | |
666 | req = kzalloc(sizeof(*req), GFP_KERNEL); |
667 | if (!req) |
668 | return -ENOMEM; |
669 | |
670 | ret = dev_pm_qos_add_request(dev, req, DEV_PM_QOS_FLAGS, val); |
671 | if (ret < 0) { |
672 | kfree(req); |
673 | return ret; |
674 | } |
675 | |
676 | pm_runtime_get_sync(dev); |
677 | mutex_lock(&dev_pm_qos_sysfs_mtx); |
678 | |
679 | mutex_lock(&dev_pm_qos_mtx); |
680 | |
681 | if (IS_ERR_OR_NULL(dev->power.qos)) |
682 | ret = -ENODEV; |
683 | else if (dev->power.qos->flags_req) |
684 | ret = -EEXIST; |
685 | |
686 | if (ret < 0) { |
687 | __dev_pm_qos_remove_request(req); |
688 | kfree(req); |
689 | mutex_unlock(&dev_pm_qos_mtx); |
690 | goto out; |
691 | } |
692 | dev->power.qos->flags_req = req; |
693 | |
694 | mutex_unlock(&dev_pm_qos_mtx); |
695 | |
696 | ret = pm_qos_sysfs_add_flags(dev); |
697 | if (ret) |
698 | dev_pm_qos_drop_user_request(dev, DEV_PM_QOS_FLAGS); |
699 | |
700 | out: |
701 | mutex_unlock(&dev_pm_qos_sysfs_mtx); |
702 | pm_runtime_put(dev); |
703 | return ret; |
704 | } |
705 | EXPORT_SYMBOL_GPL(dev_pm_qos_expose_flags); |
706 | |
707 | static void __dev_pm_qos_hide_flags(struct device *dev) |
708 | { |
709 | if (!IS_ERR_OR_NULL(dev->power.qos) && dev->power.qos->flags_req) |
710 | __dev_pm_qos_drop_user_request(dev, DEV_PM_QOS_FLAGS); |
711 | } |
712 | |
713 | /** |
714 | * dev_pm_qos_hide_flags - Hide PM QoS flags of a device from user space. |
715 | * @dev: Device whose PM QoS flags are to be hidden from user space. |
716 | */ |
717 | void dev_pm_qos_hide_flags(struct device *dev) |
718 | { |
719 | pm_runtime_get_sync(dev); |
720 | mutex_lock(&dev_pm_qos_sysfs_mtx); |
721 | |
722 | pm_qos_sysfs_remove_flags(dev); |
723 | |
724 | mutex_lock(&dev_pm_qos_mtx); |
725 | __dev_pm_qos_hide_flags(dev); |
726 | mutex_unlock(&dev_pm_qos_mtx); |
727 | |
728 | mutex_unlock(&dev_pm_qos_sysfs_mtx); |
729 | pm_runtime_put(dev); |
730 | } |
731 | EXPORT_SYMBOL_GPL(dev_pm_qos_hide_flags); |
732 | |
733 | /** |
734 | * dev_pm_qos_update_flags - Update PM QoS flags request owned by user space. |
735 | * @dev: Device to update the PM QoS flags request for. |
736 | * @mask: Flags to set/clear. |
737 | * @set: Whether to set or clear the flags (true means set). |
738 | */ |
739 | int dev_pm_qos_update_flags(struct device *dev, s32 mask, bool set) |
740 | { |
741 | s32 value; |
742 | int ret; |
743 | |
744 | pm_runtime_get_sync(dev); |
745 | mutex_lock(&dev_pm_qos_mtx); |
746 | |
747 | if (IS_ERR_OR_NULL(dev->power.qos) || !dev->power.qos->flags_req) { |
748 | ret = -EINVAL; |
749 | goto out; |
750 | } |
751 | |
752 | value = dev_pm_qos_requested_flags(dev); |
753 | if (set) |
754 | value |= mask; |
755 | else |
756 | value &= ~mask; |
757 | |
758 | ret = __dev_pm_qos_update_request(dev->power.qos->flags_req, value); |
759 | |
760 | out: |
761 | mutex_unlock(&dev_pm_qos_mtx); |
762 | pm_runtime_put(dev); |
763 | return ret; |
764 | } |
765 | #else /* !CONFIG_PM_RUNTIME */ |
766 | static void __dev_pm_qos_hide_latency_limit(struct device *dev) {} |
767 | static void __dev_pm_qos_hide_flags(struct device *dev) {} |
768 | #endif /* CONFIG_PM_RUNTIME */ |
769 |
Branches:
ben-wpan
ben-wpan-stefan
javiroman/ks7010
jz-2.6.34
jz-2.6.34-rc5
jz-2.6.34-rc6
jz-2.6.34-rc7
jz-2.6.35
jz-2.6.36
jz-2.6.37
jz-2.6.38
jz-2.6.39
jz-3.0
jz-3.1
jz-3.11
jz-3.12
jz-3.13
jz-3.15
jz-3.16
jz-3.18-dt
jz-3.2
jz-3.3
jz-3.4
jz-3.5
jz-3.6
jz-3.6-rc2-pwm
jz-3.9
jz-3.9-clk
jz-3.9-rc8
jz47xx
jz47xx-2.6.38
master
Tags:
od-2011-09-04
od-2011-09-18
v2.6.34-rc5
v2.6.34-rc6
v2.6.34-rc7
v3.9