Skip to content

Commit e37440e

Browse files
committed
OPP: Call dev_pm_opp_set_opp() for required OPPs
Configuring the required OPP was never properly implemented, we just took an exception for genpds and configured them directly, while leaving out all other required OPP types. Now that a standard call to dev_pm_opp_set_opp() takes care of configuring the opp->level too, the special handling for genpds can be avoided by simply calling dev_pm_opp_set_opp() for the required OPPs, which shall eventually configure the corresponding level for genpds. This also makes it possible for us to configure other type of required OPPs (no concrete users yet though), via the same path. This is how other frameworks take care of parent nodes, like clock, regulators, etc, where we recursively call the same helper. In order to call dev_pm_opp_set_opp() for the virtual genpd devices, they must share the OPP table of the genpd. Call _add_opp_dev() for them to get that done. This commit also extends the struct dev_pm_opp_config to pass required devices, for non-genpd cases, which can be used to call dev_pm_opp_set_opp() for the non-genpd required devices. Reviewed-by: Ulf Hansson <ulf.hansson@linaro.org> Tested-by: Stephan Gerhold <stephan.gerhold@kernkonzept.com> Signed-off-by: Viresh Kumar <viresh.kumar@linaro.org>
1 parent 6d366d0 commit e37440e

File tree

4 files changed

+100
-100
lines changed

4 files changed

+100
-100
lines changed

drivers/opp/core.c

Lines changed: 82 additions & 86 deletions
Original file line numberDiff line numberDiff line change
@@ -1054,48 +1054,22 @@ static int _set_opp_bw(const struct opp_table *opp_table,
10541054
return 0;
10551055
}
10561056

