diff options
-rw-r--r-- | drivers/base/power/main.c | 2 | ||||
-rw-r--r-- | drivers/base/power/power.h | 8 | ||||
-rw-r--r-- | drivers/base/power/qos.c | 120 | ||||
-rw-r--r-- | drivers/base/power/sysfs.c | 1 |
4 files changed, 55 insertions, 76 deletions
diff --git a/drivers/base/power/main.c b/drivers/base/power/main.c index 2b7f77d3fcb0..15beb500a4e4 100644 --- a/drivers/base/power/main.c +++ b/drivers/base/power/main.c | |||
@@ -99,7 +99,6 @@ void device_pm_add(struct device *dev) | |||
99 | dev_warn(dev, "parent %s should not be sleeping\n", | 99 | dev_warn(dev, "parent %s should not be sleeping\n", |
100 | dev_name(dev->parent)); | 100 | dev_name(dev->parent)); |
101 | list_add_tail(&dev->power.entry, &dpm_list); | 101 | list_add_tail(&dev->power.entry, &dpm_list); |
102 | dev_pm_qos_constraints_init(dev); | ||
103 | mutex_unlock(&dpm_list_mtx); | 102 | mutex_unlock(&dpm_list_mtx); |
104 | } | 103 | } |
105 | 104 | ||
@@ -113,7 +112,6 @@ void device_pm_remove(struct device *dev) | |||
113 | dev->bus ? dev->bus->name : "No Bus", dev_name(dev)); | 112 | dev->bus ? dev->bus->name : "No Bus", dev_name(dev)); |
114 | complete_all(&dev->power.completion); | 113 | complete_all(&dev->power.completion); |
115 | mutex_lock(&dpm_list_mtx); | 114 | mutex_lock(&dpm_list_mtx); |
116 | dev_pm_qos_constraints_destroy(dev); | ||
117 | list_del_init(&dev->power.entry); | 115 | list_del_init(&dev->power.entry); |
118 | mutex_unlock(&dpm_list_mtx); | 116 | mutex_unlock(&dpm_list_mtx); |
119 | device_wakeup_disable(dev); | 117 | device_wakeup_disable(dev); |
diff --git a/drivers/base/power/power.h b/drivers/base/power/power.h index b16686a0a5a2..cfc3226ec492 100644 --- a/drivers/base/power/power.h +++ b/drivers/base/power/power.h | |||
@@ -4,7 +4,7 @@ static inline void device_pm_init_common(struct device *dev) | |||
4 | { | 4 | { |
5 | if (!dev->power.early_init) { | 5 | if (!dev->power.early_init) { |
6 | spin_lock_init(&dev->power.lock); | 6 | spin_lock_init(&dev->power.lock); |
7 | dev->power.power_state = PMSG_INVALID; | 7 | dev->power.qos = NULL; |
8 | dev->power.early_init = true; | 8 | dev->power.early_init = true; |
9 | } | 9 | } |
10 | } | 10 | } |
@@ -56,14 +56,10 @@ extern void device_pm_move_last(struct device *); | |||
56 | 56 | ||
57 | static inline void device_pm_sleep_init(struct device *dev) {} | 57 | static inline void device_pm_sleep_init(struct device *dev) {} |
58 | 58 | ||
59 | static inline void device_pm_add(struct device *dev) | 59 | static inline void device_pm_add(struct device *dev) {} |
60 | { | ||
61 | dev_pm_qos_constraints_init(dev); | ||
62 | } | ||
63 | 60 | ||
64 | static inline void device_pm_remove(struct device *dev) | 61 | static inline void device_pm_remove(struct device *dev) |
65 | { | 62 | { |
66 | dev_pm_qos_constraints_destroy(dev); | ||
67 | pm_runtime_remove(dev); | 63 | pm_runtime_remove(dev); |
68 | } | 64 | } |
69 | 65 | ||
diff --git a/drivers/base/power/qos.c b/drivers/base/power/qos.c index 2159d62c858a..5f74587ef258 100644 --- a/drivers/base/power/qos.c +++ b/drivers/base/power/qos.c | |||
@@ -41,6 +41,7 @@ | |||
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 | #include <linux/pm_runtime.h> |
44 | #include <linux/err.h> | ||
44 | 45 | ||
45 | #include "power.h" | 46 | #include "power.h" |
46 | 47 | ||
@@ -61,7 +62,7 @@ enum pm_qos_flags_status __dev_pm_qos_flags(struct device *dev, s32 mask) | |||
61 | struct pm_qos_flags *pqf; | 62 | struct pm_qos_flags *pqf; |
62 | s32 val; | 63 | s32 val; |
63 | 64 | ||
64 | if (!qos) | 65 | if (IS_ERR_OR_NULL(qos)) |
65 | return PM_QOS_FLAGS_UNDEFINED; | 66 | return PM_QOS_FLAGS_UNDEFINED; |
66 | 67 | ||
67 | pqf = &qos->flags; | 68 | pqf = &qos->flags; |
@@ -101,7 +102,8 @@ EXPORT_SYMBOL_GPL(dev_pm_qos_flags); | |||
101 | */ | 102 | */ |
102 | s32 __dev_pm_qos_read_value(struct device *dev) | 103 | s32 __dev_pm_qos_read_value(struct device *dev) |
103 | { | 104 | { |
104 | return dev->power.qos ? pm_qos_read_value(&dev->power.qos->latency) : 0; | 105 | return IS_ERR_OR_NULL(dev->power.qos) ? |
106 | 0 : pm_qos_read_value(&dev->power.qos->latency); | ||
105 | } | 107 | } |
106 | 108 | ||
107 | /** | 109 | /** |
@@ -198,20 +200,8 @@ static int dev_pm_qos_constraints_allocate(struct device *dev) | |||
198 | return 0; | 200 | return 0; |
199 | } | 201 | } |
200 | 202 | ||
201 | /** | 203 | static void __dev_pm_qos_hide_latency_limit(struct device *dev); |
202 | * dev_pm_qos_constraints_init - Initalize device's PM QoS constraints pointer. | 204 | static void __dev_pm_qos_hide_flags(struct device *dev); |
203 | * @dev: target device | ||
204 | * | ||
205 | * Called from the device PM subsystem during device insertion under | ||
206 | * device_pm_lock(). | ||
207 | */ | ||
208 | void dev_pm_qos_constraints_init(struct device *dev) | ||
209 | { | ||
210 | mutex_lock(&dev_pm_qos_mtx); | ||
211 | dev->power.qos = NULL; | ||
212 | dev->power.power_state = PMSG_ON; | ||
213 | mutex_unlock(&dev_pm_qos_mtx); | ||
214 | } | ||
215 | 205 | ||
216 | /** | 206 | /** |
217 | * dev_pm_qos_constraints_destroy | 207 | * dev_pm_qos_constraints_destroy |
@@ -226,16 +216,15 @@ void dev_pm_qos_constraints_destroy(struct device *dev) | |||
226 | struct pm_qos_constraints *c; | 216 | struct pm_qos_constraints *c; |
227 | struct pm_qos_flags *f; | 217 | struct pm_qos_flags *f; |
228 | 218 | ||
219 | mutex_lock(&dev_pm_qos_mtx); | ||
220 | |||
229 | /* | 221 | /* |
230 | * If the device's PM QoS resume latency limit or PM QoS flags have been | 222 | * If the device's PM QoS resume latency limit or PM QoS flags have been |
231 | * exposed to user space, they have to be hidden at this point. | 223 | * exposed to user space, they have to be hidden at this point. |
232 | */ | 224 | */ |
233 | dev_pm_qos_hide_latency_limit(dev); | 225 | __dev_pm_qos_hide_latency_limit(dev); |
234 | dev_pm_qos_hide_flags(dev); | 226 | __dev_pm_qos_hide_flags(dev); |
235 | |||
236 | mutex_lock(&dev_pm_qos_mtx); | ||
237 | 227 | ||
238 | dev->power.power_state = PMSG_INVALID; | ||
239 | qos = dev->power.qos; | 228 | qos = dev->power.qos; |
240 | if (!qos) | 229 | if (!qos) |
241 | goto out; | 230 | goto out; |
@@ -257,7 +246,7 @@ void dev_pm_qos_constraints_destroy(struct device *dev) | |||
257 | } | 246 | } |
258 | 247 | ||
259 | spin_lock_irq(&dev->power.lock); | 248 | spin_lock_irq(&dev->power.lock); |
260 | dev->power.qos = NULL; | 249 | dev->power.qos = ERR_PTR(-ENODEV); |
261 | spin_unlock_irq(&dev->power.lock); | 250 | spin_unlock_irq(&dev->power.lock); |
262 | 251 | ||
263 | kfree(c->notifiers); | 252 | kfree(c->notifiers); |
@@ -301,32 +290,19 @@ int dev_pm_qos_add_request(struct device *dev, struct dev_pm_qos_request *req, | |||
301 | "%s() called for already added request\n", __func__)) | 290 | "%s() called for already added request\n", __func__)) |
302 | return -EINVAL; | 291 | return -EINVAL; |
303 | 292 | ||
304 | req->dev = dev; | ||
305 | |||
306 | mutex_lock(&dev_pm_qos_mtx); | 293 | mutex_lock(&dev_pm_qos_mtx); |
307 | 294 | ||
308 | if (!dev->power.qos) { | 295 | if (IS_ERR(dev->power.qos)) |
309 | if (dev->power.power_state.event == PM_EVENT_INVALID) { | 296 | ret = -ENODEV; |
310 | /* The device has been removed from the system. */ | 297 | else if (!dev->power.qos) |
311 | req->dev = NULL; | 298 | ret = dev_pm_qos_constraints_allocate(dev); |
312 | ret = -ENODEV; | ||
313 | goto out; | ||
314 | } else { | ||
315 | /* | ||
316 | * Allocate the constraints data on the first call to | ||
317 | * add_request, i.e. only if the data is not already | ||
318 | * allocated and if the device has not been removed. | ||
319 | */ | ||
320 | ret = dev_pm_qos_constraints_allocate(dev); | ||
321 | } | ||
322 | } | ||
323 | 299 | ||
324 | if (!ret) { | 300 | if (!ret) { |
301 | req->dev = dev; | ||
325 | req->type = type; | 302 | req->type = type; |
326 | ret = apply_constraint(req, PM_QOS_ADD_REQ, value); | 303 | ret = apply_constraint(req, PM_QOS_ADD_REQ, value); |
327 | } | 304 | } |
328 | 305 | ||
329 | out: | ||
330 | mutex_unlock(&dev_pm_qos_mtx); | 306 | mutex_unlock(&dev_pm_qos_mtx); |
331 | 307 | ||
332 | return ret; | 308 | return ret; |
@@ -351,7 +327,7 @@ static int __dev_pm_qos_update_request(struct dev_pm_qos_request *req, | |||
351 | "%s() called for unknown object\n", __func__)) | 327 | "%s() called for unknown object\n", __func__)) |
352 | return -EINVAL; | 328 | return -EINVAL; |
353 | 329 | ||
354 | if (!req->dev->power.qos) | 330 | if (IS_ERR_OR_NULL(req->dev->power.qos)) |
355 | return -ENODEV; | 331 | return -ENODEV; |
356 | 332 | ||
357 | switch(req->type) { | 333 | switch(req->type) { |
@@ -402,7 +378,7 @@ EXPORT_SYMBOL_GPL(dev_pm_qos_update_request); | |||
402 | 378 | ||
403 | static int __dev_pm_qos_remove_request(struct dev_pm_qos_request *req) | 379 | static int __dev_pm_qos_remove_request(struct dev_pm_qos_request *req) |
404 | { | 380 | { |
405 | int ret = 0; | 381 | int ret; |
406 | 382 | ||
407 | if (!req) /*guard against callers passing in null */ | 383 | if (!req) /*guard against callers passing in null */ |
408 | return -EINVAL; | 384 | return -EINVAL; |
@@ -411,13 +387,11 @@ static int __dev_pm_qos_remove_request(struct dev_pm_qos_request *req) | |||
411 | "%s() called for unknown object\n", __func__)) | 387 | "%s() called for unknown object\n", __func__)) |
412 | return -EINVAL; | 388 | return -EINVAL; |
413 | 389 | ||
414 | if (req->dev->power.qos) { | 390 | if (IS_ERR_OR_NULL(req->dev->power.qos)) |
415 | ret = apply_constraint(req, PM_QOS_REMOVE_REQ, | 391 | return -ENODEV; |
416 | PM_QOS_DEFAULT_VALUE); | 392 | |
417 | memset(req, 0, sizeof(*req)); | 393 | ret = apply_constraint(req, PM_QOS_REMOVE_REQ, PM_QOS_DEFAULT_VALUE); |
418 | } else { | 394 | memset(req, 0, sizeof(*req)); |
419 | ret = -ENODEV; | ||
420 | } | ||
421 | return ret; | 395 | return ret; |
422 | } | 396 | } |
423 | 397 | ||
@@ -466,9 +440,10 @@ int dev_pm_qos_add_notifier(struct device *dev, struct notifier_block *notifier) | |||
466 | 440 | ||
467 | mutex_lock(&dev_pm_qos_mtx); | 441 | mutex_lock(&dev_pm_qos_mtx); |
468 | 442 | ||
469 | if (!dev->power.qos) | 443 | if (IS_ERR(dev->power.qos)) |
470 | ret = dev->power.power_state.event != PM_EVENT_INVALID ? | 444 | ret = -ENODEV; |
471 | dev_pm_qos_constraints_allocate(dev) : -ENODEV; | 445 | else if (!dev->power.qos) |
446 | ret = dev_pm_qos_constraints_allocate(dev); | ||
472 | 447 | ||
473 | if (!ret) | 448 | if (!ret) |
474 | ret = blocking_notifier_chain_register( | 449 | ret = blocking_notifier_chain_register( |
@@ -497,7 +472,7 @@ int dev_pm_qos_remove_notifier(struct device *dev, | |||
497 | mutex_lock(&dev_pm_qos_mtx); | 472 | mutex_lock(&dev_pm_qos_mtx); |
498 | 473 | ||
499 | /* Silently return if the constraints object is not present. */ | 474 | /* Silently return if the constraints object is not present. */ |
500 | if (dev->power.qos) | 475 | if (!IS_ERR_OR_NULL(dev->power.qos)) |
501 | retval = blocking_notifier_chain_unregister( | 476 | retval = blocking_notifier_chain_unregister( |
502 | dev->power.qos->latency.notifiers, | 477 | dev->power.qos->latency.notifiers, |
503 | notifier); | 478 | notifier); |
@@ -608,7 +583,7 @@ int dev_pm_qos_expose_latency_limit(struct device *dev, s32 value) | |||
608 | 583 | ||
609 | mutex_lock(&dev_pm_qos_mtx); | 584 | mutex_lock(&dev_pm_qos_mtx); |
610 | 585 | ||
611 | if (!dev->power.qos) | 586 | if (IS_ERR_OR_NULL(dev->power.qos)) |
612 | ret = -ENODEV; | 587 | ret = -ENODEV; |
613 | else if (dev->power.qos->latency_req) | 588 | else if (dev->power.qos->latency_req) |
614 | ret = -EEXIST; | 589 | ret = -EEXIST; |
@@ -630,6 +605,14 @@ int dev_pm_qos_expose_latency_limit(struct device *dev, s32 value) | |||
630 | } | 605 | } |
631 | EXPORT_SYMBOL_GPL(dev_pm_qos_expose_latency_limit); | 606 | EXPORT_SYMBOL_GPL(dev_pm_qos_expose_latency_limit); |
632 | 607 | ||
608 | static void __dev_pm_qos_hide_latency_limit(struct device *dev) | ||
609 | { | ||
610 | if (!IS_ERR_OR_NULL(dev->power.qos) && dev->power.qos->latency_req) { | ||
611 | pm_qos_sysfs_remove_latency(dev); | ||
612 | __dev_pm_qos_drop_user_request(dev, DEV_PM_QOS_LATENCY); | ||
613 | } | ||
614 | } | ||
615 | |||
633 | /** | 616 | /** |
634 | * dev_pm_qos_hide_latency_limit - Hide PM QoS latency limit from user space. | 617 | * dev_pm_qos_hide_latency_limit - Hide PM QoS latency limit from user space. |
635 | * @dev: Device whose PM QoS latency limit is to be hidden from user space. | 618 | * @dev: Device whose PM QoS latency limit is to be hidden from user space. |
@@ -637,12 +620,7 @@ EXPORT_SYMBOL_GPL(dev_pm_qos_expose_latency_limit); | |||
637 | void dev_pm_qos_hide_latency_limit(struct device *dev) | 620 | void dev_pm_qos_hide_latency_limit(struct device *dev) |
638 | { | 621 | { |
639 | mutex_lock(&dev_pm_qos_mtx); | 622 | mutex_lock(&dev_pm_qos_mtx); |
640 | 623 | __dev_pm_qos_hide_latency_limit(dev); | |
641 | if (dev->power.qos && dev->power.qos->latency_req) { | ||
642 | pm_qos_sysfs_remove_latency(dev); | ||
643 | __dev_pm_qos_drop_user_request(dev, DEV_PM_QOS_LATENCY); | ||
644 | } | ||
645 | |||
646 | mutex_unlock(&dev_pm_qos_mtx); | 624 | mutex_unlock(&dev_pm_qos_mtx); |
647 | } | 625 | } |
648 | EXPORT_SYMBOL_GPL(dev_pm_qos_hide_latency_limit); | 626 | EXPORT_SYMBOL_GPL(dev_pm_qos_hide_latency_limit); |
@@ -673,7 +651,7 @@ int dev_pm_qos_expose_flags(struct device *dev, s32 val) | |||
673 | pm_runtime_get_sync(dev); | 651 | pm_runtime_get_sync(dev); |
674 | mutex_lock(&dev_pm_qos_mtx); | 652 | mutex_lock(&dev_pm_qos_mtx); |
675 | 653 | ||
676 | if (!dev->power.qos) | 654 | if (IS_ERR_OR_NULL(dev->power.qos)) |
677 | ret = -ENODEV; | 655 | ret = -ENODEV; |
678 | else if (dev->power.qos->flags_req) | 656 | else if (dev->power.qos->flags_req) |
679 | ret = -EEXIST; | 657 | ret = -EEXIST; |
@@ -696,6 +674,14 @@ int dev_pm_qos_expose_flags(struct device *dev, s32 val) | |||
696 | } | 674 | } |
697 | EXPORT_SYMBOL_GPL(dev_pm_qos_expose_flags); | 675 | EXPORT_SYMBOL_GPL(dev_pm_qos_expose_flags); |
698 | 676 | ||
677 | static void __dev_pm_qos_hide_flags(struct device *dev) | ||
678 | { | ||
679 | if (!IS_ERR_OR_NULL(dev->power.qos) && dev->power.qos->flags_req) { | ||
680 | pm_qos_sysfs_remove_flags(dev); | ||
681 | __dev_pm_qos_drop_user_request(dev, DEV_PM_QOS_FLAGS); | ||
682 | } | ||
683 | } | ||
684 | |||
699 | /** | 685 | /** |
700 | * dev_pm_qos_hide_flags - Hide PM QoS flags of a device from user space. | 686 | * dev_pm_qos_hide_flags - Hide PM QoS flags of a device from user space. |
701 | * @dev: Device whose PM QoS flags are to be hidden from user space. | 687 | * @dev: Device whose PM QoS flags are to be hidden from user space. |
@@ -704,12 +690,7 @@ void dev_pm_qos_hide_flags(struct device *dev) | |||
704 | { | 690 | { |
705 | pm_runtime_get_sync(dev); | 691 | pm_runtime_get_sync(dev); |
706 | mutex_lock(&dev_pm_qos_mtx); | 692 | mutex_lock(&dev_pm_qos_mtx); |
707 | 693 | __dev_pm_qos_hide_flags(dev); | |
708 | if (dev->power.qos && dev->power.qos->flags_req) { | ||
709 | pm_qos_sysfs_remove_flags(dev); | ||
710 | __dev_pm_qos_drop_user_request(dev, DEV_PM_QOS_FLAGS); | ||
711 | } | ||
712 | |||
713 | mutex_unlock(&dev_pm_qos_mtx); | 694 | mutex_unlock(&dev_pm_qos_mtx); |
714 | pm_runtime_put(dev); | 695 | pm_runtime_put(dev); |
715 | } | 696 | } |
@@ -729,7 +710,7 @@ int dev_pm_qos_update_flags(struct device *dev, s32 mask, bool set) | |||
729 | pm_runtime_get_sync(dev); | 710 | pm_runtime_get_sync(dev); |
730 | mutex_lock(&dev_pm_qos_mtx); | 711 | mutex_lock(&dev_pm_qos_mtx); |
731 | 712 | ||
732 | if (!dev->power.qos || !dev->power.qos->flags_req) { | 713 | if (IS_ERR_OR_NULL(dev->power.qos) || !dev->power.qos->flags_req) { |
733 | ret = -EINVAL; | 714 | ret = -EINVAL; |
734 | goto out; | 715 | goto out; |
735 | } | 716 | } |
@@ -747,4 +728,7 @@ int dev_pm_qos_update_flags(struct device *dev, s32 mask, bool set) | |||
747 | pm_runtime_put(dev); | 728 | pm_runtime_put(dev); |
748 | return ret; | 729 | return ret; |
749 | } | 730 | } |
731 | #else /* !CONFIG_PM_RUNTIME */ | ||
732 | static void __dev_pm_qos_hide_latency_limit(struct device *dev) {} | ||
733 | static void __dev_pm_qos_hide_flags(struct device *dev) {} | ||
750 | #endif /* CONFIG_PM_RUNTIME */ | 734 | #endif /* CONFIG_PM_RUNTIME */ |
diff --git a/drivers/base/power/sysfs.c b/drivers/base/power/sysfs.c index 50d16e3cb0a9..a53ebd265701 100644 --- a/drivers/base/power/sysfs.c +++ b/drivers/base/power/sysfs.c | |||
@@ -708,6 +708,7 @@ void rpm_sysfs_remove(struct device *dev) | |||
708 | 708 | ||
709 | void dpm_sysfs_remove(struct device *dev) | 709 | void dpm_sysfs_remove(struct device *dev) |
710 | { | 710 | { |
711 | dev_pm_qos_constraints_destroy(dev); | ||
711 | rpm_sysfs_remove(dev); | 712 | rpm_sysfs_remove(dev); |
712 | sysfs_unmerge_group(&dev->kobj, &pm_wakeup_attr_group); | 713 | sysfs_unmerge_group(&dev->kobj, &pm_wakeup_attr_group); |
713 | sysfs_remove_group(&dev->kobj, &pm_attr_group); | 714 | sysfs_remove_group(&dev->kobj, &pm_attr_group); |