]> www.infradead.org Git - users/hch/dma-mapping.git/commitdiff
soc/tegra: pmc: Add core power domain
authorDmitry Osipenko <digetx@gmail.com>
Tue, 1 Jun 2021 02:31:17 +0000 (05:31 +0300)
committerThierry Reding <treding@nvidia.com>
Wed, 2 Jun 2021 08:58:42 +0000 (10:58 +0200)
NVIDIA Tegra SoCs have multiple power domains, each domain corresponds
to an external SoC power rail. Core power domain covers vast majority of
hardware blocks within a Tegra SoC. The voltage of a power domain should
be set to a level which satisfies all devices within the power domain.
Add support for the core power domain which controls voltage state of the
domain. This allows us to support system-wide DVFS on Tegra20-210 SoCs.
The PMC powergate domains now are sub-domains of the core domain, this
requires device-tree updating, older DTBs are unaffected and will continue
to work as before.

Tested-by: Peter Geis <pgwipeout@gmail.com> # Ouya T30
Tested-by: Paul Fertser <fercerpav@gmail.com> # PAZ00 T20
Tested-by: Nicolas Chauvet <kwizart@gmail.com> # PAZ00 T20 and TK1 T124
Tested-by: Matt Merhar <mattmerhar@protonmail.com> # Ouya T30
Signed-off-by: Dmitry Osipenko <digetx@gmail.com>
Reviewed-by: Ulf Hansson <ulf.hansson@linaro.org>
[treding@nvidia.com: squash lockdep class removal patch]
Signed-off-by: Thierry Reding <treding@nvidia.com>
drivers/soc/tegra/Kconfig
drivers/soc/tegra/pmc.c

index 976dee0364700e9f13dec6b52d7d620631a44724..20ace654553a50071ae6e4a18ac11c21f6e3e5bd 100644 (file)
@@ -144,6 +144,8 @@ config SOC_TEGRA_FLOWCTRL
 config SOC_TEGRA_PMC
        bool
        select GENERIC_PINCONF
+       select PM_OPP
+       select PM_GENERIC_DOMAINS
 
 config SOC_TEGRA_POWERGATE_BPMP
        def_bool y
index 8e3b78bb2ac2ce549b1fc526f19968169c8d11dc..ceceb1709e0a2620fc30db5aca253ff2a10c15e6 100644 (file)
@@ -38,6 +38,7 @@
 #include <linux/pinctrl/pinctrl.h>
 #include <linux/platform_device.h>
 #include <linux/pm_domain.h>
+#include <linux/pm_opp.h>
 #include <linux/reboot.h>
 #include <linux/regmap.h>
 #include <linux/reset.h>
@@ -1302,12 +1303,100 @@ free_mem:
        return err;
 }
 
+static int
+tegra_pmc_core_pd_set_performance_state(struct generic_pm_domain *genpd,
+                                       unsigned int level)
+{
+       struct dev_pm_opp *opp;
+       int err;
+
+       opp = dev_pm_opp_find_level_ceil(&genpd->dev, &level);
+       if (IS_ERR(opp)) {
+               dev_err(&genpd->dev, "failed to find OPP for level %u: %pe\n",
+                       level, opp);
+               return PTR_ERR(opp);
+       }
+
+       mutex_lock(&pmc->powergates_lock);
+       err = dev_pm_opp_set_opp(pmc->dev, opp);
+       mutex_unlock(&pmc->powergates_lock);
+
+       dev_pm_opp_put(opp);
+
+       if (err) {
+               dev_err(&genpd->dev, "failed to set voltage to %duV: %d\n",
+                       level, err);
+               return err;
+       }
+
+       return 0;
+}
+
+static unsigned int
+tegra_pmc_core_pd_opp_to_performance_state(struct generic_pm_domain *genpd,
+                                          struct dev_pm_opp *opp)
+{
+       return dev_pm_opp_get_level(opp);
+}
+
+static int tegra_pmc_core_pd_add(struct tegra_pmc *pmc, struct device_node *np)
+{
+       struct generic_pm_domain *genpd;
+       const char *rname = "core";
+       int err;
+
+       genpd = devm_kzalloc(pmc->dev, sizeof(*genpd), GFP_KERNEL);
+       if (!genpd)
+               return -ENOMEM;
+
+       genpd->name = np->name;
+       genpd->set_performance_state = tegra_pmc_core_pd_set_performance_state;
+       genpd->opp_to_performance_state = tegra_pmc_core_pd_opp_to_performance_state;
+
+       err = devm_pm_opp_set_regulators(pmc->dev, &rname, 1);
+       if (err)
+               return dev_err_probe(pmc->dev, err,
+                                    "failed to set core OPP regulator\n");
+
+       err = pm_genpd_init(genpd, NULL, false);
+       if (err) {
+               dev_err(pmc->dev, "failed to init core genpd: %d\n", err);
+               return err;
+       }
+
+       err = of_genpd_add_provider_simple(np, genpd);
+       if (err) {
+               dev_err(pmc->dev, "failed to add core genpd: %d\n", err);
+               goto remove_genpd;
+       }
+
+       return 0;
+
+remove_genpd:
+       pm_genpd_remove(genpd);
+
+       return err;
+}
+
 static int tegra_powergate_init(struct tegra_pmc *pmc,
                                struct device_node *parent)
 {
+       struct of_phandle_args child_args, parent_args;
        struct device_node *np, *child;
        int err = 0;
 
+       /*
+        * Core power domain is the parent of powergate domains, hence it
+        * should be registered first.
+        */
+       np = of_get_child_by_name(parent, "core-domain");
+       if (np) {
+               err = tegra_pmc_core_pd_add(pmc, np);
+               of_node_put(np);
+               if (err)
+                       return err;
+       }
+
        np = of_get_child_by_name(parent, "powergates");
        if (!np)
                return 0;
@@ -1318,6 +1407,21 @@ static int tegra_powergate_init(struct tegra_pmc *pmc,
                        of_node_put(child);
                        break;
                }
+
+               if (of_parse_phandle_with_args(child, "power-domains",
+                                              "#power-domain-cells",
+                                              0, &parent_args))
+                       continue;
+
+               child_args.np = child;
+               child_args.args_count = 0;
+
+               err = of_genpd_add_subdomain(&parent_args, &child_args);
+               of_node_put(parent_args.np);
+               if (err) {
+                       of_node_put(child);
+                       break;
+               }
        }
 
        of_node_put(np);
@@ -1361,6 +1465,12 @@ static void tegra_powergate_remove_all(struct device_node *parent)
        }
 
        of_node_put(np);
+
+       np = of_get_child_by_name(parent, "core-domain");
+       if (np) {
+               of_genpd_del_provider(np);
+               of_genpd_remove_last(np);
+       }
 }
 
 static const struct tegra_io_pad_soc *