Linux Watchdog driver development
 help / color / mirror / Atom feed
* [PATCH v2 0/8] watchdog: starfive-wdt: Fix runtime PM and unclocked register access bugs
@ 2026-11-05 19:05 Guenter Roeck
  2026-11-05 19:05 ` [PATCH v2 1/8] watchdog: starfive-wdt: Drop runtime PM reference if starting watchdog fails Guenter Roeck
                   ` (7 more replies)
  0 siblings, 8 replies; 15+ messages in thread
From: Guenter Roeck @ 2026-11-05 19:05 UTC (permalink / raw)
  To: linux-watchdog; +Cc: Tzung-Bi Shih, Guenter Roeck, Xingyu Wu, Ziv Xu

The StarFive watchdog driver (drivers/watchdog/starfive-wdt.c) gates its
APB and core clocks via runtime PM callbacks, but has several runtime PM
reference-counting, ordering, and unclocked MMIO access bugs across
probe, remove, start/stop, shutdown, ioctl/sysfs, and system sleep paths:

1. In starfive_wdt_pm_start(), if starfive_wdt_start() fails after
   pm_runtime_resume_and_get() succeeds, the acquired runtime PM
   reference is leaked because the watchdog core does not call the stop
   callback when start fails.
2. In starfive_wdt_probe(), early returns on pm_runtime_resume_and_get()
   or starfive_wdt_enable_clock() failure bypass pm_runtime_disable(),
   leaving the runtime PM disable_depth counter unbalanced.
3. When runtime PM has suspended an idle watchdog and gated its clocks,
   starfive_wdt_remove(), starfive_wdt_shutdown(), starfive_wdt_suspend(),
   and starfive_wdt_resume() unconditionally read and write hardware
   registers, causing unclocked MMIO bus faults. In addition,
   starfive_wdt_shutdown() duplicates watchdog_stop_on_reboot() (causing
   unbalanced runtime PM puts when not runtime-suspended and ignoring
   watchdog.stop_on_reboot=0), and starfive_wdt_pm_stop() propagates
   non-zero idle return codes from pm_runtime_put_sync() as errors to
   watchdog_stop().
4. Probe error paths after pm_runtime_resume_and_get() disable clocks
   manually without releasing the runtime PM reference (and without
   stopping an early_enable watchdog if registration fails), and
   starfive_wdt_remove() never drops the active runtime PM reference
   retained when watchdog_hw_running() is still set (e.g. early_enable).
5. In starfive_wdt_probe(), watchdog_register_device() is called before
   pm_runtime_put_sync() drops the probe-time reference, and non-zero
   idle return values such as -EAGAIN (triggered if userspace opens
   /dev/watchdog immediately upon registration) are treated as fatal
   probe errors.
6. In starfive_wdt_get_timeleft(), reading the sysfs timeleft attribute
   or issuing ioctl(WDIOC_GETTIMELEFT) while the watchdog is inactive
   reads hardware registers while clocks are gated off.
7. In starfive_wdt_probe(), omitting watchdog_stop_ping_on_suspend()
   leaves the non-freezable watchdog ping worker active across system
   suspend when early_enable=1 is used, risking unclocked MMIO in
   starfive_wdt_keepalive() after starfive_wdt_suspend() gates clocks.
8. In starfive_wdt_set_timeout(), issuing ioctl(WDIOC_SETTIMEOUT) after
   stopping the watchdog via WDIOS_DISABLECARD accesses unclocked MMIO
   registers (when runtime-suspended) or unexpectedly re-enables the
   hardware counter while inactive (when runtime PM is disabled).

This 8-patch series fixes each of these issues in sequence.

Changes in v2:
- Patch 1, 2, 4, and 5: Added Reviewed-by: tags
- Patch 3: Remove the redundant starfive_wdt_shutdown() callback instead
  of guarding it with !pm_runtime_suspended(), avoiding unbalanced
  pm_runtime_put_sync() calls when runtime suspend is forbidden or after
  watchdog_reboot_notifier() already stopped the watchdog, avoiding
  unclocked MMIO during hibernation shutdown, and respecting the
  watchdog.stop_on_reboot module parameter.
- Patch 6 (new): Return 0 in starfive_wdt_get_timeleft() when
  !watchdog_hw_running() to avoid unclocked MMIO reads.
- Patch 7 (new): Call watchdog_stop_ping_on_suspend() in
  starfive_wdt_probe() so the ping worker does not call
  starfive_wdt_keepalive() while clocks are gated during suspend.
- Patch 8 (new): Only program hardware registers in
  starfive_wdt_set_timeout() when watchdog_hw_running() is set, and
  reload the preset counter in starfive_wdt_start() when starting from
  probe or after WDIOS_DISABLECARD.

Note on tooling and testing:
This series was generated with an AI coding assistant (Jetski / Gemini)
following analysis of reported runtime PM and lifecycle bugs in
starfive-wdt.c. All patches were build-tested with make allmodconfig and
make W=1 with zero warnings, verified with scripts/checkpatch.pl
--strict, and validated against a state-machine simulation harness
covering runtime PM, clock gating, ioctl/sysfs, suspend/shutdown, and
concurrent open paths; they were not tested on physical StarFive
JH7100/JH7110 hardware.

Cc: Xingyu Wu <xingyu.wu@starfivetech.com>
Cc: Ziv Xu <ziv.xu@starfivetech.com>

Guenter Roeck (8):
  watchdog: starfive-wdt: Drop runtime PM reference if starting watchdog
    fails
  watchdog: starfive-wdt: Balance pm_runtime_enable() on probe error
    paths
  watchdog: starfive-wdt: Avoid unclocked register access in remove and
    PM paths
  watchdog: starfive-wdt: Fix runtime PM usage count leaks in probe and
    remove
  watchdog: starfive-wdt: Drop runtime PM reference before registering
    watchdog
  watchdog: starfive-wdt: Avoid unclocked MMIO read in
    starfive_wdt_get_timeleft()
  watchdog: starfive-wdt: Stop ping worker on system suspend
  watchdog: starfive-wdt: Avoid unclocked MMIO access in
    starfive_wdt_set_timeout()

 drivers/watchdog/starfive-wdt.c | 102 ++++++++++++++++++--------------
 1 file changed, 58 insertions(+), 44 deletions(-)

-- 
2.56.0

^ permalink raw reply	[flat|nested] 15+ messages in thread

* [PATCH v2 1/8] watchdog: starfive-wdt: Drop runtime PM reference if starting watchdog fails
  2026-11-05 19:05 [PATCH v2 0/8] watchdog: starfive-wdt: Fix runtime PM and unclocked register access bugs Guenter Roeck