1057-
static int _set_performance_state(struct device *dev, struct device *pd_dev,
1058-
struct dev_pm_opp *opp, int i)
1059-
{
1060-
unsigned int pstate = 0;
1061-
int ret;
1062-
1063-
if (!pd_dev)
1064-
return 0;
1065-
1066-
if (likely(opp)) {
1067-
pstate = opp->required_opps[i]->level;
1068-
if (pstate == OPP_LEVEL_UNSET)
1069-
return 0;
1070-
}
1071-
1072-
ret = dev_pm_domain_set_performance_state(pd_dev, pstate);
1073-
if (ret) {
1074-
dev_err(dev, "Failed to set performance state of %s: %d (%d)\n",
1075-
dev_name(pd_dev), pstate, ret);
1076-
}
1077-
1078-
return ret;
1079-
}
1080-
1081-
static int _opp_set_required_opps_generic(struct device *dev,
1082-
struct opp_table *opp_table, struct dev_pm_opp *opp, bool scaling_down)
1083-
{
1084-
dev_err(dev, "setting required-opps isn't supported for non-genpd devices\n");
1085-
return -ENOENT;
1086-
}
1087-
1088-
static int _opp_set_required_opps_genpd(struct device *dev,
1089-
struct opp_table *opp_table, struct dev_pm_opp *opp, bool scaling_down)
1057+
/* This is only called for PM domain for now */
1058+
static int _set_required_opps(struct device *dev, struct opp_table *opp_table,
1059+
struct dev_pm_opp *opp, bool up)
10901060
{
1091-
struct device **genpd_virt_devs = opp_table->genpd_virt_devs;
1061+
struct device **devs = opp_table->required_devs;
10921062
int index, target, delta, ret;
10931063

1094-
if (!genpd_virt_devs)
1064+
if (!devs)
10951065
return 0;
10961066

1067+
/* required-opps not fully initialized yet */
1068+
if (lazy_linking_pending(opp_table))
1069+
return -EBUSY;
1070+
10971071
/* Scaling up? Set required OPPs in normal order, else reverse */
1098-
if (!scaling_down) {
1072+
if (up) {
10991073
index = 0;
11001074
target = opp_table->required_opp_count;
11011075
delta = 1;
@@ -1106,44 +1080,18 @@ static int _opp_set_required_opps_genpd(struct device *dev,
11061080
}
11071081

11081082
while (index != target) {
1109-
ret = _set_performance_state(dev, genpd_virt_devs[index], opp, index);
1110-
if (ret)
1111-
return ret;
1083+
if (devs[index]) {
1084+
ret = dev_pm_opp_set_opp(devs[index], opp->required_opps[index]);
1085+
if (ret)
1086+
return ret;
1087+
}
11121088

11131089
index += delta;
11141090
}
11151091

11161092
return 0;
11171093
}
11181094

1119-
/* This is only called for PM domain for now */
1120-
static int _set_required_opps(struct device *dev, struct opp_table *opp_table,
1121-
struct dev_pm_opp *opp, bool up)
1122-
{
1123-
/* required-opps not fully initialized yet */
1124-
if (lazy_linking_pending(opp_table))
1125-
return -EBUSY;
1126-
1127-
if (opp_table->set_required_opps)
1128-
return opp_table->set_required_opps(dev, opp_table, opp, up);
1129-
1130-
return 0;
1131-
}
1132-
1133-
/* Update set_required_opps handler */
1134-
void _update_set_required_opps(struct opp_table *opp_table)
1135-
{
1136-
/* Already set */
1137-
if (opp_table->set_required_opps)
1138-
return;
1139-
1140-
/* All required OPPs will belong to genpd or none */
1141-
if (opp_table->required_opp_tables[0]->is_genpd)
1142-
opp_table->set_required_opps = _opp_set_required_opps_genpd;
1143-
else
1144-
opp_table->set_required_opps = _opp_set_required_opps_generic;
1145-
}
1146-
11471095
static int _set_opp_level(struct device *dev, struct opp_table *opp_table,
11481096
struct dev_pm_opp *opp)
11491097
{
@@ -2406,19 +2354,13 @@ static void _opp_detach_genpd(struct opp_table *opp_table)
24062354
{
24072355
int index;
24082356

2409-
if (!opp_table->genpd_virt_devs)
2410-
return;
2411-
24122357
for (index = 0; index < opp_table->required_opp_count; index++) {
2413-
if (!opp_table->genpd_virt_devs[index])
2358+
if (!opp_table->required_devs[index])
24142359
continue;
24152360

2416-
dev_pm_domain_detach(opp_table->genpd_virt_devs[index], false);
2417-
opp_table->genpd_virt_devs[index] = NULL;
2361+
dev_pm_domain_detach(opp_table->required_devs[index], false);
2362+
opp_table->required_devs[index] = NULL;
24182363
}
2419-
2420-
kfree(opp_table->genpd_virt_devs);
2421-
opp_table->genpd_virt_devs = NULL;
24222364
}
24232365

24242366
/*
@@ -2445,14 +2387,14 @@ static int _opp_attach_genpd(struct opp_table *opp_table, struct device *dev,
24452387
int index = 0, ret = -EINVAL;
24462388
const char * const *name = names;
24472389

2448-
if (opp_table->genpd_virt_devs)
2449-
return 0;
2390+
if (!opp_table->required_devs) {
2391+
dev_err(dev, "Required OPPs not available, can't attach genpd\n");
2392+
return -EINVAL;
2393+
}
24502394

2451-
opp_table->genpd_virt_devs = kcalloc(opp_table->required_opp_count,
2452-
sizeof(*opp_table->genpd_virt_devs),
2453-
GFP_KERNEL);
2454-
if (!opp_table->genpd_virt_devs)
2455-
return -ENOMEM;
2395+
/* Checking only the first one is enough ? */
2396+
if (opp_table->required_devs[0])
2397+
return 0;
24562398

24572399
while (*name) {
24582400
if (index >= opp_table->required_opp_count) {
@@ -2468,13 +2410,25 @@ static int _opp_attach_genpd(struct opp_table *opp_table, struct device *dev,
24682410
goto err;
24692411
}
24702412

2471-
opp_table->genpd_virt_devs[index] = virt_dev;
2413+
/*
2414+
* Add the virtual genpd device as a user of the OPP table, so
2415+
* we can call dev_pm_opp_set_opp() on it directly.
2416+
*
2417+
* This will be automatically removed when the OPP table is
2418+
* removed, don't need to handle that here.
2419+
*/
2420+
if (!_add_opp_dev(virt_dev, opp_table->required_opp_tables[index])) {
2421+
ret = -ENOMEM;
2422+
goto err;
2423+
}
2424+
2425+
opp_table->required_devs[index] = virt_dev;
24722426
index++;
24732427
name++;
24742428
}
24752429

24762430
if (virt_devs)
2477-
*virt_devs = opp_table->genpd_virt_devs;
2431+
*virt_devs = opp_table->required_devs;
24782432

24792433
return 0;
24802434

@@ -2484,10 +2438,42 @@ static int _opp_attach_genpd(struct opp_table *opp_table, struct device *dev,
24842438

24852439
}
24862440

2441+
static int _opp_set_required_devs(struct opp_table *opp_table,
2442+
struct device *dev,
2443+
struct device **required_devs)
2444+
{
2445+
int i;
2446+
2447+
if (!opp_table->required_devs) {
2448+
dev_err(dev, "Required OPPs not available, can't set required devs\n");
2449+
return -EINVAL;
2450+
}
2451+
2452+
/* Another device that shares the OPP table has set the required devs ? */
2453+
if (opp_table->required_devs[0])
2454+
return 0;
2455+
2456+
for (i = 0; i < opp_table->required_opp_count; i++)
2457+
opp_table->required_devs[i] = required_devs[i];
2458+
2459+
return 0;
2460+
}
2461+
2462+
static void _opp_put_required_devs(struct opp_table *opp_table)
2463+
{
2464+
int i;
2465+
2466+
for (i = 0; i < opp_table->required_opp_count; i++)
2467+
opp_table->required_devs[i] = NULL;
2468+
}
2469+
24872470
static void _opp_clear_config(struct opp_config_data *data)
24882471
{
2489-
if (data->flags & OPP_CONFIG_GENPD)
2472+
if (data->flags & OPP_CONFIG_REQUIRED_DEVS)
2473+
_opp_put_required_devs(data->opp_table);
2474+
else if (data->flags & OPP_CONFIG_GENPD)
24902475
_opp_detach_genpd(data->opp_table);
2476+
24912477
if (data->flags & OPP_CONFIG_REGULATOR)
24922478
_opp_put_regulators(data->opp_table);
24932479
if (data->flags & OPP_CONFIG_SUPPORTED_HW)
@@ -2601,12 +2587,22 @@ int dev_pm_opp_set_config(struct device *dev, struct dev_pm_opp_config *config)
26012587

26022588
/* Attach genpds */
26032589
if (config->genpd_names) {
2590+
if (config->required_devs)
2591+
goto err;
2592+
26042593
ret = _opp_attach_genpd(opp_table, dev, config->genpd_names,
26052594
config->virt_devs);
26062595
if (ret)
26072596
goto err;
26082597

26092598
data->flags |= OPP_CONFIG_GENPD;
2599+
} else if (config->required_devs) {
2600+
ret = _opp_set_required_devs(opp_table, dev,
2601+
config->required_devs);
2602+
if (ret)
2603+
goto err;
2604+
2605+
data->flags |= OPP_CONFIG_REQUIRED_DEVS;
26102606
}
26112607

26122608
ret = xa_alloc(&opp_configs, &id, data, XA_LIMIT(1, INT_MAX),

drivers/opp/of.c

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -165,7 +165,7 @@ static void _opp_table_alloc_required_tables(struct opp_table *opp_table,
165165
struct opp_table **required_opp_tables;
166166
struct device_node *required_np, *np;
167167
bool lazy = false;
168-
int count, i;
168+
int count, i, size;
169169

170170
/* Traversing the first OPP node is all we need */
171171
np = of_get_next_available_child(opp_np, NULL);
@@ -179,12 +179,13 @@ static void _opp_table_alloc_required_tables(struct opp_table *opp_table,
179179
if (count <= 0)
180180
goto put_np;
181181

182-
required_opp_tables = kcalloc(count, sizeof(*required_opp_tables),
183-
GFP_KERNEL);
182+
size = sizeof(*required_opp_tables) + sizeof(*opp_table->required_devs);
183+
required_opp_tables = kcalloc(count, size, GFP_KERNEL);
184184
if (!required_opp_tables)
185185
goto put_np;
186186

187187
opp_table->required_opp_tables = required_opp_tables;
188+
opp_table->required_devs = (void *)(required_opp_tables + count);
188189
opp_table->required_opp_count = count;
189190

190191
for (i = 0; i < count; i++) {
@@ -208,8 +209,6 @@ static void _opp_table_alloc_required_tables(struct opp_table *opp_table,
208209
mutex_lock(&opp_table_lock);
209210
list_add(&opp_table->lazy, &lazy_opp_tables);
210211
mutex_unlock(&opp_table_lock);
211-
} else {
212-
_update_set_required_opps(opp_table);
213212
}
214213

215214
goto put_np;
@@ -332,9 +331,14 @@ static int _link_required_opps(struct dev_pm_opp *opp, struct opp_table *opp_tab
332331
*
333332
* Just update the `level` with the right value, which
334333
* dev_pm_opp_set_opp() will take care of in the normal path itself.
334+
*
335+
* There is another case though, where a genpd's OPP table has
336+
* required-opps set to a parent genpd. The OPP core expects the user to
337+
* set the respective required `struct device` pointer via
338+
* dev_pm_opp_set_config().
335339
*/
336340
if (required_table->is_genpd && opp_table->required_opp_count == 1 &&
337-
!opp_table->genpd_virt_devs) {
341+
!opp_table->required_devs[0]) {
338342
if (!WARN_ON(opp->level != OPP_LEVEL_UNSET))
339343
opp->level = opp->required_opps[0]->level;
340344
}
@@ -447,7 +451,6 @@ static void lazy_link_required_opp_table(struct opp_table *new_table)
447451

448452
/* All required opp-tables found, remove from lazy list */
449453
if (!lazy) {
450-
_update_set_required_opps(opp_table);
451454
list_del_init(&opp_table->lazy);
452455

453456
list_for_each_entry(opp, &opp_table->opp_list, node)

drivers/opp/opp.h

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ extern struct list_head opp_tables;
3535
#define OPP_CONFIG_PROP_NAME BIT(3)
3636
#define OPP_CONFIG_SUPPORTED_HW BIT(4)
3737
#define OPP_CONFIG_GENPD BIT(5)
38+
#define OPP_CONFIG_REQUIRED_DEVS BIT(6)
3839

3940
/**
4041
* struct opp_config_data - data for set config operations
@@ -160,9 +161,9 @@ enum opp_table_access {
160161
* @rate_clk_single: Currently configured frequency for single clk.
161162
* @current_opp: Currently configured OPP for the table.
162163
* @suspend_opp: Pointer to OPP to be used during device suspend.
163-
* @genpd_virt_devs: List of virtual devices for multiple genpd support.
164164
* @required_opp_tables: List of device OPP tables that are required by OPPs in
165165
* this table.
166+
* @required_devs: List of devices for required OPP tables.
166167
* @required_opp_count: Number of required devices.
167168
* @supported_hw: Array of version number to support.
168169
* @supported_hw_count: Number of elements in supported_hw array.
@@ -180,7 +181,6 @@ enum opp_table_access {
180181
* @path_count: Number of interconnect paths
181182
* @enabled: Set to true if the device's resources are enabled/configured.
182183
* @is_genpd: Marks if the OPP table belongs to a genpd.
183-
* @set_required_opps: Helper responsible to set required OPPs.
184184
* @dentry: debugfs dentry pointer of the real device directory (not links).
185185
* @dentry_name: Name of the real dentry.
186186
*
@@ -211,8 +211,8 @@ struct opp_table {
211211
struct dev_pm_opp *current_opp;
212212
struct dev_pm_opp *suspend_opp;
213213

214-
struct device **genpd_virt_devs;
215214
struct opp_table **required_opp_tables;
215+
struct device **required_devs;
216216
unsigned int required_opp_count;
217217

218218
unsigned int *supported_hw;
@@ -229,8 +229,6 @@ struct opp_table {
229229
unsigned int path_count;
230230
bool enabled;
231231
bool is_genpd;
232-
int (*set_required_opps)(struct device *dev,
233-
struct opp_table *opp_table, struct dev_pm_opp *opp, bool scaling_down);
234232

235233
#ifdef CONFIG_DEBUG_FS
236234
struct dentry *dentry;

include/linux/pm_opp.h

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -74,8 +74,10 @@ typedef int (*config_clks_t)(struct device *dev, struct opp_table *opp_table,
7474
* @supported_hw_count: Number of elements in the array.
7575
* @regulator_names: Array of pointers to the names of the regulator, NULL terminated.
7676
* @genpd_names: Null terminated array of pointers containing names of genpd to
77-
* attach.
78-
* @virt_devs: Pointer to return the array of virtual devices.
77+
* attach. Mutually exclusive with required_devs.
78+
* @virt_devs: Pointer to return the array of genpd virtual devices. Mutually
79+
* exclusive with required_devs.
80+
* @required_devs: Required OPP devices. Mutually exclusive with genpd_names/virt_devs.
7981
*
8082
* This structure contains platform specific OPP configurations for the device.
8183
*/
@@ -90,6 +92,7 @@ struct dev_pm_opp_config {
9092
const char * const *regulator_names;
9193
const char * const *genpd_names;
9294
struct device ***virt_devs;
95+
struct device **required_devs;
9396
};
9497

9598
#define OPP_LEVEL_UNSET U32_MAX

0 commit comments

Comments
 (0)