Browse Source

drivers/watchdog/it51xxx: implement watchdog driver

Implement watchdog driver for ITE it51xxx series chip.

Signed-off-by: Ruibin Chang <Ruibin.Chang@ite.com.tw>
pull/88608/head
Ruibin Chang 3 months ago committed by Benjamin Cabé
parent
commit
41bc8efbee
  1. 1
      boards/ite/it515xx_evb/it515xx_evb.dts
  2. 1
      drivers/watchdog/CMakeLists.txt
  3. 2
      drivers/watchdog/Kconfig
  4. 29
      drivers/watchdog/Kconfig.it51xxx
  5. 322
      drivers/watchdog/wdt_ite_it51xxx.c
  6. 15
      dts/bindings/watchdog/ite,it51xxx-watchdog.yaml
  7. 7
      dts/riscv/ite/it51xxx.dtsi

1
boards/ite/it515xx_evb/it515xx_evb.dts

@ -16,6 +16,7 @@ @@ -16,6 +16,7 @@
aliases {
led0 = &led0;
watchdog0 = &twd0;
};
chosen {

1
drivers/watchdog/CMakeLists.txt

@ -17,6 +17,7 @@ zephyr_library_sources_ifdef(CONFIG_WDT_CC13XX_CC26XX wdt_cc13xx_cc26xx.c) @@ -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)

2
drivers/watchdog/Kconfig

@ -97,6 +97,8 @@ source "drivers/watchdog/Kconfig.cc32xx" @@ -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"

29
drivers/watchdog/Kconfig.it51xxx

@ -0,0 +1,29 @@ @@ -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.

322
drivers/watchdog/wdt_ite_it51xxx.c

@ -0,0 +1,322 @@ @@ -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 <errno.h>
#include <soc.h>
#include <zephyr/drivers/watchdog.h>
#include <zephyr/irq.h>
#include <zephyr/logging/log.h>
#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");

15
dts/bindings/watchdog/ite,it51xxx-watchdog.yaml

@ -0,0 +1,15 @@ @@ -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

7
dts/riscv/ite/it51xxx.dtsi

@ -1005,5 +1005,12 @@ @@ -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 = <IT51XXX_IRQ_TIMER1_DW IRQ_TYPE_EDGE_RISING>; /* Warn timer */
interrupt-parent = <&intc>;
};
};
};

Loading…
Cancel
Save