Skip to content

Commit 2a56c46

Browse files
committed
OPP: Fix required_opp_tables for multiple genpds using same table
The required_opp_tables parsing is not perfect, as the OPP core does the parsing solely based on the DT node pointers. The core sets the required_opp_tables entry to the first OPP table in the "opp_tables" list, that matches with the node pointer. If the target DT OPP table is used by multiple devices and they all create separate instances of 'struct opp_table' from it, then it is possible that the required_opp_tables entry may be set to the incorrect sibling device. Unfortunately, there is no clear way to initialize the right values during the initial parsing and we need to do this at a later point of time. Cross check the OPP table again while the genpds are attached and fix them if required. Also add a new API for the genpd core to fetch the device pointer for the genpd. Cc: Thorsten Leemhuis <regressions@leemhuis.info> Reported-by: Vladimir Lypak <vladimir.lypak@gmail.com> Closes: https://bugzilla.kernel.org/show_bug.cgi?id=218682 Co-developed-by: Vladimir Lypak <vladimir.lypak@gmail.com> Signed-off-by: Viresh Kumar <viresh.kumar@linaro.org> Reviewed-by: Ulf Hansson <ulf.hansson@linaro.org>
1 parent 4cece76 commit 2a56c46

File tree

3 files changed

+46
-1
lines changed

3 files changed

+46
-1
lines changed

drivers/opp/core.c

Lines changed: 30 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2394,7 +2394,8 @@ static void _opp_detach_genpd(struct opp_table *opp_table)
23942394
static int _opp_attach_genpd(struct opp_table *opp_table, struct device *dev,
23952395
const char * const *names, struct device ***virt_devs)
23962396
{
2397-
struct device *virt_dev;
2397+
struct device *virt_dev, *gdev;
2398+
struct opp_table *genpd_table;
23982399
int index = 0, ret = -EINVAL;
23992400
const char * const *name = names;
24002401

@@ -2427,6 +2428,34 @@ static int _opp_attach_genpd(struct opp_table *opp_table, struct device *dev,
24272428
goto err;
24282429
}
24292430

2431+
/*
2432+
* The required_opp_tables parsing is not perfect, as the OPP
2433+
* core does the parsing solely based on the DT node pointers.
2434+
* The core sets the required_opp_tables entry to the first OPP
2435+
* table in the "opp_tables" list, that matches with the node
2436+
* pointer.
2437+
*
2438+
* If the target DT OPP table is used by multiple devices and
2439+
* they all create separate instances of 'struct opp_table' from
2440+
* it, then it is possible that the required_opp_tables entry
2441+
* may be set to the incorrect sibling device.
2442+
*
2443+
* Cross check it again and fix if required.
2444+
*/
2445+
gdev = dev_to_genpd_dev(virt_dev);
2446+
if (IS_ERR(gdev))
2447+
return PTR_ERR(gdev);
2448+
2449+
genpd_table = _find_opp_table(gdev);
2450+
if (!IS_ERR(genpd_table)) {
2451+
if (genpd_table != opp_table->required_opp_tables[index]) {
2452+
dev_pm_opp_put_opp_table(opp_table->required_opp_tables[index]);
2453+
opp_table->required_opp_tables[index] = genpd_table;
2454+
} else {
2455+
dev_pm_opp_put_opp_table(genpd_table);
2456+
}
2457+
}
2458+
24302459
/*
24312460
* Add the virtual genpd device as a user of the OPP table, so
24322461
* we can call dev_pm_opp_set_opp() on it directly.

drivers/pmdomain/core.c

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -184,6 +184,16 @@ static struct generic_pm_domain *dev_to_genpd(struct device *dev)
184184
return pd_to_genpd(dev->pm_domain);
185185
}
186186

187+
struct device *dev_to_genpd_dev(struct device *dev)
188+
{
189+
struct generic_pm_domain *genpd = dev_to_genpd(dev);
190+
191+
if (IS_ERR(genpd))
192+
return ERR_CAST(genpd);
193+
194+
return &genpd->dev;
195+
}
196+
187197
static int genpd_stop_dev(const struct generic_pm_domain *genpd,
188198
struct device *dev)
189199
{

include/linux/pm_domain.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -260,6 +260,7 @@ int pm_genpd_remove_subdomain(struct generic_pm_domain *genpd,
260260
int pm_genpd_init(struct generic_pm_domain *genpd,
261261
struct dev_power_governor *gov, bool is_off);
262262
int pm_genpd_remove(struct generic_pm_domain *genpd);
263+
struct device *dev_to_genpd_dev(struct device *dev);
263264
int dev_pm_genpd_set_performance_state(struct device *dev, unsigned int state);
264265
int dev_pm_genpd_add_notifier(struct device *dev, struct notifier_block *nb);
265266
int dev_pm_genpd_remove_notifier(struct device *dev);
@@ -307,6 +308,11 @@ static inline int pm_genpd_remove(struct generic_pm_domain *genpd)
307308
return -EOPNOTSUPP;
308309
}
309310

311+
static inline struct device *dev_to_genpd_dev(struct device *dev)
312+
{
313+
return ERR_PTR(-EOPNOTSUPP);
314+
}
315+
310316
static inline int dev_pm_genpd_set_performance_state(struct device *dev,
311317
unsigned int state)
312318
{

0 commit comments

Comments
 (0)