Merge tag 'pm-6.10-rc1-2' of git://git.kernel.org/pub/scm/linux/kernel/git/rafael...
[linux-2.6-block.git] / drivers / opp / core.c
index e233734b7220580ee57165e1a172de0b41c7ff76..cb4611fe1b5b2647b827f8a307aefb785c2c71dc 100644 (file)
@@ -2394,7 +2394,8 @@ static void _opp_detach_genpd(struct opp_table *opp_table)
 static int _opp_attach_genpd(struct opp_table *opp_table, struct device *dev,
                        const char * const *names, struct device ***virt_devs)
 {
-       struct device *virt_dev;
+       struct device *virt_dev, *gdev;
+       struct opp_table *genpd_table;
        int index = 0, ret = -EINVAL;
        const char * const *name = names;
 
@@ -2427,6 +2428,34 @@ static int _opp_attach_genpd(struct opp_table *opp_table, struct device *dev,
                        goto err;
                }
 
+               /*
+                * 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.
+                *
+                * Cross check it again and fix if required.
+                */
+               gdev = dev_to_genpd_dev(virt_dev);
+               if (IS_ERR(gdev))
+                       return PTR_ERR(gdev);
+
+               genpd_table = _find_opp_table(gdev);
+               if (!IS_ERR(genpd_table)) {
+                       if (genpd_table != opp_table->required_opp_tables[index]) {
+                               dev_pm_opp_put_opp_table(opp_table->required_opp_tables[index]);
+                               opp_table->required_opp_tables[index] = genpd_table;
+                       } else {
+                               dev_pm_opp_put_opp_table(genpd_table);
+                       }
+               }
+
                /*
                 * Add the virtual genpd device as a user of the OPP table, so
                 * we can call dev_pm_opp_set_opp() on it directly.