@ 2026-11-05 19:05 ` Guenter Roeck
  2026-11-05 19:05 ` [PATCH v2 2/8] watchdog: starfive-wdt: Balance pm_runtime_enable() on probe error paths Guenter Roeck
                   ` (6 subsequent siblings)
  7 siblings, 0 replies; 15+ messages in thread
From: Guenter Roeck @ 2026-11-05 19:05 UTC (permalink / raw)
  To: linux-watchdog; +Cc: Tzung-Bi Shih, Guenter Roeck, Xingyu Wu, Ziv Xu

Commit 8f0ca55016a7 ("watchdog: starfive-wdt: Fix runtime PM leak in
starfive_wdt_pm_start()") switched starfive_wdt_pm_start() to
pm_runtime_resume_and_get() so the runtime PM usage counter is not
leaked when resuming the device fails. However, if starfive_wdt_start()
subsequently fails (for example, on a timeout in starfive_wdt_int_clr()),
starfive_wdt_pm_start() returns the error without releasing the runtime
PM reference acquired by pm_runtime_resume_and_get(). Because the
watchdog core does not invoke the stop callback when start fails, the
usage counter remains permanently elevated.

Drop the runtime PM reference with pm_runtime_put_sync() if
starfive_wdt_start() fails.

Fixes: db728ea9c7be ("drivers: watchdog: Add StarFive Watchdog driver")
Cc: Xingyu Wu <xingyu.wu@starfivetech.com>
Cc: Ziv Xu <ziv.xu@starfivetech.com>
Reviewed-by: Tzung-Bi Shih <tzungbi@kernel.org>
Signed-off-by: Guenter Roeck <linux@roeck-us.net>
---
v2: Add Reviewed-by: tag

 drivers/watchdog/starfive-wdt.c | 9 +++++++--
 1 file changed, 7 insertions(+), 2 deletions(-)

diff --git a/drivers/watchdog/starfive-wdt.c b/drivers/watchdog/starfive-wdt.c
index 044b4fdb7350..ab6920b8d7d8 100644
--- a/drivers/watchdog/starfive-wdt.c
+++ b/drivers/watchdog/starfive-wdt.c
@@ -372,12 +372,17 @@ static void starfive_wdt_stop(struct starfive_wdt *wdt)
 static int starfive_wdt_pm_start(struct watchdog_device *wdd)
 {
 	struct starfive_wdt *wdt = watchdog_get_drvdata(wdd);
-	int ret = pm_runtime_resume_and_get(wdd->parent);
+	int ret;
 
+	ret = pm_runtime_resume_and_get(wdd->parent);
 	if (ret < 0)
 		return ret;
 
-	return starfive_wdt_start(wdt);
+	ret = starfive_wdt_start(wdt);
+	if (ret)
+		pm_runtime_put_sync(wdd->parent);
+
+	return ret;
 }
 
 static int starfive_wdt_pm_stop(struct watchdog_device *wdd)
-- 
2.45.2


^ permalink raw reply related	[flat|nested] 15+ messages in thread

* [PATCH v2 2/8] watchdog: starfive-wdt: Balance pm_runtime_enable() on probe error paths
  2026-11-05 19:05 [PATCH v2 0/8] watchdog: starfive-wdt: Fix runtime PM and unclocked register access bugs Guenter Roeck
  2026-11-05 19:05 ` [PATCH v2 1/8] watchdog: starfive-wdt: Drop runtime PM reference if starting watchdog fails Guenter Roeck
