Skip to content

Commit 8d4d4e9

Browse files
vireshkrafaeljw
authored andcommitted
PM / OPP: Add helpers for initializing CPU OPPs
With "operating-points-v2" its possible to tell which devices share OPPs. We already have infrastructure to decode that information. This patch adds following APIs: - of_get_cpus_sharing_opps: Returns cpumask of CPUs sharing OPPs (only valid with v2 bindings). - of_cpumask_init_opp_table: Initializes OPPs for all CPUs present in cpumask. - of_cpumask_free_opp_table: Frees OPPs for all CPUs present in cpumask. - set_cpus_sharing_opps: Sets which CPUs share OPPs (only valid with old OPP bindings, as this information isn't present in DT). Reviewed-by: Stephen Boyd <[email protected]> Reviewed-by: Bartlomiej Zolnierkiewicz <[email protected]> Signed-off-by: Viresh Kumar <[email protected]> Signed-off-by: Rafael J. Wysocki <[email protected]>
1 parent ad656a6 commit 8d4d4e9

File tree

2 files changed

+198
-0
lines changed

2 files changed

+198
-0
lines changed

drivers/base/power/opp.c

Lines changed: 175 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
* published by the Free Software Foundation.
1212
*/
1313

14+
#include <linux/cpu.h>
1415
#include <linux/kernel.h>
1516
#include <linux/errno.h>
1617
#include <linux/err.h>
@@ -1195,6 +1196,26 @@ void of_free_opp_table(struct device *dev)
11951196
}
11961197
EXPORT_SYMBOL_GPL(of_free_opp_table);
11971198

1199+
void of_cpumask_free_opp_table(cpumask_var_t cpumask)
1200+
{
1201+
struct device *cpu_dev;
1202+
int cpu;
1203+
1204+
WARN_ON(cpumask_empty(cpumask));
1205+
1206+
for_each_cpu(cpu, cpumask) {
1207+
cpu_dev = get_cpu_device(cpu);
1208+
if (!cpu_dev) {
1209+
pr_err("%s: failed to get cpu%d device\n", __func__,
1210+
cpu);
1211+
continue;
1212+
}
1213+
1214+
of_free_opp_table(cpu_dev);
1215+
}
1216+
}
1217+
EXPORT_SYMBOL_GPL(of_cpumask_free_opp_table);
1218+
11981219
/* Returns opp descriptor node from its phandle. Caller must do of_node_put() */
11991220
static struct device_node *
12001221
_of_get_opp_desc_node_from_prop(struct device *dev, const struct property *prop)
@@ -1211,6 +1232,31 @@ _of_get_opp_desc_node_from_prop(struct device *dev, const struct property *prop)
12111232
return opp_np;
12121233
}
12131234

