diff options
author | Viresh Kumar <viresh.kumar@linaro.org> | 2015-09-04 04:17:26 -0400 |
---|---|---|
committer | Rafael J. Wysocki <rafael.j.wysocki@intel.com> | 2015-09-14 20:03:16 -0400 |
commit | f59d3ee8480d30f41914cb4bed5086237e8507b0 (patch) | |
tree | 06c8cea1e5d4eaa0cf94769edc892fa8ec386655 /drivers/base | |
parent | 33692dc381f9b89ddfc408631bf670ac2fd08ffc (diff) |
PM / OPP: Move cpu specific code to opp/cpu.c
Move cpu device specific code out of generic opp library, and add it to
cpu.c.
Along with that, create a core-internal opp.h header, which will be used
to share structures and function prototypes within opp core.
Reviewed-by: Stephen Boyd <sboyd@codeaurora.org>
Signed-off-by: Viresh Kumar <viresh.kumar@linaro.org>
Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
Diffstat (limited to 'drivers/base')
-rw-r--r-- | drivers/base/power/opp/core.c | 276 | ||||
-rw-r--r-- | drivers/base/power/opp/cpu.c | 160 | ||||
-rw-r--r-- | drivers/base/power/opp/opp.h | 143 |
3 files changed, 304 insertions, 275 deletions
diff --git a/drivers/base/power/opp/core.c b/drivers/base/power/opp/core.c index aeff1cfb46f2..a731fa66e504 100644 --- a/drivers/base/power/opp/core.c +++ b/drivers/base/power/opp/core.c | |||
@@ -11,131 +11,14 @@ | |||
11 | * published by the Free Software Foundation. | 11 | * published by the Free Software Foundation. |
12 | */ | 12 | */ |
13 | 13 | ||
14 | #include <linux/cpu.h> | ||
15 | #include <linux/kernel.h> | ||
16 | #include <linux/errno.h> | 14 | #include <linux/errno.h> |
17 | #include <linux/err.h> | 15 | #include <linux/err.h> |
18 | #include <linux/slab.h> | 16 | #include <linux/slab.h> |
19 | #include <linux/device.h> | 17 | #include <linux/device.h> |
20 | #include <linux/list.h> | ||
21 | #include <linux/rculist.h> | ||
22 | #include <linux/rcupdate.h> | ||
23 | #include <linux/pm_opp.h> | ||
24 | #include <linux/of.h> | 18 | #include <linux/of.h> |
25 | #include <linux/export.h> | 19 | #include <linux/export.h> |
26 | 20 | ||
27 | /* | 21 | #include "opp.h" |
28 | * Internal data structure organization with the OPP layer library is as | ||
29 | * follows: | ||
30 | * dev_opp_list (root) | ||
31 | * |- device 1 (represents voltage domain 1) | ||
32 | * | |- opp 1 (availability, freq, voltage) | ||
33 | * | |- opp 2 .. | ||
34 | * ... ... | ||
35 | * | `- opp n .. | ||
36 | * |- device 2 (represents the next voltage domain) | ||
37 | * ... | ||
38 | * `- device m (represents mth voltage domain) | ||
39 | * device 1, 2.. are represented by dev_opp structure while each opp | ||
40 | * is represented by the opp structure. | ||
41 | */ | ||
42 | |||
43 | /** | ||
44 | * struct dev_pm_opp - Generic OPP description structure | ||
45 | * @node: opp list node. The nodes are maintained throughout the lifetime | ||
46 | * of boot. It is expected only an optimal set of OPPs are | ||
47 | * added to the library by the SoC framework. | ||
48 | * RCU usage: opp list is traversed with RCU locks. node | ||
49 | * modification is possible realtime, hence the modifications | ||
50 | * are protected by the dev_opp_list_lock for integrity. | ||
51 | * IMPORTANT: the opp nodes should be maintained in increasing | ||
52 | * order. | ||
53 | * @dynamic: not-created from static DT entries. | ||
54 | * @available: true/false - marks if this OPP as available or not | ||
55 | * @turbo: true if turbo (boost) OPP | ||
56 | * @rate: Frequency in hertz | ||
57 | * @u_volt: Target voltage in microvolts corresponding to this OPP | ||
58 | * @u_volt_min: Minimum voltage in microvolts corresponding to this OPP | ||
59 | * @u_volt_max: Maximum voltage in microvolts corresponding to this OPP | ||
60 | * @u_amp: Maximum current drawn by the device in microamperes | ||
61 | * @clock_latency_ns: Latency (in nanoseconds) of switching to this OPP's | ||
62 | * frequency from any other OPP's frequency. | ||
63 | * @dev_opp: points back to the device_opp struct this opp belongs to | ||
64 | * @rcu_head: RCU callback head used for deferred freeing | ||
65 | * @np: OPP's device node. | ||
66 | * | ||
67 | * This structure stores the OPP information for a given device. | ||
68 | */ | ||
69 | struct dev_pm_opp { | ||
70 | struct list_head node; | ||
71 | |||
72 | bool available; | ||
73 | bool dynamic; | ||
74 | bool turbo; | ||
75 | unsigned long rate; | ||
76 | |||
77 | unsigned long u_volt; | ||
78 | unsigned long u_volt_min; | ||
79 | unsigned long u_volt_max; | ||
80 | unsigned long u_amp; | ||
81 | unsigned long clock_latency_ns; | ||
82 | |||
83 | struct device_opp *dev_opp; | ||
84 | struct rcu_head rcu_head; | ||
85 | |||
86 | struct device_node *np; | ||
87 | }; | ||
88 | |||
89 | /** | ||
90 | * struct device_list_opp - devices managed by 'struct device_opp' | ||
91 | * @node: list node | ||
92 | * @dev: device to which the struct object belongs | ||
93 | * @rcu_head: RCU callback head used for deferred freeing | ||
94 | * | ||
95 | * This is an internal data structure maintaining the list of devices that are | ||
96 | * managed by 'struct device_opp'. | ||
97 | */ | ||
98 | struct device_list_opp { | ||
99 | struct list_head node; | ||
100 | const struct device *dev; | ||
101 | struct rcu_head rcu_head; | ||
102 | }; | ||
103 | |||
104 | /** | ||
105 | * struct device_opp - Device opp structure | ||
106 | * @node: list node - contains the devices with OPPs that | ||
107 | * have been registered. Nodes once added are not modified in this | ||
108 | * list. | ||
109 | * RCU usage: nodes are not modified in the list of device_opp, | ||
110 | * however addition is possible and is secured by dev_opp_list_lock | ||
111 | * @srcu_head: notifier head to notify the OPP availability changes. | ||
112 | * @rcu_head: RCU callback head used for deferred freeing | ||
113 | * @dev_list: list of devices that share these OPPs | ||
114 | * @opp_list: list of opps | ||
115 | * @np: struct device_node pointer for opp's DT node. | ||
116 | * @shared_opp: OPP is shared between multiple devices. | ||
117 | * | ||
118 | * This is an internal data structure maintaining the link to opps attached to | ||
119 | * a device. This structure is not meant to be shared to users as it is | ||
120 | * meant for book keeping and private to OPP library. | ||
121 | * | ||
122 | * Because the opp structures can be used from both rcu and srcu readers, we | ||
123 | * need to wait for the grace period of both of them before freeing any | ||
124 | * resources. And so we have used kfree_rcu() from within call_srcu() handlers. | ||
125 | */ | ||
126 | struct device_opp { | ||
127 | struct list_head node; | ||
128 | |||
129 | struct srcu_notifier_head srcu_head; | ||
130 | struct rcu_head rcu_head; | ||
131 | struct list_head dev_list; | ||
132 | struct list_head opp_list; | ||
133 | |||
134 | struct device_node *np; | ||
135 | unsigned long clock_latency_ns_max; | ||
136 | bool shared_opp; | ||
137 | struct dev_pm_opp *suspend_opp; | ||
138 | }; | ||
139 | 22 | ||
140 | /* | 23 | /* |
141 | * The root of the list of all devices. All device_opp structures branch off | 24 | * The root of the list of all devices. All device_opp structures branch off |
@@ -200,7 +83,7 @@ static struct device_opp *_managed_opp(const struct device_node *np) | |||
200 | * is a RCU protected pointer. This means that device_opp is valid as long | 83 | * is a RCU protected pointer. This means that device_opp is valid as long |
201 | * as we are under RCU lock. | 84 | * as we are under RCU lock. |
202 | */ | 85 | */ |
203 | static struct device_opp *_find_device_opp(struct device *dev) | 86 | struct device_opp *_find_device_opp(struct device *dev) |
204 | { | 87 | { |
205 | struct device_opp *dev_opp; | 88 | struct device_opp *dev_opp; |
206 | 89 | ||
@@ -579,8 +462,8 @@ static void _remove_list_dev(struct device_list_opp *list_dev, | |||
579 | _kfree_list_dev_rcu); | 462 | _kfree_list_dev_rcu); |
580 | } | 463 | } |
581 | 464 | ||
582 | static struct device_list_opp *_add_list_dev(const struct device *dev, | 465 | struct device_list_opp *_add_list_dev(const struct device *dev, |
583 | struct device_opp *dev_opp) | 466 | struct device_opp *dev_opp) |
584 | { | 467 | { |
585 | struct device_list_opp *list_dev; | 468 | struct device_list_opp *list_dev; |
586 | 469 | ||
@@ -1262,28 +1145,8 @@ unlock: | |||
1262 | } | 1145 | } |
1263 | EXPORT_SYMBOL_GPL(dev_pm_opp_of_remove_table); | 1146 | EXPORT_SYMBOL_GPL(dev_pm_opp_of_remove_table); |
1264 | 1147 | ||
1265 | void dev_pm_opp_of_cpumask_remove_table(cpumask_var_t cpumask) | ||
1266 | { | ||
1267 | struct device *cpu_dev; | ||
1268 | int cpu; | ||
1269 | |||
1270 | WARN_ON(cpumask_empty(cpumask)); | ||
1271 | |||
1272 | for_each_cpu(cpu, cpumask) { | ||
1273 | cpu_dev = get_cpu_device(cpu); | ||
1274 | if (!cpu_dev) { | ||
1275 | pr_err("%s: failed to get cpu%d device\n", __func__, | ||
1276 | cpu); | ||
1277 | continue; | ||
1278 | } | ||
1279 | |||
1280 | dev_pm_opp_of_remove_table(cpu_dev); | ||
1281 | } | ||
1282 | } | ||
1283 | EXPORT_SYMBOL_GPL(dev_pm_opp_of_cpumask_remove_table); | ||
1284 | |||
1285 | /* Returns opp descriptor node for a device, caller must do of_node_put() */ | 1148 | /* Returns opp descriptor node for a device, caller must do of_node_put() */ |
1286 | static struct device_node *_of_get_opp_desc_node(struct device *dev) | 1149 | struct device_node *_of_get_opp_desc_node(struct device *dev) |
1287 | { | 1150 | { |
1288 | /* | 1151 | /* |
1289 | * TODO: Support for multiple OPP tables. | 1152 | * TODO: Support for multiple OPP tables. |
@@ -1427,133 +1290,4 @@ int dev_pm_opp_of_add_table(struct device *dev) | |||
1427 | return ret; | 1290 | return ret; |
1428 | } | 1291 | } |
1429 | EXPORT_SYMBOL_GPL(dev_pm_opp_of_add_table); | 1292 | EXPORT_SYMBOL_GPL(dev_pm_opp_of_add_table); |
1430 | |||
1431 | int dev_pm_opp_of_cpumask_add_table(cpumask_var_t cpumask) | ||
1432 | { | ||
1433 | struct device *cpu_dev; | ||
1434 | int cpu, ret = 0; | ||
1435 | |||
1436 | WARN_ON(cpumask_empty(cpumask)); | ||
1437 | |||
1438 | for_each_cpu(cpu, cpumask) { | ||
1439 | cpu_dev = get_cpu_device(cpu); | ||
1440 | if (!cpu_dev) { | ||
1441 | pr_err("%s: failed to get cpu%d device\n", __func__, | ||
1442 | cpu); | ||
1443 | continue; | ||
1444 | } | ||
1445 | |||
1446 | ret = dev_pm_opp_of_add_table(cpu_dev); | ||
1447 | if (ret) { | ||
1448 | pr_err("%s: couldn't find opp table for cpu:%d, %d\n", | ||
1449 | __func__, cpu, ret); | ||
1450 | |||
1451 | /* Free all other OPPs */ | ||
1452 | dev_pm_opp_of_cpumask_remove_table(cpumask); | ||
1453 | break; | ||
1454 | } | ||
1455 | } | ||
1456 | |||
1457 | return ret; | ||
1458 | } | ||
1459 | EXPORT_SYMBOL_GPL(dev_pm_opp_of_cpumask_add_table); | ||
1460 | |||
1461 | /* Required only for V1 bindings, as v2 can manage it from DT itself */ | ||
1462 | int dev_pm_opp_set_sharing_cpus(struct device *cpu_dev, cpumask_var_t cpumask) | ||
1463 | { | ||
1464 | struct device_list_opp *list_dev; | ||
1465 | struct device_opp *dev_opp; | ||
1466 | struct device *dev; | ||
1467 | int cpu, ret = 0; | ||
1468 | |||
1469 | rcu_read_lock(); | ||
1470 | |||
1471 | dev_opp = _find_device_opp(cpu_dev); | ||
1472 | if (IS_ERR(dev_opp)) { | ||
1473 | ret = -EINVAL; | ||
1474 | goto out_rcu_read_unlock; | ||
1475 | } | ||
1476 | |||
1477 | for_each_cpu(cpu, cpumask) { | ||
1478 | if (cpu == cpu_dev->id) | ||
1479 | continue; | ||
1480 | |||
1481 | dev = get_cpu_device(cpu); | ||
1482 | if (!dev) { | ||
1483 | dev_err(cpu_dev, "%s: failed to get cpu%d device\n", | ||
1484 | __func__, cpu); | ||
1485 | continue; | ||
1486 | } | ||
1487 | |||
1488 | list_dev = _add_list_dev(dev, dev_opp); | ||
1489 | if (!list_dev) { | ||
1490 | dev_err(dev, "%s: failed to add list-dev for cpu%d device\n", | ||
1491 | __func__, cpu); | ||
1492 | continue; | ||
1493 | } | ||
1494 | } | ||
1495 | out_rcu_read_unlock: | ||
1496 | rcu_read_unlock(); | ||
1497 | |||
1498 | return 0; | ||
1499 | } | ||
1500 | EXPORT_SYMBOL_GPL(dev_pm_opp_set_sharing_cpus); | ||
1501 | |||
1502 | /* | ||
1503 | * Works only for OPP v2 bindings. | ||
1504 | * | ||
1505 | * cpumask should be already set to mask of cpu_dev->id. | ||
1506 | * Returns -ENOENT if operating-points-v2 bindings aren't supported. | ||
1507 | */ | ||
1508 | int dev_pm_opp_of_get_sharing_cpus(struct device *cpu_dev, cpumask_var_t cpumask) | ||
1509 | { | ||
1510 | struct device_node *np, *tmp_np; | ||
1511 | struct device *tcpu_dev; | ||
1512 | int cpu, ret = 0; | ||
1513 | |||
1514 | /* Get OPP descriptor node */ | ||
1515 | np = _of_get_opp_desc_node(cpu_dev); | ||
1516 | if (!np) { | ||
1517 | dev_dbg(cpu_dev, "%s: Couldn't find opp node: %ld\n", __func__, | ||
1518 | PTR_ERR(np)); | ||
1519 | return -ENOENT; | ||
1520 | } | ||
1521 | |||
1522 | /* OPPs are shared ? */ | ||
1523 | if (!of_property_read_bool(np, "opp-shared")) | ||
1524 | goto put_cpu_node; | ||
1525 | |||
1526 | for_each_possible_cpu(cpu) { | ||
1527 | if (cpu == cpu_dev->id) | ||
1528 | continue; | ||
1529 | |||
1530 | tcpu_dev = get_cpu_device(cpu); | ||
1531 | if (!tcpu_dev) { | ||
1532 | dev_err(cpu_dev, "%s: failed to get cpu%d device\n", | ||
1533 | __func__, cpu); | ||
1534 | ret = -ENODEV; | ||
1535 | goto put_cpu_node; | ||
1536 | } | ||
1537 | |||
1538 | /* Get OPP descriptor node */ | ||
1539 | tmp_np = _of_get_opp_desc_node(tcpu_dev); | ||
1540 | if (!tmp_np) { | ||
1541 | dev_err(tcpu_dev, "%s: Couldn't find opp node: %ld\n", | ||
1542 | __func__, PTR_ERR(tmp_np)); | ||
1543 | ret = PTR_ERR(tmp_np); | ||
1544 | goto put_cpu_node; | ||
1545 | } | ||
1546 | |||
1547 | /* CPUs are sharing opp node */ | ||
1548 | if (np == tmp_np) | ||
1549 | cpumask_set_cpu(cpu, cpumask); | ||
1550 | |||
1551 | of_node_put(tmp_np); | ||
1552 | } | ||
1553 | |||
1554 | put_cpu_node: | ||
1555 | of_node_put(np); | ||
1556 | return ret; | ||
1557 | } | ||
1558 | EXPORT_SYMBOL_GPL(dev_pm_opp_of_get_sharing_cpus); | ||
1559 | #endif | 1293 | #endif |
diff --git a/drivers/base/power/opp/cpu.c b/drivers/base/power/opp/cpu.c index 0dd033016e9d..3d946b508a13 100644 --- a/drivers/base/power/opp/cpu.c +++ b/drivers/base/power/opp/cpu.c | |||
@@ -10,17 +10,18 @@ | |||
10 | * it under the terms of the GNU General Public License version 2 as | 10 | * it under the terms of the GNU General Public License version 2 as |
11 | * published by the Free Software Foundation. | 11 | * published by the Free Software Foundation. |
12 | */ | 12 | */ |
13 | #include <linux/cpu.h> | ||
13 | #include <linux/cpufreq.h> | 14 | #include <linux/cpufreq.h> |
14 | #include <linux/device.h> | ||
15 | #include <linux/err.h> | 15 | #include <linux/err.h> |
16 | #include <linux/errno.h> | 16 | #include <linux/errno.h> |
17 | #include <linux/export.h> | 17 | #include <linux/export.h> |
18 | #include <linux/kernel.h> | 18 | #include <linux/of.h> |
19 | #include <linux/pm_opp.h> | ||
20 | #include <linux/rcupdate.h> | ||
21 | #include <linux/slab.h> | 19 | #include <linux/slab.h> |
22 | 20 | ||
21 | #include "opp.h" | ||
22 | |||
23 | #ifdef CONFIG_CPU_FREQ | 23 | #ifdef CONFIG_CPU_FREQ |
24 | |||
24 | /** | 25 | /** |
25 | * dev_pm_opp_init_cpufreq_table() - create a cpufreq table for a device | 26 | * dev_pm_opp_init_cpufreq_table() - create a cpufreq table for a device |
26 | * @dev: device for which we do this operation | 27 | * @dev: device for which we do this operation |
@@ -114,3 +115,154 @@ void dev_pm_opp_free_cpufreq_table(struct device *dev, | |||
114 | } | 115 | } |
115 | EXPORT_SYMBOL_GPL(dev_pm_opp_free_cpufreq_table); | 116 | EXPORT_SYMBOL_GPL(dev_pm_opp_free_cpufreq_table); |
116 | #endif /* CONFIG_CPU_FREQ */ | 117 | #endif /* CONFIG_CPU_FREQ */ |
118 | |||
119 | /* Required only for V1 bindings, as v2 can manage it from DT itself */ | ||
120 | int dev_pm_opp_set_sharing_cpus(struct device *cpu_dev, cpumask_var_t cpumask) | ||
121 | { | ||
122 | struct device_list_opp *list_dev; | ||
123 | struct device_opp *dev_opp; | ||
124 | struct device *dev; | ||
125 | int cpu, ret = 0; | ||
126 | |||
127 | rcu_read_lock(); | ||
128 | |||
129 | dev_opp = _find_device_opp(cpu_dev); | ||
130 | if (IS_ERR(dev_opp)) { | ||
131 | ret = -EINVAL; | ||
132 | goto out_rcu_read_unlock; | ||
133 | } | ||
134 | |||
135 | for_each_cpu(cpu, cpumask) { | ||
136 | if (cpu == cpu_dev->id) | ||
137 | continue; | ||
138 | |||
139 | dev = get_cpu_device(cpu); | ||
140 | if (!dev) { | ||
141 | dev_err(cpu_dev, "%s: failed to get cpu%d device\n", | ||
142 | __func__, cpu); | ||
143 | continue; | ||
144 | } | ||
145 | |||
146 | list_dev = _add_list_dev(dev, dev_opp); | ||
147 | if (!list_dev) { | ||
148 | dev_err(dev, "%s: failed to add list-dev for cpu%d device\n", | ||
149 | __func__, cpu); | ||
150 | continue; | ||
151 | } | ||
152 | } | ||
153 | out_rcu_read_unlock: | ||
154 | rcu_read_unlock(); | ||
155 | |||
156 | return 0; | ||
157 | } | ||
158 | EXPORT_SYMBOL_GPL(dev_pm_opp_set_sharing_cpus); | ||
159 | |||
160 | #ifdef CONFIG_OF | ||
161 | void dev_pm_opp_of_cpumask_remove_table(cpumask_var_t cpumask) | ||
162 | { | ||
163 | struct device *cpu_dev; | ||
164 | int cpu; | ||
165 | |||
166 | WARN_ON(cpumask_empty(cpumask)); | ||
167 | |||
168 | for_each_cpu(cpu, cpumask) { | ||
169 | cpu_dev = get_cpu_device(cpu); | ||
170 | if (!cpu_dev) { | ||
171 | pr_err("%s: failed to get cpu%d device\n", __func__, | ||
172 | cpu); | ||
173 | continue; | ||
174 | } | ||
175 | |||
176 | dev_pm_opp_of_remove_table(cpu_dev); | ||
177 | } | ||
178 | } | ||
179 | EXPORT_SYMBOL_GPL(dev_pm_opp_of_cpumask_remove_table); | ||
180 | |||
181 | int dev_pm_opp_of_cpumask_add_table(cpumask_var_t cpumask) | ||
182 | { | ||
183 | struct device *cpu_dev; | ||
184 | int cpu, ret = 0; | ||
185 | |||
186 | WARN_ON(cpumask_empty(cpumask)); | ||
187 | |||
188 | for_each_cpu(cpu, cpumask) { | ||
189 | cpu_dev = get_cpu_device(cpu); | ||
190 | if (!cpu_dev) { | ||
191 | pr_err("%s: failed to get cpu%d device\n", __func__, | ||
192 | cpu); | ||
193 | continue; | ||
194 | } | ||
195 | |||
196 | ret = dev_pm_opp_of_add_table(cpu_dev); | ||
197 | if (ret) { | ||
198 | pr_err("%s: couldn't find opp table for cpu:%d, %d\n", | ||
199 | __func__, cpu, ret); | ||
200 | |||
201 | /* Free all other OPPs */ | ||
202 | dev_pm_opp_of_cpumask_remove_table(cpumask); | ||
203 | break; | ||
204 | } | ||
205 | } | ||
206 | |||
207 | return ret; | ||
208 | } | ||
209 | EXPORT_SYMBOL_GPL(dev_pm_opp_of_cpumask_add_table); | ||
210 | |||
211 | /* | ||
212 | * Works only for OPP v2 bindings. | ||
213 | * | ||
214 | * cpumask should be already set to mask of cpu_dev->id. | ||
215 | * Returns -ENOENT if operating-points-v2 bindings aren't supported. | ||
216 | */ | ||
217 | int dev_pm_opp_of_get_sharing_cpus(struct device *cpu_dev, cpumask_var_t cpumask) | ||
218 | { | ||
219 | struct device_node *np, *tmp_np; | ||
220 | struct device *tcpu_dev; | ||
221 | int cpu, ret = 0; | ||
222 | |||
223 | /* Get OPP descriptor node */ | ||
224 | np = _of_get_opp_desc_node(cpu_dev); | ||
225 | if (!np) { | ||
226 | dev_dbg(cpu_dev, "%s: Couldn't find opp node: %ld\n", __func__, | ||
227 | PTR_ERR(np)); | ||
228 | return -ENOENT; | ||
229 | } | ||
230 | |||
231 | /* OPPs are shared ? */ | ||
232 | if (!of_property_read_bool(np, "opp-shared")) | ||
233 | goto put_cpu_node; | ||
234 | |||
235 | for_each_possible_cpu(cpu) { | ||
236 | if (cpu == cpu_dev->id) | ||
237 | continue; | ||
238 | |||
239 | tcpu_dev = get_cpu_device(cpu); | ||
240 | if (!tcpu_dev) { | ||
241 | dev_err(cpu_dev, "%s: failed to get cpu%d device\n", | ||
242 | __func__, cpu); | ||
243 | ret = -ENODEV; | ||
244 | goto put_cpu_node; | ||
245 | } | ||
246 | |||
247 | /* Get OPP descriptor node */ | ||
248 | tmp_np = _of_get_opp_desc_node(tcpu_dev); | ||
249 | if (!tmp_np) { | ||
250 | dev_err(tcpu_dev, "%s: Couldn't find opp node: %ld\n", | ||
251 | __func__, PTR_ERR(tmp_np)); | ||
252 | ret = PTR_ERR(tmp_np); | ||
253 | goto put_cpu_node; | ||
254 | } | ||
255 | |||
256 | /* CPUs are sharing opp node */ | ||
257 | if (np == tmp_np) | ||
258 | cpumask_set_cpu(cpu, cpumask); | ||
259 | |||
260 | of_node_put(tmp_np); | ||
261 | } | ||
262 | |||
263 | put_cpu_node: | ||
264 | of_node_put(np); | ||
265 | return ret; | ||
266 | } | ||
267 | EXPORT_SYMBOL_GPL(dev_pm_opp_of_get_sharing_cpus); | ||
268 | #endif | ||
diff --git a/drivers/base/power/opp/opp.h b/drivers/base/power/opp/opp.h new file mode 100644 index 000000000000..dcb38f78dae4 --- /dev/null +++ b/drivers/base/power/opp/opp.h | |||
@@ -0,0 +1,143 @@ | |||
1 | /* | ||
2 | * Generic OPP Interface | ||
3 | * | ||
4 | * Copyright (C) 2009-2010 Texas Instruments Incorporated. | ||
5 | * Nishanth Menon | ||
6 | * Romit Dasgupta | ||
7 | * Kevin Hilman | ||
8 | * | ||
9 | * This program is free software; you can redistribute it and/or modify | ||
10 | * it under the terms of the GNU General Public License version 2 as | ||
11 | * published by the Free Software Foundation. | ||
12 | */ | ||
13 | |||
14 | #ifndef __DRIVER_OPP_H__ | ||
15 | #define __DRIVER_OPP_H__ | ||
16 | |||
17 | #include <linux/device.h> | ||
18 | #include <linux/kernel.h> | ||
19 | #include <linux/list.h> | ||
20 | #include <linux/pm_opp.h> | ||
21 | #include <linux/rculist.h> | ||
22 | #include <linux/rcupdate.h> | ||
23 | |||
24 | /* | ||
25 | * Internal data structure organization with the OPP layer library is as | ||
26 | * follows: | ||
27 | * dev_opp_list (root) | ||
28 | * |- device 1 (represents voltage domain 1) | ||
29 | * | |- opp 1 (availability, freq, voltage) | ||
30 | * | |- opp 2 .. | ||
31 | * ... ... | ||
32 | * | `- opp n .. | ||
33 | * |- device 2 (represents the next voltage domain) | ||
34 | * ... | ||
35 | * `- device m (represents mth voltage domain) | ||
36 | * device 1, 2.. are represented by dev_opp structure while each opp | ||
37 | * is represented by the opp structure. | ||
38 | */ | ||
39 | |||
40 | /** | ||
41 | * struct dev_pm_opp - Generic OPP description structure | ||
42 | * @node: opp list node. The nodes are maintained throughout the lifetime | ||
43 | * of boot. It is expected only an optimal set of OPPs are | ||
44 | * added to the library by the SoC framework. | ||
45 | * RCU usage: opp list is traversed with RCU locks. node | ||
46 | * modification is possible realtime, hence the modifications | ||
47 | * are protected by the dev_opp_list_lock for integrity. | ||
48 | * IMPORTANT: the opp nodes should be maintained in increasing | ||
49 | * order. | ||
50 | * @dynamic: not-created from static DT entries. | ||
51 | * @available: true/false - marks if this OPP as available or not | ||
52 | * @turbo: true if turbo (boost) OPP | ||
53 | * @rate: Frequency in hertz | ||
54 | * @u_volt: Target voltage in microvolts corresponding to this OPP | ||
55 | * @u_volt_min: Minimum voltage in microvolts corresponding to this OPP | ||
56 | * @u_volt_max: Maximum voltage in microvolts corresponding to this OPP | ||
57 | * @u_amp: Maximum current drawn by the device in microamperes | ||
58 | * @clock_latency_ns: Latency (in nanoseconds) of switching to this OPP's | ||
59 | * frequency from any other OPP's frequency. | ||
60 | * @dev_opp: points back to the device_opp struct this opp belongs to | ||
61 | * @rcu_head: RCU callback head used for deferred freeing | ||
62 | * @np: OPP's device node. | ||
63 | * | ||
64 | * This structure stores the OPP information for a given device. | ||
65 | */ | ||
66 | struct dev_pm_opp { | ||
67 | struct list_head node; | ||
68 | |||
69 | bool available; | ||
70 | bool dynamic; | ||
71 | bool turbo; | ||
72 | unsigned long rate; | ||
73 | |||
74 | unsigned long u_volt; | ||
75 | unsigned long u_volt_min; | ||
76 | unsigned long u_volt_max; | ||
77 | unsigned long u_amp; | ||
78 | unsigned long clock_latency_ns; | ||
79 | |||
80 | struct device_opp *dev_opp; | ||
81 | struct rcu_head rcu_head; | ||
82 | |||
83 | struct device_node *np; | ||
84 | }; | ||
85 | |||
86 | /** | ||
87 | * struct device_list_opp - devices managed by 'struct device_opp' | ||
88 | * @node: list node | ||
89 | * @dev: device to which the struct object belongs | ||
90 | * @rcu_head: RCU callback head used for deferred freeing | ||
91 | * | ||
92 | * This is an internal data structure maintaining the list of devices that are | ||
93 | * managed by 'struct device_opp'. | ||
94 | */ | ||
95 | struct device_list_opp { | ||
96 | struct list_head node; | ||
97 | const struct device *dev; | ||
98 | struct rcu_head rcu_head; | ||
99 | }; | ||
100 | |||
101 | /** | ||
102 | * struct device_opp - Device opp structure | ||
103 | * @node: list node - contains the devices with OPPs that | ||
104 | * have been registered. Nodes once added are not modified in this | ||
105 | * list. | ||
106 | * RCU usage: nodes are not modified in the list of device_opp, | ||
107 | * however addition is possible and is secured by dev_opp_list_lock | ||
108 | * @srcu_head: notifier head to notify the OPP availability changes. | ||
109 | * @rcu_head: RCU callback head used for deferred freeing | ||
110 | * @dev_list: list of devices that share these OPPs | ||
111 | * @opp_list: list of opps | ||
112 | * @np: struct device_node pointer for opp's DT node. | ||
113 | * @shared_opp: OPP is shared between multiple devices. | ||
114 | * | ||
115 | * This is an internal data structure maintaining the link to opps attached to | ||
116 | * a device. This structure is not meant to be shared to users as it is | ||
117 | * meant for book keeping and private to OPP library. | ||
118 | * | ||
119 | * Because the opp structures can be used from both rcu and srcu readers, we | ||
120 | * need to wait for the grace period of both of them before freeing any | ||
121 | * resources. And so we have used kfree_rcu() from within call_srcu() handlers. | ||
122 | */ | ||
123 | struct device_opp { | ||
124 | struct list_head node; | ||
125 | |||
126 | struct srcu_notifier_head srcu_head; | ||
127 | struct rcu_head rcu_head; | ||
128 | struct list_head dev_list; | ||
129 | struct list_head opp_list; | ||
130 | |||
131 | struct device_node *np; | ||
132 | unsigned long clock_latency_ns_max; | ||
133 | bool shared_opp; | ||
134 | struct dev_pm_opp *suspend_opp; | ||
135 | }; | ||
136 | |||
137 | /* Routines internal to opp core */ | ||
138 | struct device_opp *_find_device_opp(struct device *dev); | ||
139 | struct device_list_opp *_add_list_dev(const struct device *dev, | ||
140 | struct device_opp *dev_opp); | ||
141 | struct device_node *_of_get_opp_desc_node(struct device *dev); | ||
142 | |||
143 | #endif /* __DRIVER_OPP_H__ */ | ||