Skip to content

Commit 6d3f922

Browse files
Georgi Djakovvireshk
authored andcommitted
opp: Add support for parsing interconnect bandwidth
The OPP bindings now support bandwidth values, so add support to parse it from device tree and store it into the new dev_pm_opp_icc_bw struct, which is part of the dev_pm_opp. Signed-off-by: Georgi Djakov <[email protected]> Reviewed-by: Matthias Kaehlcke <[email protected]> [ Viresh: Create _read_bw() and use it, renamed _of_find_icc_paths() to dev_pm_opp_of_find_icc_paths(), exported it and made opp_table argument optional. Also drop the depends on from Kconfig. ] Signed-off-by: Viresh Kumar <[email protected]>
1 parent 90562c8 commit 6d3f922

File tree

4 files changed

+162
-5
lines changed

4 files changed

+162
-5
lines changed

drivers/opp/core.c

Lines changed: 24 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -999,6 +999,12 @@ static struct opp_table *_allocate_opp_table(struct device *dev, int index)
999999
ret);
10001000
}
10011001

1002+
/* Find interconnect path(s) for the device */
1003+
ret = dev_pm_opp_of_find_icc_paths(dev, opp_table);
1004+
if (ret)
1005+
dev_warn(dev, "%s: Error finding interconnect paths: %d\n",
1006+
__func__, ret);
1007+
10021008
BLOCKING_INIT_NOTIFIER_HEAD(&opp_table->head);
10031009
INIT_LIST_HEAD(&opp_table->opp_list);
10041010
kref_init(&opp_table->kref);
@@ -1057,13 +1063,20 @@ static void _opp_table_kref_release(struct kref *kref)
10571063
{
10581064
struct opp_table *opp_table = container_of(kref, struct opp_table, kref);
10591065
struct opp_device *opp_dev, *temp;
1066+
int i;
10601067

10611068
_of_clear_opp_table(opp_table);
10621069

10631070
/* Release clk */
10641071
if (!IS_ERR(opp_table->clk))
10651072
clk_put(opp_table->clk);
10661073

1074+
if (opp_table->paths) {
1075+
for (i = 0; i < opp_table->path_count; i++)
1076+
icc_put(opp_table->paths[i]);
1077+
kfree(opp_table->paths);
1078+
}
1079+
10671080
WARN_ON(!list_empty(&opp_table->opp_list));
10681081

10691082
list_for_each_entry_safe(opp_dev, temp, &opp_table->dev_list, node) {
@@ -1243,19 +1256,23 @@ EXPORT_SYMBOL_GPL(dev_pm_opp_remove_all_dynamic);
12431256
struct dev_pm_opp *_opp_allocate(struct opp_table *table)
12441257
{
12451258
struct dev_pm_opp *opp;
1246-
int count, supply_size;
1259+
int supply_count, supply_size, icc_size;
12471260

12481261
/* Allocate space for at least one supply */
1249-
count = table->regulator_count > 0 ? table->regulator_count : 1;
1250-
supply_size = sizeof(*opp->supplies) * count;
1262+
supply_count = table->regulator_count > 0 ? table->regulator_count : 1;
1263+
supply_size = sizeof(*opp->supplies) * supply_count;
1264+
icc_size = sizeof(*opp->bandwidth) * table->path_count;
12511265

12521266
/* allocate new OPP node and supplies structures */
1253-
opp = kzalloc(sizeof(*opp) + supply_size, GFP_KERNEL);
1267+
opp = kzalloc(sizeof(*opp) + supply_size + icc_size, GFP_KERNEL);
1268+
12541269
if (!opp)
12551270
return NULL;
12561271

12571272
/* Put the supplies at the end of the OPP structure as an empty array */
12581273
opp->supplies = (struct dev_pm_opp_supply *)(opp + 1);
1274+
if (icc_size)
1275+
opp->bandwidth = (struct dev_pm_opp_icc_bw *)(opp->supplies + supply_count);
12591276
INIT_LIST_HEAD(&opp->node);
12601277

12611278
return opp;
@@ -1290,6 +1307,9 @@ int _opp_compare_key(struct dev_pm_opp *opp1, struct dev_pm_opp *opp2)
12901307
{
12911308
if (opp1->rate != opp2->rate)
12921309
return opp1->rate < opp2->rate ? -1 : 1;
1310+
if (opp1->bandwidth && opp2->bandwidth &&
1311+
opp1->bandwidth[0].peak != opp2->bandwidth[0].peak)
1312+
return opp1->bandwidth[0].peak < opp2->bandwidth[0].peak ? -1 : 1;
12931313
if (opp1->level != opp2->level)
12941314
return opp1->level < opp2->level ? -1 : 1;
12951315
return 0;

drivers/opp/of.c

Lines changed: 113 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -332,6 +332,62 @@ static int _of_opp_alloc_required_opps(struct opp_table *opp_table,
332332
return ret;
333333
}
334334

335+
int dev_pm_opp_of_find_icc_paths(struct device *dev,
336+
struct opp_table *opp_table)
337+
{
338+
struct device_node *np;
339+
int ret = 0, i, count, num_paths;
340+
struct icc_path **paths;
341+
342+
np = of_node_get(dev->of_node);
343+
if (!np)
344+
return 0;
345+
346+
count = of_count_phandle_with_args(np, "interconnects",
347+
"#interconnect-cells");
348+
of_node_put(np);
349+
if (count < 0)
350+
return 0;
351+
352+
/* two phandles when #interconnect-cells = <1> */
353+
if (count % 2) {
354+
dev_err(dev, "%s: Invalid interconnects values\n", __func__);
355+
return -EINVAL;
356+
}
357+
358+
num_paths = count / 2;
359+
paths = kcalloc(num_paths, sizeof(*paths), GFP_KERNEL);
360+
if (!paths)
361+
return -ENOMEM;
362+
363+
for (i = 0; i < num_paths; i++) {
364+
paths[i] = of_icc_get_by_index(dev, i);
365+
if (IS_ERR(paths[i])) {
366+
ret = PTR_ERR(paths[i]);
367+
if (ret != -EPROBE_DEFER) {
368+
dev_err(dev, "%s: Unable to get path%d: %d\n",
369+
__func__, i, ret);
370+
}
371+
goto err;
372+
}
373+
}
374+
375+
if (opp_table) {
376+
opp_table->paths = paths;
377+
opp_table->path_count = num_paths;
378+
return 0;
379+
}
380+
381+
err:
382+
while (i--)
383+
icc_put(paths[i]);
384+
385+
kfree(paths);
386+
387+
return ret;
388+
}
389+
EXPORT_SYMBOL_GPL(dev_pm_opp_of_find_icc_paths);
390+
335391
static bool _opp_is_supported(struct device *dev, struct opp_table *opp_table,
336392
struct device_node *np)
337393
{
@@ -521,9 +577,45 @@ void dev_pm_opp_of_remove_table(struct device *dev)
521577
}
522578
EXPORT_SYMBOL_GPL(dev_pm_opp_of_remove_table);
523579

580+
static int _read_bw(struct dev_pm_opp *new_opp, struct device_node *np,
581+
bool peak)
582+
{
583+
const char *name = peak ? "opp-peak-kBps" : "opp-avg-kBps";
584+
struct property *prop;
585+
int i, count, ret;
586+
u32 *bw;
587+
588+
prop = of_find_property(np, name, NULL);
589+
if (!prop)
590+
return -ENODEV;
591+
592+
count = prop->length / sizeof(u32);
593+
bw = kmalloc_array(count, sizeof(*bw), GFP_KERNEL);
594+
if (!bw)
595+
return -ENOMEM;
596+
597+
ret = of_property_read_u32_array(np, name, bw, count);
598+
if (ret) {
599+
pr_err("%s: Error parsing %s: %d\n", __func__, name, ret);
600+
goto out;
601+
}
602+
603+
for (i = 0; i < count; i++) {
604+
if (peak)
605+
new_opp->bandwidth[i].peak = kBps_to_icc(bw[i]);
606+
else
607+
new_opp->bandwidth[i].avg = kBps_to_icc(bw[i]);
608+
}
609+
610+
out:
611+
kfree(bw);
612+
return ret;
613+
}
614+
524615
static int _read_opp_key(struct dev_pm_opp *new_opp, struct device_node *np,
525616
bool *rate_not_available)
526617
{
618+
bool found = false;
527619
u64 rate;
528620
int ret;
529621

@@ -535,10 +627,30 @@ static int _read_opp_key(struct dev_pm_opp *new_opp, struct device_node *np,
535627
* bit guaranteed in clk API.
536628
*/
537629
new_opp->rate = (unsigned long)rate;
630+
found = true;
538631
}
539632
*rate_not_available = !!ret;
540633

541-
of_property_read_u32(np, "opp-level", &new_opp->level);
634+
/*
635+
* Bandwidth consists of peak and average (optional) values:
636+
* opp-peak-kBps = <path1_value path2_value>;
637+
* opp-avg-kBps = <path1_value path2_value>;
638+
*/
639+
ret = _read_bw(new_opp, np, true);
640+
if (!ret) {
641+
found = true;
642+
ret = _read_bw(new_opp, np, false);
643+
}
644+
645+
/* The properties were found but we failed to parse them */
646+
if (ret && ret != -ENODEV)
647+
return ret;
648+
649+
if (!of_property_read_u32(np, "opp-level", &new_opp->level))
650+
found = true;
651+
652+
if (found)
653+
return 0;
542654

543655
return ret;
544656
}

drivers/opp/opp.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
#define __DRIVER_OPP_H__
1313

1414
#include <linux/device.h>
15+
#include <linux/interconnect.h>
1516
#include <linux/kernel.h>
1617
#include <linux/kref.h>
1718
#include <linux/list.h>
@@ -59,6 +60,7 @@ extern struct list_head opp_tables;
5960
* @rate: Frequency in hertz
6061
* @level: Performance level
6162
* @supplies: Power supplies voltage/current values
63+
* @bandwidth: Interconnect bandwidth values
6264
* @clock_latency_ns: Latency (in nanoseconds) of switching to this OPP's
6365
* frequency from any other OPP's frequency.
6466
* @required_opps: List of OPPs that are required by this OPP.
@@ -81,6 +83,7 @@ struct dev_pm_opp {
8183
unsigned int level;
8284

8385
struct dev_pm_opp_supply *supplies;
86+
struct dev_pm_opp_icc_bw *bandwidth;
8487

8588
unsigned long clock_latency_ns;
8689

@@ -146,6 +149,8 @@ enum opp_table_access {
146149
* @regulator_count: Number of power supply regulators. Its value can be -1
147150
* (uninitialized), 0 (no opp-microvolt property) or > 0 (has opp-microvolt
148151
* property).
152+
* @paths: Interconnect path handles
153+
* @path_count: Number of interconnect paths
149154
* @genpd_performance_state: Device's power domain support performance state.
150155
* @is_genpd: Marks if the OPP table belongs to a genpd.
151156
* @set_opp: Platform specific set_opp callback
@@ -189,6 +194,8 @@ struct opp_table {
189194
struct clk *clk;
190195
struct regulator **regulators;
191196
int regulator_count;
197+
struct icc_path **paths;
198+
unsigned int path_count;
192199
bool genpd_performance_state;
193200
bool is_genpd;
194201

include/linux/pm_opp.h

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,18 @@ struct dev_pm_opp_supply {
4141
unsigned long u_amp;
4242
};
4343

44+
/**
45+
* struct dev_pm_opp_icc_bw - Interconnect bandwidth values
46+
* @avg: Average bandwidth corresponding to this OPP (in icc units)
47+
* @peak: Peak bandwidth corresponding to this OPP (in icc units)
48+
*
49+
* This structure stores the bandwidth values for a single interconnect path.
50+
*/
51+
struct dev_pm_opp_icc_bw {
52+
u32 avg;
53+
u32 peak;
54+
};
55+
4456
/**
4557
* struct dev_pm_opp_info - OPP freq/voltage/current values
4658
* @rate: Target clk rate in hz
@@ -360,6 +372,7 @@ int dev_pm_opp_of_get_sharing_cpus(struct device *cpu_dev, struct cpumask *cpuma
360372
struct device_node *dev_pm_opp_of_get_opp_desc_node(struct device *dev);
361373
struct device_node *dev_pm_opp_get_of_node(struct dev_pm_opp *opp);
362374
int of_get_required_opp_performance_state(struct device_node *np, int index);
375+
int dev_pm_opp_of_find_icc_paths(struct device *dev, struct opp_table *opp_table);
363376
void dev_pm_opp_of_register_em(struct cpumask *cpus);
364377
#else
365378
static inline int dev_pm_opp_of_add_table(struct device *dev)
@@ -408,6 +421,11 @@ static inline int of_get_required_opp_performance_state(struct device_node *np,
408421
{
409422
return -ENOTSUPP;
410423
}
424+
425+
static inline int dev_pm_opp_of_find_icc_paths(struct device *dev, struct opp_table *opp_table)
426+
{
427+
return -ENOTSUPP;
428+
}
411429
#endif
412430

413431
#endif /* __LINUX_OPP_H__ */

0 commit comments

Comments
 (0)