mirror of
https://github.com/torvalds/linux.git
synced 2026-09-24 06:24:02 +02:00
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: 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>
This commit is contained in:
parent
30b44e8177
commit
f880ee9e96
|
|
@ -144,6 +144,8 @@ config SOC_TEGRA_FLOWCTRL
|
||||||
config SOC_TEGRA_PMC
|
config SOC_TEGRA_PMC
|
||||||
bool
|
bool
|
||||||
select GENERIC_PINCONF
|
select GENERIC_PINCONF
|
||||||
|
select PM_OPP
|
||||||
|
select PM_GENERIC_DOMAINS
|
||||||
|
|
||||||
config SOC_TEGRA_POWERGATE_BPMP
|
config SOC_TEGRA_POWERGATE_BPMP
|
||||||
def_bool y
|
def_bool y
|
||||||
|
|
|
||||||
|
|
@ -38,6 +38,7 @@
|
||||||
#include <linux/pinctrl/pinctrl.h>
|
#include <linux/pinctrl/pinctrl.h>
|
||||||
#include <linux/platform_device.h>
|
#include <linux/platform_device.h>
|
||||||
#include <linux/pm_domain.h>
|
#include <linux/pm_domain.h>
|
||||||
|
#include <linux/pm_opp.h>
|
||||||
#include <linux/reboot.h>
|
#include <linux/reboot.h>
|
||||||
#include <linux/regmap.h>
|
#include <linux/regmap.h>
|
||||||
#include <linux/reset.h>
|
#include <linux/reset.h>
|
||||||
|
|
@ -1302,12 +1303,100 @@ static int tegra_powergate_add(struct tegra_pmc *pmc, struct device_node *np)
|
||||||
return err;
|
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,
|
static int tegra_powergate_init(struct tegra_pmc *pmc,
|
||||||
struct device_node *parent)
|
struct device_node *parent)
|
||||||
{
|
{
|
||||||
|
struct of_phandle_args child_args, parent_args;
|
||||||
struct device_node *np, *child;
|
struct device_node *np, *child;
|
||||||
int err = 0;
|
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");
|
np = of_get_child_by_name(parent, "powergates");
|
||||||
if (!np)
|
if (!np)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
@ -1318,6 +1407,21 @@ static int tegra_powergate_init(struct tegra_pmc *pmc,
|
||||||
of_node_put(child);
|
of_node_put(child);
|
||||||
break;
|
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);
|
of_node_put(np);
|
||||||
|
|
@ -1361,6 +1465,12 @@ static void tegra_powergate_remove_all(struct device_node *parent)
|
||||||
}
|
}
|
||||||
|
|
||||||
of_node_put(np);
|
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 *
|
static const struct tegra_io_pad_soc *
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue
Block a user