@ 2026-11-05 19:05 ` Guenter Roeck
  2026-11-05 19:05 ` [PATCH v2 3/8] watchdog: starfive-wdt: Avoid unclocked register access in remove and PM paths Guenter Roeck
                   ` (5 subsequent siblings)
  7 siblings, 0 replies; 15+ messages in thread
From: Guenter Roeck @ 2026-11-05 19:05 UTC (permalink / raw)
  To: linux-watchdog; +Cc: Tzung-Bi Shih, Guenter Roeck, Xingyu Wu, Ziv Xu

In starfive_wdt_probe(), pm_runtime_enable() is called before attempting
to resume the device via pm_runtime_resume_and_get() or enable its clocks
via starfive_wdt_enable_clock(). If either call fails, probe returns
directly without calling pm_runtime_disable(), leaving the runtime PM
disable_depth counter unbalanced (and triggering "Unbalanced
pm_runtime_enable!" warnings on deferred probe retries).

Add an err_disable_pm error label and jump to it so pm_runtime_disable()
is called on these error paths.

Fixes: db728ea9c7be ("drivers: watchdog: Add StarFive Watchdog driver")
Cc: Xingyu Wu <xingyu.wu@starfivetech.com>
Cc: Ziv Xu <ziv.xu@starfivetech.com>
Reviewed-by: Tzung-Bi Shih <tzungbi@kernel.org>
Signed-off-by: Guenter Roeck <linux@roeck-us.net>
---
v2: Add Reviewed-by: tag

 drivers/watchdog/starfive-wdt.c | 5 +++--
 1 file changed, 3 insertions(+), 2 deletions(-)

diff --git a/drivers/watchdog/starfive-wdt.c b/drivers/watchdog/starfive-wdt.c
index ab6920b8d7d8..4724fccbfe63 100644
--- a/drivers/watchdog/starfive-wdt.c
+++ b/drivers/watchdog/starfive-wdt.c
@@ -454,12 +454,12 @@ static int starfive_wdt_probe(struct platform_device *pdev)
 	if (pm_runtime_enabled(&pdev->dev)) {
 		ret = pm_runtime_resume_and_get(&pdev->dev);
 		if (ret < 0)
-			return ret;
+			goto err_disable_pm;
 	} else {
 		/* runtime PM is disabled but clocks need to be enabled */
 		ret = starfive_wdt_enable_clock(wdt);
 		if (ret)
-			return ret;
+			goto err_disable_pm;
 	}
 
 	ret = starfive_wdt_reset_init(&pdev->dev);
@@ -516,6 +516,7 @@ static int starfive_wdt_probe(struct platform_device *pdev)
 	watchdog_unregister_device(&wdt->wdd);
 err_exit:
 	starfive_wdt_disable_clock(wdt);
+err_disable_pm:
 	pm_runtime_disable(&pdev->dev);
 
 	return ret;
-- 
2.45.2


^ permalink raw reply related	[flat|nested] 15+ messages in thread

* [PATCH v2 3/8] watchdog: starfive-wdt: Avoid unclocked register access in remove and PM paths
  2026-11-05 19:05 [PATCH v2 0/8] watchdog: starfive-wdt: Fix runtime PM and unclocked register access bugs Guenter Roeck
  2026-11-05 19:05 ` [PATCH v2 1/8] watchdog: starfive-wdt: Drop runtime PM reference if starting watchdog fails Guenter Roeck
  2026-11-05 19:05 ` [PATCH v2 2/8] watchdog: starfive-wdt: Balance pm_runtime_enable() on probe error paths Guenter Roeck
@ 2026-11-05 19:05 ` Guenter Roeck
  2026-11-06 14:21   ` Tzung-Bi Shih
  2026-11-05 19:05 ` [PATCH v2 4/8] watchdog: starfive-wdt: Fix runtime PM usage count leaks in probe and remove Guenter Roeck
                   ` (4 subsequent siblings)
  7 siblings, 1 reply; 15+ messages in thread
From: Guenter Roeck @ 2026-11-05 19:05 UTC (permalink / raw)
  To: linux-watchdog; +Cc: Tzung-Bi Shih, Guenter Roeck, Xingyu Wu, Ziv Xu

When runtime PM is enabled and the watchdog is inactive, the device enters
runtime suspend and starfive_wdt_runtime_suspend() disables core_clk and
apb_clk.

However, starfive_wdt_remove(), starfive_wdt_shutdown(),
starfive_wdt_suspend(), and starfive_wdt_resume() unconditionally read and
write watchdog MMIO registers without checking whether the watchdog is
running or clocks are enabled, causing a bus fault when the device is
runtime-suspended:

- In starfive_wdt_remove(), starfive_wdt_stop() is called before
  watchdog_unregister_device() while clocks may be gated off. Unregister
  the watchdog device first (which stops an active watchdog via
  watchdog_stop_on_unregister while its runtime PM reference keeps clocks
  enabled), and only call starfive_wdt_stop() if watchdog_hw_running() is
  still set.
- In starfive_wdt_pm_stop(), check pm_runtime_enabled() and ignore
  non-zero idle return codes from pm_runtime_put_sync() so watchdog_stop()
  does not falsely treat a stopped watchdog as still running.
- Remove the redundant starfive_wdt_shutdown() callback. Because
  starfive_wdt_probe() already sets watchdog_stop_on_reboot(),
  watchdog_reboot_notifier() stops a running watchdog on reboot, halt, or
  power-off (while respecting the watchdog.stop_on_reboot parameter)
  before device_shutdown() runs; calling starfive_wdt_pm_stop() again in
  .shutdown causes unclocked MMIO when suspended and an unbalanced
  pm_runtime_put_sync() when inactive or already stopped.
- In starfive_wdt_suspend() and starfive_wdt_resume(), only save/stop and
  restore/restart the hardware watchdog when watchdog_hw_running() is set
  (in which case an active runtime PM reference ensures clocks are enabled
  before suspend and re-enabled by pm_runtime_force_resume()).

Fixes: db728ea9c7be ("drivers: watchdog: Add StarFive Watchdog driver")
Cc: Xingyu Wu <xingyu.wu@starfivetech.com>
Cc: Ziv Xu <ziv.xu@starfivetech.com>
Signed-off-by: Guenter Roeck <linux@roeck-us.net>
---
v2: Remove the redundant starfive_wdt_shutdown() callback instead
    of guarding it with !pm_runtime_suspended(), avoiding unbalanced
    pm_runtime_put_sync() calls when runtime suspend is forbidden or after
    watchdog_reboot_notifier() already stopped the watchdog, avoiding
    unclocked MMIO during hibernation shutdown, and respecting the
    watchdog.stop_on_reboot module parameter.

 drivers/watchdog/starfive-wdt.c | 38 ++++++++++++++++-----------------
 1 file changed, 19 insertions(+), 19 deletions(-)

diff --git a/drivers/watchdog/starfive-wdt.c b/drivers/watchdog/starfive-wdt.c
index 4724fccbfe63..c5f044c9d03c 100644
--- a/drivers/watchdog/starfive-wdt.c
+++ b/drivers/watchdog/starfive-wdt.c
@@ -390,7 +390,10 @@ static int starfive_wdt_pm_stop(struct watchdog_device *wdd)
 	struct starfive_wdt *wdt = watchdog_get_drvdata(wdd);
 
 	starfive_wdt_stop(wdt);
-	return pm_runtime_put_sync(wdd->parent);
+	if (pm_runtime_enabled(wdd->parent))
+		pm_runtime_put_sync(wdd->parent);
+
+	return 0;
 }
 
 static int starfive_wdt_set_timeout(struct watchdog_device *wdd,
@@ -526,9 +529,11 @@ static void starfive_wdt_remove(struct platform_device *pdev)
 {
 	struct starfive_wdt *wdt = platform_get_drvdata(pdev);
 
-	starfive_wdt_stop(wdt);
 	watchdog_unregister_device(&wdt->wdd);
 
+	if (watchdog_hw_running(&wdt->wdd))
+		starfive_wdt_stop(wdt);
+
 	if (pm_runtime_enabled(&pdev->dev))
 		pm_runtime_disable(&pdev->dev);
 	else
@@ -536,22 +541,17 @@ static void starfive_wdt_remove(struct platform_device *pdev)
 		starfive_wdt_disable_clock(wdt);
 }
 
-static void starfive_wdt_shutdown(struct platform_device *pdev)
-{
-	struct starfive_wdt *wdt = platform_get_drvdata(pdev);
-
-	starfive_wdt_pm_stop(&wdt->wdd);
-}
-
 static int starfive_wdt_suspend(struct device *dev)
 {
 	struct starfive_wdt *wdt = dev_get_drvdata(dev);
 
-	/* Save watchdog state, and turn it off. */
-	wdt->reload = starfive_wdt_get_count(wdt);
+	if (watchdog_hw_running(&wdt->wdd)) {
+		/* Save watchdog state, and turn it off. */
+		wdt->reload = starfive_wdt_get_count(wdt);
 
-	/* Note that WTCNT doesn't need to be saved. */
-	starfive_wdt_stop(wdt);
+		/* Note that WTCNT doesn't need to be saved. */
+		starfive_wdt_stop(wdt);
+	}
 
 	return pm_runtime_force_suspend(dev);
 }
@@ -565,13 +565,14 @@ static int starfive_wdt_resume(struct device *dev)
 	if (ret)
 		return ret;
 
-	starfive_wdt_unlock(wdt);
-	/* Restore watchdog state. */
-	starfive_wdt_set_reload_count(wdt, wdt->reload);
-	starfive_wdt_lock(wdt);
+	if (watchdog_hw_running(&wdt->wdd)) {
+		starfive_wdt_unlock(wdt);
+		/* Restore watchdog state. */
+		starfive_wdt_set_reload_count(wdt, wdt->reload);
+		starfive_wdt_lock(wdt);
 
-	if (watchdog_active(&wdt->wdd))
 		return starfive_wdt_start(wdt);
+	}
 
 	return 0;
 }
@@ -607,7 +608,6 @@ MODULE_DEVICE_TABLE(of, starfive_wdt_match);
 static struct platform_driver starfive_wdt_driver = {
 	.probe = starfive_wdt_probe,
 	.remove = starfive_wdt_remove,
-	.shutdown = starfive_wdt_shutdown,
 	.driver = {
 		.name = "starfive-wdt",
 		.pm = pm_ptr(&starfive_wdt_pm_ops),
-- 
2.45.2


^ permalink raw reply related	[flat|nested] 15+ messages in thread

* [PATCH v2 4/8] watchdog: starfive-wdt: Fix runtime PM usage count leaks in probe and remove
  2026-11-05 19:05 [PATCH v2 0/8] watchdog: starfive-wdt: Fix runtime PM and unclocked register access bugs Guenter Roeck
                   ` (2 preceding siblings ...)
  2026-11-05 19:05 ` [PATCH v2 3/8] watchdog: starfive-wdt: Avoid unclocked register access in remove and PM paths Guenter Roeck
@ 2026-11-05 19:05 ` Guenter Roeck
  2026-11-05 19:05 ` [PATCH v2 5/8] watchdog: starfive-wdt: Drop runtime PM reference before registering watchdog Guenter Roeck
                   ` (3 subsequent siblings)
  7 siblings, 0 replies; 15+ messages in thread
From: Guenter Roeck @ 2026-11-05 19:05 UTC (permalink / raw)
  To: linux-watchdog; +Cc: Tzung-Bi Shih, Guenter Roeck, Xingyu Wu, Ziv Xu

When runtime PM is enabled, starfive_wdt_probe() acquires a runtime PM
reference via pm_runtime_resume_and_get(). On subsequent probe failures,
err_exit manually disables clocks via starfive_wdt_disable_clock() and
calls pm_runtime_disable(), without dropping the acquired runtime PM
reference (and leaving the hardware timer running if early_enable was
enabled and watchdog_register_device() failed).

Similarly, when early_enable is set (or the watchdog was left running
on close), the active runtime PM reference is retained after probe, but
starfive_wdt_remove() never releases it before calling
pm_runtime_disable().

Release the runtime PM reference with pm_runtime_put_sync() on probe
error paths (stopping the watchdog first if running) and in
starfive_wdt_remove() when watchdog_hw_running() is still set.

Fixes: db728ea9c7be ("drivers: watchdog: Add StarFive Watchdog driver")
Cc: Xingyu Wu <xingyu.wu@starfivetech.com>
Cc: Ziv Xu <ziv.xu@starfivetech.com>
Reviewed-by: Tzung-Bi Shih <tzungbi@kernel.org>
Signed-off-by: Guenter Roeck <linux@roeck-us.net>
---
v2: Add Reviewed-by: tag

 drivers/watchdog/starfive-wdt.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/drivers/watchdog/starfive-wdt.c b/drivers/watchdog/starfive-wdt.c
index c5f044c9d03c..859232fa55a3 100644
--- a/drivers/watchdog/starfive-wdt.c
+++ b/drivers/watchdog/starfive-wdt.c
@@ -517,8 +517,14 @@ static int starfive_wdt_probe(struct platform_device *pdev)
 
 err_unregister_wdt:
 	watchdog_unregister_device(&wdt->wdd);
