OPP: Use _set_opp_level() for single genpd case
authorViresh Kumar <viresh.kumar@linaro.org>
Thu, 12 Oct 2023 10:15:21 +0000 (15:45 +0530)
committerViresh Kumar <viresh.kumar@linaro.org>
Tue, 28 Nov 2023 10:33:15 +0000 (16:03 +0530)
There are two genpd (as required-opp) cases that we need to handle,
devices with a single genpd and ones with multiple genpds.

The multiple genpds case is clear, where the OPP core calls
dev_pm_domain_attach_by_name() for them and uses the virtual devices
returned by this helper to call dev_pm_domain_set_performance_state()
later to change the performance state.

The single genpd case however requires special handling as we need to
use the same `dev` structure (instead of a virtual one provided by genpd
core) for setting the performance state via
dev_pm_domain_set_performance_state().

As we move towards more generic code to take care of the required OPPs,
where we will recursively call dev_pm_opp_set_opp() for all the required
OPPs, the above special case becomes a problem.

It doesn't make sense for a device's DT entry to have both "opp-level"
and single "required-opps" entry pointing to a genpd's OPP, as that
would make the OPP core call dev_pm_domain_set_performance_state() for
two different values for the same device structure. And so we can reuse
the 'opp->level" field in such a case and call _set_opp_level() for the
device.

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>
drivers/opp/core.c
drivers/opp/of.c

index f2e2aa07b43192f67074f14440cf9e295558aa82..aeb216f7e978282793dd4dd034173a188e094128 100644 (file)
@@ -1088,10 +1088,12 @@ static int _opp_set_required_opps_generic(struct device *dev,
 static int _opp_set_required_opps_genpd(struct device *dev,
        struct opp_table *opp_table, struct dev_pm_opp *opp, bool scaling_down)
 {
-       struct device **genpd_virt_devs =
-               opp_table->genpd_virt_devs ? opp_table->genpd_virt_devs : &dev;
+       struct device **genpd_virt_devs = opp_table->genpd_virt_devs;
        int index, target, delta, ret;
 
+       if (!genpd_virt_devs)
+               return 0;
+
        /* Scaling up? Set required OPPs in normal order, else reverse */
        if (!scaling_down) {
                index = 0;
index 85fad7ca0007a13ee10d4e8825845a1c73a0e75f..4cdeeab5ceeecd221e0580f7a18d444c234b522c 100644 (file)
@@ -296,7 +296,7 @@ void _of_clear_opp(struct opp_table *opp_table, struct dev_pm_opp *opp)
        of_node_put(opp->np);
 }
 
-static int _link_required_opps(struct dev_pm_opp *opp,
+static int _link_required_opps(struct dev_pm_opp *opp, struct opp_table *opp_table,
                               struct opp_table *required_table, int index)
 {
        struct device_node *np;
@@ -314,6 +314,31 @@ static int _link_required_opps(struct dev_pm_opp *opp,
                return -ENODEV;
        }
 
+       /*
+        * There are two genpd (as required-opp) cases that we need to handle,
+        * devices with a single genpd and ones with multiple genpds.
+        *
+        * The single genpd case requires special handling as we need to use the
+        * same `dev` structure (instead of a virtual one provided by genpd
+        * core) for setting the performance state.
+        *
+        * It doesn't make sense for a device's DT entry to have both
+        * "opp-level" and single "required-opps" entry pointing to a genpd's
+        * OPP, as that would make the OPP core call
+        * dev_pm_domain_set_performance_state() for two different values for
+        * the same device structure. Lets treat single genpd configuration as a
+        * case where the OPP's level is directly available without required-opp
+        * link in the DT.
+        *
+        * Just update the `level` with the right value, which
+        * dev_pm_opp_set_opp() will take care of in the normal path itself.
+        */
+       if (required_table->is_genpd && opp_table->required_opp_count == 1 &&
+           !opp_table->genpd_virt_devs) {
+               if (!WARN_ON(opp->level != OPP_LEVEL_UNSET))
+                       opp->level = opp->required_opps[0]->level;
+       }
+
        return 0;
 }
 
@@ -338,7 +363,7 @@ static int _of_opp_alloc_required_opps(struct opp_table *opp_table,
                if (IS_ERR_OR_NULL(required_table))
                        continue;
 
-               ret = _link_required_opps(opp, required_table, i);
+               ret = _link_required_opps(opp, opp_table, required_table, i);
                if (ret)
                        goto free_required_opps;
        }
@@ -359,7 +384,7 @@ static int lazy_link_required_opps(struct opp_table *opp_table,
        int ret;
 
        list_for_each_entry(opp, &opp_table->opp_list, node) {
-               ret = _link_required_opps(opp, new_table, index);
+               ret = _link_required_opps(opp, opp_table, new_table, index);
                if (ret)
                        return ret;
        }