Repository: zephyrproject-rtos/zephyr
Pull request #87590: subsys: task_wdt: Add suspend and resume
Author: login: RICCIARDI-Adrien
Labels: name: area: Task Watchdog
description: Task Watchdog subsystem
Created: 2025-03-24T20:29:14Z | Merged: 2025-04-02T11:05:14Z

Add a `suspend` and `resume` functions that allow to pause the Task Watchdog internal timer when the system needs to go to a suspend state.
Also add a Kconfig option to enable pausing the fallback hardware watchdog.


Diff:
diff --git a/include/zephyr/task_wdt/task_wdt.h b/include/zephyr/task_wdt/task_wdt.h
index 91cc979850571..dfeede4963a8b 100644
--- a/include/zephyr/task_wdt/task_wdt.h
+++ b/include/zephyr/task_wdt/task_wdt.h
@@ -102,6 +102,28 @@ int task_wdt_delete(int channel_id);
  */
 int task_wdt_feed(int channel_id);
 
+/**
+ * @brief Pause all channels before changing system power mode.
+ *
+ * Stop the internal timer and feed the hardware watchdog a last time
+ * (if enabled). It is expected that the system enters a low-power
+ * mode quite fast after this call.
+ *
+ * @note To pause the hardware watchdog (if this is supported),
+ * enable @kconfig{CONFIG_TASK_WDT_HW_FALLBACK_PAUSE_IN_SLEEP} in your
+ * configuration.
+ */
+void task_wdt_suspend(void);
+
+/**
+ * @brief Resume all channels execution.
+ *
+ * Resume the internal timer and feed all channels. Also feed the hardware
+ * watchdog (if enabled) to let enough time to the application to resume
+ * feeding the channels by itself.
+ */
+void task_wdt_resume(void);
+
 #ifdef __cplusplus
 }
 #endif
diff --git a/subsys/task_wdt/Kconfig b/subsys/task_wdt/Kconfig
index b78894a0b3bef..92198a90e6e74 100644
--- a/subsys/task_wdt/Kconfig
+++ b/subsys/task_wdt/Kconfig
@@ -58,6 +58,16 @@ config TASK_WDT_HW_FALLBACK_DELAY
 	  kernel timer. This is especially important if the hardware watchdog
 	  is clocked by an inaccurate low-speed RC oscillator.
 
+config TASK_WDT_HW_FALLBACK_PAUSE_IN_SLEEP
+	bool "Pause the hardware watchdog in system sleep"
+	depends on TASK_WDT_HW_FALLBACK
+	help
+	  Configure the hardware watchdog to automatically pause when the
+	  system reaches a sleep power state. When the system is resumed, the
+	  watchdog is also automatically resumed.
+	  Note that this feature is supported only by a subset of hardware
+	  watchdogs.
+
 config TASK_WDT_SHELL
 	bool "Task watchdog shell utilities"
 	depends on SHELL
diff --git a/subsys/task_wdt/task_wdt.c b/subsys/task_wdt/task_wdt.c
index 361acc0e156e8..243012198fe88 100644
--- a/subsys/task_wdt/task_wdt.c
+++ b/subsys/task_wdt/task_wdt.c
@@ -44,6 +44,9 @@ static struct k_spinlock channels_lock;
 /* timer used for watchdog handling */
 static struct k_timer timer;
 
+/* Tell whether the Task Watchdog has been fully initialized. */
+static bool task_wdt_initialized;
+
 #ifdef CONFIG_TASK_WDT_HW_FALLBACK
 /* pointer to the hardware watchdog used as a fallback */
 static const struct device *hw_wdt_dev;
@@ -149,6 +152,8 @@ int task_wdt_init(const struct device *hw_wdt)
 	k_timer_init(&timer, task_wdt_trigger, NULL);
 	schedule_next_timeout(sys_clock_tick_get());
 
+	task_wdt_initialized = true;
+
 	return 0;
 }
 
@@ -179,7 +184,11 @@ int task_wdt_add(uint32_t reload_period, task_wdt_callback_t callback,
 			if (!hw_wdt_started && hw_wdt_dev) {
 				/* also start fallback hw wdt */
 				wdt_setup(hw_wdt_dev,
-					WDT_OPT_PAUSE_HALTED_BY_DBG);
+					WDT_OPT_PAUSE_HALTED_BY_DBG
+#ifdef CONFIG_TASK_WDT_HW_FALLBACK_PAUSE_IN_SLEEP
+					| WDT_OPT_PAUSE_IN_SLEEP
+#endif
+				);
 				hw_wdt_started = true;
 			}
 #endif
@@ -242,3 +251,68 @@ int task_wdt_feed(int channel_id)
 
 	return 0;
 }