+	goto err_disable_pm;
 err_exit:
-	starfive_wdt_disable_clock(wdt);
+	if (watchdog_hw_running(&wdt->wdd))
+		starfive_wdt_stop(wdt);
+	if (pm_runtime_enabled(&pdev->dev))
+		pm_runtime_put_sync(&pdev->dev);
+	else
+		starfive_wdt_disable_clock(wdt);
 err_disable_pm:
 	pm_runtime_disable(&pdev->dev);
 
@@ -531,8 +537,11 @@ static void starfive_wdt_remove(struct platform_device *pdev)
 
 	watchdog_unregister_device(&wdt->wdd);
 
-	if (watchdog_hw_running(&wdt->wdd))
+	if (watchdog_hw_running(&wdt->wdd)) {
 		starfive_wdt_stop(wdt);
+		if (pm_runtime_enabled(&pdev->dev))
+			pm_runtime_put_sync(&pdev->dev);
+	}
 
 	if (pm_runtime_enabled(&pdev->dev))
 		pm_runtime_disable(&pdev->dev);
-- 
2.45.2


^ permalink raw reply related	[flat|nested] 15+ messages in thread

* [PATCH v2 5/8] watchdog: starfive-wdt: Drop runtime PM reference before registering watchdog
  2026-11-05 19:05 [PATCH v2 0/8] watchdog: starfive-wdt: Fix runtime PM and unclocked register access bugs Guenter Roeck
                   ` (3 preceding siblings ...)
  2026-11-05 19:05 ` [PATCH v2 4/8] watchdog: starfive-wdt: Fix runtime PM usage count leaks in probe and remove Guenter Roeck
@ 2026-11-05 19:05 ` Guenter Roeck
  2026-11-05 19:05 ` [PATCH v2 6/8] watchdog: starfive-wdt: Avoid unclocked MMIO read in starfive_wdt_get_timeleft() Guenter Roeck
                   ` (2 subsequent siblings)
  7 siblings, 0 replies; 15+ messages in thread
From: Guenter Roeck @ 2026-11-05 19:05 UTC (permalink / raw)
  To: linux-watchdog; +Cc: Tzung-Bi Shih, Guenter Roeck, Xingyu Wu, Ziv Xu

In starfive_wdt_probe(), watchdog_register_device() is called before the
probe-time runtime PM reference is released via pm_runtime_put_sync(), and
any non-zero return value from pm_runtime_put_sync() is treated as a fatal
probe error.

If userspace (such as udev or a watchdog daemon) opens /dev/watchdog
immediately after watchdog_register_device() exposes the character device,
starfive_wdt_pm_start() increments the runtime PM usage count. The
subsequent pm_runtime_put_sync() in probe then decrements the usage count
to 1 and returns -EAGAIN (it may also return 1, -EBUSY, or -EPERM in
non-error idle conditions), causing starfive_wdt_probe() to unregister the
working watchdog device and fail probe.

When early_enable is not set, no hardware registers are accessed after
starfive_wdt_stop(). Release the probe-time runtime PM reference right
after starfive_wdt_stop() and before watchdog_register_device(), without
treating pm_runtime_put_sync() idle return codes as probe errors.

Fixes: 8bc22a2f1bf0 ("watchdog: starfive: Check pm_runtime_enabled() before decrementing usage counter")
Cc: Xingyu Wu <xingyu.wu@starfivetech.com>
Cc: Ziv Xu <ziv.xu@starfivetech.com>
Reviewed-by: Tzung-Bi Shih <tzungbi@kernel.org>
Signed-off-by: Guenter Roeck <linux@roeck-us.net>
---
v2: Add Reviewed-by: tag

 drivers/watchdog/starfive-wdt.c | 23 ++++++++++-------------
 1 file changed, 10 insertions(+), 13 deletions(-)

diff --git a/drivers/watchdog/starfive-wdt.c b/drivers/watchdog/starfive-wdt.c
index 859232fa55a3..4142e1502038 100644
--- a/drivers/watchdog/starfive-wdt.c
+++ b/drivers/watchdog/starfive-wdt.c
@@ -499,28 +499,25 @@ static int starfive_wdt_probe(struct platform_device *pdev)
 		set_bit(WDOG_HW_RUNNING, &wdt->wdd.status);
 	} else {
 		starfive_wdt_stop(wdt);
+		if (pm_runtime_enabled(&pdev->dev))
+			pm_runtime_put_sync(&pdev->dev);
 	}
 
 	ret = watchdog_register_device(&wdt->wdd);
-	if (ret)
-		goto err_exit;
-
-	if (!early_enable) {
-		if (pm_runtime_enabled(&pdev->dev)) {
-			ret = pm_runtime_put_sync(&pdev->dev);
-			if (ret)
-				goto err_unregister_wdt;
+	if (ret) {
+		if (watchdog_hw_running(&wdt->wdd)) {
+			starfive_wdt_stop(wdt);
+			if (pm_runtime_enabled(&pdev->dev))
+				pm_runtime_put_sync(&pdev->dev);
 		}
+		if (!pm_runtime_enabled(&pdev->dev))
+			starfive_wdt_disable_clock(wdt);
+		goto err_disable_pm;
 	}
 
 	return 0;
 
-err_unregister_wdt:
-	watchdog_unregister_device(&wdt->wdd);
-	goto err_disable_pm;
 err_exit:
-	if (watchdog_hw_running(&wdt->wdd))
-		starfive_wdt_stop(wdt);
 	if (pm_runtime_enabled(&pdev->dev))
 		pm_runtime_put_sync(&pdev->dev);
 	else
-- 
2.45.2


^ permalink raw reply related	[flat|nested] 15+ messages in thread

* [PATCH v2 6/8] watchdog: starfive-wdt: Avoid unclocked MMIO read in starfive_wdt_get_timeleft()
  2026-11-05 19:05 [PATCH v2 0/8] watchdog: starfive-wdt: Fix runtime PM and unclocked register access bugs Guenter Roeck
                   ` (4 preceding siblings ...)
  2026-11-05 19:05 ` [PATCH v2 5/8] watchdog: starfive-wdt: Drop runtime PM reference before registering watchdog Guenter Roeck
@ 2026-11-05 19:05 ` Guenter Roeck
  2026-11-06 14:22   ` Tzung-Bi Shih
  2026-11-05 19:05 ` [PATCH v2 7/8] watchdog: starfive-wdt: Stop ping worker on system suspend Guenter Roeck
  2026-11-05 19:05 ` [PATCH v2 8/8] watchdog: starfive-wdt: Avoid unclocked MMIO access in starfive_wdt_set_timeout() Guenter Roeck
  7 siblings, 1 reply; 15+ messages in thread
From: Guenter Roeck @ 2026-11-05 19:05 UTC (permalink / raw)
  To: linux-watchdog; +Cc: Tzung-Bi Shih, Guenter Roeck, Xingyu Wu, Ziv Xu

When the watchdog is inactive, runtime PM suspends the device and gates
off apb_clk and core_clk via starfive_wdt_runtime_suspend(). However,
reading the sysfs timeleft attribute via timeleft_show() or issuing
ioctl(WDIOC_GETTIMELEFT) calls starfive_wdt_get_timeleft() unconditionally.

Because starfive_wdt_get_timeleft() reads the counter and interrupt status
registers without checking whether the hardware watchdog is running,
querying timeleft while inactive performs unclocked MMIO reads and triggers
a bus fault.

Return 0 immediately in starfive_wdt_get_timeleft() when
watchdog_hw_running() is false. When watchdog_hw_running() is true, the
driver holds an active runtime PM reference keeping the clocks enabled.

Fixes: db728ea9c7be ("drivers: watchdog: Add StarFive Watchdog driver")
Cc: Xingyu Wu <xingyu.wu@starfivetech.com>
Cc: Ziv Xu <ziv.xu@starfivetech.com>
Signed-off-by: Guenter Roeck <linux@roeck-us.net>
---
v2: New patch

 drivers/watchdog/starfive-wdt.c | 3 +++
 1 file changed, 3 insertions(+)

diff --git a/drivers/watchdog/starfive-wdt.c b/drivers/watchdog/starfive-wdt.c
index 4142e1502038..60a97ffa940a 100644
--- a/drivers/watchdog/starfive-wdt.c
+++ b/drivers/watchdog/starfive-wdt.c
@@ -312,6 +312,9 @@ static unsigned int starfive_wdt_get_timeleft(struct watchdog_device *wdd)
 	struct starfive_wdt *wdt = watchdog_get_drvdata(wdd);
 	u32 count;
 
+	if (!watchdog_hw_running(wdd))
+		return 0;
+
 	/*
 	 * If the watchdog takes twice timeout and set half count value,
 	 * timeleft value should add the count value before first timeout.
-- 
2.45.2


^ permalink raw reply related	[flat|nested] 15+ messages in thread

* [PATCH v2 7/8] watchdog: starfive-wdt: Stop ping worker on system suspend
  2026-11-05 19:05 [PATCH v2 0/8] watchdog: starfive-wdt: Fix runtime PM and unclocked register access bugs Guenter Roeck
                   ` (5 preceding siblings ...)
  2026-11-05 19:05 ` [PATCH v2 6/8] watchdog: starfive-wdt: Avoid unclocked MMIO read in starfive_wdt_get_timeleft() Guenter Roeck
@ 2026-11-05 19:05 ` Guenter Roeck
  2026-11-06 14:22   ` Tzung-Bi Shih
  2026-11-05 19:05 ` [PATCH v2 8/8] watchdog: starfive-wdt: Avoid unclocked MMIO access in starfive_wdt_set_timeout() Guenter Roeck
  7 siblings, 1 reply; 15+ messages in thread
