Commit f880ee9e authored by Dmitry Osipenko's avatar Dmitry Osipenko Committed by Thierry Reding
Browse files

soc/tegra: pmc: Add core power domain



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: default avatarDmitry Osipenko <digetx@gmail.com>
Reviewed-by: default avatarUlf Hansson <ulf.hansson@linaro.org>
[treding@nvidia.com: squash lockdep class removal patch]
Signed-off-by: default avatarThierry Reding <treding@nvidia.com>
parent 30b44e81
Loading
Loading
Loading
Loading
+2 −0
Original line number Diff line number Diff line
@@ -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
+110 −0
Original line number Diff line number Diff line
@@ -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 @@ static int tegra_powergate_add(struct tegra_pmc *pmc, struct device_node *np)
	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 *