.. | .. |
---|
| 1 | +// SPDX-License-Identifier: GPL-2.0 |
---|
1 | 2 | /* |
---|
2 | 3 | * Devices PM QoS constraints management |
---|
3 | 4 | * |
---|
4 | 5 | * 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 | 6 | * |
---|
11 | 7 | * This module exposes the interface to kernel space for specifying |
---|
12 | 8 | * per-device PM QoS dependencies. It provides infrastructure for registration |
---|
.. | .. |
---|
22 | 18 | * per-device constraint data struct. |
---|
23 | 19 | * |
---|
24 | 20 | * Note about the per-device constraint data struct allocation: |
---|
25 | | - * . The per-device constraints data struct ptr is tored into the device |
---|
| 21 | + * . The per-device constraints data struct ptr is stored into the device |
---|
26 | 22 | * dev_pm_info. |
---|
27 | 23 | * . To minimize the data usage by the per-device constraints, the data struct |
---|
28 | 24 | * is only allocated at the first call to dev_pm_qos_add_request. |
---|
.. | .. |
---|
94 | 90 | EXPORT_SYMBOL_GPL(dev_pm_qos_flags); |
---|
95 | 91 | |
---|
96 | 92 | /** |
---|
97 | | - * __dev_pm_qos_read_value - Get PM QoS constraint for a given device. |
---|
| 93 | + * __dev_pm_qos_resume_latency - Get resume latency constraint for a given device. |
---|
98 | 94 | * @dev: Device to get the PM QoS constraint value for. |
---|
99 | 95 | * |
---|
100 | 96 | * This routine must be called with dev->power.lock held. |
---|
101 | 97 | */ |
---|
102 | | -s32 __dev_pm_qos_read_value(struct device *dev) |
---|
| 98 | +s32 __dev_pm_qos_resume_latency(struct device *dev) |
---|
103 | 99 | { |
---|
104 | 100 | lockdep_assert_held(&dev->power.lock); |
---|
105 | 101 | |
---|
106 | | - return dev_pm_qos_raw_read_value(dev); |
---|
| 102 | + return dev_pm_qos_raw_resume_latency(dev); |
---|
107 | 103 | } |
---|
108 | 104 | |
---|
109 | 105 | /** |
---|
110 | 106 | * dev_pm_qos_read_value - Get PM QoS constraint for a given device (locked). |
---|
111 | 107 | * @dev: Device to get the PM QoS constraint value for. |
---|
| 108 | + * @type: QoS request type. |
---|
112 | 109 | */ |
---|
113 | | -s32 dev_pm_qos_read_value(struct device *dev) |
---|
| 110 | +s32 dev_pm_qos_read_value(struct device *dev, enum dev_pm_qos_req_type type) |
---|
114 | 111 | { |
---|
| 112 | + struct dev_pm_qos *qos = dev->power.qos; |
---|
115 | 113 | unsigned long flags; |
---|
116 | 114 | s32 ret; |
---|
117 | 115 | |
---|
118 | 116 | spin_lock_irqsave(&dev->power.lock, flags); |
---|
119 | | - ret = __dev_pm_qos_read_value(dev); |
---|
| 117 | + |
---|
| 118 | + switch (type) { |
---|
| 119 | + case DEV_PM_QOS_RESUME_LATENCY: |
---|
| 120 | + ret = IS_ERR_OR_NULL(qos) ? PM_QOS_RESUME_LATENCY_NO_CONSTRAINT |
---|
| 121 | + : pm_qos_read_value(&qos->resume_latency); |
---|
| 122 | + break; |
---|
| 123 | + case DEV_PM_QOS_MIN_FREQUENCY: |
---|
| 124 | + ret = IS_ERR_OR_NULL(qos) ? PM_QOS_MIN_FREQUENCY_DEFAULT_VALUE |
---|
| 125 | + : freq_qos_read_value(&qos->freq, FREQ_QOS_MIN); |
---|
| 126 | + break; |
---|
| 127 | + case DEV_PM_QOS_MAX_FREQUENCY: |
---|
| 128 | + ret = IS_ERR_OR_NULL(qos) ? PM_QOS_MAX_FREQUENCY_DEFAULT_VALUE |
---|
| 129 | + : freq_qos_read_value(&qos->freq, FREQ_QOS_MAX); |
---|
| 130 | + break; |
---|
| 131 | + default: |
---|
| 132 | + WARN_ON(1); |
---|
| 133 | + ret = 0; |
---|
| 134 | + } |
---|
| 135 | + |
---|
120 | 136 | spin_unlock_irqrestore(&dev->power.lock, flags); |
---|
121 | 137 | |
---|
122 | 138 | return ret; |
---|
123 | 139 | } |
---|
| 140 | +EXPORT_SYMBOL_GPL(dev_pm_qos_read_value); |
---|
124 | 141 | |
---|
125 | 142 | /** |
---|
126 | 143 | * apply_constraint - Add/modify/remove device PM QoS request. |
---|
.. | .. |
---|
143 | 160 | value = 0; |
---|
144 | 161 | |
---|
145 | 162 | ret = pm_qos_update_target(&qos->resume_latency, |
---|
146 | | - &req->data.pnode, action, value, |
---|
147 | | - true); |
---|
| 163 | + &req->data.pnode, action, value); |
---|
148 | 164 | break; |
---|
149 | 165 | case DEV_PM_QOS_LATENCY_TOLERANCE: |
---|
150 | 166 | ret = pm_qos_update_target(&qos->latency_tolerance, |
---|
151 | | - &req->data.pnode, action, value, |
---|
152 | | - true); |
---|
| 167 | + &req->data.pnode, action, value); |
---|
153 | 168 | if (ret) { |
---|
154 | 169 | value = pm_qos_read_value(&qos->latency_tolerance); |
---|
155 | 170 | req->dev->power.set_latency_tolerance(req->dev, value); |
---|
156 | 171 | } |
---|
| 172 | + break; |
---|
| 173 | + case DEV_PM_QOS_MIN_FREQUENCY: |
---|
| 174 | + case DEV_PM_QOS_MAX_FREQUENCY: |
---|
| 175 | + ret = freq_qos_apply(&req->data.freq, action, value); |
---|
157 | 176 | break; |
---|
158 | 177 | case DEV_PM_QOS_FLAGS: |
---|
159 | 178 | ret = pm_qos_update_flags(&qos->flags, &req->data.flr, |
---|
.. | .. |
---|
183 | 202 | if (!qos) |
---|
184 | 203 | return -ENOMEM; |
---|
185 | 204 | |
---|
186 | | - n = kzalloc(sizeof(*n), GFP_KERNEL); |
---|
| 205 | + n = kzalloc(3 * sizeof(*n), GFP_KERNEL); |
---|
187 | 206 | if (!n) { |
---|
188 | 207 | kfree(qos); |
---|
189 | 208 | return -ENOMEM; |
---|
190 | 209 | } |
---|
191 | | - BLOCKING_INIT_NOTIFIER_HEAD(n); |
---|
192 | 210 | |
---|
193 | 211 | c = &qos->resume_latency; |
---|
194 | 212 | plist_head_init(&c->list); |
---|
.. | .. |
---|
197 | 215 | c->no_constraint_value = PM_QOS_RESUME_LATENCY_NO_CONSTRAINT; |
---|
198 | 216 | c->type = PM_QOS_MIN; |
---|
199 | 217 | c->notifiers = n; |
---|
| 218 | + BLOCKING_INIT_NOTIFIER_HEAD(n); |
---|
200 | 219 | |
---|
201 | 220 | c = &qos->latency_tolerance; |
---|
202 | 221 | plist_head_init(&c->list); |
---|
.. | .. |
---|
204 | 223 | c->default_value = PM_QOS_LATENCY_TOLERANCE_DEFAULT_VALUE; |
---|
205 | 224 | c->no_constraint_value = PM_QOS_LATENCY_TOLERANCE_NO_CONSTRAINT; |
---|
206 | 225 | c->type = PM_QOS_MIN; |
---|
| 226 | + |
---|
| 227 | + freq_constraints_init(&qos->freq); |
---|
207 | 228 | |
---|
208 | 229 | INIT_LIST_HEAD(&qos->flags.list); |
---|
209 | 230 | |
---|
.. | .. |
---|
258 | 279 | apply_constraint(req, PM_QOS_REMOVE_REQ, PM_QOS_DEFAULT_VALUE); |
---|
259 | 280 | memset(req, 0, sizeof(*req)); |
---|
260 | 281 | } |
---|
| 282 | + |
---|
261 | 283 | c = &qos->latency_tolerance; |
---|
262 | 284 | plist_for_each_entry_safe(req, tmp, &c->list, data.pnode) { |
---|
263 | 285 | apply_constraint(req, PM_QOS_REMOVE_REQ, PM_QOS_DEFAULT_VALUE); |
---|
264 | 286 | memset(req, 0, sizeof(*req)); |
---|
265 | 287 | } |
---|
| 288 | + |
---|
| 289 | + c = &qos->freq.min_freq; |
---|
| 290 | + plist_for_each_entry_safe(req, tmp, &c->list, data.freq.pnode) { |
---|
| 291 | + apply_constraint(req, PM_QOS_REMOVE_REQ, |
---|
| 292 | + PM_QOS_MIN_FREQUENCY_DEFAULT_VALUE); |
---|
| 293 | + memset(req, 0, sizeof(*req)); |
---|
| 294 | + } |
---|
| 295 | + |
---|
| 296 | + c = &qos->freq.max_freq; |
---|
| 297 | + plist_for_each_entry_safe(req, tmp, &c->list, data.freq.pnode) { |
---|
| 298 | + apply_constraint(req, PM_QOS_REMOVE_REQ, |
---|
| 299 | + PM_QOS_MAX_FREQUENCY_DEFAULT_VALUE); |
---|
| 300 | + memset(req, 0, sizeof(*req)); |
---|
| 301 | + } |
---|
| 302 | + |
---|
266 | 303 | f = &qos->flags; |
---|
267 | 304 | list_for_each_entry_safe(req, tmp, &f->list, data.flr.node) { |
---|
268 | 305 | apply_constraint(req, PM_QOS_REMOVE_REQ, PM_QOS_DEFAULT_VALUE); |
---|
.. | .. |
---|
308 | 345 | ret = dev_pm_qos_constraints_allocate(dev); |
---|
309 | 346 | |
---|
310 | 347 | trace_dev_pm_qos_add_request(dev_name(dev), type, value); |
---|
311 | | - if (!ret) { |
---|
312 | | - req->dev = dev; |
---|
313 | | - req->type = type; |
---|
| 348 | + if (ret) |
---|
| 349 | + return ret; |
---|
| 350 | + |
---|
| 351 | + req->dev = dev; |
---|
| 352 | + req->type = type; |
---|
| 353 | + if (req->type == DEV_PM_QOS_MIN_FREQUENCY) |
---|
| 354 | + ret = freq_qos_add_request(&dev->power.qos->freq, |
---|
| 355 | + &req->data.freq, |
---|
| 356 | + FREQ_QOS_MIN, value); |
---|
| 357 | + else if (req->type == DEV_PM_QOS_MAX_FREQUENCY) |
---|
| 358 | + ret = freq_qos_add_request(&dev->power.qos->freq, |
---|
| 359 | + &req->data.freq, |
---|
| 360 | + FREQ_QOS_MAX, value); |
---|
| 361 | + else |
---|
314 | 362 | ret = apply_constraint(req, PM_QOS_ADD_REQ, value); |
---|
315 | | - } |
---|
| 363 | + |
---|
316 | 364 | return ret; |
---|
317 | 365 | } |
---|
318 | 366 | |
---|
.. | .. |
---|
375 | 423 | case DEV_PM_QOS_RESUME_LATENCY: |
---|
376 | 424 | case DEV_PM_QOS_LATENCY_TOLERANCE: |
---|
377 | 425 | curr_value = req->data.pnode.prio; |
---|
| 426 | + break; |
---|
| 427 | + case DEV_PM_QOS_MIN_FREQUENCY: |
---|
| 428 | + case DEV_PM_QOS_MAX_FREQUENCY: |
---|
| 429 | + curr_value = req->data.freq.pnode.prio; |
---|
378 | 430 | break; |
---|
379 | 431 | case DEV_PM_QOS_FLAGS: |
---|
380 | 432 | curr_value = req->data.flr.flags; |
---|
.. | .. |
---|
473 | 525 | * |
---|
474 | 526 | * @dev: target device for the constraint |
---|
475 | 527 | * @notifier: notifier block managed by caller. |
---|
| 528 | + * @type: request type. |
---|
476 | 529 | * |
---|
477 | 530 | * Will register the notifier into a notification chain that gets called |
---|
478 | 531 | * upon changes to the target value for the device. |
---|
.. | .. |
---|
480 | 533 | * If the device's constraints object doesn't exist when this routine is called, |
---|
481 | 534 | * it will be created (or error code will be returned if that fails). |
---|
482 | 535 | */ |
---|
483 | | -int dev_pm_qos_add_notifier(struct device *dev, struct notifier_block *notifier) |
---|
| 536 | +int dev_pm_qos_add_notifier(struct device *dev, struct notifier_block *notifier, |
---|
| 537 | + enum dev_pm_qos_req_type type) |
---|
484 | 538 | { |
---|
485 | 539 | int ret = 0; |
---|
486 | 540 | |
---|
.. | .. |
---|
491 | 545 | else if (!dev->power.qos) |
---|
492 | 546 | ret = dev_pm_qos_constraints_allocate(dev); |
---|
493 | 547 | |
---|
494 | | - if (!ret) |
---|
| 548 | + if (ret) |
---|
| 549 | + goto unlock; |
---|
| 550 | + |
---|
| 551 | + switch (type) { |
---|
| 552 | + case DEV_PM_QOS_RESUME_LATENCY: |
---|
495 | 553 | ret = blocking_notifier_chain_register(dev->power.qos->resume_latency.notifiers, |
---|
496 | 554 | notifier); |
---|
| 555 | + break; |
---|
| 556 | + case DEV_PM_QOS_MIN_FREQUENCY: |
---|
| 557 | + ret = freq_qos_add_notifier(&dev->power.qos->freq, |
---|
| 558 | + FREQ_QOS_MIN, notifier); |
---|
| 559 | + break; |
---|
| 560 | + case DEV_PM_QOS_MAX_FREQUENCY: |
---|
| 561 | + ret = freq_qos_add_notifier(&dev->power.qos->freq, |
---|
| 562 | + FREQ_QOS_MAX, notifier); |
---|
| 563 | + break; |
---|
| 564 | + default: |
---|
| 565 | + WARN_ON(1); |
---|
| 566 | + ret = -EINVAL; |
---|
| 567 | + } |
---|
497 | 568 | |
---|
| 569 | +unlock: |
---|
498 | 570 | mutex_unlock(&dev_pm_qos_mtx); |
---|
499 | 571 | return ret; |
---|
500 | 572 | } |
---|
.. | .. |
---|
506 | 578 | * |
---|
507 | 579 | * @dev: target device for the constraint |
---|
508 | 580 | * @notifier: notifier block to be removed. |
---|
| 581 | + * @type: request type. |
---|
509 | 582 | * |
---|
510 | 583 | * Will remove the notifier from the notification chain that gets called |
---|
511 | 584 | * upon changes to the target value. |
---|
512 | 585 | */ |
---|
513 | 586 | int dev_pm_qos_remove_notifier(struct device *dev, |
---|
514 | | - struct notifier_block *notifier) |
---|
| 587 | + struct notifier_block *notifier, |
---|
| 588 | + enum dev_pm_qos_req_type type) |
---|
515 | 589 | { |
---|
516 | | - int retval = 0; |
---|
| 590 | + int ret = 0; |
---|
517 | 591 | |
---|
518 | 592 | mutex_lock(&dev_pm_qos_mtx); |
---|
519 | 593 | |
---|
520 | 594 | /* Silently return if the constraints object is not present. */ |
---|
521 | | - if (!IS_ERR_OR_NULL(dev->power.qos)) |
---|
522 | | - retval = blocking_notifier_chain_unregister(dev->power.qos->resume_latency.notifiers, |
---|
523 | | - notifier); |
---|
| 595 | + if (IS_ERR_OR_NULL(dev->power.qos)) |
---|
| 596 | + goto unlock; |
---|
524 | 597 | |
---|
| 598 | + switch (type) { |
---|
| 599 | + case DEV_PM_QOS_RESUME_LATENCY: |
---|
| 600 | + ret = blocking_notifier_chain_unregister(dev->power.qos->resume_latency.notifiers, |
---|
| 601 | + notifier); |
---|
| 602 | + break; |
---|
| 603 | + case DEV_PM_QOS_MIN_FREQUENCY: |
---|
| 604 | + ret = freq_qos_remove_notifier(&dev->power.qos->freq, |
---|
| 605 | + FREQ_QOS_MIN, notifier); |
---|
| 606 | + break; |
---|
| 607 | + case DEV_PM_QOS_MAX_FREQUENCY: |
---|
| 608 | + ret = freq_qos_remove_notifier(&dev->power.qos->freq, |
---|
| 609 | + FREQ_QOS_MAX, notifier); |
---|
| 610 | + break; |
---|
| 611 | + default: |
---|
| 612 | + WARN_ON(1); |
---|
| 613 | + ret = -EINVAL; |
---|
| 614 | + } |
---|
| 615 | + |
---|
| 616 | +unlock: |
---|
525 | 617 | mutex_unlock(&dev_pm_qos_mtx); |
---|
526 | | - return retval; |
---|
| 618 | + return ret; |
---|
527 | 619 | } |
---|
528 | 620 | EXPORT_SYMBOL_GPL(dev_pm_qos_remove_notifier); |
---|
529 | 621 | |
---|
.. | .. |
---|
583 | 675 | req = dev->power.qos->flags_req; |
---|
584 | 676 | dev->power.qos->flags_req = NULL; |
---|
585 | 677 | break; |
---|
| 678 | + default: |
---|
| 679 | + WARN_ON(1); |
---|
| 680 | + return; |
---|
586 | 681 | } |
---|
587 | 682 | __dev_pm_qos_remove_request(req); |
---|
588 | 683 | kfree(req); |
---|