Skip to content

Added EWM Driver #88389

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions drivers/watchdog/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -59,5 +59,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)
2 changes: 2 additions & 0 deletions drivers/watchdog/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -153,4 +153,6 @@ source "drivers/watchdog/Kconfig.renesas_ra"

source "drivers/watchdog/Kconfig.wch"

source "drivers/watchdog/Kconfig.nxp_ewm"

endif # WATCHDOG
9 changes: 9 additions & 0 deletions drivers/watchdog/Kconfig.nxp_ewm
Original file line number Diff line number Diff line change
@@ -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.
192 changes: 192 additions & 0 deletions drivers/watchdog/wdt_nxp_ewm.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,192 @@
/*
* Copyright 2025 NXP
*
* SPDX-License-Identifier: Apache-2.0
*/

#define DT_DRV_COMPAT nxp_ewm

#include <zephyr/drivers/watchdog.h>
#include <zephyr/irq.h>

#include <zephyr/logging/log.h>
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);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

is this macro going to convert the 16 bit integer literal to 8 bit ?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It should but extra precaution I added a cast to 8 bit

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)
8 changes: 8 additions & 0 deletions dts/arm/nxp/nxp_mcxw7x_common.dtsi
Original file line number Diff line number Diff line change
Expand Up @@ -343,6 +343,14 @@
clock-frequency = <32768>;
prescaler = <32768>;
};

ewm0: ewm@13000 {
compatible = "nxp,ewm";
reg = <0x13000 0x6>;
status = "disabled";
interrupts = <18 0>;
clk-divider = <0x0>;
};
};

&fast_peripheral0 {
Expand Down
32 changes: 32 additions & 0 deletions dts/bindings/watchdog/nxp,ewm.yaml
Original file line number Diff line number Diff line change
@@ -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.
4 changes: 4 additions & 0 deletions soc/nxp/mcx/mcxw/soc.c
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
22 changes: 22 additions & 0 deletions tests/drivers/watchdog/wdt_basic_reset_none/Kconfig
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
# Copyright 2025 NXP
# SPDX-License-Identifier: Apache-2.0

source "Kconfig.zephyr"

config TEST_WDT_MAX_WINDOW_TIME
int "Watchdog Max Value"
default 1000
help
Set the watchdog max window time to feed the watchdog.
The units for this value will change based on the watchdog
driver being tested. Most drivers represent this as milliseconds
due to the max window reaching the millisecond feed window.

config TEST_WDT_SLEEP_TIME
int "Watchdog Sleep Time"
default 500
help
Set the test sleep time between watchdog feeds.
The units for this value will change based on the watchdog
driver being tested. Most drivers represent this as milliseconds
due to the max window reaching the millisecond feed window.
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
/*
* Copyright 2025 NXP
* SPDX-License-Identifier: Apache-2.0
*/

/ {
aliases {
watchdog0 = &ewm0;
};
};

&ewm0 {
status = "okay";
};
20 changes: 14 additions & 6 deletions tests/drivers/watchdog/wdt_basic_reset_none/src/main.c
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright 2022 NXP
* Copyright 2022, 2025 NXP
*
* SPDX-License-Identifier: Apache-2.0
*/
Expand All @@ -20,10 +20,18 @@
#endif

#define WDT_FEED_TRIES 2
#define WDT_MAX_WINDOW 1000
#define WDT_TIMEOUT K_MSEC(1100)
#define SLEEP_TIME K_MSEC(500)
#define WDT_TEST_CB_TEST_VALUE 0xCB
#define WDT_TIMEOUT_VALUE CONFIG_TEST_WDT_MAX_WINDOW_TIME + 10

#if defined(CONFIG_WDT_NXP_EWM)
#define WDT_SETUP_FLAGS 0
#define WDT_TIMEOUT K_TICKS(WDT_TIMEOUT_VALUE)
Copy link
Collaborator

@mmahadevan108 mmahadevan108 Apr 14, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why does the NXP_EWM get the value in ticks?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The watchdog window to feed the watchdog is in the Microsecond range. The Tick value is the most accurate speed to test this EWM without having to worry about the test unintentionally missing a watchdog feed.

#define SLEEP_TIME K_TICKS(CONFIG_TEST_WDT_SLEEP_TIME)
#else
#define WDT_SETUP_FLAGS WDT_OPT_PAUSE_HALTED_BY_DBG
#define WDT_TIMEOUT K_MSEC(WDT_TIMEOUT_VALUE)
#define SLEEP_TIME K_MSEC(CONFIG_TEST_WDT_SLEEP_TIME)
#endif

static struct wdt_timeout_cfg m_cfg_wdt0;
static volatile int wdt_interrupted_flag;
Expand All @@ -49,7 +57,7 @@ static int test_wdt_callback_reset_none(void)
}

m_cfg_wdt0.window.min = 0U;
m_cfg_wdt0.window.max = WDT_MAX_WINDOW;
m_cfg_wdt0.window.max = CONFIG_TEST_WDT_MAX_WINDOW_TIME;
m_cfg_wdt0.flags = WDT_FLAG_RESET_NONE;
m_cfg_wdt0.callback = wdt_callback;

Expand All @@ -62,7 +70,7 @@ static int test_wdt_callback_reset_none(void)
return TC_FAIL;
}

err = wdt_setup(wdt, WDT_OPT_PAUSE_HALTED_BY_DBG);
err = wdt_setup(wdt, WDT_SETUP_FLAGS);
if (err != 0) {
TC_PRINT("Watchdog setup error\n");
return TC_FAIL;
Expand Down
11 changes: 11 additions & 0 deletions tests/drivers/watchdog/wdt_basic_reset_none/testcase.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -10,3 +10,14 @@ tests:
- drivers
- watchdog
depends_on: watchdog
drivers.watchdog.reset_none_ewm:
filter: dt_compat_enabled("nxp,ewm")
platform_allow:
- frdm_mcxw71
- frdm_mcxw72/mcxw727c/cpu0
integration_platforms:
- frdm_mcxw71
extra_args: DTC_OVERLAY_FILE="boards/mcxw7x_ewm.overlay"
extra_configs:
- CONFIG_TEST_WDT_MAX_WINDOW_TIME=254
- CONFIG_TEST_WDT_SLEEP_TIME=68