diff options
-rw-r--r-- | zephyr/Kconfig.watchdog | 10 | ||||
-rw-r--r-- | zephyr/shim/src/watchdog.c | 180 | ||||
-rw-r--r-- | zephyr/test/drivers/default/src/watchdog.c | 26 |
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); |