From: Artur Kowalski <arturkow2000@gmail.com>
To: Daniel Lezcano <daniel.lezcano@kernel.org>,
Thomas Gleixner <tglx@kernel.org>,
Thierry Reding <thierry.reding@kernel.org>,
Jonathan Hunter <jonathanh@nvidia.com>,
Wim Van Sebroeck <wim@linux-watchdog.org>,
Guenter Roeck <linux@roeck-us.net>,
Jonathan Corbet <corbet@lwn.net>,
Shuah Khan <skhan@linuxfoundation.org>,
Randy Dunlap <rdunlap@infradead.org>,
Philipp Zabel <p.zabel@pengutronix.de>
Cc: linux-kernel@vger.kernel.org, linux-tegra@vger.kernel.org,
linux-watchdog@vger.kernel.org, linux-doc@vger.kernel.org,
clamor95@gmail.com, Artur Kowalski <arturkow2000@gmail.com>
Subject: [PATCH 5/6] soc/tegra: pmc: Add a partition power off notifier
Date: Tue, 10 Nov 2026 13:39:10 +0100 [thread overview]
Message-ID: <20261110-t20-for-upstream-v1-5-5923449951fe@gmail.com> (raw)
In-Reply-To: <20261110-t20-for-upstream-v1-0-5923449951fe@gmail.com>
After a watchdog reset, which doesn't reset the PMC, Tegra20's boot ROM
hangs if the VDE partition is powered off. Unless booted with
tegra_pmc.vde_always_on=1, the PMC driver powers the partition off when
it registers it, and genpd powers it off while the VDE is idle. The
Tegra watchdog driver will need to stop the watchdog before that
happens.
genpd notifiers can't be used for this: they only reach devices attached
to the domain, which the watchdog isn't, and the partitions are powered
off before their genpds are registered. Add a notifier that is called
before a partition is powered off, from tegra_powergate_set(), which all
power off paths go through.
When a notifier is registered, it is also called for every partition that
is already powered off, except one kept powered on with
tegra_pmc.vde_always_on, which the PMC driver powers on when it registers
it. A lock is held across both the notification and the power off, so a
partition can't be powered off unnoticed while a notifier is being
registered. Registration uses the early PMC, so it works before the PMC
driver has probed, which is when the partitions are first powered off.
The notifier, including the hook on the power off path, is therefore only
built on 32-bit ARM, the only architecture with an early PMC.
Assisted-by: Claude:claude-opus-5-5
Signed-off-by: Artur Kowalski <arturkow2000@gmail.com>
---
drivers/soc/tegra/pmc.c | 104 ++++++++++++++++++++++++++++++++++++++++++++++++
include/soc/tegra/pmc.h | 19 +++++++++
2 files changed, 123 insertions(+)
diff --git a/drivers/soc/tegra/pmc.c b/drivers/soc/tegra/pmc.c
index 807977f52449..1d9a6aeb0e57 100644
--- a/drivers/soc/tegra/pmc.c
+++ b/drivers/soc/tegra/pmc.c
@@ -31,6 +31,7 @@
#include <linux/irq.h>
#include <linux/irq_work.h>
#include <linux/kernel.h>
+#include <linux/notifier.h>
#include <linux/of_address.h>
#include <linux/of_clk.h>
#include <linux/of.h>
@@ -520,6 +521,34 @@ static struct tegra_pmc *early_pmc = &(struct tegra_pmc) {
.base = NULL,
.suspend_mode = TEGRA_SUSPEND_NOT_READY,
};
+
+/*
+ * Notified before a partition is powered off. The lock is held across the
+ * notification and the power off, so that a notifier being registered either
+ * sees the partition still powered or is notified.
+ */
+static DEFINE_MUTEX(tegra_powergate_nh_lock);
+static RAW_NOTIFIER_HEAD(tegra_powergate_nh);
+
+static void tegra_powergate_power_off_begin(unsigned int id)
+{
+ mutex_lock(&tegra_powergate_nh_lock);
+ raw_notifier_call_chain(&tegra_powergate_nh,
+ TEGRA_PMC_POWERGATE_POWER_OFF, &id);
+}
+
+static void tegra_powergate_power_off_end(void)
+{
+ mutex_unlock(&tegra_powergate_nh_lock);
+}
+#else
+static inline void tegra_powergate_power_off_begin(unsigned int id)
+{
+}
+
+static inline void tegra_powergate_power_off_end(void)
+{
+}
#endif
#ifdef MODULE_PARAM_PREFIX
@@ -730,8 +759,14 @@ static int tegra_powergate_set(struct tegra_pmc *pmc, unsigned int id,
return 0;
}
+ if (!new_state)
+ tegra_powergate_power_off_begin(id);
+
err = pmc->soc->powergate_set(pmc, id, new_state);
+ if (!new_state)
+ tegra_powergate_power_off_end();
+
mutex_unlock(&pmc->powergates_lock);
return err;
@@ -5166,6 +5201,75 @@ int tegra_pmc_cpu_remove_clamping(unsigned int cpuid)
return tegra_pmc_powergate_remove_clamping(early_pmc, id);
}
+/**
+ * tegra_pmc_powergate_register_notifier() - get notified of partition power off
+ * @nb: notifier block
+ *
+ * The notifier is called with %TEGRA_PMC_POWERGATE_POWER_OFF and a pointer to
+ * the partition ID (unsigned int) as data before a partition is powered off,
+ * from a context that may sleep. Its return value is ignored. It is also
+ * called for every partition that is already powered off before this
+ * function returns, so a partition is either still powered once the function
+ * returns or the notifier has been called for it. A partition that is kept
+ * powered on (tegra_pmc.vde_always_on) is never reported, as the driver powers
+ * it on when it registers it.
+ *
+ * This works before the PMC driver has probed, as the PMC is set up early on
+ * 32-bit ARM, so it covers the partitions that the driver powers off when it
+ * registers them.
+ */
+int tegra_pmc_powergate_register_notifier(struct notifier_block *nb)
+{
+ unsigned int id;
+ int err;
+
+ mutex_lock(&tegra_powergate_nh_lock);
+
+ err = raw_notifier_chain_register(&tegra_powergate_nh, nb);
+ if (err)
+ goto unlock;
+
+ /* the base is switched to the driver's mapping under this lock */
+ mutex_lock(&early_pmc->powergates_lock);
+
+ if (early_pmc->base && early_pmc->soc) {
+ for (id = 0; id < early_pmc->soc->num_powergates; id++) {
+ /* powered on when the driver registers it */
+ if (tegra_powergate_is_always_on(id))
+ continue;
+
+ if (tegra_powergate_is_powered(early_pmc, id) == 0)
+ nb->notifier_call(nb,
+ TEGRA_PMC_POWERGATE_POWER_OFF,
+ &id);
+ }
+ }
+
+ mutex_unlock(&early_pmc->powergates_lock);
+
+unlock:
+ mutex_unlock(&tegra_powergate_nh_lock);
+
+ return err;
+}
+EXPORT_SYMBOL(tegra_pmc_powergate_register_notifier);
+
+/**
+ * tegra_pmc_powergate_unregister_notifier() - unregister a power off notifier
+ * @nb: notifier block registered with tegra_pmc_powergate_register_notifier()
+ */
+int tegra_pmc_powergate_unregister_notifier(struct notifier_block *nb)
+{
+ int err;
+
+ mutex_lock(&tegra_powergate_nh_lock);
+ err = raw_notifier_chain_unregister(&tegra_powergate_nh, nb);
+ mutex_unlock(&tegra_powergate_nh_lock);
+
+ return err;
+}
+EXPORT_SYMBOL(tegra_pmc_powergate_unregister_notifier);
+
bool tegra_pmc_core_domain_state_synced(void)
{
return early_pmc->core_domain_state_synced;
diff --git a/include/soc/tegra/pmc.h b/include/soc/tegra/pmc.h
index 53f6d02889db..c574a6e81a1a 100644
--- a/include/soc/tegra/pmc.h
+++ b/include/soc/tegra/pmc.h
@@ -231,14 +231,33 @@ static inline void tegra_pmc_enter_suspend_mode(enum tegra_suspend_mode mode)
#endif
#endif
+/* event of the partition power off notifier, data points to the partition ID */
+#define TEGRA_PMC_POWERGATE_POWER_OFF 0
+
+struct notifier_block;
+
#if defined(CONFIG_ARM) && defined(CONFIG_SOC_TEGRA_PMC)
bool tegra_pmc_core_domain_state_synced(void);
+int tegra_pmc_powergate_register_notifier(struct notifier_block *nb);
+int tegra_pmc_powergate_unregister_notifier(struct notifier_block *nb);
#else
/* needed for COMPILE_TEST */
static inline bool tegra_pmc_core_domain_state_synced(void)
{
return false;
}
+
+static inline int
+tegra_pmc_powergate_register_notifier(struct notifier_block *nb)
+{
+ return -ENOSYS;
+}
+
+static inline int
+tegra_pmc_powergate_unregister_notifier(struct notifier_block *nb)
+{
+ return -ENOSYS;
+}
#endif
#endif /* __SOC_TEGRA_PMC_H__ */
--
2.56.0
next prev parent reply other threads:[~2026-11-10 12:39 UTC|newest]
Thread overview: 8+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-11-10 12:39 [PATCH 0/6] watchdog: tegra_wdt: Add Tegra20 support Artur Kowalski
2026-11-10 12:39 ` [PATCH 1/6] clocksource/drivers/timer-tegra: Keep Tegra20's watchdog timers free Artur Kowalski
2026-11-10 12:39 ` [PATCH 2/6] watchdog: tegra_wdt: Move SoC specific parts into match data Artur Kowalski
2026-11-10 12:39 ` [PATCH 3/6] watchdog: tegra_wdt: Support Tegra20 Artur Kowalski
2026-11-10 16:21 ` Krzysztof Kozlowski
2026-11-10 12:39 ` [PATCH 4/6] soc/tegra: pmc: Add an option to keep the VDE partition on Artur Kowalski
2026-11-10 12:39 ` Artur Kowalski [this message]
2026-11-10 12:39 ` [PATCH 6/6] watchdog: tegra_wdt: Stop on Tegra20 VDE power off Artur Kowalski
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=20261110-t20-for-upstream-v1-5-5923449951fe@gmail.com \
--to=arturkow2000@gmail.com \
--cc=clamor95@gmail.com \
--cc=corbet@lwn.net \
--cc=daniel.lezcano@kernel.org \
--cc=jonathanh@nvidia.com \
--cc=linux-doc@vger.kernel.org \
--cc=linux-kernel@vger.kernel.org \
--cc=linux-tegra@vger.kernel.org \
--cc=linux-watchdog@vger.kernel.org \
--cc=linux@roeck-us.net \
--cc=p.zabel@pengutronix.de \
--cc=rdunlap@infradead.org \
--cc=skhan@linuxfoundation.org \
--cc=tglx@kernel.org \
--cc=thierry.reding@kernel.org \
--cc=wim@linux-watchdog.org \
/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
all inboxes | Powered by JetHome®