From 5fd671591700f7538cd886878c2bbd6799e55340 Mon Sep 17 00:00:00 2001 From: Emilio Benavente Date: Wed, 9 Apr 2025 11:27:38 -0500 Subject: [PATCH] drivers: watchdog: Added Driver for the EWM Added a driver for the External Watchdog Driver Signed-off-by: Emilio Benavente --- drivers/watchdog/CMakeLists.txt | 1 + drivers/watchdog/Kconfig | 2 + drivers/watchdog/Kconfig.nxp_ewm | 9 ++ drivers/watchdog/wdt_nxp_ewm.c | 192 +++++++++++++++++++++++++++++ dts/bindings/watchdog/nxp,ewm.yaml | 32 +++++ soc/nxp/mcx/mcxw/soc.c | 4 + 6 files changed, 240 insertions(+) create mode 100644 drivers/watchdog/Kconfig.nxp_ewm create mode 100644 drivers/watchdog/wdt_nxp_ewm.c create mode 100644 dts/bindings/watchdog/nxp,ewm.yaml diff --git a/drivers/watchdog/CMakeLists.txt b/drivers/watchdog/CMakeLists.txt index 08f3d6818f6..e7264362e3f 100644 --- a/drivers/watchdog/CMakeLists.txt +++ b/drivers/watchdog/CMakeLists.txt @@ -60,5 +60,6 @@ zephyr_library_sources_ifdef(CONFIG_WDT_ANDES_ATCWDT200 wdt_andes_atcwdt200.c) zephyr_library_sources_ifdef(CONFIG_WDT_NXP_FS26 wdt_nxp_fs26.c) zephyr_library_sources_ifdef(CONFIG_WDT_SHELL wdt_shell.c) zephyr_library_sources_ifdef(CONFIG_WDT_RENESAS_RA wdt_renesas_ra.c) +zephyr_library_sources_ifdef(CONFIG_WDT_NXP_EWM wdt_nxp_ewm.c) zephyr_library_sources_ifdef(CONFIG_USERSPACE wdt_handlers.c) diff --git a/drivers/watchdog/Kconfig b/drivers/watchdog/Kconfig index 0a2671aa756..bd445bda94f 100644 --- a/drivers/watchdog/Kconfig +++ b/drivers/watchdog/Kconfig @@ -155,4 +155,6 @@ source "drivers/watchdog/Kconfig.renesas_ra" source "drivers/watchdog/Kconfig.wch" +source "drivers/watchdog/Kconfig.nxp_ewm" + endif # WATCHDOG diff --git a/drivers/watchdog/Kconfig.nxp_ewm b/drivers/watchdog/Kconfig.nxp_ewm new file mode 100644 index 00000000000..7b26336351d --- /dev/null +++ b/drivers/watchdog/Kconfig.nxp_ewm @@ -0,0 +1,9 @@ +# Copyright 2025 NXP +# SPDX-License-Identifier: Apache-2.0 + +config WDT_NXP_EWM + bool "NXP EWM driver" + default y + depends on DT_HAS_NXP_EWM_ENABLED + help + Enable the nxp ewm driver. diff --git a/drivers/watchdog/wdt_nxp_ewm.c b/drivers/watchdog/wdt_nxp_ewm.c new file mode 100644 index 00000000000..32c697e3e22 --- /dev/null +++ b/drivers/watchdog/wdt_nxp_ewm.c @@ -0,0 +1,192 @@ +/* + * Copyright 2025 NXP + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#define DT_DRV_COMPAT nxp_ewm + +#include +#include + +#include +LOG_MODULE_REGISTER(wdt_nxp_ewm); + +#define NXP_EWM_FEED_MAGIC_NUMBER 0x2CB4 +#define NXP_EWM_MAX_TIMEOUT_WINDOW 0xFE + +struct nxp_ewm_config { + EWM_Type *base; + void (*irq_config_func)(const struct device *dev); + bool is_input_enabled; + bool is_input_active_high; + uint8_t clk_divider; +}; + +struct nxp_ewm_data { + struct wdt_timeout_cfg timeout_cfg; + bool is_watchdog_setup; +}; + +static int nxp_ewm_setup(const struct device *dev, uint8_t options) +{ + const struct nxp_ewm_config *config = dev->config; + struct nxp_ewm_data *data = dev->data; + EWM_Type *base = config->base; + + if (data->is_watchdog_setup) { + /* Watchdog cannot be re-configured after enabled. */ + return -EBUSY; + } + + if (options) { + /* Unable to halt counter during debugging */ + return -ENOTSUP; + } + + data->is_watchdog_setup = true; + base->CMPL = EWM_CMPL_COMPAREL(data->timeout_cfg.window.min); + base->CMPH = EWM_CMPH_COMPAREH(data->timeout_cfg.window.max); + + /* + * base->CTRL should be the last thing touched due to + * the small watchdog window time. + * After this write, only the INTEN bit is writable until reset. + * + * EWM_CTRL_INTEN enables the interrupt signal + * EWM_CTRL_EWMEN enables the watchdog. + */ + base->CTRL |= EWM_CTRL_INEN(config->is_input_enabled) | + EWM_CTRL_ASSIN(config->is_input_active_high) | + EWM_CTRL_INTEN(1) | EWM_CTRL_EWMEN(1); + + return 0; +} + +static int nxp_ewm_disable(const struct device *dev) +{ + struct nxp_ewm_data *data = dev->data; + + if (!data->is_watchdog_setup) { + return -EFAULT; + } + + return -EPERM; +} + +static int nxp_ewm_install_timeout(const struct device *dev, + const struct wdt_timeout_cfg *cfg) +{ + struct nxp_ewm_data *data = dev->data; + + if (cfg->flags) { + return -ENOTSUP; + } + + if (data->is_watchdog_setup) { + return -ENOMEM; + } + + if (cfg && cfg->window.max <= NXP_EWM_MAX_TIMEOUT_WINDOW && + cfg->window.min <= cfg->window.max && + cfg->window.max > 0 && + cfg->window.min >= 0) { + data->timeout_cfg.window = cfg->window; + } else { + return -EINVAL; + } + +#if defined(CONFIG_WDT_MULTISTAGE) + if (cfg->next) { + return -EINVAL; + } +#endif + + if (cfg->callback) { + data->timeout_cfg.callback = cfg->callback; + } + + return 0; +} + +static int nxp_ewm_feed(const struct device *dev, int channel_id) +{ + ARG_UNUSED(channel_id); + const struct nxp_ewm_config *config = dev->config; + EWM_Type *base = config->base; + unsigned int key = irq_lock(); + + base->SERV = EWM_SERV_SERVICE(NXP_EWM_FEED_MAGIC_NUMBER); + base->SERV = EWM_SERV_SERVICE((uint8_t)(NXP_EWM_FEED_MAGIC_NUMBER >> 8)); + irq_unlock(key); + + return 0; +} + +static void nxp_ewm_isr(const struct device *dev) +{ + const struct nxp_ewm_config *config = dev->config; + struct nxp_ewm_data *data = dev->data; + EWM_Type *base = config->base; + + base->CTRL &= (~EWM_CTRL_INTEN_MASK); + + if (data->timeout_cfg.callback) { + data->timeout_cfg.callback(dev, 0); + } +} + +static int nxp_ewm_init(const struct device *dev) +{ + const struct nxp_ewm_config *config = dev->config; + EWM_Type *base = config->base; + + if (config->clk_divider >= 0 && config->clk_divider <= 0xFF) { + base->CLKPRESCALER = EWM_CLKPRESCALER_CLK_DIV(config->clk_divider); + } + config->irq_config_func(dev); + + return 0; +} + +static DEVICE_API(wdt, nxp_ewm_api) = { + .setup = nxp_ewm_setup, + .disable = nxp_ewm_disable, + .install_timeout = nxp_ewm_install_timeout, + .feed = nxp_ewm_feed, +}; + +#define WDT_EWM_INIT(n) \ + static void nxp_ewm_config_func_##n(const struct device *dev); \ + \ + static const struct nxp_ewm_config nxp_ewm_config_##n = { \ + .base = (EWM_Type *)DT_INST_REG_ADDR(n), \ + .irq_config_func = nxp_ewm_config_func_##n, \ + .is_input_enabled = DT_INST_PROP(n, input_trigger_en), \ + .is_input_active_high = \ + DT_INST_PROP(n, input_trigger_active_high), \ + .clk_divider = DT_INST_PROP(n, clk_divider), \ + }; \ + \ + static struct nxp_ewm_data nxp_ewm_data_##n; \ + \ + DEVICE_DT_INST_DEFINE(n, \ + nxp_ewm_init, \ + NULL, \ + &nxp_ewm_data_##n, &nxp_ewm_config_##n, \ + POST_KERNEL, \ + CONFIG_KERNEL_INIT_PRIORITY_DEVICE, \ + &nxp_ewm_api); \ + \ + static void nxp_ewm_config_func_##n(const struct device *dev) \ + { \ + ARG_UNUSED(dev); \ + \ + IRQ_CONNECT(DT_INST_IRQN(n), \ + DT_INST_IRQ(n, priority), \ + nxp_ewm_isr, DEVICE_DT_INST_GET(n), 0); \ + \ + irq_enable(DT_INST_IRQN(n)); \ + } + +DT_INST_FOREACH_STATUS_OKAY(WDT_EWM_INIT) diff --git a/dts/bindings/watchdog/nxp,ewm.yaml b/dts/bindings/watchdog/nxp,ewm.yaml new file mode 100644 index 00000000000..5bd1e6a36eb --- /dev/null +++ b/dts/bindings/watchdog/nxp,ewm.yaml @@ -0,0 +1,32 @@ +# Copyright 2025 NXP +# SPDX-License-Identifier: Apache-2.0 + +description: NXP External Watchdog Monitor + +compatible: "nxp,ewm" + +include: base.yaml + +properties: + reg: + required: true + + interrupts: + required: true + + clk-divider: + type: int + description: Watchdog clock divider + required: true + + input_trigger_en: + type: boolean + description: | + When enabled the ewm_in signal can be used + to assert the ewm. + + input_trigger_active_high: + type: boolean + description: | + When enabled the ewm_in signal is active high. + The ewm_in signal is active low otherwise. diff --git a/soc/nxp/mcx/mcxw/soc.c b/soc/nxp/mcx/mcxw/soc.c index 94b22710234..1032a5e3c5c 100644 --- a/soc/nxp/mcx/mcxw/soc.c +++ b/soc/nxp/mcx/mcxw/soc.c @@ -188,6 +188,10 @@ __weak void clock_init(void) if (DT_NODE_HAS_COMPAT_STATUS(adc0, nxp_lpadc, okay)) { CLOCK_EnableClock(kCLOCK_Lpadc0); } + + if (DT_NODE_HAS_COMPAT_STATUS(DT_NODELABEL(ewm0), nxp_ewm, okay)) { + CLOCK_EnableClock(kCLOCK_Ewm0); + } } static void vbat_init(void)