summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--zephyr/Kconfig.watchdog10
-rw-r--r--zephyr/shim/src/watchdog.c180
-rw-r--r--zephyr/test/drivers/default/src/watchdog.c26
3 files changed, 149 insertions, 67 deletions
diff --git a/zephyr/Kconfig.watchdog b/zephyr/Kconfig.watchdog
index 653cc58e22..bca08f22fe 100644
--- a/zephyr/Kconfig.watchdog
+++ b/zephyr/Kconfig.watchdog
@@ -27,4 +27,14 @@ config PLATFORM_EC_WATCHDOG_WARNING_LEADING_TIME_MS
For the ITE chip, use CONFIG_WDT_ITE_WARNING_LEADING_TIME_MS instead
of this config.
+config PLATFORM_EC_WATCHDOG_HELPER
+ bool "Use watchdog helper to trigger warning"
+ default n
+ depends on WDT_COUNTER
+ help
+ If enabled, watchdog shim will setup cros-ec,watchdog-helper
+ with CONFIG_WATCHDOG_AUX_TIMER_PERIOD_MS timeout.
+ Watchdog helper is used to trigger warning and gather debugging
+ information before hardware watchdog resets the SoC.
+
endif # WATCHDOG
diff --git a/zephyr/shim/src/watchdog.c b/zephyr/shim/src/watchdog.c
index 208930f5ea..0b96cbaa0e 100644
--- a/zephyr/shim/src/watchdog.c
+++ b/zephyr/shim/src/watchdog.c
@@ -14,81 +14,159 @@
LOG_MODULE_REGISTER(watchdog_shim, LOG_LEVEL_ERR);
-#define wdt DEVICE_DT_GET(DT_CHOSEN(cros_ec_watchdog))
+struct watchdog_info {
+ const struct device *wdt_dev;
+ struct wdt_timeout_cfg config;
+};
+
+__maybe_unused static void wdt_warning_handler(const struct device *wdt_dev,
+ int channel_id);
+
+const struct watchdog_info wdt_info[] = {
+ {
+ .wdt_dev = DEVICE_DT_GET(DT_CHOSEN(cros_ec_watchdog)),
+ .config = {
+#if DT_NODE_HAS_COMPAT(DT_CHOSEN(cros_ec_watchdog), st_stm32_watchdog)
+ .flags = WDT_FLAG_RESET_SOC,
+ .window.min = 0U,
+ .window.max = CONFIG_WATCHDOG_PERIOD_MS,
+ .callback = NULL,
+#else
+ .flags = WDT_FLAG_RESET_SOC,
+ .window.min = 0U,
+ .window.max = CONFIG_AUX_TIMER_PERIOD_MS,
+ .callback = wdt_warning_handler,
+#endif
+ },
+ },
+#ifdef CONFIG_PLATFORM_EC_WATCHDOG_HELPER
+ {
+ .wdt_dev = DEVICE_DT_GET(DT_CHOSEN(cros_ec_watchdog_helper)),
+ .config = {
+ .flags = 0U,
+ .window.min = 0U,
+ .window.max = CONFIG_AUX_TIMER_PERIOD_MS,
+ .callback = wdt_warning_handler,
+ },
+ },
+#endif
+};
+
+/* Array to keep channel used to implement watchdog */
+int wdt_chan[ARRAY_SIZE(wdt_info)];
+bool watchdog_initialized;
#ifdef TEST_BUILD
-extern bool wdt_warning_triggered;
+bool wdt_warning_triggered;
#endif /* TEST_BUILD */
-static void wdt_warning_handler(const struct device *wdt_dev, int channel_id)
+static int watchdog_config(const struct watchdog_info *info)
{
- const char *thread_name = k_thread_name_get(k_current_get());
+ const struct device *wdt_dev = info->wdt_dev;
+ const struct wdt_timeout_cfg *config = &info->config;
+ int chan;
-#ifdef CONFIG_RISCV
- printk("WDT pre-warning MEPC:%p THREAD_NAME:%s\n",
- (void *)csr_read(mepc), thread_name);
-#else
- /* TODO(b/176523207): watchdog warning message */
- printk("Watchdog deadline is close! THREAD_NAME:%s\n", thread_name);
-#endif
-#ifdef TEST_BUILD
- wdt_warning_triggered = true;
-#endif
-#ifdef CONFIG_SOC_SERIES_MEC172X
- extern void cros_chip_wdt_handler(const struct device *wdt_dev,
- int channel_id);
- cros_chip_wdt_handler(wdt_dev, channel_id);
-#endif
+ chan = wdt_install_timeout(wdt_dev, config);
+
+ /* If watchdog is running, reinstall it. */
+ if (chan == -EBUSY) {
+ wdt_disable(wdt_dev);
+ chan = wdt_install_timeout(wdt_dev, config);
+ }
+
+ if (chan < 0) {
+ LOG_ERR("Watchdog install error: %d", chan);
+ }
+
+ return chan;
}
-int watchdog_init(void)
+static int watchdog_enable(const struct device *wdt_dev)
{
int err;
- struct wdt_timeout_cfg wdt_config;
-
- if (!device_is_ready(wdt)) {
- LOG_ERR("Error: device %s is not ready", wdt->name);
- return -1;
- }
- /* Reset SoC when watchdog timer expires. */
- wdt_config.flags = WDT_FLAG_RESET_SOC;
+ err = wdt_setup(wdt_dev, 0);
+ if (err < 0)
+ LOG_ERR("Watchdog %s setup error: %d", wdt_dev->name, err);
- /*
- * Set the Warning timer as CONFIG_AUX_TIMER_PERIOD_MS.
- * Then the watchdog reset time = CONFIG_WATCHDOG_PERIOD_MS.
- */
- wdt_config.window.min = 0U;
- wdt_config.window.max = CONFIG_AUX_TIMER_PERIOD_MS;
- wdt_config.callback = wdt_warning_handler;
+ return err;
+}
- err = wdt_install_timeout(wdt, &wdt_config);
+static int watchdog_init_device(const struct watchdog_info *info)
+{
+ const struct device *wdt_dev = info->wdt_dev;
+ int chan, err;
- /* If watchdog is running, reinstall it. */
- if (err == -EBUSY) {
- wdt_disable(wdt);
- err = wdt_install_timeout(wdt, &wdt_config);
+ if (!device_is_ready(wdt_dev)) {
+ LOG_ERR("Error: device %s is not ready", wdt_dev->name);
+ return -ENODEV;
}
- if (err < 0) {
- LOG_ERR("Watchdog install error");
- return err;
- }
+ chan = watchdog_config(info);
+ if (chan < 0)
+ return chan;
- err = wdt_setup(wdt, 0);
- if (err < 0) {
- LOG_ERR("Watchdog setup error");
+ err = watchdog_enable(wdt_dev);
+ if (err < 0)
return err;
+
+ return chan;
+}
+
+int watchdog_init(void)
+{
+ int err = EC_SUCCESS;
+
+ if (watchdog_initialized)
+ return -EBUSY;
+
+ for (int i = 0; i < ARRAY_SIZE(wdt_info); i++) {
+ wdt_chan[i] = watchdog_init_device(&wdt_info[i]);
+ if (wdt_chan[i] < 0 && err == EC_SUCCESS)
+ err = wdt_chan[i];
}
- return EC_SUCCESS;
+ watchdog_initialized = true;
+ watchdog_reload();
+
+ return err;
}
void watchdog_reload(void)
{
- if (!device_is_ready(wdt))
- LOG_ERR("Error: device %s is not ready", wdt->name);
+ if (!watchdog_initialized)
+ return;
+
+ for (int i = 0; i < ARRAY_SIZE(wdt_info); i++) {
+ if (wdt_chan[i] < 0)
+ continue;
- wdt_feed(wdt, 0);
+ wdt_feed(wdt_info[i].wdt_dev, wdt_chan[i]);
+ }
}
DECLARE_HOOK(HOOK_TICK, watchdog_reload, HOOK_PRIO_DEFAULT);
+
+__maybe_unused static void wdt_warning_handler(const struct device *wdt_dev,
+ int channel_id)
+{
+ const char *thread_name = k_thread_name_get(k_current_get());
+
+#ifdef CONFIG_RISCV
+ printk("WDT pre-warning MEPC:%p THREAD_NAME:%s\n",
+ (void *)csr_read(mepc), thread_name);
+#else
+ /* TODO(b/176523207): watchdog warning message */
+ printk("Watchdog deadline is close! THREAD_NAME:%s\n", thread_name);
+#endif
+#ifdef TEST_BUILD
+ wdt_warning_triggered = true;
+#endif
+#ifdef CONFIG_SOC_SERIES_MEC172X
+ extern void cros_chip_wdt_handler(const struct device *wdt_dev,
+ int channel_id);
+ cros_chip_wdt_handler(wdt_dev, channel_id);
+#endif
+
+ /* Watchdog is disabled after calling handler. Re-enable it now. */
+ watchdog_enable(wdt_dev);
+}
diff --git a/zephyr/test/drivers/default/src/watchdog.c b/zephyr/test/drivers/default/src/watchdog.c
index a82800a6de..46e981e2b5 100644
--- a/zephyr/test/drivers/default/src/watchdog.c
+++ b/zephyr/test/drivers/default/src/watchdog.c
@@ -33,7 +33,8 @@
/**
* @brief Boolean to indicate watchdog alert triggered
*/
-bool wdt_warning_triggered;
+extern bool wdt_warning_triggered;
+bool wdt_initialized;
/**
* @brief timer to used to validate watchdog expiries.
@@ -52,17 +53,10 @@ static void watchdog_before(void *state)
/* When shuffling need watchdog initialized and running
* for other tests.
*/
- (void)watchdog_init();
- (void)wdt_setup(wdt, 0);
-}
-
-/**
- * @brief Watchdog test teardown handler.
- */
-static void watchdog_after(void *state)
-{
- ARG_UNUSED(state);
- wdt_warning_triggered = false;
+ if (!wdt_initialized) {
+ (void)watchdog_init();
+ wdt_initialized = true;
+ }
}
/**
@@ -81,8 +75,7 @@ ZTEST(watchdog, test_watchdog_init)
/* Test already initialized (initialized in watchdog_before) */
retval = watchdog_init();
- zassert_equal(-ENOMEM, retval, "Expected -ENOMEM, returned %d.",
- retval);
+ zassert_equal(-EBUSY, retval, "Expected -EBUSY, returned %d.", retval);
}
/**
@@ -100,6 +93,7 @@ ZTEST(watchdog, test_watchdog_reload)
int safe_wait_ms = DEFAULT_WDT_EXPIRY_MS / 2;
zassert_false(wdt_warning_triggered, "Watchdog timer expired early.");
+ watchdog_reload();
for (i = 0; i < 10; i++) {
k_timer_start(&ktimer, K_MSEC(safe_wait_ms), K_NO_WAIT);
k_busy_wait(safe_wait_ms * 1000);
@@ -137,5 +131,5 @@ ZTEST(watchdog, test_wdt_warning_handler)
/**
* @brief Test Suite: Verifies watchdog functionality.
*/
-ZTEST_SUITE(watchdog, drivers_predicate_post_main, NULL, watchdog_before,
- watchdog_after, NULL);
+ZTEST_SUITE(watchdog, drivers_predicate_post_main, NULL, watchdog_before, NULL,
+ NULL);