From c6198008f55f6dab08f16f7e5735633fac54c1d1 Mon Sep 17 00:00:00 2001 From: Sai Santhosh Malae Date: Thu, 13 Feb 2025 12:57:04 +0530 Subject: [PATCH] drivers: watchdog: siwx91x: Add siwx91x WDT driver Implement Watchdog driver for siwx91x device Signed-off-by: Sai Santhosh Malae --- .../siwx917_rb4338a/siwx917_rb4338a.yaml | 1 + .../clock_control_silabs_siwx91x.c | 12 + drivers/watchdog/CMakeLists.txt | 1 + drivers/watchdog/Kconfig | 2 + drivers/watchdog/Kconfig.siwx91x | 9 + drivers/watchdog/wdt_silabs_siwx91x.c | 270 ++++++++++++++++++ dts/arm/silabs/siwg917.dtsi | 11 + dts/bindings/watchdog/silabs,siwx91x-wdt.yaml | 12 + .../dt-bindings/clock/silabs/siwx91x-clock.h | 1 + modules/hal_silabs/wiseconnect/CMakeLists.txt | 3 + .../watchdog/boards/siwx917_rb4338a.overlay | 15 + .../boards/siwx917_rb4338a.overlay | 9 + .../watchdog/wdt_basic_api/src/test_wdt.c | 2 + 13 files changed, 348 insertions(+) create mode 100644 drivers/watchdog/Kconfig.siwx91x create mode 100644 drivers/watchdog/wdt_silabs_siwx91x.c create mode 100644 dts/bindings/watchdog/silabs,siwx91x-wdt.yaml create mode 100644 samples/drivers/watchdog/boards/siwx917_rb4338a.overlay create mode 100644 tests/drivers/watchdog/wdt_basic_api/boards/siwx917_rb4338a.overlay diff --git a/boards/silabs/radio_boards/siwx917_rb4338a/siwx917_rb4338a.yaml b/boards/silabs/radio_boards/siwx917_rb4338a/siwx917_rb4338a.yaml index 2a57439c4fa..29a0ddccea5 100644 --- a/boards/silabs/radio_boards/siwx917_rb4338a/siwx917_rb4338a.yaml +++ b/boards/silabs/radio_boards/siwx917_rb4338a/siwx917_rb4338a.yaml @@ -15,5 +15,6 @@ supported: - gpio - i2c - pwm + - watchdog - wifi vendor: silabs diff --git a/drivers/clock_control/clock_control_silabs_siwx91x.c b/drivers/clock_control/clock_control_silabs_siwx91x.c index 3d89921b332..b9247268e80 100644 --- a/drivers/clock_control/clock_control_silabs_siwx91x.c +++ b/drivers/clock_control/clock_control_silabs_siwx91x.c @@ -12,10 +12,13 @@ #include "rsi_power_save.h" #include "rsi_rom_ulpss_clk.h" #include "rsi_rom_clks.h" +#include "rsi_sysrtc.h" #include "clock_update.h" #include "sl_si91x_clock_manager.h" #define DT_DRV_COMPAT silabs_siwx91x_clock +#define DT_DRV_COMPAT silabs_siwx91x_clock +#define LF_FSM_CLOCK_FREQUENCY 32768 LOG_MODULE_REGISTER(siwx91x_clock, CONFIG_CLOCK_CONTROL_LOG_LEVEL); @@ -68,6 +71,12 @@ static int siwx91x_clock_on(const struct device *dev, clock_control_subsys_t sys RSI_PS_M4ssPeriPowerUp(M4SS_PWRGATE_ULP_EFUSE_PERI); RSI_CLK_PeripheralClkEnable(M4CLK, PWM_CLK, ENABLE_STATIC_CLK); break; + case SIWX91X_CLK_WATCHDOG: + /* Both SYSRTC and WDT are clocked using LF-FSM XTAL which is initialized in + * SystemCoreClockUpdate(). This function allows clock to stabilize before use. + */ + rsi_sysrtc_clk_set(RSI_SYSRTC_CLK_32kHz_Xtal, 0); + break; default: return -EINVAL; } @@ -129,6 +138,9 @@ static int siwx91x_clock_get_rate(const struct device *dev, clock_control_subsys /* PWM peripheral operates at the system clock frequency */ *rate = CONFIG_SYS_CLOCK_HW_CYCLES_PER_SEC; return 0; + case SIWX91X_CLK_WATCHDOG: + *rate = LF_FSM_CLOCK_FREQUENCY; + return 0; default: /* For now, no other driver need clock rate */ return -EINVAL; diff --git a/drivers/watchdog/CMakeLists.txt b/drivers/watchdog/CMakeLists.txt index ce8144d5a37..38b224e38a2 100644 --- a/drivers/watchdog/CMakeLists.txt +++ b/drivers/watchdog/CMakeLists.txt @@ -36,6 +36,7 @@ zephyr_library_sources_ifdef(CONFIG_WDT_SAM wdt_sam.c) zephyr_library_sources_ifdef(CONFIG_WDT_SAM4L wdt_sam4l.c) zephyr_library_sources_ifdef(CONFIG_WDT_SAM0 wdt_sam0.c) zephyr_library_sources_ifdef(CONFIG_WDT_SIFIVE wdt_sifive.c) +zephyr_library_sources_ifdef(CONFIG_WDT_SILABS_SIWX91X wdt_silabs_siwx91x.c) zephyr_library_sources_ifdef(CONFIG_WDT_TCO wdt_tco.c) zephyr_library_sources_ifdef(CONFIG_WDT_XEC wdt_mchp_xec.c) zephyr_library_sources_ifdef(CONFIG_WDT_COUNTER wdt_counter.c) diff --git a/drivers/watchdog/Kconfig b/drivers/watchdog/Kconfig index 605cfa02c6c..1fb98e81a0c 100644 --- a/drivers/watchdog/Kconfig +++ b/drivers/watchdog/Kconfig @@ -87,6 +87,8 @@ source "drivers/watchdog/Kconfig.xec" source "drivers/watchdog/Kconfig.gecko" +source "drivers/watchdog/Kconfig.siwx91x" + source "drivers/watchdog/Kconfig.sifive" source "drivers/watchdog/Kconfig.npcx" diff --git a/drivers/watchdog/Kconfig.siwx91x b/drivers/watchdog/Kconfig.siwx91x new file mode 100644 index 00000000000..d2441a3b274 --- /dev/null +++ b/drivers/watchdog/Kconfig.siwx91x @@ -0,0 +1,9 @@ +# Copyright (c) 2025 Silicon Laboratories Inc. +# SPDX-License-Identifier: Apache-2.0 + +config WDT_SILABS_SIWX91X + bool "Silabs SiWx91x Watchdog driver" + default y + depends on DT_HAS_SILABS_SIWX91X_WDT_ENABLED + help + Enable the Watchdog driver for the Silabs SiWx91x SoC series. diff --git a/drivers/watchdog/wdt_silabs_siwx91x.c b/drivers/watchdog/wdt_silabs_siwx91x.c new file mode 100644 index 00000000000..4da0813ed5c --- /dev/null +++ b/drivers/watchdog/wdt_silabs_siwx91x.c @@ -0,0 +1,270 @@ +/* + * Copyright (c) 2025 Silicon Laboratories Inc. + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "rsi_wwdt.h" +#include "rsi_sysrtc.h" + +#define DT_DRV_COMPAT silabs_siwx91x_wdt +#define SIWX91X_WDT_SYSTEM_RESET_TIMER_MASK 0x0000001F + +struct siwx91x_wdt_config { + /* WDT register base address */ + MCU_WDT_Type *reg; + /* Pointer to the clock device structure */ + const struct device *clock_dev; + /* Clock control subsystem */ + clock_control_subsys_t clock_subsys; + /* Function pointer for the IRQ (Interrupt Request) configuration */ + void (*irq_config)(void); +}; + +struct siwx91x_wdt_data { + /* Callback function to be called on watchdog timer events */ + wdt_callback_t callback; + /* WDT operating clock (LF-FSM) frequency */ + uint32_t clock_frequency; + /* Timer system reset duration in ms */ + uint8_t delay_reset; + /* Timer interrupt duration in ms */ + uint8_t delay_irq; + /* Flag indicating the timeout install status */ + bool timeout_install_status; + /* Flag indicating the setup status */ + bool setup_status; +}; + +/* Function to get the delay in milliseconds from the register value */ +static uint32_t siwx91x_wdt_delay_from_hw(uint8_t value, int clock_frequency) +{ + uint32_t ticks = BIT(value); + float timeout = (float)ticks / clock_frequency; + + timeout *= 1000; + /* Return the timeout value as an unsigned 32-bit integer in milliseconds */ + return (uint32_t)timeout; +} + +/* Function to get the register value from the delay in milliseconds */ +static uint8_t siwx91x_wdt_delay_to_hw(uint32_t delay, int clock_frequency) +{ + /* reg_value = log((timeout * clock_frequency)/1000)base2 */ + float value = ((float)delay * (float)clock_frequency) / 1000; + float result = log2f(value); + + /* Round the result to nearest integer */ + result = roundf(result); + + return (uint8_t)result; +} + +static int siwx91x_wdt_install_timeout(const struct device *dev, const struct wdt_timeout_cfg *cfg) +{ + struct siwx91x_wdt_data *data = dev->data; + + /* Check the WDT setup status */ + if (data->setup_status) { + /* WDT setup is already done */ + return -EBUSY; + } + + /* Check the WDT timeout status */ + if (data->timeout_install_status) { + /* Only single timeout can be installed */ + return -ENOMEM; + } + + if (cfg->window.max > siwx91x_wdt_delay_from_hw(SIWX91X_WDT_SYSTEM_RESET_TIMER_MASK, + data->clock_frequency) || + cfg->window.max == 0) { + /* Requested value is out of range */ + return -EINVAL; + } + + if (cfg->window.min > 0) { + /* This feature is currently not supported */ + return -ENOTSUP; + } + + switch (cfg->flags) { + case WDT_FLAG_RESET_SOC: + case WDT_FLAG_RESET_CPU_CORE: + if (cfg->callback != NULL) { + /* Callback is not supported for reset flags */ + return -ENOTSUP; + } + data->delay_reset = siwx91x_wdt_delay_to_hw(cfg->window.max, data->clock_frequency); + /* During a system or CPU core reset, interrupts are not needed. Thus, we set + * the interrupt time to 0 to ensure no interrupts occur while resetting. + */ + data->delay_irq = 0; + /* Mask the WWDT interrupt */ + RSI_WWDT_IntrMask(); + break; + + case WDT_FLAG_RESET_NONE: + /* Set the reset time to maximum value */ + data->delay_reset = SIWX91X_WDT_SYSTEM_RESET_TIMER_MASK; + data->delay_irq = siwx91x_wdt_delay_to_hw(cfg->window.max, data->clock_frequency); + if (cfg->callback != NULL) { + data->callback = cfg->callback; + } + break; + + default: + /* Unsupported WDT config options */ + return -ENOTSUP; + } + + data->timeout_install_status = true; + return 0; +} + +/* Function to setup and start WDT */ +static int siwx91x_wdt_setup(const struct device *dev, uint8_t options) +{ + const struct siwx91x_wdt_config *config = dev->config; + struct siwx91x_wdt_data *data = dev->data; + + /* Check the WDT setup status */ + if (data->setup_status) { + /* WDT is already running */ + return -EBUSY; + } + + /* Check the WDT timeout status */ + if (!data->timeout_install_status) { + /* Timeout need to be set before setup */ + return -ENOTSUP; + } + + if (options & (WDT_OPT_PAUSE_IN_SLEEP)) { + return -ENOTSUP; + } + + RSI_WWDT_ConfigSysRstTimer(config->reg, data->delay_reset); + RSI_WWDT_ConfigIntrTimer(config->reg, data->delay_irq); + + RSI_WWDT_Start(config->reg); + + data->setup_status = true; + + return 0; +} + +static int siwx91x_wdt_disable(const struct device *dev) +{ + const struct siwx91x_wdt_config *config = dev->config; + struct siwx91x_wdt_data *data = dev->data; + + if (!data->timeout_install_status) { + /* No timeout installed */ + return -EFAULT; + } + + RSI_WWDT_Disable(config->reg); + + data->timeout_install_status = false; + data->setup_status = false; + + return 0; +} + +static int siwx91x_wdt_feed(const struct device *dev, int channel_id) +{ + const struct siwx91x_wdt_config *config = dev->config; + struct siwx91x_wdt_data *data = dev->data; + + if (!(data->timeout_install_status && data->setup_status)) { + /* WDT is not configured */ + return -EINVAL; + } + + if (channel_id != 0) { + /* Channel id must be 0 */ + return -EINVAL; + } + + RSI_WWDT_ReStart(config->reg); + return 0; +} + +static void siwx91x_wdt_isr(const struct device *dev) +{ + const struct siwx91x_wdt_config *config = dev->config; + struct siwx91x_wdt_data *data = dev->data; + + /* Clear WDT interrupt */ + RSI_WWDT_IntrClear(); + + if (data->delay_irq) { + /* Restart the timer */ + RSI_WWDT_ReStart(config->reg); + } + + if (data->callback != NULL) { + data->callback(dev, 0); + } +} + +static int siwx91x_wdt_init(const struct device *dev) +{ + const struct siwx91x_wdt_config *config = dev->config; + struct siwx91x_wdt_data *data = dev->data; + int ret; + + ret = clock_control_on(config->clock_dev, config->clock_subsys); + if (ret) { + return ret; + } + ret = clock_control_get_rate(config->clock_dev, config->clock_subsys, + &data->clock_frequency); + if (ret) { + return ret; + } + + RSI_WWDT_Init(config->reg); + + config->irq_config(); + RSI_WWDT_IntrUnMask(); + + return 0; +} + +static DEVICE_API(wdt, siwx91x_wdt_driver_api) = { + .setup = siwx91x_wdt_setup, + .disable = siwx91x_wdt_disable, + .install_timeout = siwx91x_wdt_install_timeout, + .feed = siwx91x_wdt_feed, +}; + +#define siwx91x_WDT_INIT(inst) \ + static struct siwx91x_wdt_data siwx91x_wdt_data_##inst; \ + static void siwx91x_wdt_irq_configure_##inst(void) \ + { \ + IRQ_CONNECT(DT_INST_IRQ(inst, irq), DT_INST_IRQ(inst, priority), siwx91x_wdt_isr, \ + DEVICE_DT_INST_GET(inst), 0); \ + irq_enable(DT_INST_IRQ(inst, irq)); \ + } \ + static const struct siwx91x_wdt_config siwx91x_wdt_config_##inst = { \ + .reg = (MCU_WDT_Type *)DT_INST_REG_ADDR(inst), \ + .clock_dev = DEVICE_DT_GET(DT_INST_CLOCKS_CTLR(inst)), \ + .clock_subsys = (clock_control_subsys_t)DT_INST_PHA(inst, clocks, clkid), \ + .irq_config = siwx91x_wdt_irq_configure_##inst, \ + }; \ + DEVICE_DT_INST_DEFINE(inst, &siwx91x_wdt_init, NULL, &siwx91x_wdt_data_##inst, \ + &siwx91x_wdt_config_##inst, PRE_KERNEL_1, \ + CONFIG_KERNEL_INIT_PRIORITY_DEFAULT, &siwx91x_wdt_driver_api); + +DT_INST_FOREACH_STATUS_OKAY(siwx91x_WDT_INIT) diff --git a/dts/arm/silabs/siwg917.dtsi b/dts/arm/silabs/siwg917.dtsi index 6fa3e202f9c..82ea3f4fd3e 100644 --- a/dts/arm/silabs/siwg917.dtsi +++ b/dts/arm/silabs/siwg917.dtsi @@ -274,6 +274,17 @@ silabs,ch_prescaler = <64 64 64 64>; status = "disabled"; }; + + watchdog: wdt@24048300 { + compatible = "silabs,siwx91x-wdt"; + #address-cells = <1>; + #size-cells = <0>; + reg = <0x24048300 0x1C>; + interrupts = <20 0>; + interrupt-names = "watchdog"; + clocks = <&clock0 SIWX91X_CLK_WATCHDOG>; + status = "disabled"; + }; }; }; diff --git a/dts/bindings/watchdog/silabs,siwx91x-wdt.yaml b/dts/bindings/watchdog/silabs,siwx91x-wdt.yaml new file mode 100644 index 00000000000..5c65a383f9a --- /dev/null +++ b/dts/bindings/watchdog/silabs,siwx91x-wdt.yaml @@ -0,0 +1,12 @@ +description: Silabs SiWx91x Watchdog node + +compatible: "silabs,siwx91x-wdt" + +include: base.yaml + +properties: + reg: + required: true + + interrupts: + required: true diff --git a/include/zephyr/dt-bindings/clock/silabs/siwx91x-clock.h b/include/zephyr/dt-bindings/clock/silabs/siwx91x-clock.h index 21042b215c9..a5c51fec2bb 100644 --- a/include/zephyr/dt-bindings/clock/silabs/siwx91x-clock.h +++ b/include/zephyr/dt-bindings/clock/silabs/siwx91x-clock.h @@ -12,6 +12,7 @@ #define SIWX91X_CLK_I2C0 5 #define SIWX91X_CLK_I2C1 6 #define SIWX91X_CLK_DMA0 7 +#define SIWX91X_CLK_WATCHDOG 8 #define SIWX91X_CLK_PWM 9 #endif diff --git a/modules/hal_silabs/wiseconnect/CMakeLists.txt b/modules/hal_silabs/wiseconnect/CMakeLists.txt index 1bca4a528d8..a67820af5e1 100644 --- a/modules/hal_silabs/wiseconnect/CMakeLists.txt +++ b/modules/hal_silabs/wiseconnect/CMakeLists.txt @@ -25,6 +25,7 @@ zephyr_include_directories( ${WISECONNECT_DIR}/components/board/silabs/inc ${WISECONNECT_DIR}/components/device/silabs/si91x/mcu/core/config ${WISECONNECT_DIR}/components/device/silabs/si91x/mcu/core/chip/inc + ${WISECONNECT_DIR}/components/device/silabs/si91x/mcu/core/chip/config ${WISECONNECT_DIR}/components/device/silabs/si91x/mcu/drivers/rom_driver/inc ${WISECONNECT_DIR}/components/device/silabs/si91x/mcu/drivers/peripheral_drivers/inc ${WISECONNECT_DIR}/components/device/silabs/si91x/mcu/drivers/service/clock_manager/inc @@ -42,9 +43,11 @@ zephyr_library_sources( ${WISECONNECT_DIR}/components/device/silabs/si91x/mcu/core/chip/src/system_si91x.c ${WISECONNECT_DIR}/components/device/silabs/si91x/mcu/drivers/peripheral_drivers/src/clock_update.c ${WISECONNECT_DIR}/components/device/silabs/si91x/mcu/drivers/peripheral_drivers/src/rsi_pwm.c + ${WISECONNECT_DIR}/components/device/silabs/si91x/mcu/drivers/peripheral_drivers/src/rsi_sysrtc.c ${WISECONNECT_DIR}/components/device/silabs/si91x/mcu/drivers/systemlevel/src/rsi_ipmu.c ${WISECONNECT_DIR}/components/device/silabs/si91x/mcu/drivers/systemlevel/src/rsi_pll.c ${WISECONNECT_DIR}/components/device/silabs/si91x/mcu/drivers/systemlevel/src/rsi_ulpss_clk.c + ${WISECONNECT_DIR}/components/device/silabs/si91x/mcu/drivers/systemlevel/src/rsi_wwdt.c ${WISECONNECT_DIR}/components/device/silabs/si91x/mcu/drivers/service/clock_manager/src/sl_si91x_clock_manager.c ${WISECONNECT_DIR}/components/device/silabs/si91x/mcu/drivers/unified_api/src/sl_si91x_driver_gpio.c ${WISECONNECT_DIR}/components/device/silabs/si91x/mcu/drivers/unified_api/src/sl_si91x_pwm.c diff --git a/samples/drivers/watchdog/boards/siwx917_rb4338a.overlay b/samples/drivers/watchdog/boards/siwx917_rb4338a.overlay new file mode 100644 index 00000000000..bccfe1a3d69 --- /dev/null +++ b/samples/drivers/watchdog/boards/siwx917_rb4338a.overlay @@ -0,0 +1,15 @@ +/* + * Copyright (c) 2025 Silicon Laboratories Inc. + * + * SPDX-License-Identifier: Apache-2.0 + */ + +/ { + aliases { + watchdog0 = &watchdog; + }; +}; + +&watchdog { + status = "okay"; +}; diff --git a/tests/drivers/watchdog/wdt_basic_api/boards/siwx917_rb4338a.overlay b/tests/drivers/watchdog/wdt_basic_api/boards/siwx917_rb4338a.overlay new file mode 100644 index 00000000000..25e5763dc59 --- /dev/null +++ b/tests/drivers/watchdog/wdt_basic_api/boards/siwx917_rb4338a.overlay @@ -0,0 +1,9 @@ +/* + * Copyright (c) 2025 Silicon Laboratories Inc. + * + * SPDX-License-Identifier: Apache-2.0 + */ + +&watchdog { + status = "okay"; +}; diff --git a/tests/drivers/watchdog/wdt_basic_api/src/test_wdt.c b/tests/drivers/watchdog/wdt_basic_api/src/test_wdt.c index 6f4fd727d3b..38cfeb58902 100644 --- a/tests/drivers/watchdog/wdt_basic_api/src/test_wdt.c +++ b/tests/drivers/watchdog/wdt_basic_api/src/test_wdt.c @@ -98,6 +98,8 @@ #define WDT_NODE DT_INST(0, gd_gd32_fwdgt) #elif DT_HAS_COMPAT_STATUS_OKAY(zephyr_counter_watchdog) #define WDT_NODE DT_COMPAT_GET_ANY_STATUS_OKAY(zephyr_counter_watchdog) +#elif DT_HAS_COMPAT_STATUS_OKAY(silabs_siwx91x_wdt) +#define WDT_NODE DT_INST(0, silabs_siwx91x_wdt) #elif DT_HAS_COMPAT_STATUS_OKAY(nuvoton_numaker_wwdt) #define WDT_NODE DT_INST(0, nuvoton_numaker_wwdt) #define TIMEOUTS 1