diff options
author | Viresh Kumar <viresh.kumar@linaro.org> | 2019-07-04 03:36:20 -0400 |
---|---|---|
committer | Rafael J. Wysocki <rafael.j.wysocki@intel.com> | 2019-07-04 04:40:54 -0400 |
commit | 208637b37824c8956fe28d277835a403ee35fa84 (patch) | |
tree | 645fc6e0353a09e80ff50bdda11541a9f0e2f33c | |
parent | 2a79ea5ec53973c8711b54d33ace5c77659dc8f8 (diff) |
PM / QoS: Add support for MIN/MAX frequency constraints
This patch introduces the min-frequency and max-frequency device
constraints, which will be used by the cpufreq core to begin with.
Reviewed-by: Matthias Kaehlcke <mka@chromium.org>
Reviewed-by: Ulf Hansson <ulf.hansson@linaro.org>
Signed-off-by: Viresh Kumar <viresh.kumar@linaro.org>
Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
-rw-r--r-- | drivers/base/power/qos.c | 111 | ||||
-rw-r--r-- | include/linux/pm_qos.h | 12 |
2 files changed, 109 insertions, 14 deletions
diff --git a/drivers/base/power/qos.c b/drivers/base/power/qos.c index 2461fed0efa0..6c90fd7e2ff8 100644 --- a/drivers/base/power/qos.c +++ b/drivers/base/power/qos.c | |||
@@ -120,6 +120,14 @@ s32 dev_pm_qos_read_value(struct device *dev, enum dev_pm_qos_req_type type) | |||
120 | ret = IS_ERR_OR_NULL(qos) ? PM_QOS_RESUME_LATENCY_NO_CONSTRAINT | 120 | ret = IS_ERR_OR_NULL(qos) ? PM_QOS_RESUME_LATENCY_NO_CONSTRAINT |
121 | : pm_qos_read_value(&qos->resume_latency); | 121 | : pm_qos_read_value(&qos->resume_latency); |
122 | break; | 122 | break; |
123 | case DEV_PM_QOS_MIN_FREQUENCY: | ||
124 | ret = IS_ERR_OR_NULL(qos) ? PM_QOS_MIN_FREQUENCY_DEFAULT_VALUE | ||
125 | : pm_qos_read_value(&qos->min_frequency); | ||
126 | break; | ||
127 | case DEV_PM_QOS_MAX_FREQUENCY: | ||
128 | ret = IS_ERR_OR_NULL(qos) ? PM_QOS_MAX_FREQUENCY_DEFAULT_VALUE | ||
129 | : pm_qos_read_value(&qos->max_frequency); | ||
130 | break; | ||
123 | default: | 131 | default: |
124 | WARN_ON(1); | 132 | WARN_ON(1); |
125 | ret = 0; | 133 | ret = 0; |
@@ -161,6 +169,14 @@ static int apply_constraint(struct dev_pm_qos_request *req, | |||
161 | req->dev->power.set_latency_tolerance(req->dev, value); | 169 | req->dev->power.set_latency_tolerance(req->dev, value); |
162 | } | 170 | } |
163 | break; | 171 | break; |
172 | case DEV_PM_QOS_MIN_FREQUENCY: | ||
173 | ret = pm_qos_update_target(&qos->min_frequency, | ||
174 | &req->data.pnode, action, value); | ||
175 | break; | ||
176 | case DEV_PM_QOS_MAX_FREQUENCY: | ||
177 | ret = pm_qos_update_target(&qos->max_frequency, | ||
178 | &req->data.pnode, action, value); | ||
179 | break; | ||
164 | case DEV_PM_QOS_FLAGS: | 180 | case DEV_PM_QOS_FLAGS: |
165 | ret = pm_qos_update_flags(&qos->flags, &req->data.flr, | 181 | ret = pm_qos_update_flags(&qos->flags, &req->data.flr, |
166 | action, value); | 182 | action, value); |
@@ -189,12 +205,11 @@ static int dev_pm_qos_constraints_allocate(struct device *dev) | |||
189 | if (!qos) | 205 | if (!qos) |
190 | return -ENOMEM; | 206 | return -ENOMEM; |
191 | 207 | ||
192 | n = kzalloc(sizeof(*n), GFP_KERNEL); | 208 | n = kzalloc(3 * sizeof(*n), GFP_KERNEL); |
193 | if (!n) { | 209 | if (!n) { |
194 | kfree(qos); | 210 | kfree(qos); |
195 | return -ENOMEM; | 211 | return -ENOMEM; |
196 | } | 212 | } |
197 | BLOCKING_INIT_NOTIFIER_HEAD(n); | ||
198 | 213 | ||
199 | c = &qos->resume_latency; | 214 | c = &qos->resume_latency; |
200 | plist_head_init(&c->list); | 215 | plist_head_init(&c->list); |
@@ -203,6 +218,7 @@ static int dev_pm_qos_constraints_allocate(struct device *dev) | |||
203 | c->no_constraint_value = PM_QOS_RESUME_LATENCY_NO_CONSTRAINT; | 218 | c->no_constraint_value = PM_QOS_RESUME_LATENCY_NO_CONSTRAINT; |
204 | c->type = PM_QOS_MIN; | 219 | c->type = PM_QOS_MIN; |
205 | c->notifiers = n; | 220 | c->notifiers = n; |
221 | BLOCKING_INIT_NOTIFIER_HEAD(n); | ||
206 | 222 | ||
207 | c = &qos->latency_tolerance; | 223 | c = &qos->latency_tolerance; |
208 | plist_head_init(&c->list); | 224 | plist_head_init(&c->list); |
@@ -211,6 +227,24 @@ static int dev_pm_qos_constraints_allocate(struct device *dev) | |||
211 | c->no_constraint_value = PM_QOS_LATENCY_TOLERANCE_NO_CONSTRAINT; | 227 | c->no_constraint_value = PM_QOS_LATENCY_TOLERANCE_NO_CONSTRAINT; |
212 | c->type = PM_QOS_MIN; | 228 | c->type = PM_QOS_MIN; |
213 | 229 | ||
230 | c = &qos->min_frequency; | ||
231 | plist_head_init(&c->list); | ||
232 | c->target_value = PM_QOS_MIN_FREQUENCY_DEFAULT_VALUE; | ||
233 | c->default_value = PM_QOS_MIN_FREQUENCY_DEFAULT_VALUE; | ||
234 | c->no_constraint_value = PM_QOS_MIN_FREQUENCY_DEFAULT_VALUE; | ||
235 | c->type = PM_QOS_MAX; | ||
236 | c->notifiers = ++n; | ||
237 | BLOCKING_INIT_NOTIFIER_HEAD(n); | ||
238 | |||
239 | c = &qos->max_frequency; | ||
240 | plist_head_init(&c->list); | ||
241 | c->target_value = PM_QOS_MAX_FREQUENCY_DEFAULT_VALUE; | ||
242 | c->default_value = PM_QOS_MAX_FREQUENCY_DEFAULT_VALUE; | ||
243 | c->no_constraint_value = PM_QOS_MAX_FREQUENCY_DEFAULT_VALUE; | ||
244 | c->type = PM_QOS_MIN; | ||
245 | c->notifiers = ++n; | ||
246 | BLOCKING_INIT_NOTIFIER_HEAD(n); | ||
247 | |||
214 | INIT_LIST_HEAD(&qos->flags.list); | 248 | INIT_LIST_HEAD(&qos->flags.list); |
215 | 249 | ||
216 | spin_lock_irq(&dev->power.lock); | 250 | spin_lock_irq(&dev->power.lock); |
@@ -264,11 +298,25 @@ void dev_pm_qos_constraints_destroy(struct device *dev) | |||
264 | apply_constraint(req, PM_QOS_REMOVE_REQ, PM_QOS_DEFAULT_VALUE); | 298 | apply_constraint(req, PM_QOS_REMOVE_REQ, PM_QOS_DEFAULT_VALUE); |
265 | memset(req, 0, sizeof(*req)); | 299 | memset(req, 0, sizeof(*req)); |
266 | } | 300 | } |
301 | |||
267 | c = &qos->latency_tolerance; | 302 | c = &qos->latency_tolerance; |
268 | plist_for_each_entry_safe(req, tmp, &c->list, data.pnode) { | 303 | plist_for_each_entry_safe(req, tmp, &c->list, data.pnode) { |
269 | apply_constraint(req, PM_QOS_REMOVE_REQ, PM_QOS_DEFAULT_VALUE); | 304 | apply_constraint(req, PM_QOS_REMOVE_REQ, PM_QOS_DEFAULT_VALUE); |
270 | memset(req, 0, sizeof(*req)); | 305 | memset(req, 0, sizeof(*req)); |
271 | } | 306 | } |
307 | |||
308 | c = &qos->min_frequency; | ||
309 | plist_for_each_entry_safe(req, tmp, &c->list, data.pnode) { | ||
310 | apply_constraint(req, PM_QOS_REMOVE_REQ, PM_QOS_MIN_FREQUENCY_DEFAULT_VALUE); | ||
311 | memset(req, 0, sizeof(*req)); | ||
312 | } | ||
313 | |||
314 | c = &qos->max_frequency; | ||
315 | plist_for_each_entry_safe(req, tmp, &c->list, data.pnode) { | ||
316 | apply_constraint(req, PM_QOS_REMOVE_REQ, PM_QOS_MAX_FREQUENCY_DEFAULT_VALUE); | ||
317 | memset(req, 0, sizeof(*req)); | ||
318 | } | ||
319 | |||
272 | f = &qos->flags; | 320 | f = &qos->flags; |
273 | list_for_each_entry_safe(req, tmp, &f->list, data.flr.node) { | 321 | list_for_each_entry_safe(req, tmp, &f->list, data.flr.node) { |
274 | apply_constraint(req, PM_QOS_REMOVE_REQ, PM_QOS_DEFAULT_VALUE); | 322 | apply_constraint(req, PM_QOS_REMOVE_REQ, PM_QOS_DEFAULT_VALUE); |
@@ -380,6 +428,8 @@ static int __dev_pm_qos_update_request(struct dev_pm_qos_request *req, | |||
380 | switch(req->type) { | 428 | switch(req->type) { |
381 | case DEV_PM_QOS_RESUME_LATENCY: | 429 | case DEV_PM_QOS_RESUME_LATENCY: |
382 | case DEV_PM_QOS_LATENCY_TOLERANCE: | 430 | case DEV_PM_QOS_LATENCY_TOLERANCE: |
431 | case DEV_PM_QOS_MIN_FREQUENCY: | ||
432 | case DEV_PM_QOS_MAX_FREQUENCY: | ||
383 | curr_value = req->data.pnode.prio; | 433 | curr_value = req->data.pnode.prio; |
384 | break; | 434 | break; |
385 | case DEV_PM_QOS_FLAGS: | 435 | case DEV_PM_QOS_FLAGS: |
@@ -492,9 +542,6 @@ int dev_pm_qos_add_notifier(struct device *dev, struct notifier_block *notifier, | |||
492 | { | 542 | { |
493 | int ret = 0; | 543 | int ret = 0; |
494 | 544 | ||
495 | if (WARN_ON(type != DEV_PM_QOS_RESUME_LATENCY)) | ||
496 | return -EINVAL; | ||
497 | |||
498 | mutex_lock(&dev_pm_qos_mtx); | 545 | mutex_lock(&dev_pm_qos_mtx); |
499 | 546 | ||
500 | if (IS_ERR(dev->power.qos)) | 547 | if (IS_ERR(dev->power.qos)) |
@@ -502,10 +549,28 @@ int dev_pm_qos_add_notifier(struct device *dev, struct notifier_block *notifier, | |||
502 | else if (!dev->power.qos) | 549 | else if (!dev->power.qos) |
503 | ret = dev_pm_qos_constraints_allocate(dev); | 550 | ret = dev_pm_qos_constraints_allocate(dev); |
504 | 551 | ||
505 | if (!ret) | 552 | if (ret) |
553 | goto unlock; | ||
554 | |||
555 | switch (type) { | ||
556 | case DEV_PM_QOS_RESUME_LATENCY: | ||
506 | ret = blocking_notifier_chain_register(dev->power.qos->resume_latency.notifiers, | 557 | ret = blocking_notifier_chain_register(dev->power.qos->resume_latency.notifiers, |
507 | notifier); | 558 | notifier); |
559 | break; | ||
560 | case DEV_PM_QOS_MIN_FREQUENCY: | ||
561 | ret = blocking_notifier_chain_register(dev->power.qos->min_frequency.notifiers, | ||
562 | notifier); | ||
563 | break; | ||
564 | case DEV_PM_QOS_MAX_FREQUENCY: | ||
565 | ret = blocking_notifier_chain_register(dev->power.qos->max_frequency.notifiers, | ||
566 | notifier); | ||
567 | break; | ||
568 | default: | ||
569 | WARN_ON(1); | ||
570 | ret = -EINVAL; | ||
571 | } | ||
508 | 572 | ||
573 | unlock: | ||
509 | mutex_unlock(&dev_pm_qos_mtx); | 574 | mutex_unlock(&dev_pm_qos_mtx); |
510 | return ret; | 575 | return ret; |
511 | } | 576 | } |
@@ -526,20 +591,35 @@ int dev_pm_qos_remove_notifier(struct device *dev, | |||
526 | struct notifier_block *notifier, | 591 | struct notifier_block *notifier, |
527 | enum dev_pm_qos_req_type type) | 592 | enum dev_pm_qos_req_type type) |
528 | { | 593 | { |
529 | int retval = 0; | 594 | int ret = 0; |
530 | |||
531 | if (WARN_ON(type != DEV_PM_QOS_RESUME_LATENCY)) | ||
532 | return -EINVAL; | ||
533 | 595 | ||
534 | mutex_lock(&dev_pm_qos_mtx); | 596 | mutex_lock(&dev_pm_qos_mtx); |
535 | 597 | ||
536 | /* Silently return if the constraints object is not present. */ | 598 | /* Silently return if the constraints object is not present. */ |
537 | if (!IS_ERR_OR_NULL(dev->power.qos)) | 599 | if (IS_ERR_OR_NULL(dev->power.qos)) |
538 | retval = blocking_notifier_chain_unregister(dev->power.qos->resume_latency.notifiers, | 600 | goto unlock; |
539 | notifier); | 601 | |
602 | switch (type) { | ||
603 | case DEV_PM_QOS_RESUME_LATENCY: | ||
604 | ret = blocking_notifier_chain_unregister(dev->power.qos->resume_latency.notifiers, | ||
605 | notifier); | ||
606 | break; | ||
607 | case DEV_PM_QOS_MIN_FREQUENCY: | ||
608 | ret = blocking_notifier_chain_unregister(dev->power.qos->min_frequency.notifiers, | ||
609 | notifier); | ||
610 | break; | ||
611 | case DEV_PM_QOS_MAX_FREQUENCY: | ||
612 | ret = blocking_notifier_chain_unregister(dev->power.qos->max_frequency.notifiers, | ||
613 | notifier); | ||
614 | break; | ||
615 | default: | ||
616 | WARN_ON(1); | ||
617 | ret = -EINVAL; | ||
618 | } | ||
540 | 619 | ||
620 | unlock: | ||
541 | mutex_unlock(&dev_pm_qos_mtx); | 621 | mutex_unlock(&dev_pm_qos_mtx); |
542 | return retval; | 622 | return ret; |
543 | } | 623 | } |
544 | EXPORT_SYMBOL_GPL(dev_pm_qos_remove_notifier); | 624 | EXPORT_SYMBOL_GPL(dev_pm_qos_remove_notifier); |
545 | 625 | ||
@@ -599,6 +679,9 @@ static void __dev_pm_qos_drop_user_request(struct device *dev, | |||
599 | req = dev->power.qos->flags_req; | 679 | req = dev->power.qos->flags_req; |
600 | dev->power.qos->flags_req = NULL; | 680 | dev->power.qos->flags_req = NULL; |
601 | break; | 681 | break; |
682 | default: | ||
683 | WARN_ON(1); | ||
684 | return; | ||
602 | } | 685 | } |
603 | __dev_pm_qos_remove_request(req); | 686 | __dev_pm_qos_remove_request(req); |
604 | kfree(req); | 687 | kfree(req); |
diff --git a/include/linux/pm_qos.h b/include/linux/pm_qos.h index 9a21b7ba72ae..2aebbc5b9950 100644 --- a/include/linux/pm_qos.h +++ b/include/linux/pm_qos.h | |||
@@ -40,6 +40,8 @@ enum pm_qos_flags_status { | |||
40 | #define PM_QOS_RESUME_LATENCY_NO_CONSTRAINT PM_QOS_LATENCY_ANY | 40 | #define PM_QOS_RESUME_LATENCY_NO_CONSTRAINT PM_QOS_LATENCY_ANY |
41 | #define PM_QOS_RESUME_LATENCY_NO_CONSTRAINT_NS PM_QOS_LATENCY_ANY_NS | 41 | #define PM_QOS_RESUME_LATENCY_NO_CONSTRAINT_NS PM_QOS_LATENCY_ANY_NS |
42 | #define PM_QOS_LATENCY_TOLERANCE_DEFAULT_VALUE 0 | 42 | #define PM_QOS_LATENCY_TOLERANCE_DEFAULT_VALUE 0 |
43 | #define PM_QOS_MIN_FREQUENCY_DEFAULT_VALUE 0 | ||
44 | #define PM_QOS_MAX_FREQUENCY_DEFAULT_VALUE (-1) | ||
43 | #define PM_QOS_LATENCY_TOLERANCE_NO_CONSTRAINT (-1) | 45 | #define PM_QOS_LATENCY_TOLERANCE_NO_CONSTRAINT (-1) |
44 | 46 | ||
45 | #define PM_QOS_FLAG_NO_POWER_OFF (1 << 0) | 47 | #define PM_QOS_FLAG_NO_POWER_OFF (1 << 0) |
@@ -58,6 +60,8 @@ struct pm_qos_flags_request { | |||
58 | enum dev_pm_qos_req_type { | 60 | enum dev_pm_qos_req_type { |
59 | DEV_PM_QOS_RESUME_LATENCY = 1, | 61 | DEV_PM_QOS_RESUME_LATENCY = 1, |
60 | DEV_PM_QOS_LATENCY_TOLERANCE, | 62 | DEV_PM_QOS_LATENCY_TOLERANCE, |
63 | DEV_PM_QOS_MIN_FREQUENCY, | ||
64 | DEV_PM_QOS_MAX_FREQUENCY, | ||
61 | DEV_PM_QOS_FLAGS, | 65 | DEV_PM_QOS_FLAGS, |
62 | }; | 66 | }; |
63 | 67 | ||
@@ -99,10 +103,14 @@ struct pm_qos_flags { | |||
99 | struct dev_pm_qos { | 103 | struct dev_pm_qos { |
100 | struct pm_qos_constraints resume_latency; | 104 | struct pm_qos_constraints resume_latency; |
101 | struct pm_qos_constraints latency_tolerance; | 105 | struct pm_qos_constraints latency_tolerance; |
106 | struct pm_qos_constraints min_frequency; | ||
107 | struct pm_qos_constraints max_frequency; | ||
102 | struct pm_qos_flags flags; | 108 | struct pm_qos_flags flags; |
103 | struct dev_pm_qos_request *resume_latency_req; | 109 | struct dev_pm_qos_request *resume_latency_req; |
104 | struct dev_pm_qos_request *latency_tolerance_req; | 110 | struct dev_pm_qos_request *latency_tolerance_req; |
105 | struct dev_pm_qos_request *flags_req; | 111 | struct dev_pm_qos_request *flags_req; |
112 | struct dev_pm_qos_request *min_frequency_req; | ||
113 | struct dev_pm_qos_request *max_frequency_req; | ||
106 | }; | 114 | }; |
107 | 115 | ||
108 | /* Action requested to pm_qos_update_target */ | 116 | /* Action requested to pm_qos_update_target */ |
@@ -197,6 +205,10 @@ static inline s32 dev_pm_qos_read_value(struct device *dev, | |||
197 | switch (type) { | 205 | switch (type) { |
198 | case DEV_PM_QOS_RESUME_LATENCY: | 206 | case DEV_PM_QOS_RESUME_LATENCY: |
199 | return PM_QOS_RESUME_LATENCY_NO_CONSTRAINT; | 207 | return PM_QOS_RESUME_LATENCY_NO_CONSTRAINT; |
208 | case DEV_PM_QOS_MIN_FREQUENCY: | ||
209 | return PM_QOS_MIN_FREQUENCY_DEFAULT_VALUE; | ||
210 | case DEV_PM_QOS_MAX_FREQUENCY: | ||
211 | return PM_QOS_MAX_FREQUENCY_DEFAULT_VALUE; | ||
200 | default: | 212 | default: |
201 | WARN_ON(1); | 213 | WARN_ON(1); |
202 | return 0; | 214 | return 0; |