From e35e2da998fbff3693229d62cacd41c77507a58a Mon Sep 17 00:00:00 2001 From: Patryk Duda Date: Mon, 19 Dec 2022 15:47:54 +0100 Subject: shim/src/watchdog: Add support for watchdog helper Watchdog helper is used to print a warning along with useful debugging information before hardware watchdog will reset SOC. Zephyr provides counter based watchdog which is very similar to watchdog helper from EC. This patch adds support for using multiple watchdogs simultaneously. One of the watchdogs will be counter based, the other will be hardware based (e.g. IWDG on STM32). Watchdog tests was modified to address the problem with allocating new counter channel every time watchdog_init() is called while there is no way to free them. BUG=b:239712345 BRANCH=none TEST=Add 'zephyr,counter-watchdog' node to DTS. Use 'waitms' command to check if a warning from watchdog based counter is triggered. TEST=./twister -i -T zephyr/test/drivers \ --test external/platform/ec/zephyr/test/drivers/drivers.default Signed-off-by: Patryk Duda Change-Id: I66b5474d7e65978791450c2fd62dd3d345cd474a Reviewed-on: https://chromium-review.googlesource.com/c/chromiumos/platform/ec/+/4116769 Tested-by: Patryk Duda Reviewed-by: Daisuke Nojiri Commit-Queue: Patryk Duda --- zephyr/Kconfig.watchdog | 10 ++ zephyr/shim/src/watchdog.c | 180 +++++++++++++++++++++-------- 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); -- cgit v1.2.1