Skip to content

Commit c53fb67

Browse files
RICCIARDI-Adrienkartben
authored andcommitted
task_wdt: Add suspend and resume API functions
The goal is to be able to use the Task Watchdog on a system that is also using power management to reach low-power modes. In some low-power modes, the watchdog channels can't be feed anymore. The task_wdt_suspend() function allows to prepare the Task Watchdog for a system low-power mode, in which the hardware watchdog (if enabled) is also suspended. The task_wdt_resume() function will reschedule the internal timer that manages the channels, feed all channels and also the hardware watchdog. Thus, the application is good to go and has enough time to feed the channels by itself. Signed-off-by: Adrien Ricciardi <[email protected]>
1 parent 6ca9bff commit c53fb67

File tree

2 files changed

+92
-0
lines changed

2 files changed

+92
-0
lines changed

include/zephyr/task_wdt/task_wdt.h

+22
Original file line numberDiff line numberDiff line change
@@ -102,6 +102,28 @@ int task_wdt_delete(int channel_id);
102102
*/
103103
int task_wdt_feed(int channel_id);
104104

105+
/**
106+
* @brief Pause all channels before changing system power mode.
107+
*
108+
* Stop the internal timer and feed the hardware watchdog a last time
109+
* (if enabled). It is expected that the system enters a low-power
110+
* mode quite fast after this call.
111+
*
112+
* @note To pause the hardware watchdog (if this is supported),
113+
* enable @kconfig{CONFIG_TASK_WDT_HW_FALLBACK_PAUSE_IN_SLEEP} in your
114+
* configuration.
115+
*/
116+
void task_wdt_suspend(void);
117+
118+
/**
119+
* @brief Resume all channels execution.
120+
*
121+
* Resume the internal timer and feed all channels. Also feed the hardware
122+
* watchdog (if enabled) to let enough time to the application to resume
123+
* feeding the channels by itself.
124+
*/
125+
void task_wdt_resume(void);
126+
105127
#ifdef __cplusplus
106128
}
107129
#endif

subsys/task_wdt/task_wdt.c

+70
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,9 @@ static struct k_spinlock channels_lock;
4444
/* timer used for watchdog handling */
4545
static struct k_timer timer;
4646

47+
/* Tell whether the Task Watchdog has been fully initialized. */
48+
static bool task_wdt_initialized;
49+
4750
#ifdef CONFIG_TASK_WDT_HW_FALLBACK
4851
/* pointer to the hardware watchdog used as a fallback */
4952
static const struct device *hw_wdt_dev;
@@ -149,6 +152,8 @@ int task_wdt_init(const struct device *hw_wdt)
149152
k_timer_init(&timer, task_wdt_trigger, NULL);
150153
schedule_next_timeout(sys_clock_tick_get());
151154

155+
task_wdt_initialized = true;
156+
152157
return 0;
153158
}
154159

@@ -246,3 +251,68 @@ int task_wdt_feed(int channel_id)
246251

247252
return 0;
248253
}
254+
255+
void task_wdt_suspend(void)
256+
{
257+
k_spinlock_key_t key;
258+
259+
/*
260+
* Allow the function to be called from a custom PM policy callback, even when
261+
* the Task Watchdog was not initialized yet.
262+
*/
263+
if (!task_wdt_initialized) {
264+
return;
265+
}
266+
267+
/*
268+
* Prevent all task watchdog channels from triggering.
269+
* Protect the timer access with the spinlock to avoid the timer being started
270+
* concurrently by a call to schedule_next_timeout().
271+
*/
272+
key = k_spin_lock(&channels_lock);
273+
k_timer_stop(&timer);
274+
k_spin_unlock(&channels_lock, key);
275+
276+
#ifdef CONFIG_TASK_WDT_HW_FALLBACK
277+
/*
278+
* Give a whole hardware watchdog timer period of time to the application to put
279+
* the system in a suspend mode that will pause the hardware watchdog.
280+
*/
281+
if (hw_wdt_started) {
282+
wdt_feed(hw_wdt_dev, hw_wdt_channel);
283+
}
284+
#endif
285+
}
286+
287+
void task_wdt_resume(void)
288+
{
289+
k_spinlock_key_t key;
290+
int64_t current_ticks;
291+
292+
/*
293+
* Allow the function to be called from a custom PM policy callback, even when
294+
* the Task Watchdog was not initialized yet.
295+
*/
296+
if (!task_wdt_initialized) {
297+
return;
298+
}
299+
300+
key = k_spin_lock(&channels_lock);
301+
302+
/*
303+
* Feed all enabled channels, so the application threads have time to resume
304+
* feeding the channels by themselves.
305+
*/
306+
current_ticks = sys_clock_tick_get();
307+
for (size_t id = 0; id < ARRAY_SIZE(channels); id++) {
308+
if (channels[id].reload_period != 0) {
309+
channels[id].timeout_abs_ticks = current_ticks +
310+
k_ms_to_ticks_ceil64(channels[id].reload_period);
311+
}
312+
}
313+
314+
/* Restart the Task Watchdog timer */
315+
schedule_next_timeout(current_ticks);
316+
317+
k_spin_unlock(&channels_lock, key);
318+
}

0 commit comments

Comments
 (0)