+
+void task_wdt_suspend(void)
+{
+	k_spinlock_key_t key;
+
+	/*
+	 * Allow the function to be called from a custom PM policy callback, even when
+	 * the Task Watchdog was not initialized yet.
+	 */
+	if (!task_wdt_initialized) {
+		return;
+	}
+
+	/*
+	 * Prevent all task watchdog channels from triggering.
+	 * Protect the timer access with the spinlock to avoid the timer being started
+	 * concurrently by a call to schedule_next_timeout().
+	 */
+	key = k_spin_lock(&channels_lock);
+	k_timer_stop(&timer);
+	k_spin_unlock(&channels_lock, key);
+
+#ifdef CONFIG_TASK_WDT_HW_FALLBACK
+	/*
+	 * Give a whole hardware watchdog timer period of time to the application to put
+	 * the system in a suspend mode that will pause the hardware watchdog.
+	 */
+	if (hw_wdt_started) {
+		wdt_feed(hw_wdt_dev, hw_wdt_channel);
+	}
+#endif
+}
+
+void task_wdt_resume(void)
+{
+	k_spinlock_key_t key;
+	int64_t current_ticks;
+
+	/*
+	 * Allow the function to be called from a custom PM policy callback, even when
+	 * the Task Watchdog was not initialized yet.
+	 */
+	if (!task_wdt_initialized) {
+		return;
+	}
+
+	key = k_spin_lock(&channels_lock);
+
+	/*
+	 * Feed all enabled channels, so the application threads have time to resume
+	 * feeding the channels by themselves.
+	 */
+	current_ticks = sys_clock_tick_get();
+	for (size_t id = 0; id < ARRAY_SIZE(channels); id++) {
+		if (channels[id].reload_period != 0) {
+			channels[id].timeout_abs_ticks = current_ticks +
+				k_ms_to_ticks_ceil64(channels[id].reload_period);
+		}
+	}
+
+	/* Restart the Task Watchdog timer */
+	schedule_next_timeout(current_ticks);
+
+	k_spin_unlock(&channels_lock, key);
+}


Reviews:
user: login: martinjaeger
body: Thanks for the nice improvement. Looks good, only found a minor language nit-pick.
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-04-02T07:52:54Z
user: login: RICCIARDI-Adrien
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-02T08:19:16Z
user: login: martinjaeger
state: APPROVED
author_association: MEMBER
submitted_at: 2025-04-02T08:32:24Z
user: login: henrikbrixandersen
body: Looks good. It would be nice to have test cases for the added functionality.
state: APPROVED
author_association: MEMBER
submitted_at: 2025-04-02T08:48:19Z

Review comments:
diff_hunk: @@ -58,6 +58,16 @@ config TASK_WDT_HW_FALLBACK_DELAY
 	  kernel timer. This is especially important if the hardware watchdog
 	  is clocked by an inaccurate low-speed RC oscillator.
 
+config TASK_WDT_HW_FALLBACK_PAUSE_IN_SLEEP
+	bool "Pause the hardware watchdog in system sleep"
+	depends on TASK_WDT_HW_FALLBACK
+	help
+	  Configure the hardware watchdog to automatically pause when the
+	  system reaches a sleep power state. When the system resumed, the
path: subsys/task_wdt/Kconfig
user: login: martinjaeger
body: Nits:

```suggestion
	  system reaches a sleep power state. When the system is resumed, the
```
created_at: 2025-04-02T07:43:25Z
author_association: MEMBER
diff_hunk: @@ -58,6 +58,16 @@ config TASK_WDT_HW_FALLBACK_DELAY
 	  kernel timer. This is especially important if the hardware watchdog
 	  is clocked by an inaccurate low-speed RC oscillator.
 
+config TASK_WDT_HW_FALLBACK_PAUSE_IN_SLEEP
+	bool "Pause the hardware watchdog in system sleep"
+	depends on TASK_WDT_HW_FALLBACK
+	help
+	  Configure the hardware watchdog to automatically pause when the
+	  system reaches a sleep power state. When the system resumed, the
path: subsys/task_wdt/Kconfig
user: login: RICCIARDI-Adrien
body: Thanks for spotting it, I fixed it !
created_at: 2025-04-02T08:19:16Z
author_association: CONTRIBUTOR

PR comments:
user: login: martinjaeger
created_at: 2025-04-02T08:33:15Z
body: I will ask for a second review on Discord.
author_association: MEMBER
user: login: RICCIARDI-Adrien
created_at: 2025-04-02T08:33:47Z
body: > I will ask for a second review on Discord.

Thanks !
author_association: CONTRIBUTOR
user: login: martinjaeger
created_at: 2025-04-02T09:19:41Z
body: > Looks good. It would be nice to have test cases for the added functionality.

Thanks for the quick review! Unfortunately, we don't currently have tests for the task watchdog at all... its on my ToDo list for "when I find some time".
author_association: MEMBER