1235+
/* Returns opp descriptor node for a device. Caller must do of_node_put() */
1236+
static struct device_node *_of_get_opp_desc_node(struct device *dev)
1237+
{
1238+
const struct property *prop;
1239+
1240+
prop = of_find_property(dev->of_node, "operating-points-v2", NULL);
1241+
if (!prop)
1242+
return ERR_PTR(-ENODEV);
1243+
if (!prop->value)
1244+
return ERR_PTR(-ENODATA);
1245+
1246+
/*
1247+
* TODO: Support for multiple OPP tables.
1248+
*
1249+
* There should be only ONE phandle present in "operating-points-v2"
1250+
* property.
1251+
*/
1252+
if (prop->length != sizeof(__be32)) {
1253+
dev_err(dev, "%s: Invalid opp desc phandle\n", __func__);
1254+
return ERR_PTR(-EINVAL);
1255+
}
1256+
1257+
return _of_get_opp_desc_node_from_prop(dev, prop);
1258+
}
1259+
12141260
/* Initializes OPP tables based on new bindings */
12151261
static int _of_init_opp_table_v2(struct device *dev,
12161262
const struct property *prop)
@@ -1351,4 +1397,133 @@ int of_init_opp_table(struct device *dev)
13511397
return _of_init_opp_table_v2(dev, prop);
13521398
}
13531399
EXPORT_SYMBOL_GPL(of_init_opp_table);
1400+
1401+
int of_cpumask_init_opp_table(cpumask_var_t cpumask)
1402+
{
1403+
struct device *cpu_dev;
1404+
int cpu, ret = 0;
1405+
1406+
WARN_ON(cpumask_empty(cpumask));
1407+
1408+
for_each_cpu(cpu, cpumask) {
1409+
cpu_dev = get_cpu_device(cpu);
1410+
if (!cpu_dev) {
1411+
pr_err("%s: failed to get cpu%d device\n", __func__,
1412+
cpu);
1413+
continue;
1414+
}
1415+
1416+
ret = of_init_opp_table(cpu_dev);
1417+
if (ret) {
1418+
pr_err("%s: couldn't find opp table for cpu:%d, %d\n",
1419+
__func__, cpu, ret);
1420+
1421+
/* Free all other OPPs */
1422+
of_cpumask_free_opp_table(cpumask);
1423+
break;
1424+
}
1425+
}
1426+
1427+
return ret;
1428+
}
1429+
EXPORT_SYMBOL_GPL(of_cpumask_init_opp_table);
1430+
1431+
/* Required only for V1 bindings, as v2 can manage it from DT itself */
1432+
int set_cpus_sharing_opps(struct device *cpu_dev, cpumask_var_t cpumask)
1433+
{
1434+
struct device_list_opp *list_dev;
1435+
struct device_opp *dev_opp;
1436+
struct device *dev;
1437+
int cpu, ret = 0;
1438+
1439+
rcu_read_lock();
1440+
1441+
dev_opp = _find_device_opp(cpu_dev);
1442+
if (IS_ERR(dev_opp)) {
1443+
ret = -EINVAL;
1444+
goto out_rcu_read_unlock;
1445+
}
1446+
1447+
for_each_cpu(cpu, cpumask) {
1448+
if (cpu == cpu_dev->id)
1449+
continue;
1450+
1451+
dev = get_cpu_device(cpu);
1452+
if (!dev) {
1453+
dev_err(cpu_dev, "%s: failed to get cpu%d device\n",
1454+
__func__, cpu);
1455+
continue;
1456+
}
1457+
1458+
list_dev = _add_list_dev(dev, dev_opp);
1459+
if (!list_dev) {
1460+
dev_err(dev, "%s: failed to add list-dev for cpu%d device\n",
1461+
__func__, cpu);
1462+
continue;
1463+
}
1464+
}
1465+
out_rcu_read_unlock:
1466+
rcu_read_unlock();
1467+
1468+
return 0;
1469+
}
1470+
EXPORT_SYMBOL_GPL(set_cpus_sharing_opps);
1471+
1472+
/*
1473+
* Works only for OPP v2 bindings.
1474+
*
1475+
* cpumask should be already set to mask of cpu_dev->id.
1476+
* Returns -ENOENT if operating-points-v2 bindings aren't supported.
1477+
*/
1478+
int of_get_cpus_sharing_opps(struct device *cpu_dev, cpumask_var_t cpumask)
1479+
{
1480+
struct device_node *np, *tmp_np;
1481+
struct device *tcpu_dev;
1482+
int cpu, ret = 0;
1483+
1484+
/* Get OPP descriptor node */
1485+
np = _of_get_opp_desc_node(cpu_dev);
1486+
if (IS_ERR(np)) {
1487+
dev_dbg(cpu_dev, "%s: Couldn't find opp node: %ld\n", __func__,
1488+
PTR_ERR(np));
1489+
return -ENOENT;
1490+
}
1491+
1492+
/* OPPs are shared ? */
1493+
if (!of_property_read_bool(np, "opp-shared"))
1494+
goto put_cpu_node;
1495+
1496+
for_each_possible_cpu(cpu) {
1497+
if (cpu == cpu_dev->id)
1498+
continue;
1499+
1500+
tcpu_dev = get_cpu_device(cpu);
1501+
if (!tcpu_dev) {
1502+
dev_err(cpu_dev, "%s: failed to get cpu%d device\n",
1503+
__func__, cpu);
1504+
ret = -ENODEV;
1505+
goto put_cpu_node;
1506+
}
1507+
1508+
/* Get OPP descriptor node */
1509+
tmp_np = _of_get_opp_desc_node(tcpu_dev);
1510+
if (IS_ERR(tmp_np)) {
1511+
dev_err(tcpu_dev, "%s: Couldn't find opp node: %ld\n",
1512+
__func__, PTR_ERR(tmp_np));
1513+
ret = PTR_ERR(tmp_np);
1514+
goto put_cpu_node;
1515+
}
1516+
1517+
/* CPUs are sharing opp node */
1518+
if (np == tmp_np)
1519+
cpumask_set_cpu(cpu, cpumask);
1520+
1521+
of_node_put(tmp_np);
1522+
}
1523+
1524+
put_cpu_node:
1525+
of_node_put(np);
1526+
return ret;
1527+
}
1528+
EXPORT_SYMBOL_GPL(of_get_cpus_sharing_opps);
13541529
#endif

include/linux/pm_opp.h

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -121,6 +121,10 @@ static inline struct srcu_notifier_head *dev_pm_opp_get_notifier(
121121
#if defined(CONFIG_PM_OPP) && defined(CONFIG_OF)
122122
int of_init_opp_table(struct device *dev);
123123
void of_free_opp_table(struct device *dev);
124+
int of_cpumask_init_opp_table(cpumask_var_t cpumask);
125+
void of_cpumask_free_opp_table(cpumask_var_t cpumask);
126+
int of_get_cpus_sharing_opps(struct device *cpu_dev, cpumask_var_t cpumask);
127+
int set_cpus_sharing_opps(struct device *cpu_dev, cpumask_var_t cpumask);
124128
#else
125129
static inline int of_init_opp_table(struct device *dev)
126130
{
@@ -130,6 +134,25 @@ static inline int of_init_opp_table(struct device *dev)
130134
static inline void of_free_opp_table(struct device *dev)
131135
{
132136
}
137+
138+
static inline int of_cpumask_init_opp_table(cpumask_var_t cpumask)
139+
{
140+
return -ENOSYS;
141+
}
142+
143+
static inline void of_cpumask_free_opp_table(cpumask_var_t cpumask)
144+
{
145+
}
146+
147+
static inline int of_get_cpus_sharing_opps(struct device *cpu_dev, cpumask_var_t cpumask)
148+
{
149+
return -ENOSYS;
150+
}
151+
152+
static inline int set_cpus_sharing_opps(struct device *cpu_dev, cpumask_var_t cpumask)
153+
{
154+
return -ENOSYS;
155+
}
133156
#endif
134157

135158
#endif /* __LINUX_OPP_H__ */

0 commit comments

Comments
 (0)