From: Guenter Roeck @ 2026-11-05 19:05 UTC (permalink / raw)
  To: linux-watchdog; +Cc: Tzung-Bi Shih, Guenter Roeck, Xingyu Wu, Ziv Xu

When early_enable=1 is set and CONFIG_WATCHDOG_HANDLE_BOOT_ENABLED is
enabled, watchdog_register_device() starts the watchdog core hrtimer so the
non-freezable watchdogd kthread worker periodically pings the hardware
before userspace opens /dev/watchdog.

Because starfive_wdt_probe() does not call watchdog_stop_ping_on_suspend(),
WDOG_NO_PING_ON_SUSPEND is not set and the watchdog core does not cancel
its hrtimer and ping work across system suspend. After
starfive_wdt_suspend() stops the counter and calls
pm_runtime_force_suspend() to gate core_clk and apb_clk, WDOG_HW_RUNNING
remains set; if the timer fires before starfive_wdt_resume() re-enables
the clocks, watchdog_ping_work() invokes starfive_wdt_keepalive() with
gated clocks and causes an unclocked MMIO bus fault.

Call watchdog_stop_ping_on_suspend() in starfive_wdt_probe() so the
watchdog core cancels its ping timer and work in PM_SUSPEND_PREPARE before
clocks are gated and resumes pinging in PM_POST_SUSPEND after clocks are
restored.

Fixes: db728ea9c7be ("drivers: watchdog: Add StarFive Watchdog driver")
Cc: Xingyu Wu <xingyu.wu@starfivetech.com>
Cc: Ziv Xu <ziv.xu@starfivetech.com>
Signed-off-by: Guenter Roeck <linux@roeck-us.net>
---
v2: New patch

 drivers/watchdog/starfive-wdt.c | 1 +
 1 file changed, 1 insertion(+)

diff --git a/drivers/watchdog/starfive-wdt.c b/drivers/watchdog/starfive-wdt.c
index 60a97ffa940a..ae2d84ccc76f 100644
--- a/drivers/watchdog/starfive-wdt.c
+++ b/drivers/watchdog/starfive-wdt.c
@@ -494,6 +494,7 @@ static int starfive_wdt_probe(struct platform_device *pdev)
 	watchdog_set_nowayout(&wdt->wdd, nowayout);
 	watchdog_stop_on_reboot(&wdt->wdd);
 	watchdog_stop_on_unregister(&wdt->wdd);
+	watchdog_stop_ping_on_suspend(&wdt->wdd);
 
 	if (early_enable) {
 		ret = starfive_wdt_start(wdt);
-- 
2.45.2


^ permalink raw reply related	[flat|nested] 15+ messages in thread

* [PATCH v2 8/8] watchdog: starfive-wdt: Avoid unclocked MMIO access in starfive_wdt_set_timeout()
  2026-11-05 19:05 [PATCH v2 0/8] watchdog: starfive-wdt: Fix runtime PM and unclocked register access bugs Guenter Roeck
                   ` (6 preceding siblings ...)
  2026-11-05 19:05 ` [PATCH v2 7/8] watchdog: starfive-wdt: Stop ping worker on system suspend Guenter Roeck
@ 2026-11-05 19:05 ` Guenter Roeck
  2026-11-06 14:21   ` Tzung-Bi Shih
  7 siblings, 1 reply; 15+ messages in thread
From: Guenter Roeck @ 2026-11-05 19:05 UTC (permalink / raw)
  To: linux-watchdog; +Cc: Tzung-Bi Shih, Guenter Roeck, Xingyu Wu, Ziv Xu

When the watchdog is stopped via ioctl(WDIOC_SETOPTIONS, WDIOS_DISABLECARD)
while /dev/watchdog remains open, watchdog_stop() clears WDOG_HW_RUNNING
and WDOG_ACTIVE, and starfive_wdt_pm_stop() halts the hardware counter and
drops the runtime PM reference, gating core_clk and apb_clk.

A subsequent ioctl(WDIOC_SETTIMEOUT) invokes starfive_wdt_set_timeout()
regardless of whether the watchdog is running. Because
starfive_wdt_set_timeout() unconditionally accesses MMIO registers and
calls starfive_wdt_enable(), updating the timeout while stopped triggers an
unclocked MMIO bus fault when runtime-suspended, or unexpectedly starts the
hardware counter without WDOG_ACTIVE or WDOG_HW_RUNNING set when runtime PM
is disabled.

Only program the hardware registers in starfive_wdt_set_timeout() when
watchdog_hw_running() is set. In addition, reload the initial counter value
via starfive_wdt_set_reload_count() in starfive_wdt_start() (preserving
wdt->reload on resume) so JH7100 loads the configured timeout into the
counter when started from probe or after WDIOS_DISABLECARD.

Fixes: db728ea9c7be ("drivers: watchdog: Add StarFive Watchdog driver")
Cc: Xingyu Wu <xingyu.wu@starfivetech.com>
Cc: Ziv Xu <ziv.xu@starfivetech.com>
Signed-off-by: Guenter Roeck <linux@roeck-us.net>
---
v2: New patch

 drivers/watchdog/starfive-wdt.c | 32 +++++++++++++++-----------------
 1 file changed, 15 insertions(+), 17 deletions(-)

diff --git a/drivers/watchdog/starfive-wdt.c b/drivers/watchdog/starfive-wdt.c
index ae2d84ccc76f..758030ad9c65 100644
--- a/drivers/watchdog/starfive-wdt.c
+++ b/drivers/watchdog/starfive-wdt.c
@@ -344,7 +344,7 @@ static int starfive_wdt_keepalive(struct watchdog_device *wdd)
 	return ret;
 }
 
