Browse Source

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 <aricciardi@baylibre.com>
pull/88042/head
Adrien Ricciardi 4 months ago committed by Benjamin Cabé
parent
commit
c53fb67f56
  1. 22
      include/zephyr/task_wdt/task_wdt.h
  2. 70
      subsys/task_wdt/task_wdt.c

22
include/zephyr/task_wdt/task_wdt.h

@ -102,6 +102,28 @@ int task_wdt_delete(int channel_id); @@ -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

70
subsys/task_wdt/task_wdt.c

@ -44,6 +44,9 @@ static struct k_spinlock channels_lock; @@ -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) @@ -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;
}
@ -246,3 +251,68 @@ int task_wdt_feed(int channel_id) @@ -246,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);
}

Loading…
Cancel
Save