diff options
author | Rafael J. Wysocki <rafael.j.wysocki@intel.com> | 2015-11-01 18:54:37 -0500 |
---|---|---|
committer | Rafael J. Wysocki <rafael.j.wysocki@intel.com> | 2015-11-01 18:54:37 -0500 |
commit | 394f7164e65a734f815543d918c7bdc0587593a6 (patch) | |
tree | 19e57816a0c95a67e5f8cd20276673bc69df0556 /drivers/base | |
parent | dcf3d0183f100a14f60eb5993e124a4a1322ee9a (diff) | |
parent | a6eed752f5fb40990eb28fddb2b93258fb7e3be0 (diff) |
Merge branch 'pm-opp'
* pm-opp:
PM / OPP: passing NULL to PTR_ERR()
PM / OPP: Move cpu specific code to opp/cpu.c
PM / OPP: Move opp core to its own directory
PM / OPP: Prefix exported opp routines with dev_pm_opp_
PM / OPP: Rename opp init/free table routines
PM / OPP: reuse of_parse_phandle()
Diffstat (limited to 'drivers/base')
-rw-r--r-- | drivers/base/power/Makefile | 2 | ||||
-rw-r--r-- | drivers/base/power/opp/Makefile | 2 | ||||
-rw-r--r-- | drivers/base/power/opp/core.c (renamed from drivers/base/power/opp.c) | 363 | ||||
-rw-r--r-- | drivers/base/power/opp/cpu.c | 267 | ||||
-rw-r--r-- | drivers/base/power/opp/opp.h | 143 |
5 files changed, 443 insertions, 334 deletions
diff --git a/drivers/base/power/Makefile b/drivers/base/power/Makefile index f94a6ccfe787..5998c53280f5 100644 --- a/drivers/base/power/Makefile +++ b/drivers/base/power/Makefile | |||
@@ -1,7 +1,7 @@ | |||
1 | obj-$(CONFIG_PM) += sysfs.o generic_ops.o common.o qos.o runtime.o wakeirq.o | 1 | obj-$(CONFIG_PM) += sysfs.o generic_ops.o common.o qos.o runtime.o wakeirq.o |
2 | obj-$(CONFIG_PM_SLEEP) += main.o wakeup.o | 2 | obj-$(CONFIG_PM_SLEEP) += main.o wakeup.o |
3 | obj-$(CONFIG_PM_TRACE_RTC) += trace.o | 3 | obj-$(CONFIG_PM_TRACE_RTC) += trace.o |
4 | obj-$(CONFIG_PM_OPP) += opp.o | 4 | obj-$(CONFIG_PM_OPP) += opp/ |
5 | obj-$(CONFIG_PM_GENERIC_DOMAINS) += domain.o domain_governor.o | 5 | obj-$(CONFIG_PM_GENERIC_DOMAINS) += domain.o domain_governor.o |
6 | obj-$(CONFIG_HAVE_CLK) += clock_ops.o | 6 | obj-$(CONFIG_HAVE_CLK) += clock_ops.o |
7 | 7 | ||
diff --git a/drivers/base/power/opp/Makefile b/drivers/base/power/opp/Makefile new file mode 100644 index 000000000000..33c1e18c41a4 --- /dev/null +++ b/drivers/base/power/opp/Makefile | |||
@@ -0,0 +1,2 @@ | |||
1 | ccflags-$(CONFIG_DEBUG_DRIVER) := -DDEBUG | ||
2 | obj-y += core.o cpu.o | ||
diff --git a/drivers/base/power/opp.c b/drivers/base/power/opp/core.c index 7ae7cd990fbf..d5c1149ff123 100644 --- a/drivers/base/power/opp.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 | ||
@@ -828,8 +711,8 @@ static int _opp_add(struct device *dev, struct dev_pm_opp *new_opp, | |||
828 | * The opp is made available by default and it can be controlled using | 711 | * The opp is made available by default and it can be controlled using |
829 | * dev_pm_opp_enable/disable functions and may be removed by dev_pm_opp_remove. | 712 | * dev_pm_opp_enable/disable functions and may be removed by dev_pm_opp_remove. |
830 | * | 713 | * |
831 | * NOTE: "dynamic" parameter impacts OPPs added by the of_init_opp_table and | 714 | * NOTE: "dynamic" parameter impacts OPPs added by the dev_pm_opp_of_add_table |
832 | * freed by of_free_opp_table. | 715 | * and freed by dev_pm_opp_of_remove_table. |
833 | * | 716 | * |
834 | * Locking: The internal device_opp and opp structures are RCU protected. | 717 | * Locking: The internal device_opp and opp structures are RCU protected. |
835 | * Hence this function internally uses RCU updater strategy with mutex locks | 718 | * Hence this function internally uses RCU updater strategy with mutex locks |
@@ -1220,7 +1103,8 @@ EXPORT_SYMBOL_GPL(dev_pm_opp_get_notifier); | |||
1220 | 1103 | ||
1221 | #ifdef CONFIG_OF | 1104 | #ifdef CONFIG_OF |
1222 | /** | 1105 | /** |
1223 | * of_free_opp_table() - Free OPP table entries created from static DT entries | 1106 | * dev_pm_opp_of_remove_table() - Free OPP table entries created from static DT |
1107 | * entries | ||
1224 | * @dev: device pointer used to lookup device OPPs. | 1108 | * @dev: device pointer used to lookup device OPPs. |
1225 | * | 1109 | * |
1226 | * Free OPPs created using static entries present in DT. | 1110 | * Free OPPs created using static entries present in DT. |
@@ -1231,7 +1115,7 @@ EXPORT_SYMBOL_GPL(dev_pm_opp_get_notifier); | |||
1231 | * that this function is *NOT* called under RCU protection or in contexts where | 1115 | * that this function is *NOT* called under RCU protection or in contexts where |
1232 | * mutex cannot be locked. | 1116 | * mutex cannot be locked. |
1233 | */ | 1117 | */ |
1234 | void of_free_opp_table(struct device *dev) | 1118 | void dev_pm_opp_of_remove_table(struct device *dev) |
1235 | { | 1119 | { |
1236 | struct device_opp *dev_opp; | 1120 | struct device_opp *dev_opp; |
1237 | struct dev_pm_opp *opp, *tmp; | 1121 | struct dev_pm_opp *opp, *tmp; |
@@ -1266,91 +1150,34 @@ void of_free_opp_table(struct device *dev) | |||
1266 | unlock: | 1150 | unlock: |
1267 | mutex_unlock(&dev_opp_list_lock); | 1151 | mutex_unlock(&dev_opp_list_lock); |
1268 | } | 1152 | } |
1269 | EXPORT_SYMBOL_GPL(of_free_opp_table); | 1153 | EXPORT_SYMBOL_GPL(dev_pm_opp_of_remove_table); |
1270 | 1154 | ||
1271 | void of_cpumask_free_opp_table(cpumask_var_t cpumask) | 1155 | /* Returns opp descriptor node for a device, caller must do of_node_put() */ |
1156 | struct device_node *_of_get_opp_desc_node(struct device *dev) | ||
1272 | { | 1157 | { |
1273 | struct device *cpu_dev; | ||
1274 | int cpu; | ||
1275 | |||
1276 | WARN_ON(cpumask_empty(cpumask)); | ||
1277 | |||
1278 | for_each_cpu(cpu, cpumask) { | ||
1279 | cpu_dev = get_cpu_device(cpu); | ||
1280 | if (!cpu_dev) { | ||
1281 | pr_err("%s: failed to get cpu%d device\n", __func__, | ||
1282 | cpu); | ||
1283 | continue; | ||
1284 | } | ||
1285 | |||
1286 | of_free_opp_table(cpu_dev); | ||
1287 | } | ||
1288 | } | ||
1289 | EXPORT_SYMBOL_GPL(of_cpumask_free_opp_table); | ||
1290 | |||
1291 | /* Returns opp descriptor node from its phandle. Caller must do of_node_put() */ | ||
1292 | static struct device_node * | ||
1293 | _of_get_opp_desc_node_from_prop(struct device *dev, const struct property *prop) | ||
1294 | { | ||
1295 | struct device_node *opp_np; | ||
1296 | |||
1297 | opp_np = of_find_node_by_phandle(be32_to_cpup(prop->value)); | ||
1298 | if (!opp_np) { | ||
1299 | dev_err(dev, "%s: Prop: %s contains invalid opp desc phandle\n", | ||
1300 | __func__, prop->name); | ||
1301 | return ERR_PTR(-EINVAL); | ||
1302 | } | ||
1303 | |||
1304 | return opp_np; | ||
1305 | } | ||
1306 | |||
1307 | /* Returns opp descriptor node for a device. Caller must do of_node_put() */ | ||
1308 | static struct device_node *_of_get_opp_desc_node(struct device *dev) | ||
1309 | { | ||
1310 | const struct property *prop; | ||
1311 | |||
1312 | prop = of_find_property(dev->of_node, "operating-points-v2", NULL); | ||
1313 | if (!prop) | ||
1314 | return ERR_PTR(-ENODEV); | ||
1315 | if (!prop->value) | ||
1316 | return ERR_PTR(-ENODATA); | ||
1317 | |||
1318 | /* | 1158 | /* |
1319 | * TODO: Support for multiple OPP tables. | 1159 | * TODO: Support for multiple OPP tables. |
1320 | * | 1160 | * |
1321 | * There should be only ONE phandle present in "operating-points-v2" | 1161 | * There should be only ONE phandle present in "operating-points-v2" |
1322 | * property. | 1162 | * property. |
1323 | */ | 1163 | */ |
1324 | if (prop->length != sizeof(__be32)) { | ||
1325 | dev_err(dev, "%s: Invalid opp desc phandle\n", __func__); | ||
1326 | return ERR_PTR(-EINVAL); | ||
1327 | } | ||
1328 | 1164 | ||
1329 | return _of_get_opp_desc_node_from_prop(dev, prop); | 1165 | return of_parse_phandle(dev->of_node, "operating-points-v2", 0); |
1330 | } | 1166 | } |
1331 | 1167 | ||
1332 | /* Initializes OPP tables based on new bindings */ | 1168 | /* Initializes OPP tables based on new bindings */ |
1333 | static int _of_init_opp_table_v2(struct device *dev, | 1169 | static int _of_add_opp_table_v2(struct device *dev, struct device_node *opp_np) |
1334 | const struct property *prop) | ||
1335 | { | 1170 | { |
1336 | struct device_node *opp_np, *np; | 1171 | struct device_node *np; |
1337 | struct device_opp *dev_opp; | 1172 | struct device_opp *dev_opp; |
1338 | int ret = 0, count = 0; | 1173 | int ret = 0, count = 0; |
1339 | 1174 | ||
1340 | if (!prop->value) | ||
1341 | return -ENODATA; | ||
1342 | |||
1343 | /* Get opp node */ | ||
1344 | opp_np = _of_get_opp_desc_node_from_prop(dev, prop); | ||
1345 | if (IS_ERR(opp_np)) | ||
1346 | return PTR_ERR(opp_np); | ||
1347 | |||
1348 | dev_opp = _managed_opp(opp_np); | 1175 | dev_opp = _managed_opp(opp_np); |
1349 | if (dev_opp) { | 1176 | if (dev_opp) { |
1350 | /* OPPs are already managed */ | 1177 | /* OPPs are already managed */ |
1351 | if (!_add_list_dev(dev, dev_opp)) | 1178 | if (!_add_list_dev(dev, dev_opp)) |
1352 | ret = -ENOMEM; | 1179 | ret = -ENOMEM; |
1353 | goto put_opp_np; | 1180 | return ret; |
1354 | } | 1181 | } |
1355 | 1182 | ||
1356 | /* We have opp-list node now, iterate over it and add OPPs */ | 1183 | /* We have opp-list node now, iterate over it and add OPPs */ |
@@ -1366,10 +1193,8 @@ static int _of_init_opp_table_v2(struct device *dev, | |||
1366 | } | 1193 | } |
1367 | 1194 | ||
1368 | /* There should be one of more OPP defined */ | 1195 | /* There should be one of more OPP defined */ |
1369 | if (WARN_ON(!count)) { | 1196 | if (WARN_ON(!count)) |
1370 | ret = -ENOENT; | 1197 | return -ENOENT; |
1371 | goto put_opp_np; | ||
1372 | } | ||
1373 | 1198 | ||
1374 | dev_opp = _find_device_opp(dev); | 1199 | dev_opp = _find_device_opp(dev); |
1375 | if (WARN_ON(IS_ERR(dev_opp))) { | 1200 | if (WARN_ON(IS_ERR(dev_opp))) { |
@@ -1380,19 +1205,16 @@ static int _of_init_opp_table_v2(struct device *dev, | |||
1380 | dev_opp->np = opp_np; | 1205 | dev_opp->np = opp_np; |
1381 | dev_opp->shared_opp = of_property_read_bool(opp_np, "opp-shared"); | 1206 | dev_opp->shared_opp = of_property_read_bool(opp_np, "opp-shared"); |
1382 | 1207 | ||
1383 | of_node_put(opp_np); | ||
1384 | return 0; | 1208 | return 0; |
1385 | 1209 | ||
1386 | free_table: | 1210 | free_table: |
1387 | of_free_opp_table(dev); | 1211 | dev_pm_opp_of_remove_table(dev); |
1388 | put_opp_np: | ||
1389 | of_node_put(opp_np); | ||
1390 | 1212 | ||
1391 | return ret; | 1213 | return ret; |
1392 | } | 1214 | } |
1393 | 1215 | ||
1394 | /* Initializes OPP tables based on old-deprecated bindings */ | 1216 | /* Initializes OPP tables based on old-deprecated bindings */ |
1395 | static int _of_init_opp_table_v1(struct device *dev) | 1217 | static int _of_add_opp_table_v1(struct device *dev) |
1396 | { | 1218 | { |
1397 | const struct property *prop; | 1219 | const struct property *prop; |
1398 | const __be32 *val; | 1220 | const __be32 *val; |
@@ -1429,7 +1251,7 @@ static int _of_init_opp_table_v1(struct device *dev) | |||
1429 | } | 1251 | } |
1430 | 1252 | ||
1431 | /** | 1253 | /** |
1432 | * of_init_opp_table() - Initialize opp table from device tree | 1254 | * dev_pm_opp_of_add_table() - Initialize opp table from device tree |
1433 | * @dev: device pointer used to lookup device OPPs. | 1255 | * @dev: device pointer used to lookup device OPPs. |
1434 | * | 1256 | * |
1435 | * Register the initial OPP table with the OPP library for given device. | 1257 | * Register the initial OPP table with the OPP library for given device. |
@@ -1451,153 +1273,28 @@ static int _of_init_opp_table_v1(struct device *dev) | |||
1451 | * -ENODATA when empty 'operating-points' property is found | 1273 | * -ENODATA when empty 'operating-points' property is found |
1452 | * -EINVAL when invalid entries are found in opp-v2 table | 1274 | * -EINVAL when invalid entries are found in opp-v2 table |
1453 | */ | 1275 | */ |
1454 | int of_init_opp_table(struct device *dev) | 1276 | int dev_pm_opp_of_add_table(struct device *dev) |
1455 | { | 1277 | { |
1456 | const struct property *prop; | 1278 | struct device_node *opp_np; |
1279 | int ret; | ||
1457 | 1280 | ||
1458 | /* | 1281 | /* |
1459 | * OPPs have two version of bindings now. The older one is deprecated, | 1282 | * OPPs have two version of bindings now. The older one is deprecated, |
1460 | * try for the new binding first. | 1283 | * try for the new binding first. |
1461 | */ | 1284 | */ |
1462 | prop = of_find_property(dev->of_node, "operating-points-v2", NULL); | 1285 | opp_np = _of_get_opp_desc_node(dev); |
1463 | if (!prop) { | 1286 | if (!opp_np) { |
1464 | /* | 1287 | /* |
1465 | * Try old-deprecated bindings for backward compatibility with | 1288 | * Try old-deprecated bindings for backward compatibility with |
1466 | * older dtbs. | 1289 | * older dtbs. |
1467 | */ | 1290 | */ |
1468 | return _of_init_opp_table_v1(dev); | 1291 | return _of_add_opp_table_v1(dev); |
1469 | } | ||
1470 | |||
1471 | return _of_init_opp_table_v2(dev, prop); | ||
1472 | } | ||
1473 | EXPORT_SYMBOL_GPL(of_init_opp_table); | ||
1474 | |||
1475 | int of_cpumask_init_opp_table(cpumask_var_t cpumask) | ||
1476 | { | ||
1477 | struct device *cpu_dev; | ||
1478 | int cpu, ret = 0; | ||
1479 | |||
1480 | WARN_ON(cpumask_empty(cpumask)); | ||
1481 | |||
1482 | for_each_cpu(cpu, cpumask) { | ||
1483 | cpu_dev = get_cpu_device(cpu); | ||
1484 | if (!cpu_dev) { | ||
1485 | pr_err("%s: failed to get cpu%d device\n", __func__, | ||
1486 | cpu); | ||
1487 | continue; | ||
1488 | } | ||
1489 | |||
1490 | ret = of_init_opp_table(cpu_dev); | ||
1491 | if (ret) { | ||
1492 | pr_err("%s: couldn't find opp table for cpu:%d, %d\n", | ||
1493 | __func__, cpu, ret); | ||
1494 | |||
1495 | /* Free all other OPPs */ | ||
1496 | of_cpumask_free_opp_table(cpumask); | ||
1497 | break; | ||
1498 | } | ||
1499 | } | 1292 | } |
1500 | 1293 | ||
1501 | return ret; | 1294 | ret = _of_add_opp_table_v2(dev, opp_np); |
1502 | } | 1295 | of_node_put(opp_np); |
1503 | EXPORT_SYMBOL_GPL(of_cpumask_init_opp_table); | ||
1504 | |||
1505 | /* Required only for V1 bindings, as v2 can manage it from DT itself */ | ||
1506 | int set_cpus_sharing_opps(struct device *cpu_dev, cpumask_var_t cpumask) | ||
1507 | { | ||
1508 | struct device_list_opp *list_dev; | ||
1509 | struct device_opp *dev_opp; | ||
1510 | struct device *dev; | ||
1511 | int cpu, ret = 0; | ||
1512 | |||
1513 | rcu_read_lock(); | ||
1514 | |||
1515 | dev_opp = _find_device_opp(cpu_dev); | ||
1516 | if (IS_ERR(dev_opp)) { | ||
1517 | ret = -EINVAL; | ||
1518 | goto out_rcu_read_unlock; | ||
1519 | } | ||
1520 | |||
1521 | for_each_cpu(cpu, cpumask) { | ||
1522 | if (cpu == cpu_dev->id) | ||
1523 | continue; | ||
1524 | |||
1525 | dev = get_cpu_device(cpu); | ||
1526 | if (!dev) { | ||
1527 | dev_err(cpu_dev, "%s: failed to get cpu%d device\n", | ||
1528 | __func__, cpu); | ||
1529 | continue; | ||
1530 | } | ||
1531 | |||
1532 | list_dev = _add_list_dev(dev, dev_opp); | ||
1533 | if (!list_dev) { | ||
1534 | dev_err(dev, "%s: failed to add list-dev for cpu%d device\n", | ||
1535 | __func__, cpu); | ||
1536 | continue; | ||
1537 | } | ||
1538 | } | ||
1539 | out_rcu_read_unlock: | ||
1540 | rcu_read_unlock(); | ||
1541 | |||
1542 | return 0; | ||
1543 | } | ||
1544 | EXPORT_SYMBOL_GPL(set_cpus_sharing_opps); | ||
1545 | |||
1546 | /* | ||
1547 | * Works only for OPP v2 bindings. | ||
1548 | * | ||
1549 | * cpumask should be already set to mask of cpu_dev->id. | ||
1550 | * Returns -ENOENT if operating-points-v2 bindings aren't supported. | ||
1551 | */ | ||
1552 | int of_get_cpus_sharing_opps(struct device *cpu_dev, cpumask_var_t cpumask) | ||
1553 | { | ||
1554 | struct device_node *np, *tmp_np; | ||
1555 | struct device *tcpu_dev; | ||
1556 | int cpu, ret = 0; | ||
1557 | |||
1558 | /* Get OPP descriptor node */ | ||
1559 | np = _of_get_opp_desc_node(cpu_dev); | ||
1560 | if (IS_ERR(np)) { | ||
1561 | dev_dbg(cpu_dev, "%s: Couldn't find opp node: %ld\n", __func__, | ||
1562 | PTR_ERR(np)); | ||
1563 | return -ENOENT; | ||
1564 | } | ||
1565 | |||
1566 | /* OPPs are shared ? */ | ||
1567 | if (!of_property_read_bool(np, "opp-shared")) | ||
1568 | goto put_cpu_node; | ||
1569 | |||
1570 | for_each_possible_cpu(cpu) { | ||
1571 | if (cpu == cpu_dev->id) | ||
1572 | continue; | ||
1573 | |||
1574 | tcpu_dev = get_cpu_device(cpu); | ||
1575 | if (!tcpu_dev) { | ||
1576 | dev_err(cpu_dev, "%s: failed to get cpu%d device\n", | ||
1577 | __func__, cpu); | ||
1578 | ret = -ENODEV; | ||
1579 | goto put_cpu_node; | ||
1580 | } | ||
1581 | |||
1582 | /* Get OPP descriptor node */ | ||
1583 | tmp_np = _of_get_opp_desc_node(tcpu_dev); | ||
1584 | if (IS_ERR(tmp_np)) { | ||
1585 | dev_err(tcpu_dev, "%s: Couldn't find opp node: %ld\n", | ||
1586 | __func__, PTR_ERR(tmp_np)); | ||
1587 | ret = PTR_ERR(tmp_np); | ||
1588 | goto put_cpu_node; | ||
1589 | } | ||
1590 | |||
1591 | /* CPUs are sharing opp node */ | ||
1592 | if (np == tmp_np) | ||
1593 | cpumask_set_cpu(cpu, cpumask); | ||
1594 | |||
1595 | of_node_put(tmp_np); | ||
1596 | } | ||
1597 | 1296 | ||
1598 | put_cpu_node: | ||
1599 | of_node_put(np); | ||
1600 | return ret; | 1297 | return ret; |
1601 | } | 1298 | } |
1602 | EXPORT_SYMBOL_GPL(of_get_cpus_sharing_opps); | 1299 | EXPORT_SYMBOL_GPL(dev_pm_opp_of_add_table); |
1603 | #endif | 1300 | #endif |
diff --git a/drivers/base/power/opp/cpu.c b/drivers/base/power/opp/cpu.c new file mode 100644 index 000000000000..7654c5606307 --- /dev/null +++ b/drivers/base/power/opp/cpu.c | |||
@@ -0,0 +1,267 @@ | |||
1 | /* | ||
2 | * Generic OPP helper interface for CPU device | ||
3 | * | ||
4 | * Copyright (C) 2009-2014 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 | #include <linux/cpu.h> | ||
14 | #include <linux/cpufreq.h> | ||
15 | #include <linux/err.h> | ||
16 | #include <linux/errno.h> | ||
17 | #include <linux/export.h> | ||
18 | #include <linux/of.h> | ||
19 | #include <linux/slab.h> | ||
20 | |||
21 | #include "opp.h" | ||
22 | |||
23 | #ifdef CONFIG_CPU_FREQ | ||
24 | |||
25 | /** | ||
26 | * dev_pm_opp_init_cpufreq_table() - create a cpufreq table for a device | ||
27 | * @dev: device for which we do this operation | ||
28 | * @table: Cpufreq table returned back to caller | ||
29 | * | ||
30 | * Generate a cpufreq table for a provided device- this assumes that the | ||
31 | * opp list is already initialized and ready for usage. | ||
32 | * | ||
33 | * This function allocates required memory for the cpufreq table. It is | ||
34 | * expected that the caller does the required maintenance such as freeing | ||
35 | * the table as required. | ||
36 | * | ||
37 | * Returns -EINVAL for bad pointers, -ENODEV if the device is not found, -ENOMEM | ||
38 | * if no memory available for the operation (table is not populated), returns 0 | ||
39 | * if successful and table is populated. | ||
40 | * | ||
41 | * WARNING: It is important for the callers to ensure refreshing their copy of | ||
42 | * the table if any of the mentioned functions have been invoked in the interim. | ||
43 | * | ||
44 | * Locking: The internal device_opp and opp structures are RCU protected. | ||
45 | * Since we just use the regular accessor functions to access the internal data | ||
46 | * structures, we use RCU read lock inside this function. As a result, users of | ||
47 | * this function DONOT need to use explicit locks for invoking. | ||
48 | */ | ||
49 | int dev_pm_opp_init_cpufreq_table(struct device *dev, | ||
50 | struct cpufreq_frequency_table **table) | ||
51 | { | ||
52 | struct dev_pm_opp *opp; | ||
53 | struct cpufreq_frequency_table *freq_table = NULL; | ||
54 | int i, max_opps, ret = 0; | ||
55 | unsigned long rate; | ||
56 | |||
57 | rcu_read_lock(); | ||
58 | |||
59 | max_opps = dev_pm_opp_get_opp_count(dev); | ||
60 | if (max_opps <= 0) { | ||
61 | ret = max_opps ? max_opps : -ENODATA; | ||
62 | goto out; | ||
63 | } | ||
64 | |||
65 | freq_table = kcalloc((max_opps + 1), sizeof(*freq_table), GFP_ATOMIC); | ||
66 | if (!freq_table) { | ||
67 | ret = -ENOMEM; | ||
68 | goto out; | ||
69 | } | ||
70 | |||
71 | for (i = 0, rate = 0; i < max_opps; i++, rate++) { | ||
72 | /* find next rate */ | ||
73 | opp = dev_pm_opp_find_freq_ceil(dev, &rate); | ||
74 | if (IS_ERR(opp)) { | ||
75 | ret = PTR_ERR(opp); | ||
76 | goto out; | ||
77 | } | ||
78 | freq_table[i].driver_data = i; | ||
79 | freq_table[i].frequency = rate / 1000; | ||
80 | |||
81 | /* Is Boost/turbo opp ? */ | ||
82 | if (dev_pm_opp_is_turbo(opp)) | ||
83 | freq_table[i].flags = CPUFREQ_BOOST_FREQ; | ||
84 | } | ||
85 | |||
86 | freq_table[i].driver_data = i; | ||
87 | freq_table[i].frequency = CPUFREQ_TABLE_END; | ||
88 | |||
89 | *table = &freq_table[0]; | ||
90 | |||
91 | out: | ||
92 | rcu_read_unlock(); | ||
93 | if (ret) | ||
94 | kfree(freq_table); | ||
95 | |||
96 | return ret; | ||
97 | } | ||
98 | EXPORT_SYMBOL_GPL(dev_pm_opp_init_cpufreq_table); | ||
99 | |||
100 | /** | ||
101 | * dev_pm_opp_free_cpufreq_table() - free the cpufreq table | ||
102 | * @dev: device for which we do this operation | ||
103 | * @table: table to free | ||
104 | * | ||
105 | * Free up the table allocated by dev_pm_opp_init_cpufreq_table | ||
106 | */ | ||
107 | void dev_pm_opp_free_cpufreq_table(struct device *dev, | ||
108 | struct cpufreq_frequency_table **table) | ||
109 | { | ||
110 | if (!table) | ||
111 | return; | ||
112 | |||
113 | kfree(*table); | ||
114 | *table = NULL; | ||
115 | } | ||
116 | EXPORT_SYMBOL_GPL(dev_pm_opp_free_cpufreq_table); | ||
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 cpu_dev node.\n", __func__); | ||
227 | return -ENOENT; | ||
228 | } | ||
229 | |||
230 | /* OPPs are shared ? */ | ||
231 | if (!of_property_read_bool(np, "opp-shared")) | ||
232 | goto put_cpu_node; | ||
233 | |||
234 | for_each_possible_cpu(cpu) { | ||
235 | if (cpu == cpu_dev->id) | ||
236 | continue; | ||
237 | |||
238 | tcpu_dev = get_cpu_device(cpu); | ||
239 | if (!tcpu_dev) { | ||
240 | dev_err(cpu_dev, "%s: failed to get cpu%d device\n", | ||
241 | __func__, cpu); | ||
242 | ret = -ENODEV; | ||
243 | goto put_cpu_node; | ||
244 | } | ||
245 | |||
246 | /* Get OPP descriptor node */ | ||
247 | tmp_np = _of_get_opp_desc_node(tcpu_dev); | ||
248 | if (!tmp_np) { | ||
249 | dev_err(tcpu_dev, "%s: Couldn't find tcpu_dev node.\n", | ||
250 | __func__); | ||
251 | ret = -ENOENT; | ||
252 | goto put_cpu_node; | ||
253 | } | ||
254 | |||
255 | /* CPUs are sharing opp node */ | ||
256 | if (np == tmp_np) | ||
257 | cpumask_set_cpu(cpu, cpumask); | ||
258 | |||
259 | of_node_put(tmp_np); | ||
260 | } | ||
261 | |||
262 | put_cpu_node: | ||
263 | of_node_put(np); | ||
264 | return ret; | ||
265 | } | ||
266 | EXPORT_SYMBOL_GPL(dev_pm_opp_of_get_sharing_cpus); | ||
267 | #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__ */ | ||