Skip to content

Commit c7e5fcb

Browse files
sangtranjcquytranpzz
authored andcommitted
drivers: watchdog: Support Renesas RX independent watchdog timer driver
Add initial support for independent watchdog driver for Renesas RX with r_iwdt_rx RDP HAL Signed-off-by: Sang Tran <[email protected]>
1 parent 161e256 commit c7e5fcb

File tree

6 files changed

+339
-0
lines changed

6 files changed

+339
-0
lines changed

drivers/watchdog/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,7 @@ zephyr_library_sources_ifdef(CONFIG_WDT_ANDES_ATCWDT200 wdt_andes_atcwdt200.c)
6262
zephyr_library_sources_ifdef(CONFIG_WDT_NXP_FS26 wdt_nxp_fs26.c)
6363
zephyr_library_sources_ifdef(CONFIG_WDT_SHELL wdt_shell.c)
6464
zephyr_library_sources_ifdef(CONFIG_WDT_RENESAS_RA wdt_renesas_ra.c)
65+
zephyr_library_sources_ifdef(CONFIG_WDT_RENESAS_RX_IWDT wdt_renesas_rx_iwdt.c)
6566
zephyr_library_sources_ifdef(CONFIG_WDT_NXP_EWM wdt_nxp_ewm.c)
6667

6768
zephyr_library_sources_ifdef(CONFIG_WDT_TI_RTI wdt_ti_rti.c)

drivers/watchdog/Kconfig

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -153,6 +153,8 @@ source "drivers/watchdog/Kconfig.rts5912"
153153

154154
source "drivers/watchdog/Kconfig.renesas_ra"
155155

156+
source "drivers/watchdog/Kconfig.renesas_rx"
157+
156158
source "drivers/watchdog/Kconfig.wch"
157159

158160
source "drivers/watchdog/Kconfig.nxp_ewm"
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
# Renesas RX Watchdog configuration
2+
3+
# Copyright (c) 2025 Renesas Electronics Corporation
4+
# SPDX-License-Identifier: Apache-2.0
5+
6+
config WDT_RENESAS_RX_IWDT
7+
bool "Renesas RX Series Independent Watchdog Driver"
8+
default y
9+
depends on DT_HAS_RENESAS_RX_IWDT_ENABLED
10+
select HAS_WDT_DISABLE_AT_BOOT
11+
select USE_RX_RDP_IWDT
12+
help
13+
Enable Renesas RX series watchdog driver.
14+
15+
if WDT_RENESAS_RX_IWDT
16+
17+
config WDT_RENESAS_RX_IWDT_USE_NMI
18+
bool "Non-maskable interrupt for IWDT"
19+
default y
20+
help
21+
Enable NMI for IWDT.
22+
23+
choice
24+
prompt "IWDT Start Mode"
25+
default WDT_RENESAS_RX_IWDT_REGISTER_START_MODE
26+
help
27+
Select the IWDT start mode.
28+
- WDT_RENESAS_RX_IWDT_AUTO_START_MODE: Counting automatically starts after a reset
29+
(auto-start mode), controlled by option function select register 0 (OFS0).
30+
- WDT_RENESAS_RX_IWDT_REGISTER_START_MODE: Counting is started by refreshing the counter.
31+
(controlled by the IWDT registers)
32+
33+
config WDT_RENESAS_RX_IWDT_AUTO_START_MODE
34+
bool "Start IWDT automatically on reset"
35+
36+
config WDT_RENESAS_RX_IWDT_REGISTER_START_MODE
37+
bool "IWDT Start Mode Select"
38+
39+
endchoice
40+
41+
endif # WDT_RENESAS_RX_IWDT
Lines changed: 251 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,251 @@
1+
/*
2+
* Copyright (c) 2025 Renesas Electronics Corporation and/or its affiliates
3+
*
4+
* SPDX-License-Identifier: Apache-2.0
5+
*/
6+
7+
/**
8+
* @brief Independent Watchdog (IWDT) Driver for Renesas RX
9+
*/
10+
11+
#define DT_DRV_COMPAT renesas_rx_iwdt
12+
13+
#include <zephyr/drivers/watchdog.h>
14+
#include <soc.h>
15+
#include <zephyr/kernel.h>
16+
#include <stdlib.h>
17+
#include <zephyr/arch/rx/sw_nmi_table.h>
18+
19+
#include "r_iwdt_rx_if.h"
20+
21+
#include <platform.h>
22+
23+
#define LOG_LEVEL CONFIG_WDT_LOG_LEVEL
24+
#include <zephyr/logging/log.h>
25+
LOG_MODULE_REGISTER(wdt_iwdt_rx);
26+
27+
#define IWDT_NODELABEL DT_NODELABEL(iwdt)
28+
#define IWDT_NMI_VECTOR 2
29+
30+
#ifdef CONFIG_WDT_RENESAS_RX_IWDT_REGISTER_START_MODE
31+
#define IWDT_WINDOW_START DT_PROP(IWDT_NODELABEL, window_start)
32+
#define IWDT_WINDOW_END DT_PROP(IWDT_NODELABEL, window_end)
33+
#endif /* CONFIG_WDT_RENESAS_RX_IWDT_REGISTER_START_MODE */
34+
35+
#define WDT_RENESAS_RX_SUPPORTED_FLAGS (WDT_FLAG_RESET_NONE | WDT_FLAG_RESET_SOC)
36+
struct wdt_iwdt_rx_config {
37+
struct st_iwdt *const regs;
38+
const struct device *clock_dev;
39+
};
40+
41+
struct wdt_iwdt_rx_data {
42+
wdt_callback_t callback;
43+
iwdt_config_t iwdt_config;
44+
bool timeout_installed;
45+
};
46+
47+
#if CONFIG_WDT_RENESAS_RX_IWDT_USE_NMI
48+
static void wdt_iwdt_isr(const struct device *dev)
49+
{
50+
struct wdt_iwdt_rx_data *data = dev->data;
51+
52+
if (data->callback) {
53+
data->callback(dev, 0);
54+
}
55+
}
56+
#endif /* CONFIG_WDT_RENESAS_RX_IWDT_USE_NMI */
57+
58+
static int wdt_iwdt_rx_disable(const struct device *dev)
59+
{
60+
ARG_UNUSED(dev);
61+
62+
/* watchdog cannot be stopped once started */
63+
LOG_ERR("Independent Watchdog cannot be stopped once started");
64+
65+
return -EPERM;
66+
}
67+
68+
static int wdt_iwdt_rx_feed(const struct device *dev, int channel_id)
69+
{
70+
ARG_UNUSED(dev);
71+
ARG_UNUSED(channel_id);
72+
iwdt_err_t err;
73+
74+
/* Reset counter */
75+
err = R_IWDT_Control(IWDT_CMD_REFRESH_COUNTING, NULL);
76+
if (err != IWDT_SUCCESS) {
77+
return -EIO;
78+
}
79+
80+
return 0;
81+
}
82+
83+
static int wdt_iwdt_rx_install_timeout(const struct device *dev, const struct wdt_timeout_cfg *cfg)
84+
{
85+
struct wdt_iwdt_rx_data *data = dev->data;
86+
87+
if (cfg->window.min > cfg->window.max || cfg->window.max == 0) {
88+
return -EINVAL;
89+
}
90+
91+
if ((cfg->flags & ~WDT_RENESAS_RX_SUPPORTED_FLAGS) != 0) {
92+
return -ENOTSUP;
93+
}
94+
95+
if (cfg->callback != NULL && (cfg->flags & WDT_FLAG_RESET_MASK) != 0) {
96+
LOG_ERR("WDT_FLAG_RESET_NONE should be chosen in case of interrupt response");
97+
return -ENOTSUP;
98+
}
99+
100+
#ifdef CONFIG_WDT_RENESAS_RX_IWDT_AUTO_START_MODE
101+
/* In auto-start mode, IWDT is started by OFS setting when the chip is reset.
102+
* So timeout and other parameters must be configured via Kconfig.
103+
* Runtime configuration is ignored.
104+
*/
105+
if (CONFIG_WDT_RENESAS_RX_OFS0_IWDTRSTIRQS == 0) {
106+
if ((cfg->flags & WDT_FLAG_RESET_MASK) != WDT_FLAG_RESET_NONE) {
107+
LOG_ERR("Reset flag is not consistent with OFS setting");
108+
return -EINVAL;
109+
}
110+
} else if (CONFIG_WDT_RENESAS_RX_OFS0_IWDTRSTIRQS == 1) {
111+
if ((cfg->flags & WDT_FLAG_RESET_MASK) == WDT_FLAG_RESET_NONE) {
112+
LOG_ERR("Reset flag is not consistent with OFS setting");
113+
return -EINVAL;
114+
}
115+
} else {
116+
LOG_ERR("Invalid OFS setting");
117+
return -EINVAL;
118+
}
119+
120+
LOG_WRN("IWDT is auto-started by OFS. Timeout and other parameters must be configured via "
121+
"Kconfig, runtime configuration is ignored.");
122+
#ifdef CONFIG_WDT_RENESAS_RX_IWDT_USE_NMI
123+
data->callback = cfg->callback;
124+
#endif
125+
data->timeout_installed = true;
126+
return 0;
127+
#else /* CONFIG_WDT_RENESAS_RX_IWDT_AUTO_START_MODE */
128+
const struct wdt_iwdt_rx_config *iwdt_cfg = dev->config;
129+
uint32_t clock_rate;
130+
int ret;
131+
132+
/* Get the iwdt clock rate in hz */
133+
ret = clock_control_get_rate(iwdt_cfg->clock_dev, NULL, &clock_rate);
134+
if (ret != 0) {
135+
return ret;
136+
}
137+
138+
uint32_t clock_rate_khz = clock_rate / 1000;
139+
140+
const uint16_t timeout_period[4] = {128, 512, 1024, 2048};
141+
const uint16_t clock_divide[6][2] = {{IWDT_CLOCK_DIV_1, 1}, {IWDT_CLOCK_DIV_16, 16},
142+
{IWDT_CLOCK_DIV_32, 32}, {IWDT_CLOCK_DIV_64, 64},
143+
{IWDT_CLOCK_DIV_128, 128}, {IWDT_CLOCK_DIV_256, 256}};
144+
int16_t last_error = INT16_MAX;
145+
int32_t error;
146+
uint16_t iwdt_tops = 0;
147+
uint16_t iwdt_clock_div = 0;
148+
uint16_t iwdt_timeout = ((uint32_t)timeout_period[0] * clock_divide[0][1]) / clock_rate_khz;
149+
150+
for (int idx_p = 0; idx_p < 4; idx_p++) {
151+
for (int idx_d = 0; idx_d < 6; idx_d++) {
152+
iwdt_timeout = ((uint32_t)timeout_period[idx_p] * clock_divide[idx_d][1]) /
153+
clock_rate_khz;
154+
error = cfg->window.max - iwdt_timeout;
155+
156+
if (error < 0) {
157+
break;
158+
}
159+
160+
if (error < last_error) {
161+
last_error = error;
162+
iwdt_tops = idx_p;
163+
iwdt_clock_div = clock_divide[idx_d][0];
164+
}
165+
}
166+
}
167+
168+
data->iwdt_config.timeout = iwdt_tops;
169+
data->iwdt_config.iwdtclk_div = iwdt_clock_div;
170+
data->iwdt_config.window_start = IWDT_WINDOW_START;
171+
data->iwdt_config.window_end = IWDT_WINDOW_END;
172+
data->iwdt_config.timeout_control =
173+
(cfg->flags & WDT_FLAG_RESET_MASK) != 0 ? IWDT_TIMEOUT_RESET : IWDT_TIMEOUT_NMI;
174+
data->iwdt_config.count_stop_enable = IWDT_COUNT_STOP_DISABLE;
175+
176+
data->timeout_installed = true;
177+
178+
#if CONFIG_WDT_RENESAS_RX_IWDT_USE_NMI
179+
data->callback = cfg->callback;
180+
#endif
181+
return 0;
182+
183+
#endif /* CONFIG_WDT_RENESAS_RX_IWDT_AUTO_START_MODE */
184+
}
185+
186+
static int wdt_iwdt_rx_setup(const struct device *dev, uint8_t options)
187+
{
188+
struct wdt_iwdt_rx_data *data = dev->data;
189+
190+
if (!data->timeout_installed) {
191+
return -EINVAL;
192+
}
193+
194+
#ifdef CONFIG_WDT_RENESAS_RX_IWDT_USE_NMI
195+
nmi_enable(IWDT_NMI_VECTOR, (nmi_callback_t)wdt_iwdt_isr, (void *)dev);
196+
#endif /* CONFIG_WDT_RENESAS_RX_IWDT_USE_NMI */
197+
198+
#if CONFIG_WDT_RENESAS_RX_IWDT_AUTO_START_MODE
199+
LOG_WRN("WDT is configured in auto-start mode, setup via API is not supported");
200+
return 0;
201+
#else /* CONFIG_WDT_RENESAS_RX_IWDT_AUTO_START_MODE */
202+
203+
iwdt_err_t err;
204+
int ret;
205+
206+
if (options & WDT_OPT_PAUSE_HALTED_BY_DBG) {
207+
return -ENOTSUP;
208+
}
209+
210+
if (options & WDT_OPT_PAUSE_IN_SLEEP) {
211+
data->iwdt_config.count_stop_enable = IWDT_COUNT_STOP_ENABLE;
212+
} else {
213+
data->iwdt_config.count_stop_enable = IWDT_COUNT_STOP_DISABLE;
214+
}
215+
216+
err = R_IWDT_Open(&data->iwdt_config);
217+
if (err != IWDT_SUCCESS) {
218+
return -EINVAL;
219+
}
220+
221+
ret = wdt_iwdt_rx_feed(dev, 0);
222+
if (ret != 0) {
223+
return ret;
224+
}
225+
226+
return 0;
227+
#endif /* CONFIG_WDT_RENESAS_RX_IWDT_AUTO_START_MODE */
228+
}
229+
230+
static DEVICE_API(wdt, wdt_iwdt_rx_api) = {
231+
.disable = wdt_iwdt_rx_disable,
232+
.feed = wdt_iwdt_rx_feed,
233+
.install_timeout = wdt_iwdt_rx_install_timeout,
234+
.setup = wdt_iwdt_rx_setup,
235+
};
236+
237+
#define IWDT_RENESAS_RX_DEFINE(idx) \
238+
static struct wdt_iwdt_rx_config iwdt_rx_cfg##idx = { \
239+
.regs = (struct st_iwdt *)DT_REG_ADDR(IWDT_NODELABEL), \
240+
.clock_dev = DEVICE_DT_GET(DT_CLOCKS_CTLR(idx)), \
241+
}; \
242+
static struct wdt_iwdt_rx_data iwdt_rx_data##idx = { \
243+
.timeout_installed = false, \
244+
.iwdt_config = {.count_stop_enable = IWDT_COUNT_STOP_DISABLE, \
245+
.timeout_control = IWDT_TIMEOUT_RESET}, \
246+
}; \
247+
\
248+
DEVICE_DT_DEFINE(idx, NULL, NULL, &iwdt_rx_data##idx, &iwdt_rx_cfg##idx, POST_KERNEL, \
249+
CONFIG_KERNEL_INIT_PRIORITY_DEVICE, &wdt_iwdt_rx_api);
250+
251+
DT_FOREACH_STATUS_OKAY(renesas_rx_iwdt, IWDT_RENESAS_RX_DEFINE);
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
# Copyright (c) 2025 Renesas Electronics Corporation and/or its affiliates
2+
# SPDX-License-Identifier: Apache-2.0
3+
4+
description: Renesas RX Independent Watchdog
5+
6+
compatible: "renesas,rx-iwdt"
7+
8+
include: base.yaml
9+
10+
properties:
11+
reg:
12+
required: true
13+
14+
clocks:
15+
required: true
16+
17+
window-start:
18+
required: true
19+
type: int
20+
enum: [0, 0x1000, 0x2000, 0x3000]
21+
description: |
22+
The start of the window in clock cycles. The watchdog will reset the system
23+
if the watchdog is not refreshed within the window.
24+
- 0: 25%
25+
- 0x1000: 50%
26+
- 0x2000: 75%
27+
- 0x3000: 100% (window end position is not specified.)
28+
29+
window-end:
30+
required: true
31+
type: int
32+
enum: [0, 0x100, 0x200, 0x300]
33+
description: |
34+
The end of the window in clock cycles. The watchdog will reset the system
35+
if the watchdog is not refreshed within the window.
36+
- 0: 75%
37+
- 0x200: 50%
38+
- 0x200: 25%
39+
- 0x300: 0% (window start position is not specified.)

modules/Kconfig.renesas

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -356,4 +356,9 @@ config USE_RX_RDP_ADC
356356
help
357357
Enable RX RDP ADC driver
358358

359+
config USE_RX_RDP_IWDT
360+
bool
361+
help
362+
Enable RX RDP IWDT driver
363+
359364
endif # HAS_RENESAS_RX_RDP

0 commit comments

Comments
 (0)