[PATCH] watchdog: npcm: fix clock prepare leak when setting timeout on a running watchdog

From: Zhao Feng

Date: Mon Nov 09 2026 - 02:10:15 EST


npcm_wdt_start() unconditionally prepares and enables the watchdog
clock, and npcm_wdt_stop() is the only place that disables it.
npcm_wdt_set_timeout() calls npcm_wdt_start() again whenever the
watchdog is already active, so every WDIOC_SETTIMEOUT ioctl issued
while the watchdog is running takes one extra clock prepare/enable
reference that is never dropped.

Split the register programming out of npcm_wdt_start() into a
__npcm_wdt_start() helper that assumes the clock is already running,
and call the helper from npcm_wdt_set_timeout() instead; only the
watchdog .start callback (and the restart path, which intentionally
keeps the clock) now acquires the clock.

Fixes: af084fdccfaf ("watchdog: npcm: Enable clock if provided")
Cc: stable@xxxxxxxxxxxxxxx
Signed-off-by: Zhao Feng <zhaofeng2@xxxxxxxxxxxxxxxxxxxx>
---
Verified against current upstream master. Not runtime-tested (no
Nuvoton NPCM hardware).

Note: this overlaps textually with Triet Hoang's pending
error-propagation patch
(https://marc.info/?l=linux-kernel&m=178955112320092)
but fixes a different bug; whichever lands second will need a
trivial rebase.

drivers/watchdog/npcm_wdt.c | 17 +++++++++++++----
1 file changed, 13 insertions(+), 4 deletions(-)

diff --git a/drivers/watchdog/npcm_wdt.c b/drivers/watchdog/npcm_wdt.c
--- a/drivers/watchdog/npcm_wdt.c
+++ b/drivers/watchdog/npcm_wdt.c
@@ -148,10 +148,8 @@
-static int npcm_wdt_start(struct watchdog_device *wdd)
+static void __npcm_wdt_start(struct watchdog_device *wdd)
{
struct npcm_wdt *wdt = to_npcm_wdt(wdd);
u32 val;

- clk_prepare_enable(wdt->clk);
-
if (wdd->timeout < 2)
val = 0x800;
else if (wdd->timeout < 3)
@@ -176,10 +174,19 @@
val |= NPCM_WTRE | NPCM_WTE | NPCM_WTR | NPCM_WTIE;

writel(val, wdt->reg);
-
- return 0;
}

+static int npcm_wdt_start(struct watchdog_device *wdd)
+{
+ struct npcm_wdt *wdt = to_npcm_wdt(wdd);
+
+ clk_prepare_enable(wdt->clk);
+
+ __npcm_wdt_start(wdd);
+
+ return 0;
+}
+
static int npcm_wdt_stop(struct watchdog_device *wdd)
{
struct npcm_wdt *wdt = to_npcm_wdt(wdd);
@@ -216,7 +223,7 @@
wdd->timeout = 2750;

if (watchdog_active(wdd))
- npcm_wdt_start(wdd);
+ __npcm_wdt_start(wdd);

return 0;
}
--
2.39.2