diff options
-rw-r--r-- | drivers/base/power/opp.c | 30 | ||||
-rw-r--r-- | include/linux/opp.h | 12 |
2 files changed, 42 insertions, 0 deletions
diff --git a/drivers/base/power/opp.c b/drivers/base/power/opp.c index b23de185cb04..434a6c011675 100644 --- a/drivers/base/power/opp.c +++ b/drivers/base/power/opp.c | |||
@@ -73,6 +73,7 @@ struct opp { | |||
73 | * RCU usage: nodes are not modified in the list of device_opp, | 73 | * RCU usage: nodes are not modified in the list of device_opp, |
74 | * however addition is possible and is secured by dev_opp_list_lock | 74 | * however addition is possible and is secured by dev_opp_list_lock |
75 | * @dev: device pointer | 75 | * @dev: device pointer |
76 | * @head: notifier head to notify the OPP availability changes. | ||
76 | * @opp_list: list of opps | 77 | * @opp_list: list of opps |
77 | * | 78 | * |
78 | * This is an internal data structure maintaining the link to opps attached to | 79 | * This is an internal data structure maintaining the link to opps attached to |
@@ -83,6 +84,7 @@ struct device_opp { | |||
83 | struct list_head node; | 84 | struct list_head node; |
84 | 85 | ||
85 | struct device *dev; | 86 | struct device *dev; |
87 | struct srcu_notifier_head head; | ||
86 | struct list_head opp_list; | 88 | struct list_head opp_list; |
87 | }; | 89 | }; |
88 | 90 | ||
@@ -404,6 +406,7 @@ int opp_add(struct device *dev, unsigned long freq, unsigned long u_volt) | |||
404 | } | 406 | } |
405 | 407 | ||
406 | dev_opp->dev = dev; | 408 | dev_opp->dev = dev; |
409 | srcu_init_notifier_head(&dev_opp->head); | ||
407 | INIT_LIST_HEAD(&dev_opp->opp_list); | 410 | INIT_LIST_HEAD(&dev_opp->opp_list); |
408 | 411 | ||
409 | /* Secure the device list modification */ | 412 | /* Secure the device list modification */ |
@@ -428,6 +431,11 @@ int opp_add(struct device *dev, unsigned long freq, unsigned long u_volt) | |||
428 | list_add_rcu(&new_opp->node, head); | 431 | list_add_rcu(&new_opp->node, head); |
429 | mutex_unlock(&dev_opp_list_lock); | 432 | mutex_unlock(&dev_opp_list_lock); |
430 | 433 | ||
434 | /* | ||
435 | * Notify the changes in the availability of the operable | ||
436 | * frequency/voltage list. | ||
437 | */ | ||
438 | srcu_notifier_call_chain(&dev_opp->head, OPP_EVENT_ADD, new_opp); | ||
431 | return 0; | 439 | return 0; |
432 | } | 440 | } |
433 | 441 | ||
@@ -504,6 +512,14 @@ static int opp_set_availability(struct device *dev, unsigned long freq, | |||
504 | mutex_unlock(&dev_opp_list_lock); | 512 | mutex_unlock(&dev_opp_list_lock); |
505 | synchronize_rcu(); | 513 | synchronize_rcu(); |
506 | 514 | ||
515 | /* Notify the change of the OPP availability */ | ||
516 | if (availability_req) | ||
517 | srcu_notifier_call_chain(&dev_opp->head, OPP_EVENT_ENABLE, | ||
518 | new_opp); | ||
519 | else | ||
520 | srcu_notifier_call_chain(&dev_opp->head, OPP_EVENT_DISABLE, | ||
521 | new_opp); | ||
522 | |||
507 | /* clean up old opp */ | 523 | /* clean up old opp */ |
508 | new_opp = opp; | 524 | new_opp = opp; |
509 | goto out; | 525 | goto out; |
@@ -643,3 +659,17 @@ void opp_free_cpufreq_table(struct device *dev, | |||
643 | *table = NULL; | 659 | *table = NULL; |
644 | } | 660 | } |
645 | #endif /* CONFIG_CPU_FREQ */ | 661 | #endif /* CONFIG_CPU_FREQ */ |
662 | |||
663 | /** | ||
664 | * opp_get_notifier() - find notifier_head of the device with opp | ||
665 | * @dev: device pointer used to lookup device OPPs. | ||
666 | */ | ||
667 | struct srcu_notifier_head *opp_get_notifier(struct device *dev) | ||
668 | { | ||
669 | struct device_opp *dev_opp = find_device_opp(dev); | ||
670 | |||
671 | if (IS_ERR(dev_opp)) | ||
672 | return ERR_PTR(PTR_ERR(dev_opp)); /* matching type */ | ||
673 | |||
674 | return &dev_opp->head; | ||
675 | } | ||
diff --git a/include/linux/opp.h b/include/linux/opp.h index 7020e9736fc5..87a9208f8aec 100644 --- a/include/linux/opp.h +++ b/include/linux/opp.h | |||
@@ -16,9 +16,14 @@ | |||
16 | 16 | ||
17 | #include <linux/err.h> | 17 | #include <linux/err.h> |
18 | #include <linux/cpufreq.h> | 18 | #include <linux/cpufreq.h> |
19 | #include <linux/notifier.h> | ||
19 | 20 | ||
20 | struct opp; | 21 | struct opp; |
21 | 22 | ||
23 | enum opp_event { | ||
24 | OPP_EVENT_ADD, OPP_EVENT_ENABLE, OPP_EVENT_DISABLE, | ||
25 | }; | ||
26 | |||
22 | #if defined(CONFIG_PM_OPP) | 27 | #if defined(CONFIG_PM_OPP) |
23 | 28 | ||
24 | unsigned long opp_get_voltage(struct opp *opp); | 29 | unsigned long opp_get_voltage(struct opp *opp); |
@@ -40,6 +45,8 @@ int opp_enable(struct device *dev, unsigned long freq); | |||
40 | 45 | ||
41 | int opp_disable(struct device *dev, unsigned long freq); | 46 | int opp_disable(struct device *dev, unsigned long freq); |
42 | 47 | ||
48 | struct srcu_notifier_head *opp_get_notifier(struct device *dev); | ||
49 | |||
43 | #else | 50 | #else |
44 | static inline unsigned long opp_get_voltage(struct opp *opp) | 51 | static inline unsigned long opp_get_voltage(struct opp *opp) |
45 | { | 52 | { |
@@ -89,6 +96,11 @@ static inline int opp_disable(struct device *dev, unsigned long freq) | |||
89 | { | 96 | { |
90 | return 0; | 97 | return 0; |
91 | } | 98 | } |
99 | |||
100 | struct srcu_notifier_head *opp_get_notifier(struct device *dev) | ||
101 | { | ||
102 | return ERR_PTR(-EINVAL); | ||
103 | } | ||
92 | #endif /* CONFIG_PM */ | 104 | #endif /* CONFIG_PM */ |
93 | 105 | ||
94 | #if defined(CONFIG_CPU_FREQ) && defined(CONFIG_PM_OPP) | 106 | #if defined(CONFIG_CPU_FREQ) && defined(CONFIG_PM_OPP) |