From 41bc8efbee2724b8b4ae36f3a9a36fbcb4ae90fe Mon Sep 17 00:00:00 2001 From: Ruibin Chang Date: Mon, 14 Apr 2025 10:34:21 +0800 Subject: [PATCH] drivers/watchdog/it51xxx: implement watchdog driver Implement watchdog driver for ITE it51xxx series chip. Signed-off-by: Ruibin Chang --- boards/ite/it515xx_evb/it515xx_evb.dts | 1 + drivers/watchdog/CMakeLists.txt | 1 + drivers/watchdog/Kconfig | 2 + drivers/watchdog/Kconfig.it51xxx | 29 ++ drivers/watchdog/wdt_ite_it51xxx.c | 322 ++++++++++++++++++ .../watchdog/ite,it51xxx-watchdog.yaml | 15 + dts/riscv/ite/it51xxx.dtsi | 7 + 7 files changed, 377 insertions(+) create mode 100644 drivers/watchdog/Kconfig.it51xxx create mode 100644 drivers/watchdog/wdt_ite_it51xxx.c create mode 100644 dts/bindings/watchdog/ite,it51xxx-watchdog.yaml diff --git a/boards/ite/it515xx_evb/it515xx_evb.dts b/boards/ite/it515xx_evb/it515xx_evb.dts index ea147be17f7..df5e0b3e83c 100644 --- a/boards/ite/it515xx_evb/it515xx_evb.dts +++ b/boards/ite/it515xx_evb/it515xx_evb.dts @@ -16,6 +16,7 @@ aliases { led0 = &led0; + watchdog0 = &twd0; }; chosen { diff --git a/drivers/watchdog/CMakeLists.txt b/drivers/watchdog/CMakeLists.txt index 79a5649b839..2c5140eea9a 100644 --- a/drivers/watchdog/CMakeLists.txt +++ b/drivers/watchdog/CMakeLists.txt @@ -17,6 +17,7 @@ zephyr_library_sources_ifdef(CONFIG_WDT_CC13XX_CC26XX wdt_cc13xx_cc26xx.c) zephyr_library_sources_ifdef(CONFIG_WDT_ESP32 wdt_esp32.c) zephyr_library_sources_ifdef(CONFIG_WDT_XT_ESP32 xt_wdt_esp32.c) zephyr_library_sources_ifdef(CONFIG_WDT_GECKO wdt_gecko.c) +zephyr_library_sources_ifdef(CONFIG_WDT_ITE_IT51XXX wdt_ite_it51xxx.c) zephyr_library_sources_ifdef(CONFIG_WDT_ITE_IT8XXX2 wdt_ite_it8xxx2.c) zephyr_library_sources_ifdef(CONFIG_WDT_LITEX wdt_litex.c) zephyr_library_sources_ifdef(CONFIG_WDT_MAX32 wdt_max32.c) diff --git a/drivers/watchdog/Kconfig b/drivers/watchdog/Kconfig index 30d623e22ad..6bb30ff61b4 100644 --- a/drivers/watchdog/Kconfig +++ b/drivers/watchdog/Kconfig @@ -97,6 +97,8 @@ source "drivers/watchdog/Kconfig.cc32xx" source "drivers/watchdog/Kconfig.cc13xx_cc26xx" +source "drivers/watchdog/Kconfig.it51xxx" + source "drivers/watchdog/Kconfig.it8xxx2" source "drivers/watchdog/Kconfig.rpi_pico" diff --git a/drivers/watchdog/Kconfig.it51xxx b/drivers/watchdog/Kconfig.it51xxx new file mode 100644 index 00000000000..2ac7c7307ee --- /dev/null +++ b/drivers/watchdog/Kconfig.it51xxx @@ -0,0 +1,29 @@ +# Copyright (c) 2025 ITE Corporation. All Rights Reserved. +# SPDX-License-Identifier: Apache-2.0 + +config WDT_ITE_IT51XXX + bool "ITE it51xxx Watchdog Timer (WDT) driver" + default y + depends on DT_HAS_ITE_IT51XXX_WATCHDOG_ENABLED + select HAS_WDT_DISABLE_AT_BOOT + help + This option enables the Watchdog Timer driver for ITE it51xxx. + This driver supports only one channel that id is 0 and 16-bits + resolution WDT. + +config WDT_ITE_WARNING_LEADING_TIME_MS + int "Number of ms before generating watchdog event/signal" + depends on WDT_ITE_IT51XXX + default 500 + help + This option defines the window in which a watchdog event must be + handled. After this time window, the watchdog reset triggers + immediately. + +config WDT_ITE_REDUCE_WARNING_LEADING_TIME + bool "Reduce warning leading time" + depends on WDT_ITE_IT51XXX + help + Once warning timer triggered, if watchdog timer isn't reloaded, + then we will reduce interval of warning timer to 30ms to print + more warning messages before watchdog reset. diff --git a/drivers/watchdog/wdt_ite_it51xxx.c b/drivers/watchdog/wdt_ite_it51xxx.c new file mode 100644 index 00000000000..17ed95a86c1 --- /dev/null +++ b/drivers/watchdog/wdt_ite_it51xxx.c @@ -0,0 +1,322 @@ +/* + * Copyright (c) 2025 ITE Corporation. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +#define DT_DRV_COMPAT ite_it51xxx_watchdog + +#include +#include +#include +#include + +#include +#define LOG_LEVEL CONFIG_WDT_LOG_LEVEL +LOG_MODULE_REGISTER(wdt_ite_it51xxx); + +#define IT51XXX_WATCHDOG_MAGIC_BYTE 0x5c +#define WARNING_TIMER_PERIOD_MS_TO_1024HZ_COUNT(ms) ((ms) * 1024 / 1000) + +/* 0x81: External Timer1/WDT Configuration */ +#define REG_ETWCFG 0x01 +#define WDT_EWDKEYEN BIT(5) +#define WDT_EWDSRC BIT(4) +#define WDT_LEWDCNTL BIT(3) +#define WDT_LET1CNTL BIT(2) +#define WDT_LET1PS BIT(1) +#define WDT_LETWCFG BIT(0) +/* 0x82: External Timer1 Prescaler */ +#define REG_ET1PSR 0x02 +#define WDT_ETPS_32P768_KHZ 0x00 +#define WDT_ETPS_1P024_KHZ 0x01 +#define WDT_ETPS_32_HZ 0x02 +/* 0x83: External Timer1 Counter High Byte */ +#define REG_ET1CNTLHR 0x03 +/* 0x84: External Timer1 Counter Low Byte */ +#define REG_ET1CNTLLR 0x04 +/* 0x85: External Timer1/WDT Control */ +#define REG_ETWCTRL 0x05 +#define WDT_EWDSCEN BIT(5) +#define WDT_EWDSCMS BIT(4) +#define WDT_ET1TC BIT(1) +#define WDT_ET1RST BIT(0) +/* 0x86: External WDT Counter Low Byte */ +#define REG_EWDCNTLR 0x06 +/* 0x87: External WDT Key */ +#define REG_EWDKEYR 0x07 +/* 0x89: External WDT Counter High Byte */ +#define REG_EWDCNTHR 0x09 + +/* device config */ +struct wdt_it51xxx_config { + /* wdt register base address */ + uintptr_t base; +}; + +/* driver data */ +struct wdt_it51xxx_data { + /* timeout callback used to handle watchdog event */ + wdt_callback_t callback; + /* indicate whether a watchdog timeout is installed */ + bool timeout_installed; + /* watchdog feed timeout in milliseconds */ + uint32_t timeout; + /* pre-warning timer1 fired times */ + int wdt_warning_fired; +}; + +static int wdt_it51xxx_install_timeout(const struct device *dev, + const struct wdt_timeout_cfg *config) +{ + const struct wdt_it51xxx_config *const wdt_config = dev->config; + struct wdt_it51xxx_data *data = dev->data; + const uintptr_t base = wdt_config->base; + + /* if watchdog is already running */ + if (sys_read8(base + REG_ETWCFG) & WDT_LEWDCNTL) { + return -EBUSY; + } + + /* + * Not support lower limit window timeouts (min value must be equal to + * 0). Upper limit window timeouts can't be 0 when we install timeout. + */ + if ((config->window.min != 0) || (config->window.max == 0)) { + data->timeout_installed = false; + return -EINVAL; + } + + /* save watchdog timeout */ + data->timeout = config->window.max; + + /* install user timeout isr */ + data->callback = config->callback; + + /* mark installed */ + data->timeout_installed = true; + + return 0; +} + +static int wdt_it51xxx_setup(const struct device *dev, uint8_t options) +{ + const struct wdt_it51xxx_config *const wdt_config = dev->config; + struct wdt_it51xxx_data *data = dev->data; + const uintptr_t base = wdt_config->base; + uint16_t cnt0 = WARNING_TIMER_PERIOD_MS_TO_1024HZ_COUNT(data->timeout); + uint16_t cnt1 = WARNING_TIMER_PERIOD_MS_TO_1024HZ_COUNT( + (data->timeout + CONFIG_WDT_ITE_WARNING_LEADING_TIME_MS)); + uint8_t reg_val; + + /* disable pre-warning timer1 interrupt */ + irq_disable(DT_INST_IRQN(0)); + + if (!data->timeout_installed) { + LOG_ERR("No valid WDT timeout installed"); + return -EINVAL; + } + + if (sys_read8(base + REG_ETWCFG) & WDT_LEWDCNTL) { + LOG_ERR("WDT is already running"); + return -EBUSY; + } + + if ((options & WDT_OPT_PAUSE_IN_SLEEP) != 0) { + LOG_ERR("WDT_OPT_PAUSE_IN_SLEEP is not supported"); + return -ENOTSUP; + } + + /* pre-warning timer1 is 16-bit counter down timer */ + sys_write8((cnt0 >> 8) & 0xff, base + REG_ET1CNTLHR); + sys_write8(cnt0 & 0xff, base + REG_ET1CNTLLR); + + /* clear pre-warning timer1 interrupt status */ + ite_intc_isr_clear(DT_INST_IRQN(0)); + + /* enable pre-warning timer1 interrupt */ + irq_enable(DT_INST_IRQN(0)); + + /* don't stop watchdog timer counting */ + reg_val = sys_read8(base + REG_ETWCTRL); + sys_write8(reg_val & ~WDT_EWDSCEN, base + REG_ETWCTRL); + + /* set watchdog timer count */ + sys_write8((cnt1 >> 8) & 0xff, base + REG_EWDCNTHR); + sys_write8(cnt1 & 0xff, base + REG_EWDCNTLR); + + /* allow to write timer1 count register */ + reg_val = sys_read8(base + REG_ETWCFG); + sys_write8(reg_val & ~WDT_LET1CNTL, base + REG_ETWCFG); + + /* + * bit5 = 1: enable key match function to touch watchdog + * bit4 = 1: select watchdog clock source from prescaler + * bit3 = 1: lock watchdog count register (also mark as watchdog running) + * bit1 = 1: lock timer1 prescaler register + */ + sys_write8((WDT_EWDKEYEN | WDT_EWDSRC | WDT_LEWDCNTL | WDT_LET1PS), base + REG_ETWCFG); + + LOG_DBG("WDT Setup and enabled"); + + return 0; +} + +/* + * reload the WDT and pre-warning timer1 counter + * + * @param dev Pointer to the device structure for the driver instance. + * @param channel_id Index of the fed channel, and we only support + * channel_id = 0 now. + */ +static int wdt_it51xxx_feed(const struct device *dev, int channel_id) +{ + const struct wdt_it51xxx_config *const wdt_config = dev->config; + struct wdt_it51xxx_data *data = dev->data; + const uintptr_t base = wdt_config->base; + uint16_t cnt0 = WARNING_TIMER_PERIOD_MS_TO_1024HZ_COUNT(data->timeout); + uint8_t reg_val; + + ARG_UNUSED(channel_id); + + /* reset pre-warning timer1 */ + reg_val = sys_read8(base + REG_ETWCTRL); + sys_write8(reg_val | WDT_ET1RST, base + REG_ETWCTRL); + + /* restart watchdog timer */ + sys_write8(IT51XXX_WATCHDOG_MAGIC_BYTE, base + REG_EWDKEYR); + + /* reset pre-warning timer1 to default if time is touched */ + if (data->wdt_warning_fired) { + data->wdt_warning_fired = 0; + + /* pre-warning timer1 is 16-bit counter down timer */ + sys_write8((cnt0 >> 8) & 0xff, base + REG_ET1CNTLHR); + sys_write8(cnt0 & 0xff, base + REG_ET1CNTLLR); + + /* clear timer1 interrupt status */ + ite_intc_isr_clear(DT_INST_IRQN(0)); + + /* enable timer1 interrupt */ + irq_enable(DT_INST_IRQN(0)); + } + + LOG_DBG("WDT Kicking"); + + return 0; +} + +static int wdt_it51xxx_disable(const struct device *dev) +{ + const struct wdt_it51xxx_config *const wdt_config = dev->config; + struct wdt_it51xxx_data *data = dev->data; + const uintptr_t base = wdt_config->base; + uint8_t reg_val; + + /* stop watchdog timer counting */ + reg_val = sys_read8(base + REG_ETWCTRL); + sys_write8(reg_val | WDT_EWDSCEN, base + REG_ETWCTRL); + + /* unlock watchdog count register (also mark as watchdog not running) */ + reg_val = sys_read8(base + REG_ETWCFG); + sys_write8(reg_val & ~WDT_LEWDCNTL, base + REG_ETWCFG); + + /* disable pre-warning timer1 interrupt */ + irq_disable(DT_INST_IRQN(0)); + + /* mark uninstalled */ + data->timeout_installed = false; + + LOG_DBG("WDT Disabled"); + + return 0; +} + +static void wdt_it51xxx_isr(const struct device *dev) +{ + const struct wdt_it51xxx_config *const wdt_config = dev->config; + struct wdt_it51xxx_data *data = dev->data; + const uintptr_t base = wdt_config->base; + uint8_t reg_val; + + /* clear pre-warning timer1 interrupt status */ + ite_intc_isr_clear(DT_INST_IRQN(0)); + + /* reset pre-warning timer1 */ + reg_val = sys_read8(base + REG_ETWCTRL); + sys_write8(reg_val | WDT_ET1RST, base + REG_ETWCTRL); + + /* callback function, ex. print warning message */ + if (data->callback) { + data->callback(dev, 0); + } + + if (IS_ENABLED(CONFIG_WDT_ITE_REDUCE_WARNING_LEADING_TIME)) { + /* + * Once warning timer triggered: if watchdog timer isn't reloaded, + * then we will reduce interval of warning timer to 30ms to print + * more warning messages before watchdog reset. + */ + if (!(data->wdt_warning_fired)) { + uint16_t cnt0 = WARNING_TIMER_PERIOD_MS_TO_1024HZ_COUNT(30); + + /* pre-warning timer1 is 16-bit counter down timer */ + sys_write8((cnt0 >> 8) & 0xff, base + REG_ET1CNTLHR); + sys_write8(cnt0 & 0xff, base + REG_ET1CNTLLR); + + /* clear pre-warning timer1 interrupt status */ + ite_intc_isr_clear(DT_INST_IRQN(0)); + } + } + data->wdt_warning_fired++; + + LOG_DBG("WDT ISR"); +} + +static DEVICE_API(wdt, wdt_it51xxx_api) = { + .setup = wdt_it51xxx_setup, + .disable = wdt_it51xxx_disable, + .install_timeout = wdt_it51xxx_install_timeout, + .feed = wdt_it51xxx_feed, +}; + +static int wdt_it51xxx_init(const struct device *dev) +{ + const struct wdt_it51xxx_config *const wdt_config = dev->config; + const uintptr_t base = wdt_config->base; + uint8_t reg_val; + + if (IS_ENABLED(CONFIG_WDT_DISABLE_AT_BOOT)) { + wdt_it51xxx_disable(dev); + } + + /* unlock access to watchdog registers */ + sys_write8(0x00, base + REG_ETWCFG); + + /* set WDT and timer1 to use 1.024kHz clock */ + sys_write8(WDT_ETPS_1P024_KHZ, base + REG_ET1PSR); + + /* set WDT key match enabled and WDT clock to use ET1PSR */ + sys_write8(WDT_EWDKEYEN | WDT_EWDSRC, base + REG_ETWCFG); + + /* + * select the mode that watchdog can be stopped, this is needed for + * wdt_it51xxx_disable() api and WDT_OPT_PAUSE_HALTED_BY_DBG flag + */ + reg_val = sys_read8(base + REG_ETWCTRL); + sys_write8(reg_val | WDT_EWDSCMS, base + REG_ETWCTRL); + + IRQ_CONNECT(DT_INST_IRQN(0), 0, wdt_it51xxx_isr, DEVICE_DT_INST_GET(0), 0); + return 0; +} + +static const struct wdt_it51xxx_config wdt_it51xxx_cfg_0 = { + .base = DT_INST_REG_ADDR(0), +}; + +static struct wdt_it51xxx_data wdt_it51xxx_dev_data; + +DEVICE_DT_INST_DEFINE(0, wdt_it51xxx_init, NULL, &wdt_it51xxx_dev_data, &wdt_it51xxx_cfg_0, + PRE_KERNEL_1, CONFIG_KERNEL_INIT_PRIORITY_DEFAULT, &wdt_it51xxx_api); + +BUILD_ASSERT(DT_NUM_INST_STATUS_OKAY(DT_DRV_COMPAT) == 1, + "only one ite,it51xxx-watchdog compatible node can be supported"); diff --git a/dts/bindings/watchdog/ite,it51xxx-watchdog.yaml b/dts/bindings/watchdog/ite,it51xxx-watchdog.yaml new file mode 100644 index 00000000000..1c666b9ae70 --- /dev/null +++ b/dts/bindings/watchdog/ite,it51xxx-watchdog.yaml @@ -0,0 +1,15 @@ +# Copyright (c) 2025 ITE Corporation. All Rights Reserved. +# SPDX-License-Identifier: Apache-2.0 + +description: ITE watchdog timer + +include: base.yaml + +compatible: "ite,it51xxx-watchdog" + +properties: + reg: + required: true + + interrupts: + required: true diff --git a/dts/riscv/ite/it51xxx.dtsi b/dts/riscv/ite/it51xxx.dtsi index 6f5d1f757d1..660c3552274 100644 --- a/dts/riscv/ite/it51xxx.dtsi +++ b/dts/riscv/ite/it51xxx.dtsi @@ -1005,5 +1005,12 @@ kso16-gpios = <&gpioc 3 (GPIO_OPEN_DRAIN | GPIO_PULL_UP)>; kso17-gpios = <&gpioc 5 (GPIO_OPEN_DRAIN | GPIO_PULL_UP)>; }; + + twd0: watchdog@f04780 { + compatible = "ite,it51xxx-watchdog"; + reg = <0x00f04780 0x20>; + interrupts = ; /* Warn timer */ + interrupt-parent = <&intc>; + }; }; };