summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPatryk Duda <pdk@semihalf.com>2022-12-19 15:47:54 +0100
committerChromeos LUCI <chromeos-scoped@luci-project-accounts.iam.gserviceaccount.com>2023-01-19 16:15:24 +0000
commite35e2da998fbff3693229d62cacd41c77507a58a (patch)
tree371c3e4d3b8aaf8a4ab6915b346ebb23b162ea41
parentd93ce30aabb04d96f1b632dda847aac992f5a562 (diff)
downloadchrome-ec-e35e2da998fbff3693229d62cacd41c77507a58a.tar.gz
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 <pdk@semihalf.com> Change-Id: I66b5474d7e65978791450c2fd62dd3d345cd474a Reviewed-on: https://chromium-review.googlesource.com/c/chromiumos/platform/ec/+/4116769 Tested-by: Patryk Duda <patrykd@google.com> Reviewed-by: Daisuke Nojiri <dnojiri@chromium.org> Commit-Queue: Patryk Duda <patrykd@google.com>
-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);