drivers/watchdog/it51xxx: implement watchdog driver
Implement watchdog driver for ITE it51xxx series chip. Signed-off-by: Ruibin Chang <Ruibin.Chang@ite.com.tw>
This commit is contained in:
parent
38a81f13ea
commit
41bc8efbee
@ -16,6 +16,7 @@
|
||||
|
||||
aliases {
|
||||
led0 = &led0;
|
||||
watchdog0 = &twd0;
|
||||
};
|
||||
|
||||
chosen {
|
||||
|
||||
@ -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)
|
||||
|
||||
@ -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
Normal file
29
drivers/watchdog/Kconfig.it51xxx
Normal file
@ -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
Normal file
322
drivers/watchdog/wdt_ite_it51xxx.c
Normal file
@ -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
Normal file
15
dts/bindings/watchdog/ite,it51xxx-watchdog.yaml
Normal file
@ -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
|
||||
@ -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…
Reference in New Issue
Block a user