-static int starfive_wdt_start(struct starfive_wdt *wdt)
+static int starfive_wdt_start(struct starfive_wdt *wdt, u32 count)
 {
 	int ret;
 
@@ -357,7 +357,9 @@ static int starfive_wdt_start(struct starfive_wdt *wdt)
 	if (ret)
 		goto exit;
 
-	starfive_wdt_set_count(wdt, wdt->count);
+	starfive_wdt_set_reload_count(wdt, count);
+	if (count != wdt->count)
+		starfive_wdt_set_count(wdt, wdt->count);
 	starfive_wdt_enable(wdt);
 
 exit:
@@ -381,7 +383,7 @@ static int starfive_wdt_pm_start(struct watchdog_device *wdd)
 	if (ret < 0)
 		return ret;
 
-	ret = starfive_wdt_start(wdt);
+	ret = starfive_wdt_start(wdt, wdt->count);
 	if (ret)
 		pm_runtime_put_sync(wdd->parent);
 
@@ -412,11 +414,13 @@ static int starfive_wdt_set_timeout(struct watchdog_device *wdd,
 	wdt->count = count;
 	wdd->timeout = timeout;
 
-	starfive_wdt_unlock(wdt);
-	starfive_wdt_disable(wdt);
-	starfive_wdt_set_reload_count(wdt, wdt->count);
-	starfive_wdt_enable(wdt);
-	starfive_wdt_lock(wdt);
+	if (watchdog_hw_running(wdd)) {
+		starfive_wdt_unlock(wdt);
+		starfive_wdt_disable(wdt);
+		starfive_wdt_set_reload_count(wdt, wdt->count);
+		starfive_wdt_enable(wdt);
+		starfive_wdt_lock(wdt);
+	}
 
 	return 0;
 }
@@ -497,7 +501,7 @@ static int starfive_wdt_probe(struct platform_device *pdev)
 	watchdog_stop_ping_on_suspend(&wdt->wdd);
 
 	if (early_enable) {
-		ret = starfive_wdt_start(wdt);
+		ret = starfive_wdt_start(wdt, wdt->count);
 		if (ret)
 			goto err_exit;
 		set_bit(WDOG_HW_RUNNING, &wdt->wdd.status);
@@ -575,14 +579,8 @@ static int starfive_wdt_resume(struct device *dev)
 	if (ret)
 		return ret;
 
-	if (watchdog_hw_running(&wdt->wdd)) {
-		starfive_wdt_unlock(wdt);
-		/* Restore watchdog state. */
-		starfive_wdt_set_reload_count(wdt, wdt->reload);
-		starfive_wdt_lock(wdt);
-
-		return starfive_wdt_start(wdt);
-	}
+	if (watchdog_hw_running(&wdt->wdd))
+		return starfive_wdt_start(wdt, wdt->reload);
 
 	return 0;
 }
-- 
2.45.2


^ permalink raw reply related	[flat|nested] 15+ messages in thread

* Re: [PATCH v2 8/8] watchdog: starfive-wdt: Avoid unclocked MMIO access in starfive_wdt_set_timeout()
  2026-11-05 19:05 ` [PATCH v2 8/8] watchdog: starfive-wdt: Avoid unclocked MMIO access in starfive_wdt_set_timeout() Guenter Roeck
@ 2026-11-06 14:21   ` Tzung-Bi Shih
  2026-11-06 15:20     ` Guenter Roeck
  0 siblings, 1 reply; 15+ messages in thread
From: Tzung-Bi Shih @ 2026-11-06 14:21 UTC (permalink / raw)
  To: Guenter Roeck; +Cc: linux-watchdog, Xingyu Wu, Ziv Xu

On Thu, Nov 05, 2026 at 11:05:29AM -0800, Guenter Roeck wrote:
> Only program the hardware registers in starfive_wdt_set_timeout() when
> watchdog_hw_running() is set. In addition, reload the initial counter value
> via starfive_wdt_set_reload_count() in starfive_wdt_start() (preserving

Is the additional change relevant to this patch (i.e., avoid unclocked
MMIO access)?

> wdt->reload on resume) so JH7100 loads the configured timeout into the
> counter when started from probe or after WDIOS_DISABLECARD.

Is it really JH7100 specific (not for JH7110)?

> @@ -344,7 +344,7 @@ static int starfive_wdt_keepalive(struct watchdog_device *wdd)
>  	return ret;
>  }
>  
> -static int starfive_wdt_start(struct starfive_wdt *wdt)
> +static int starfive_wdt_start(struct starfive_wdt *wdt, u32 count)
>  {
>  	int ret;
>  
> @@ -357,7 +357,9 @@ static int starfive_wdt_start(struct starfive_wdt *wdt)
>  	if (ret)
>  		goto exit;
>  
> -	starfive_wdt_set_count(wdt, wdt->count);
> +	starfive_wdt_set_reload_count(wdt, count);
> +	if (count != wdt->count)
> +		starfive_wdt_set_count(wdt, wdt->count);
>  	starfive_wdt_enable(wdt);
>  
>  exit:
> @@ -381,7 +383,7 @@ static int starfive_wdt_pm_start(struct watchdog_device *wdd)
>  	if (ret < 0)
>  		return ret;
>  
> -	ret = starfive_wdt_start(wdt);
> +	ret = starfive_wdt_start(wdt, wdt->count);
>  	if (ret)
>  		pm_runtime_put_sync(wdd->parent);
>  
> @@ -412,11 +414,13 @@ static int starfive_wdt_set_timeout(struct watchdog_device *wdd,
>  	wdt->count = count;
>  	wdd->timeout = timeout;
>  
> -	starfive_wdt_unlock(wdt);
> -	starfive_wdt_disable(wdt);
> -	starfive_wdt_set_reload_count(wdt, wdt->count);
> -	starfive_wdt_enable(wdt);
> -	starfive_wdt_lock(wdt);
> +	if (watchdog_hw_running(wdd)) {
> +		starfive_wdt_unlock(wdt);
> +		starfive_wdt_disable(wdt);
> +		starfive_wdt_set_reload_count(wdt, wdt->count);
> +		starfive_wdt_enable(wdt);
> +		starfive_wdt_lock(wdt);
> +	}
>  
>  	return 0;
>  }
> @@ -497,7 +501,7 @@ static int starfive_wdt_probe(struct platform_device *pdev)
>  	watchdog_stop_ping_on_suspend(&wdt->wdd);
>  
>  	if (early_enable) {
> -		ret = starfive_wdt_start(wdt);
> +		ret = starfive_wdt_start(wdt, wdt->count);
>  		if (ret)
>  			goto err_exit;
>  		set_bit(WDOG_HW_RUNNING, &wdt->wdd.status);
> @@ -575,14 +579,8 @@ static int starfive_wdt_resume(struct device *dev)
>  	if (ret)
>  		return ret;
>  
> -	if (watchdog_hw_running(&wdt->wdd)) {
> -		starfive_wdt_unlock(wdt);
> -		/* Restore watchdog state. */
> -		starfive_wdt_set_reload_count(wdt, wdt->reload);
> -		starfive_wdt_lock(wdt);
> -
> -		return starfive_wdt_start(wdt);
> -	}
> +	if (watchdog_hw_running(&wdt->wdd))
> +		return starfive_wdt_start(wdt, wdt->reload);
>  
>  	return 0;
>  }

^ permalink raw reply	[flat|nested] 15+ messages in thread

* Re: [PATCH v2 3/8] watchdog: starfive-wdt: Avoid unclocked register access in remove and PM paths
  2026-11-05 19:05 ` [PATCH v2 3/8] watchdog: starfive-wdt: Avoid unclocked register access in remove and PM paths Guenter Roeck
@ 2026-11-06 14:21   ` Tzung-Bi Shih
  0 siblings, 0 replies; 15+ messages in thread
From: Tzung-Bi Shih @ 2026-11-06 14:21 UTC (permalink / raw)
  To: Guenter Roeck; +Cc: linux-watchdog, Xingyu Wu, Ziv Xu

On Thu, Nov 05, 2026 at 11:05:24AM -0800, Guenter Roeck wrote:
> When runtime PM is enabled and the watchdog is inactive, the device enters
> runtime suspend and starfive_wdt_runtime_suspend() disables core_clk and
> apb_clk.
> 
> However, starfive_wdt_remove(), starfive_wdt_shutdown(),
> starfive_wdt_suspend(), and starfive_wdt_resume() unconditionally read and
> write watchdog MMIO registers without checking whether the watchdog is
> running or clocks are enabled, causing a bus fault when the device is
> runtime-suspended:
> 
> - In starfive_wdt_remove(), starfive_wdt_stop() is called before
>   watchdog_unregister_device() while clocks may be gated off. Unregister
>   the watchdog device first (which stops an active watchdog via
>   watchdog_stop_on_unregister while its runtime PM reference keeps clocks
>   enabled), and only call starfive_wdt_stop() if watchdog_hw_running() is
>   still set.
> - In starfive_wdt_pm_stop(), check pm_runtime_enabled() and ignore
>   non-zero idle return codes from pm_runtime_put_sync() so watchdog_stop()
>   does not falsely treat a stopped watchdog as still running.
> - Remove the redundant starfive_wdt_shutdown() callback. Because
>   starfive_wdt_probe() already sets watchdog_stop_on_reboot(),
>   watchdog_reboot_notifier() stops a running watchdog on reboot, halt, or
>   power-off (while respecting the watchdog.stop_on_reboot parameter)
>   before device_shutdown() runs; calling starfive_wdt_pm_stop() again in
>   .shutdown causes unclocked MMIO when suspended and an unbalanced
>   pm_runtime_put_sync() when inactive or already stopped.
> - In starfive_wdt_suspend() and starfive_wdt_resume(), only save/stop and
>   restore/restart the hardware watchdog when watchdog_hw_running() is set
>   (in which case an active runtime PM reference ensures clocks are enabled
>   before suspend and re-enabled by pm_runtime_force_resume()).
> 
> Fixes: db728ea9c7be ("drivers: watchdog: Add StarFive Watchdog driver")
> Cc: Xingyu Wu <xingyu.wu@starfivetech.com>
> Cc: Ziv Xu <ziv.xu@starfivetech.com>
> Signed-off-by: Guenter Roeck <linux@roeck-us.net>

Reviewed-by: Tzung-Bi Shih <tzungbi@kernel.org>

^ permalink raw reply	[flat|nested] 15+ messages in thread

* Re: [PATCH v2 6/8] watchdog: starfive-wdt: Avoid unclocked MMIO read in starfive_wdt_get_timeleft()
  2026-11-05 19:05 ` [PATCH v2 6/8] watchdog: starfive-wdt: Avoid unclocked MMIO read in starfive_wdt_get_timeleft() Guenter Roeck
@ 2026-11-06 14:22   ` Tzung-Bi Shih
  0 siblings, 0 replies; 15+ messages in thread
From: Tzung-Bi Shih @ 2026-11-06 14:22 UTC (permalink / raw)
  To: Guenter Roeck; +Cc: linux-watchdog, Xingyu Wu, Ziv Xu

On Thu, Nov 05, 2026 at 11:05:27AM -0800, Guenter Roeck wrote:
> When the watchdog is inactive, runtime PM suspends the device and gates
> off apb_clk and core_clk via starfive_wdt_runtime_suspend(). However,
> reading the sysfs timeleft attribute via timeleft_show() or issuing
> ioctl(WDIOC_GETTIMELEFT) calls starfive_wdt_get_timeleft() unconditionally.
> 
> Because starfive_wdt_get_timeleft() reads the counter and interrupt status
> registers without checking whether the hardware watchdog is running,
> querying timeleft while inactive performs unclocked MMIO reads and triggers
> a bus fault.
> 
> Return 0 immediately in starfive_wdt_get_timeleft() when
> watchdog_hw_running() is false. When watchdog_hw_running() is true, the
> driver holds an active runtime PM reference keeping the clocks enabled.
> 
> Fixes: db728ea9c7be ("drivers: watchdog: Add StarFive Watchdog driver")
> Cc: Xingyu Wu <xingyu.wu@starfivetech.com>
> Cc: Ziv Xu <ziv.xu@starfivetech.com>
> Signed-off-by: Guenter Roeck <linux@roeck-us.net>

Reviewed-by: Tzung-Bi Shih <tzungbi@kernel.org>

^ permalink raw reply	[flat|nested] 15+ messages in thread

* Re: [PATCH v2 7/8] watchdog: starfive-wdt: Stop ping worker on system suspend
  2026-11-05 19:05 ` [PATCH v2 7/8] watchdog: starfive-wdt: Stop ping worker on system suspend Guenter Roeck
@ 2026-11-06 14:22   ` Tzung-Bi Shih
  0 siblings, 0 replies; 15+ messages in thread
From: Tzung-Bi Shih @ 2026-11-06 14:22 UTC (permalink / raw)
  To: Guenter Roeck; +Cc: linux-watchdog, Xingyu Wu, Ziv Xu

On Thu, Nov 05, 2026 at 11:05:28AM -0800, Guenter Roeck wrote:
> When early_enable=1 is set and CONFIG_WATCHDOG_HANDLE_BOOT_ENABLED is
> enabled, watchdog_register_device() starts the watchdog core hrtimer so the
> non-freezable watchdogd kthread worker periodically pings the hardware
> before userspace opens /dev/watchdog.
> 
> Because starfive_wdt_probe() does not call watchdog_stop_ping_on_suspend(),
> WDOG_NO_PING_ON_SUSPEND is not set and the watchdog core does not cancel
> its hrtimer and ping work across system suspend. After
> starfive_wdt_suspend() stops the counter and calls
> pm_runtime_force_suspend() to gate core_clk and apb_clk, WDOG_HW_RUNNING
> remains set; if the timer fires before starfive_wdt_resume() re-enables
> the clocks, watchdog_ping_work() invokes starfive_wdt_keepalive() with
> gated clocks and causes an unclocked MMIO bus fault.
> 
> Call watchdog_stop_ping_on_suspend() in starfive_wdt_probe() so the
> watchdog core cancels its ping timer and work in PM_SUSPEND_PREPARE before
> clocks are gated and resumes pinging in PM_POST_SUSPEND after clocks are
> restored.
> 
> Fixes: db728ea9c7be ("drivers: watchdog: Add StarFive Watchdog driver")
> Cc: Xingyu Wu <xingyu.wu@starfivetech.com>
> Cc: Ziv Xu <ziv.xu@starfivetech.com>
> Signed-off-by: Guenter Roeck <linux@roeck-us.net>

Reviewed-by: Tzung-Bi Shih <tzungbi@kernel.org>

^ permalink raw reply	[flat|nested] 15+ messages in thread

* Re: [PATCH v2 8/8] watchdog: starfive-wdt: Avoid unclocked MMIO access in starfive_wdt_set_timeout()
  2026-11-06 14:21   ` Tzung-Bi Shih
@ 2026-11-06 15:20     ` Guenter Roeck
  2026-11-06 15:51       ` Tzung-Bi Shih
  0 siblings, 1 reply; 15+ messages in thread
From: Guenter Roeck @ 2026-11-06 15:20 UTC (permalink / raw)
  To: Tzung-Bi Shih; +Cc: linux-watchdog, Xingyu Wu, Ziv Xu

On 11/6/26 06:21, Tzung-Bi Shih wrote:
> On Thu, Nov 05, 2026 at 11:05:29AM -0800, Guenter Roeck wrote:
>> Only program the hardware registers in starfive_wdt_set_timeout() when
>> watchdog_hw_running() is set. In addition, reload the initial counter value
>> via starfive_wdt_set_reload_count() in starfive_wdt_start() (preserving
> 
> Is the additional change relevant to this patch (i.e., avoid unclocked
> MMIO access)?

I think so, since reload_count is no longer set in starfive_wdt_set_timeout()
if the watchdog is not running.

> 
>> wdt->reload on resume) so JH7100 loads the configured timeout into the
>> counter when started from probe or after WDIOS_DISABLECARD.
> 
> Is it really JH7100 specific (not for JH7110)?
> 

Yes, the other chip doesn't have a reload count register.
See "if (wdt->variant->reload)" in starfive_wdt_set_reload_count().

Thanks,
Guenter

>> @@ -344,7 +344,7 @@ static int starfive_wdt_keepalive(struct watchdog_device *wdd)
>>   	return ret;
>>   }
>>   
>> -static int starfive_wdt_start(struct starfive_wdt *wdt)
>> +static int starfive_wdt_start(struct starfive_wdt *wdt, u32 count)
>>   {
>>   	int ret;
>>   
>> @@ -357,7 +357,9 @@ static int starfive_wdt_start(struct starfive_wdt *wdt)
>>   	if (ret)
>>   		goto exit;
>>   
>> -	starfive_wdt_set_count(wdt, wdt->count);
>> +	starfive_wdt_set_reload_count(wdt, count);
>> +	if (count != wdt->count)
>> +		starfive_wdt_set_count(wdt, wdt->count);
>>   	starfive_wdt_enable(wdt);
>>   
>>   exit:
>> @@ -381,7 +383,7 @@ static int starfive_wdt_pm_start(struct watchdog_device *wdd)
>>   	if (ret < 0)
>>   		return ret;
>>   
>> -	ret = starfive_wdt_start(wdt);
>> +	ret = starfive_wdt_start(wdt, wdt->count);
>>   	if (ret)
>>   		pm_runtime_put_sync(wdd->parent);
>>   
>> @@ -412,11 +414,13 @@ static int starfive_wdt_set_timeout(struct watchdog_device *wdd,
>>   	wdt->count = count;
>>   	wdd->timeout = timeout;
>>   
>> -	starfive_wdt_unlock(wdt);
>> -	starfive_wdt_disable(wdt);
>> -	starfive_wdt_set_reload_count(wdt, wdt->count);
>> -	starfive_wdt_enable(wdt);
>> -	starfive_wdt_lock(wdt);
>> +	if (watchdog_hw_running(wdd)) {
>> +		starfive_wdt_unlock(wdt);
>> +		starfive_wdt_disable(wdt);
>> +		starfive_wdt_set_reload_count(wdt, wdt->count);
>> +		starfive_wdt_enable(wdt);
>> +		starfive_wdt_lock(wdt);
>> +	}
>>   
>>   	return 0;
>>   }
>> @@ -497,7 +501,7 @@ static int starfive_wdt_probe(struct platform_device *pdev)
>>   	watchdog_stop_ping_on_suspend(&wdt->wdd);
>>   
>>   	if (early_enable) {
>> -		ret = starfive_wdt_start(wdt);
>> +		ret = starfive_wdt_start(wdt, wdt->count);
>>   		if (ret)
>>   			goto err_exit;
>>   		set_bit(WDOG_HW_RUNNING, &wdt->wdd.status);
>> @@ -575,14 +579,8 @@ static int starfive_wdt_resume(struct device *dev)
>>   	if (ret)
>>   		return ret;
>>   
>> -	if (watchdog_hw_running(&wdt->wdd)) {
>> -		starfive_wdt_unlock(wdt);
>> -		/* Restore watchdog state. */
>> -		starfive_wdt_set_reload_count(wdt, wdt->reload);
>> -		starfive_wdt_lock(wdt);
>> -
>> -		return starfive_wdt_start(wdt);
>> -	}
>> +	if (watchdog_hw_running(&wdt->wdd))
>> +		return starfive_wdt_start(wdt, wdt->reload);
>>   
>>   	return 0;
>>   }


^ permalink raw reply	[flat|nested] 15+ messages in thread

* Re: [PATCH v2 8/8] watchdog: starfive-wdt: Avoid unclocked MMIO access in starfive_wdt_set_timeout()
  2026-11-06 15:20     ` Guenter Roeck
@ 2026-11-06 15:51       ` Tzung-Bi Shih
  0 siblings, 0 replies; 15+ messages in thread
From: Tzung-Bi Shih @ 2026-11-06 15:51 UTC (permalink / raw)
  To: Guenter Roeck; +Cc: linux-watchdog, Xingyu Wu, Ziv Xu

On Fri, Nov 06, 2026 at 07:20:32AM -0800, Guenter Roeck wrote:
> On 11/6/26 06:21, Tzung-Bi Shih wrote:
> > On Thu, Nov 05, 2026 at 11:05:29AM -0800, Guenter Roeck wrote:
> > > Only program the hardware registers in starfive_wdt_set_timeout() when
> > > watchdog_hw_running() is set. In addition, reload the initial counter value
> > > via starfive_wdt_set_reload_count() in starfive_wdt_start() (preserving
> > 
> > Is the additional change relevant to this patch (i.e., avoid unclocked
> > MMIO access)?
> 
> I think so, since reload_count is no longer set in starfive_wdt_set_timeout()
> if the watchdog is not running.

Ah, I see.

> 
> > 
> > > wdt->reload on resume) so JH7100 loads the configured timeout into the
> > > counter when started from probe or after WDIOS_DISABLECARD.
> > 
> > Is it really JH7100 specific (not for JH7110)?
> > 
> 
> Yes, the other chip doesn't have a reload count register.
> See "if (wdt->variant->reload)" in starfive_wdt_set_reload_count().

I see.

Reviewed-by: Tzung-Bi Shih <tzungbi@kernel.org>

^ permalink raw reply	[flat|nested] 15+ messages in thread

end of thread, other threads:[~2026-11-06 15:51 UTC | newest]

Thread overview: 15+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-11-05 19:05 [PATCH v2 0/8] watchdog: starfive-wdt: Fix runtime PM and unclocked register access bugs Guenter Roeck
2026-11-05 19:05 ` [PATCH v2 1/8] watchdog: starfive-wdt: Drop runtime PM reference if starting watchdog fails Guenter Roeck
2026-11-05 19:05 ` [PATCH v2 2/8] watchdog: starfive-wdt: Balance pm_runtime_enable() on probe error paths Guenter Roeck
2026-11-05 19:05 ` [PATCH v2 3/8] watchdog: starfive-wdt: Avoid unclocked register access in remove and PM paths Guenter Roeck
2026-11-06 14:21   ` Tzung-Bi Shih
2026-11-05 19:05 ` [PATCH v2 4/8] watchdog: starfive-wdt: Fix runtime PM usage count leaks in probe and remove Guenter Roeck
2026-11-05 19:05 ` [PATCH v2 5/8] watchdog: starfive-wdt: Drop runtime PM reference before registering watchdog Guenter Roeck
2026-11-05 19:05 ` [PATCH v2 6/8] watchdog: starfive-wdt: Avoid unclocked MMIO read in starfive_wdt_get_timeleft() Guenter Roeck
2026-11-06 14:22   ` Tzung-Bi Shih
2026-11-05 19:05 ` [PATCH v2 7/8] watchdog: starfive-wdt: Stop ping worker on system suspend Guenter Roeck
2026-11-06 14:22   ` Tzung-Bi Shih
2026-11-05 19:05 ` [PATCH v2 8/8] watchdog: starfive-wdt: Avoid unclocked MMIO access in starfive_wdt_set_timeout() Guenter Roeck
2026-11-06 14:21   ` Tzung-Bi Shih
2026-11-06 15:20     ` Guenter Roeck
2026-11-06 15:51       ` Tzung-Bi Shih

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox