drivers: watchdog: initial support for watchdog driver on Renesas RA family
First commit to add support for Renesas RA watchdog driver Signed-off-by: The Nguyen <the.nguyen.yf@renesas.com>
This commit is contained in:
parent
b72b271682
commit
710fb596df
6 changed files with 384 additions and 0 deletions
|
@ -56,5 +56,6 @@ zephyr_library_sources_ifdef(CONFIG_WDT_INTEL_ADSP wdt_intel_adsp.c wdt_dw_commo
|
||||||
zephyr_library_sources_ifdef(CONFIG_WDT_ANDES_ATCWDT200 wdt_andes_atcwdt200.c)
|
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_NXP_FS26 wdt_nxp_fs26.c)
|
||||||
zephyr_library_sources_ifdef(CONFIG_WDT_SHELL wdt_shell.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_USERSPACE wdt_handlers.c)
|
zephyr_library_sources_ifdef(CONFIG_USERSPACE wdt_handlers.c)
|
||||||
|
|
|
@ -147,4 +147,6 @@ source "drivers/watchdog/Kconfig.litex"
|
||||||
|
|
||||||
source "drivers/watchdog/Kconfig.rts5912"
|
source "drivers/watchdog/Kconfig.rts5912"
|
||||||
|
|
||||||
|
source "drivers/watchdog/Kconfig.renesas_ra"
|
||||||
|
|
||||||
endif # WATCHDOG
|
endif # WATCHDOG
|
||||||
|
|
29
drivers/watchdog/Kconfig.renesas_ra
Normal file
29
drivers/watchdog/Kconfig.renesas_ra
Normal file
|
@ -0,0 +1,29 @@
|
||||||
|
# Copyright (c) 2025 Renesas Electronics Corporation
|
||||||
|
# SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
|
config WDT_RENESAS_RA
|
||||||
|
bool "Watchdog Driver for Renesas RA family"
|
||||||
|
default y
|
||||||
|
depends on DT_HAS_RENESAS_RA_WDT_ENABLED
|
||||||
|
select HAS_WDT_DISABLE_AT_BOOT
|
||||||
|
select USE_RA_FSP_WDT
|
||||||
|
help
|
||||||
|
Enable watchdog driver for Renesas RA MCUs
|
||||||
|
|
||||||
|
if WDT_RENESAS_RA
|
||||||
|
|
||||||
|
config WDT_RENESAS_RA_NMI
|
||||||
|
bool "NMI interrupt enable"
|
||||||
|
default y
|
||||||
|
select RUNTIME_NMI
|
||||||
|
help
|
||||||
|
Watchdog timer generates NMI at value 0
|
||||||
|
|
||||||
|
config WDT_RENESAS_RA_START_IN_BOOT
|
||||||
|
bool "Start WDT in boot time"
|
||||||
|
default y
|
||||||
|
depends on !HAS_WDT_DISABLE_AT_BOOT
|
||||||
|
help
|
||||||
|
Start watchdog in boot time
|
||||||
|
|
||||||
|
endif # WDT_RENESAS_RA
|
332
drivers/watchdog/wdt_renesas_ra.c
Normal file
332
drivers/watchdog/wdt_renesas_ra.c
Normal file
|
@ -0,0 +1,332 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2025 Renesas Electronics Corporation
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <soc.h>
|
||||||
|
#include <zephyr/kernel.h>
|
||||||
|
#include <zephyr/drivers/clock_control/renesas_ra_cgc.h>
|
||||||
|
#include <zephyr/logging/log.h>
|
||||||
|
#include <zephyr/drivers/watchdog.h>
|
||||||
|
#include "r_wdt.h"
|
||||||
|
|
||||||
|
LOG_MODULE_REGISTER(wdt_renesas_ra, CONFIG_WDT_LOG_LEVEL);
|
||||||
|
|
||||||
|
struct wdt_renesas_ra_config {
|
||||||
|
const struct device *clock_dev;
|
||||||
|
const struct clock_control_ra_subsys_cfg clock_subsys;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct wdt_renesas_ra_data {
|
||||||
|
struct wdt_timeout_cfg timeout;
|
||||||
|
struct st_wdt_instance_ctrl wdt_ctrl;
|
||||||
|
struct st_wdt_cfg wdt_cfg;
|
||||||
|
struct k_mutex inst_lock;
|
||||||
|
atomic_t device_state;
|
||||||
|
};
|
||||||
|
|
||||||
|
#define WDT_RENESAS_RA_ATOMIC_ENABLE (0)
|
||||||
|
#define WDT_RENESAS_RA_ATOMIC_TIMEOUT_SET (1)
|
||||||
|
|
||||||
|
/* Lookup table for WDT period raw cycle */
|
||||||
|
const float timeout_period_lut[] = {
|
||||||
|
[WDT_TIMEOUT_128] = 128, [WDT_TIMEOUT_512] = 512, [WDT_TIMEOUT_1024] = 1024,
|
||||||
|
[WDT_TIMEOUT_2048] = 2048, [WDT_TIMEOUT_4096] = 4096, [WDT_TIMEOUT_8192] = 8192,
|
||||||
|
[WDT_TIMEOUT_16384] = 16384};
|
||||||
|
|
||||||
|
/* Lookup table for the division value of the input clock count */
|
||||||
|
const float clock_div_lut[] = {[WDT_CLOCK_DIVISION_1] = 1, [WDT_CLOCK_DIVISION_4] = 4,
|
||||||
|
[WDT_CLOCK_DIVISION_16] = 16, [WDT_CLOCK_DIVISION_32] = 32,
|
||||||
|
[WDT_CLOCK_DIVISION_64] = 64, [WDT_CLOCK_DIVISION_128] = 128,
|
||||||
|
[WDT_CLOCK_DIVISION_256] = 256, [WDT_CLOCK_DIVISION_512] = 512,
|
||||||
|
[WDT_CLOCK_DIVISION_2048] = 2048, [WDT_CLOCK_DIVISION_8192] = 8192};
|
||||||
|
|
||||||
|
#define WDT_WINDOW_INVALID (-1)
|
||||||
|
|
||||||
|
/* Lookup table for the window start position setting */
|
||||||
|
const int window_start_lut[] = {
|
||||||
|
[0] = WDT_WINDOW_START_100, [1] = WDT_WINDOW_START_75, [2] = WDT_WINDOW_START_50,
|
||||||
|
[3] = WDT_WINDOW_START_25, [4] = WDT_WINDOW_INVALID,
|
||||||
|
};
|
||||||
|
|
||||||
|
/* Lookup table for the window end position setting */
|
||||||
|
const int window_end_lut[] = {
|
||||||
|
[0] = WDT_WINDOW_INVALID, [1] = WDT_WINDOW_END_75, [2] = WDT_WINDOW_END_50,
|
||||||
|
[3] = WDT_WINDOW_END_25, [4] = WDT_WINDOW_END_0,
|
||||||
|
};
|
||||||
|
|
||||||
|
static inline void wdt_renesas_ra_inst_lock(const struct device *dev)
|
||||||
|
{
|
||||||
|
struct wdt_renesas_ra_data *data = dev->data;
|
||||||
|
|
||||||
|
k_mutex_lock(&data->inst_lock, K_FOREVER);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void wdt_renesas_ra_inst_unlock(const struct device *dev)
|
||||||
|
{
|
||||||
|
struct wdt_renesas_ra_data *data = dev->data;
|
||||||
|
|
||||||
|
k_mutex_unlock(&data->inst_lock);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int wdt_renesas_ra_timeout_calculate(const struct device *dev,
|
||||||
|
const struct wdt_timeout_cfg *config)
|
||||||
|
{
|
||||||
|
struct wdt_renesas_ra_data *data = dev->data;
|
||||||
|
const struct wdt_renesas_ra_config *cfg = dev->config;
|
||||||
|
struct st_wdt_cfg *p_cfg = &data->wdt_cfg;
|
||||||
|
unsigned int window_start_idx;
|
||||||
|
unsigned int window_end_idx;
|
||||||
|
unsigned int best_divisor = WDT_CLOCK_DIVISION_1;
|
||||||
|
unsigned int best_timeout = WDT_TIMEOUT_128;
|
||||||
|
unsigned int best_period_ms = UINT_MAX;
|
||||||
|
unsigned int min_delta = UINT_MAX;
|
||||||
|
uint32_t clock_rate;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
if (atomic_test_bit(&data->device_state, WDT_RENESAS_RA_ATOMIC_TIMEOUT_SET)) {
|
||||||
|
if (config->window.min != data->timeout.window.min ||
|
||||||
|
config->window.max != data->timeout.window.max ||
|
||||||
|
config->flags != data->timeout.flags) {
|
||||||
|
LOG_ERR("wdt support only one timeout setting value");
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
data->timeout.callback = config->callback;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = clock_control_get_rate(cfg->clock_dev, (clock_control_subsys_t)&cfg->clock_subsys,
|
||||||
|
&clock_rate);
|
||||||
|
if (unlikely(ret)) {
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (unsigned int divisor = WDT_CLOCK_DIVISION_1; divisor < ARRAY_SIZE(clock_div_lut);
|
||||||
|
divisor++) {
|
||||||
|
for (unsigned int timeout = WDT_TIMEOUT_128;
|
||||||
|
timeout < ARRAY_SIZE(timeout_period_lut); timeout++) {
|
||||||
|
unsigned int period_ms =
|
||||||
|
(unsigned int)(1000.0F * clock_div_lut[divisor] *
|
||||||
|
timeout_period_lut[timeout] / (float)clock_rate);
|
||||||
|
unsigned int delta = period_ms > config->window.max
|
||||||
|
? period_ms - config->window.max
|
||||||
|
: config->window.max - period_ms;
|
||||||
|
|
||||||
|
if (delta < min_delta) {
|
||||||
|
min_delta = delta;
|
||||||
|
best_divisor = divisor;
|
||||||
|
best_timeout = timeout;
|
||||||
|
best_period_ms = period_ms;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (min_delta == UINT_MAX) {
|
||||||
|
LOG_ERR("wdt timeout out of range");
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
window_start_idx = (config->window.min * 4 + best_period_ms - 1) / best_period_ms;
|
||||||
|
window_end_idx = (config->window.max * 4 + best_period_ms - 1) / best_period_ms;
|
||||||
|
|
||||||
|
if (window_start_lut[window_start_idx] == WDT_WINDOW_INVALID ||
|
||||||
|
window_end_lut[window_end_idx] == WDT_WINDOW_INVALID) {
|
||||||
|
LOG_ERR("this wdt timeout is not supported");
|
||||||
|
return -ENOTSUP;
|
||||||
|
}
|
||||||
|
|
||||||
|
p_cfg->clock_division = (wdt_clock_division_t)best_divisor;
|
||||||
|
p_cfg->timeout = (wdt_timeout_t)best_timeout;
|
||||||
|
p_cfg->window_start = (wdt_window_start_t)window_start_lut[window_start_idx];
|
||||||
|
p_cfg->window_end = (wdt_window_end_t)window_end_lut[window_end_idx];
|
||||||
|
|
||||||
|
LOG_INF("actual window min = %.2f ms", window_start_idx * best_period_ms * 0.25);
|
||||||
|
LOG_INF("actual window max = %.2f ms", window_end_idx * best_period_ms * 0.25);
|
||||||
|
|
||||||
|
memcpy(&data->timeout, config, sizeof(struct wdt_timeout_cfg));
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int wdt_renesas_ra_setup(const struct device *dev, uint8_t options)
|
||||||
|
{
|
||||||
|
struct wdt_renesas_ra_data *data = dev->data;
|
||||||
|
int ret = 0;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* TODO: WDT must be restarted with wdt_feed call after sleep -> will be added later when PM
|
||||||
|
* mode is supported
|
||||||
|
*/
|
||||||
|
if ((options & WDT_OPT_PAUSE_IN_SLEEP) != 0) {
|
||||||
|
LOG_ERR("wdt pause in sleep mode not supported");
|
||||||
|
return -ENOTSUP;
|
||||||
|
}
|
||||||
|
|
||||||
|
wdt_renesas_ra_inst_lock(dev);
|
||||||
|
|
||||||
|
if (atomic_test_bit(&data->device_state, WDT_RENESAS_RA_ATOMIC_ENABLE)) {
|
||||||
|
LOG_ERR("wdt has been already setup");
|
||||||
|
ret = -EBUSY;
|
||||||
|
goto end;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!atomic_test_bit(&data->device_state, WDT_RENESAS_RA_ATOMIC_TIMEOUT_SET)) {
|
||||||
|
LOG_ERR("wdt timeout should be installed before");
|
||||||
|
ret = -EFAULT;
|
||||||
|
goto end;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Pause watchdog timer when CPU is halted by the debugger. */
|
||||||
|
R_DEBUG->DBGSTOPCR_b.DBGSTOP_WDT = (options & WDT_OPT_PAUSE_HALTED_BY_DBG) != 0 ? 1 : 0;
|
||||||
|
|
||||||
|
if (R_WDT_Open(&data->wdt_ctrl, &data->wdt_cfg) != FSP_SUCCESS) {
|
||||||
|
LOG_ERR("wdt setup failed");
|
||||||
|
ret = -EIO;
|
||||||
|
goto end;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (R_WDT_Refresh(&data->wdt_ctrl) != FSP_SUCCESS) {
|
||||||
|
LOG_ERR("wdt start failed");
|
||||||
|
ret = -EIO;
|
||||||
|
goto end;
|
||||||
|
}
|
||||||
|
|
||||||
|
atomic_set_bit(&data->device_state, WDT_RENESAS_RA_ATOMIC_ENABLE);
|
||||||
|
|
||||||
|
end:
|
||||||
|
wdt_renesas_ra_inst_unlock(dev);
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int wdt_renesas_ra_disable(const struct device *dev)
|
||||||
|
{
|
||||||
|
struct wdt_renesas_ra_data *data = dev->data;
|
||||||
|
|
||||||
|
if (!atomic_test_bit(&data->device_state, WDT_RENESAS_RA_ATOMIC_ENABLE)) {
|
||||||
|
LOG_ERR("wdt has not been enabled yet");
|
||||||
|
return -EFAULT;
|
||||||
|
}
|
||||||
|
|
||||||
|
LOG_ERR("wdt can not be stopped once it has started");
|
||||||
|
return -EPERM;
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef CONFIG_WDT_RENESAS_RA_NMI
|
||||||
|
void wdt_renesas_ra_callback_adapter(wdt_callback_args_t *p_args)
|
||||||
|
{
|
||||||
|
const struct device *dev = p_args->p_context;
|
||||||
|
struct wdt_renesas_ra_data *data = dev->data;
|
||||||
|
wdt_callback_t callback = data->timeout.callback;
|
||||||
|
|
||||||
|
if (callback != NULL) {
|
||||||
|
callback(dev, 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif /* CONFIG_WDT_RENESAS_RA_NMI */
|
||||||
|
|
||||||
|
#define WDT_RENESAS_RA_SUPPORTED_FLAGS (WDT_FLAG_RESET_NONE | WDT_FLAG_RESET_SOC)
|
||||||
|
|
||||||
|
static int wdt_renesas_ra_install_timeout(const struct device *dev,
|
||||||
|
const struct wdt_timeout_cfg *config)
|
||||||
|
{
|
||||||
|
struct wdt_renesas_ra_data *data = dev->data;
|
||||||
|
struct st_wdt_cfg *p_config = &data->wdt_cfg;
|
||||||
|
int ret = 0;
|
||||||
|
|
||||||
|
if (config->window.min > config->window.max || config->window.max == 0) {
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((config->flags & ~WDT_RENESAS_RA_SUPPORTED_FLAGS) != 0) {
|
||||||
|
return -ENOTSUP;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (config->callback == NULL && (config->flags & WDT_FLAG_RESET_MASK) == 0) {
|
||||||
|
LOG_ERR("no timeout response was chosen");
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (config->callback != NULL && (config->flags & WDT_FLAG_RESET_MASK) != 0) {
|
||||||
|
LOG_ERR("WDT_FLAG_RESET_NONE should be chosen in case of interrupt response");
|
||||||
|
return -ENOTSUP;
|
||||||
|
}
|
||||||
|
|
||||||
|
wdt_renesas_ra_inst_lock(dev);
|
||||||
|
|
||||||
|
if (atomic_test_bit(&data->device_state, WDT_RENESAS_RA_ATOMIC_ENABLE)) {
|
||||||
|
LOG_ERR("cannot change timeout settings after wdt setup");
|
||||||
|
ret = -EBUSY;
|
||||||
|
goto end;
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifndef CONFIG_WDT_RENESAS_RA_NMI
|
||||||
|
if (config->callback != NULL) {
|
||||||
|
LOG_ERR("interrupt response only available in case CONFIG_WDT_RENESAS_RA_NMI=y");
|
||||||
|
ret = -ENOTSUP;
|
||||||
|
goto end;
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
p_config->reset_control = (config->flags & WDT_FLAG_RESET_MASK) != 0
|
||||||
|
? WDT_RESET_CONTROL_RESET
|
||||||
|
: WDT_RESET_CONTROL_NMI;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
ret = wdt_renesas_ra_timeout_calculate(dev, config);
|
||||||
|
if (ret < 0) {
|
||||||
|
goto end;
|
||||||
|
}
|
||||||
|
|
||||||
|
atomic_set_bit(&data->device_state, WDT_RENESAS_RA_ATOMIC_TIMEOUT_SET);
|
||||||
|
LOG_INF("wdt timeout was set successfully");
|
||||||
|
|
||||||
|
end:
|
||||||
|
wdt_renesas_ra_inst_unlock(dev);
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int wdt_renesas_ra_feed(const struct device *dev, int channel_id)
|
||||||
|
{
|
||||||
|
struct wdt_renesas_ra_data *data = dev->data;
|
||||||
|
|
||||||
|
if (!atomic_test_bit(&data->device_state, WDT_RENESAS_RA_ATOMIC_ENABLE) ||
|
||||||
|
channel_id != 0) {
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (R_WDT_Refresh(&data->wdt_ctrl) != FSP_SUCCESS) {
|
||||||
|
return -EIO;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static DEVICE_API(wdt, wdt_renesas_ra_api) = {
|
||||||
|
.setup = wdt_renesas_ra_setup,
|
||||||
|
.disable = wdt_renesas_ra_disable,
|
||||||
|
.install_timeout = wdt_renesas_ra_install_timeout,
|
||||||
|
.feed = wdt_renesas_ra_feed,
|
||||||
|
};
|
||||||
|
|
||||||
|
#define WDT_RENESAS_RA_DEFINE(id) \
|
||||||
|
static struct wdt_renesas_ra_config wdt_renesas_ra_cfg##id = { \
|
||||||
|
.clock_dev = DEVICE_DT_GET(DT_CLOCKS_CTLR(id)), \
|
||||||
|
.clock_subsys = {.mstp = DT_CLOCKS_CELL(id, mstp), \
|
||||||
|
.stop_bit = DT_CLOCKS_CELL(id, stop_bit)}, \
|
||||||
|
}; \
|
||||||
|
\
|
||||||
|
static struct wdt_renesas_ra_data wdt_renesas_ra_data##id = { \
|
||||||
|
.device_state = ATOMIC_INIT(0), \
|
||||||
|
.wdt_cfg = {.stop_control = WDT_STOP_CONTROL_DISABLE, \
|
||||||
|
.reset_control = WDT_RESET_CONTROL_RESET, \
|
||||||
|
.p_callback = wdt_renesas_ra_callback_adapter, \
|
||||||
|
.p_context = DEVICE_DT_GET(id)}, \
|
||||||
|
}; \
|
||||||
|
\
|
||||||
|
DEVICE_DT_DEFINE(id, NULL, NULL, &wdt_renesas_ra_data##id, &wdt_renesas_ra_cfg##id, \
|
||||||
|
POST_KERNEL, CONFIG_KERNEL_INIT_PRIORITY_DEVICE, &wdt_renesas_ra_api);
|
||||||
|
|
||||||
|
DT_FOREACH_STATUS_OKAY(renesas_ra_wdt, WDT_RENESAS_RA_DEFINE)
|
15
dts/bindings/watchdog/renesas,ra-wdt.yaml
Normal file
15
dts/bindings/watchdog/renesas,ra-wdt.yaml
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
# Copyright (c) 2025 Renesas Electronics Corporation
|
||||||
|
# SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
|
description: Renesas RA Watchdog (wdt)
|
||||||
|
|
||||||
|
compatible: "renesas,ra-wdt"
|
||||||
|
|
||||||
|
include: base.yaml
|
||||||
|
|
||||||
|
properties:
|
||||||
|
reg:
|
||||||
|
required: true
|
||||||
|
|
||||||
|
clocks:
|
||||||
|
required: true
|
|
@ -159,6 +159,11 @@ config USE_RA_FSP_ACMPHS
|
||||||
help
|
help
|
||||||
Enable RA FSP ACMPHS driver
|
Enable RA FSP ACMPHS driver
|
||||||
|
|
||||||
|
config USE_RA_FSP_WDT
|
||||||
|
bool
|
||||||
|
help
|
||||||
|
Enable RA FSP WDT driver
|
||||||
|
|
||||||
endif # HAS_RENESAS_RA_FSP
|
endif # HAS_RENESAS_RA_FSP
|
||||||
|
|
||||||
if HAS_RENESAS_RZ_FSP
|
if HAS_RENESAS_RZ_FSP
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue