From: Thierry Reding <thierry.reding@kernel.org>
To: "Damien Le Moal" <dlemoal@kernel.org>,
"Niklas Cassel" <cassel@kernel.org>,
"Thierry Reding" <thierry.reding@kernel.org>,
"Jonathan Hunter" <jonathanh@nvidia.com>,
"Lyude Paul" <lyude@redhat.com>,
"Danilo Krummrich" <dakr@kernel.org>,
"Maarten Lankhorst" <maarten.lankhorst@linux.intel.com>,
"Maxime Ripard" <mripard@kernel.org>,
"Thomas Zimmermann" <tzimmermann@suse.de>,
"David Airlie" <airlied@gmail.com>,
"Simona Vetter" <simona@ffwll.ch>,
"Mikko Perttunen" <mperttunen@nvidia.com>,
"Dmitry Osipenko" <digetx@gmail.com>,
"Mauro Carvalho Chehab" <mchehab@kernel.org>,
"Lorenzo Pieralisi" <lpieralisi@kernel.org>,
"Krzysztof Wilczyński" <kwilczynski@kernel.org>,
"Manivannan Sadhasivam" <mani@kernel.org>,
"Rob Herring" <robh@kernel.org>,
"Bjorn Helgaas" <bhelgaas@google.com>,
"Mathias Nyman" <mathias.nyman@intel.com>,
"Greg Kroah-Hartman" <gregkh@linuxfoundation.org>,
"Philipp Zabel" <p.zabel@pengutronix.de>
Cc: linux-ide@vger.kernel.org, linux-tegra@vger.kernel.org,
linux-kernel@vger.kernel.org, dri-devel@lists.freedesktop.org,
nouveau@lists.freedesktop.org, linux-media@vger.kernel.org,
linux-pci@vger.kernel.org, linux-usb@vger.kernel.org,
Thierry Reding <treding@nvidia.com>
Subject: [PATCH 7/9] soc/tegra: pmc: Create PMC context dynamically
Date: Wed, 06 May 2026 15:41:58 +0200 [thread overview]
Message-ID: <20260506-pmc-v1-7-a6de5da7216b@nvidia.com> (raw)
In-Reply-To: <20260506-pmc-v1-0-a6de5da7216b@nvidia.com>
From: Thierry Reding <treding@nvidia.com>
For legacy purposes, an early PMC context is needed to support certain
drivers and functionalities. However, when the PMC driver is probed in
the later boot stages, the early context is no longer needed. Allocate
the PMC context dynamically at probe time so that it can be used going
forward.
While at it, rename the early PMC context to more accurately reflect
what it is used for. It's technically not only for early boot stages,
but also to support some code that doesn't have a way of obtaining the
correct context otherwise (e.g. no access to device tree).
Signed-off-by: Thierry Reding <treding@nvidia.com>
---
drivers/soc/tegra/pmc.c | 163 ++++++++++++++++++++++++++----------------------
1 file changed, 88 insertions(+), 75 deletions(-)
diff --git a/drivers/soc/tegra/pmc.c b/drivers/soc/tegra/pmc.c
index 2ee6539d796a..50969b07fd8c 100644
--- a/drivers/soc/tegra/pmc.c
+++ b/drivers/soc/tegra/pmc.c
@@ -512,7 +512,7 @@ struct tegra_pmc {
u32 *wake_status;
};
-static struct tegra_pmc *pmc = &(struct tegra_pmc) {
+static struct tegra_pmc *early_pmc = &(struct tegra_pmc) {
.base = NULL,
.suspend_mode = TEGRA_SUSPEND_NOT_READY,
};
@@ -1069,7 +1069,7 @@ EXPORT_SYMBOL(tegra_pmc_powergate_power_on);
*/
int tegra_powergate_power_on(unsigned int id)
{
- return tegra_pmc_powergate_power_on(pmc, id);
+ return tegra_pmc_powergate_power_on(early_pmc, id);
}
EXPORT_SYMBOL(tegra_powergate_power_on);
@@ -1093,7 +1093,7 @@ EXPORT_SYMBOL(tegra_pmc_powergate_power_off);
*/
int tegra_powergate_power_off(unsigned int id)
{
- return tegra_pmc_powergate_power_off(pmc, id);
+ return tegra_pmc_powergate_power_off(early_pmc, id);
}
EXPORT_SYMBOL(tegra_powergate_power_off);
@@ -1130,7 +1130,7 @@ EXPORT_SYMBOL(tegra_pmc_powergate_remove_clamping);
*/
int tegra_powergate_remove_clamping(unsigned int id)
{
- return tegra_pmc_powergate_remove_clamping(pmc, id);
+ return tegra_pmc_powergate_remove_clamping(early_pmc, id);
}
EXPORT_SYMBOL(tegra_powergate_remove_clamping);
@@ -1192,7 +1192,7 @@ EXPORT_SYMBOL(tegra_pmc_powergate_sequence_power_up);
int tegra_powergate_sequence_power_up(unsigned int id, struct clk *clk,
struct reset_control *rst)
{
- return tegra_pmc_powergate_sequence_power_up(pmc, id, clk, rst);
+ return tegra_pmc_powergate_sequence_power_up(early_pmc, id, clk, rst);
}
EXPORT_SYMBOL(tegra_powergate_sequence_power_up);
@@ -1221,11 +1221,11 @@ bool tegra_pmc_cpu_is_powered(unsigned int cpuid)
{
int id;
- id = tegra_get_cpu_powergate_id(pmc, cpuid);
+ id = tegra_get_cpu_powergate_id(early_pmc, cpuid);
if (id < 0)
return false;
- return tegra_powergate_is_powered(pmc, id);
+ return tegra_powergate_is_powered(early_pmc, id);
}
/**
@@ -1236,11 +1236,11 @@ int tegra_pmc_cpu_power_on(unsigned int cpuid)
{
int id;
- id = tegra_get_cpu_powergate_id(pmc, cpuid);
+ id = tegra_get_cpu_powergate_id(early_pmc, cpuid);
if (id < 0)
return id;
- return tegra_powergate_set(pmc, id, true);
+ return tegra_powergate_set(early_pmc, id, true);
}
/**
@@ -1251,11 +1251,11 @@ int tegra_pmc_cpu_remove_clamping(unsigned int cpuid)
{
int id;
- id = tegra_get_cpu_powergate_id(pmc, cpuid);
+ id = tegra_get_cpu_powergate_id(early_pmc, cpuid);
if (id < 0)
return id;
- return tegra_powergate_remove_clamping(id);
+ return tegra_pmc_powergate_remove_clamping(early_pmc, id);
}
static void tegra_pmc_program_reboot_reason(struct tegra_pmc *pmc,
@@ -1533,7 +1533,7 @@ static int tegra_powergate_add(struct tegra_pmc *pmc, struct device_node *np)
bool tegra_pmc_core_domain_state_synced(void)
{
- return pmc->core_domain_state_synced;
+ return early_pmc->core_domain_state_synced;
}
static int
@@ -1831,7 +1831,7 @@ EXPORT_SYMBOL(tegra_pmc_io_pad_power_enable);
*/
int tegra_io_pad_power_enable(enum tegra_io_pad id)
{
- return tegra_pmc_io_pad_power_enable(pmc, id);
+ return tegra_pmc_io_pad_power_enable(early_pmc, id);
}
EXPORT_SYMBOL(tegra_io_pad_power_enable);
@@ -1887,7 +1887,7 @@ EXPORT_SYMBOL(tegra_pmc_io_pad_power_disable);
*/
int tegra_io_pad_power_disable(enum tegra_io_pad id)
{
- return tegra_pmc_io_pad_power_disable(pmc, id);
+ return tegra_pmc_io_pad_power_disable(early_pmc, id);
}
EXPORT_SYMBOL(tegra_io_pad_power_disable);
@@ -1976,7 +1976,7 @@ static int tegra_io_pad_get_voltage(struct tegra_pmc *pmc, enum tegra_io_pad id)
#ifdef CONFIG_PM_SLEEP
enum tegra_suspend_mode tegra_pmc_get_suspend_mode(void)
{
- return pmc->suspend_mode;
+ return early_pmc->suspend_mode;
}
void tegra_pmc_set_suspend_mode(enum tegra_suspend_mode mode)
@@ -1984,7 +1984,7 @@ void tegra_pmc_set_suspend_mode(enum tegra_suspend_mode mode)
if (mode < TEGRA_SUSPEND_NONE || mode >= TEGRA_MAX_SUSPEND_MODE)
return;
- pmc->suspend_mode = mode;
+ early_pmc->suspend_mode = mode;
}
void tegra_pmc_enter_suspend_mode(enum tegra_suspend_mode mode)
@@ -1999,7 +1999,7 @@ void tegra_pmc_enter_suspend_mode(enum tegra_suspend_mode mode)
break;
case TEGRA_SUSPEND_LP2:
- rate = pmc->rate;
+ rate = early_pmc->rate;
break;
default:
@@ -2009,18 +2009,18 @@ void tegra_pmc_enter_suspend_mode(enum tegra_suspend_mode mode)
if (WARN_ON_ONCE(rate == 0))
rate = 100000000;
- ticks = pmc->cpu_good_time * rate + USEC_PER_SEC - 1;
+ ticks = early_pmc->cpu_good_time * rate + USEC_PER_SEC - 1;
do_div(ticks, USEC_PER_SEC);
- tegra_pmc_writel(pmc, ticks, PMC_CPUPWRGOOD_TIMER);
+ tegra_pmc_writel(early_pmc, ticks, PMC_CPUPWRGOOD_TIMER);
- ticks = pmc->cpu_off_time * rate + USEC_PER_SEC - 1;
+ ticks = early_pmc->cpu_off_time * rate + USEC_PER_SEC - 1;
do_div(ticks, USEC_PER_SEC);
- tegra_pmc_writel(pmc, ticks, PMC_CPUPWROFF_TIMER);
+ tegra_pmc_writel(early_pmc, ticks, PMC_CPUPWROFF_TIMER);
- value = tegra_pmc_readl(pmc, PMC_CNTRL);
+ value = tegra_pmc_readl(early_pmc, PMC_CNTRL);
value &= ~PMC_CNTRL_SIDE_EFFECT_LP0;
value |= PMC_CNTRL_CPU_PWRREQ_OE;
- tegra_pmc_writel(pmc, value, PMC_CNTRL);
+ tegra_pmc_writel(early_pmc, value, PMC_CNTRL);
}
#endif
@@ -3110,6 +3110,44 @@ static int tegra_pmc_regmap_init(struct tegra_pmc *pmc)
return 0;
}
+static bool tegra_pmc_detect_tz_only(struct tegra_pmc *pmc)
+{
+ u32 value, saved;
+
+ saved = readl(pmc->base + pmc->soc->regs->scratch0);
+ value = saved ^ 0xffffffff;
+
+ if (value == 0xffffffff)
+ value = 0xdeadbeef;
+
+ /* write pattern and read it back */
+ writel(value, pmc->base + pmc->soc->regs->scratch0);
+ value = readl(pmc->base + pmc->soc->regs->scratch0);
+
+ /* if we read all-zeroes, access is restricted to TZ only */
+ if (value == 0) {
+ pr_info("access to PMC is restricted to TZ\n");
+ return true;
+ }
+
+ /* restore original value */
+ writel(saved, pmc->base + pmc->soc->regs->scratch0);
+
+ return false;
+}
+
+static void tegra_pmc_init_common(struct tegra_pmc *pmc)
+{
+ unsigned int i;
+
+ pmc->tz_only = tegra_pmc_detect_tz_only(pmc);
+
+ /* Create a bitmap of the available and valid partitions */
+ for (i = 0; i < pmc->soc->num_powergates; i++)
+ if (pmc->soc->powergates[i])
+ set_bit(i, pmc->powergates_available);
+}
+
static void tegra_pmc_reset_suspend_mode(void *data)
{
struct tegra_pmc *pmc = data;
@@ -3119,7 +3157,7 @@ static void tegra_pmc_reset_suspend_mode(void *data)
static int tegra_pmc_probe(struct platform_device *pdev)
{
- void __iomem *base;
+ struct tegra_pmc *pmc;
struct resource *res;
int err;
@@ -3128,9 +3166,16 @@ static int tegra_pmc_probe(struct platform_device *pdev)
* register mapping and setup the soc data pointer. If these
* are not valid then something went badly wrong!
*/
- if (WARN_ON(!pmc->base || !pmc->soc))
+ if (WARN_ON(!early_pmc->base || !early_pmc->soc))
return -ENODEV;
+ pmc = devm_kzalloc(&pdev->dev, sizeof(*pmc), GFP_KERNEL);
+ if (!pmc)
+ return -ENOMEM;
+
+ pmc->soc = device_get_match_data(&pdev->dev);
+ tegra_pmc_init_common(pmc);
+
err = tegra_pmc_parse_dt(pmc, pdev->dev.of_node);
if (err < 0)
return err;
@@ -3141,14 +3186,14 @@ static int tegra_pmc_probe(struct platform_device *pdev)
return err;
/* take over the memory region from the early initialization */
- base = devm_platform_ioremap_resource(pdev, 0);
- if (IS_ERR(base))
- return PTR_ERR(base);
+ pmc->base = devm_platform_ioremap_resource(pdev, 0);
+ if (IS_ERR(pmc->base))
+ return PTR_ERR(pmc->base);
if (pmc->soc->has_single_mmio_aperture) {
- pmc->wake = base;
- pmc->aotag = base;
- pmc->scratch = base;
+ pmc->wake = pmc->base;
+ pmc->aotag = pmc->base;
+ pmc->scratch = pmc->base;
} else {
pmc->wake = devm_platform_ioremap_resource_byname(pdev, "wake");
if (IS_ERR(pmc->wake))
@@ -3167,7 +3212,7 @@ static int tegra_pmc_probe(struct platform_device *pdev)
/* "scratch" is an optional aperture */
res = platform_get_resource_byname(pdev, IORESOURCE_MEM,
- "scratch");
+ "scratch");
if (res) {
pmc->scratch = devm_ioremap_resource(&pdev->dev, res);
if (IS_ERR(pmc->scratch))
@@ -3271,10 +3316,10 @@ static int tegra_pmc_probe(struct platform_device *pdev)
if (err < 0)
goto cleanup_powergates;
- mutex_lock(&pmc->powergates_lock);
- iounmap(pmc->base);
- pmc->base = base;
- mutex_unlock(&pmc->powergates_lock);
+ mutex_lock(&early_pmc->powergates_lock);
+ iounmap(early_pmc->base);
+ early_pmc->base = pmc->base;
+ mutex_unlock(&early_pmc->powergates_lock);
tegra_pmc_clock_register(pmc, pdev->dev.of_node);
platform_set_drvdata(pdev, pmc);
@@ -4799,6 +4844,7 @@ static const struct of_device_id tegra_pmc_match[] = {
static void tegra_pmc_sync_state(struct device *dev)
{
+ struct tegra_pmc *pmc = dev_get_drvdata(dev);
struct device_node *np, *child;
int err;
@@ -4856,32 +4902,6 @@ static struct platform_driver tegra_pmc_driver = {
};
builtin_platform_driver(tegra_pmc_driver);
-static bool __init tegra_pmc_detect_tz_only(struct tegra_pmc *pmc)
-{
- u32 value, saved;
-
- saved = readl(pmc->base + pmc->soc->regs->scratch0);
- value = saved ^ 0xffffffff;
-
- if (value == 0xffffffff)
- value = 0xdeadbeef;
-
- /* write pattern and read it back */
- writel(value, pmc->base + pmc->soc->regs->scratch0);
- value = readl(pmc->base + pmc->soc->regs->scratch0);
-
- /* if we read all-zeroes, access is restricted to TZ only */
- if (value == 0) {
- pr_info("access to PMC is restricted to TZ\n");
- return true;
- }
-
- /* restore original value */
- writel(saved, pmc->base + pmc->soc->regs->scratch0);
-
- return false;
-}
-
/*
* Early initialization to allow access to registers in the very early boot
* process.
@@ -4891,10 +4911,9 @@ static int __init tegra_pmc_early_init(void)
const struct of_device_id *match;
struct device_node *np;
struct resource regs;
- unsigned int i;
bool invert;
- mutex_init(&pmc->powergates_lock);
+ mutex_init(&early_pmc->powergates_lock);
np = of_find_matching_node_and_match(NULL, tegra_pmc_match, &match);
if (!np) {
@@ -4935,23 +4954,17 @@ static int __init tegra_pmc_early_init(void)
}
}
- pmc->base = ioremap(regs.start, resource_size(®s));
- if (!pmc->base) {
+ early_pmc->base = ioremap(regs.start, resource_size(®s));
+ if (!early_pmc->base) {
pr_err("failed to map PMC registers\n");
of_node_put(np);
return -ENXIO;
}
if (of_device_is_available(np)) {
- pmc->soc = match->data;
-
- if (pmc->soc->maybe_tz_only)
- pmc->tz_only = tegra_pmc_detect_tz_only(pmc);
+ early_pmc->soc = match->data;
- /* Create a bitmap of the available and valid partitions */
- for (i = 0; i < pmc->soc->num_powergates; i++)
- if (pmc->soc->powergates[i])
- set_bit(i, pmc->powergates_available);
+ tegra_pmc_init_common(early_pmc);
/*
* Invert the interrupt polarity if a PMC device tree node
@@ -4959,7 +4972,7 @@ static int __init tegra_pmc_early_init(void)
*/
invert = of_property_read_bool(np, "nvidia,invert-interrupt");
- pmc->soc->setup_irq_polarity(pmc, np, invert);
+ early_pmc->soc->setup_irq_polarity(early_pmc, np, invert);
of_node_put(np);
}
--
2.52.0
next prev parent reply other threads:[~2026-05-06 13:42 UTC|newest]
Thread overview: 11+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-05-06 13:41 [PATCH 0/9] soc/tegra: pmc: Clean up legacy code Thierry Reding
2026-05-06 13:41 ` [PATCH 1/9] ata: ahci_tegra: Explicitly specify PMC instance to use Thierry Reding
2026-05-06 13:54 ` Damien Le Moal
2026-05-06 13:41 ` [PATCH 2/9] drm/nouveau: tegra: " Thierry Reding
2026-05-06 13:41 ` [PATCH 3/9] drm/tegra: " Thierry Reding
2026-05-06 13:41 ` [PATCH 4/9] media: vde: " Thierry Reding
2026-05-06 13:41 ` [PATCH 5/9] PCI: tegra: " Thierry Reding
2026-05-06 13:41 ` [PATCH 6/9] usb: xhci: " Thierry Reding
2026-05-06 13:41 ` Thierry Reding [this message]
2026-05-06 13:41 ` [PATCH 8/9] soc/tegra: pmc: Remove unused legacy functions Thierry Reding
2026-05-06 13:42 ` [PATCH 9/9] soc/tegra: pmc: Move legacy code behind CONFIG_ARM guard Thierry Reding
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20260506-pmc-v1-7-a6de5da7216b@nvidia.com \
--to=thierry.reding@kernel.org \
--cc=airlied@gmail.com \
--cc=bhelgaas@google.com \
--cc=cassel@kernel.org \
--cc=dakr@kernel.org \
--cc=digetx@gmail.com \
--cc=dlemoal@kernel.org \
--cc=dri-devel@lists.freedesktop.org \
--cc=gregkh@linuxfoundation.org \
--cc=jonathanh@nvidia.com \
--cc=kwilczynski@kernel.org \
--cc=linux-ide@vger.kernel.org \
--cc=linux-kernel@vger.kernel.org \
--cc=linux-media@vger.kernel.org \
--cc=linux-pci@vger.kernel.org \
--cc=linux-tegra@vger.kernel.org \
--cc=linux-usb@vger.kernel.org \
--cc=lpieralisi@kernel.org \
--cc=lyude@redhat.com \
--cc=maarten.lankhorst@linux.intel.com \
--cc=mani@kernel.org \
--cc=mathias.nyman@intel.com \
--cc=mchehab@kernel.org \
--cc=mperttunen@nvidia.com \
--cc=mripard@kernel.org \
--cc=nouveau@lists.freedesktop.org \
--cc=p.zabel@pengutronix.de \
--cc=robh@kernel.org \
--cc=simona@ffwll.ch \
--cc=treding@nvidia.com \
--cc=tzimmermann@suse.de \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox