diff --git a/CODEOWNERS b/CODEOWNERS index 45cf62202ae..1d113fa8349 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -330,6 +330,8 @@ /drivers/watchdog/*sifive* @katsuster /drivers/watchdog/wdt_handlers.c @dcpleung @nashif /drivers/watchdog/*cc32xx* @pavlohamov +/drivers/watchdog/wdt_ite_it8xxx2.c @RuibinChang +/drivers/watchdog/Kconfig.it8xxx2 @RuibinChang /drivers/wifi/ @jukkar @tbursztyka @pfalcon /drivers/wifi/esp_at/ @mniestroj /drivers/wifi/eswifi/ @loicpoulain @nandojve diff --git a/boards/riscv/it8xxx2_evb/it8xxx2_evb_defconfig b/boards/riscv/it8xxx2_evb/it8xxx2_evb_defconfig index 77b66e7c345..65990932c20 100644 --- a/boards/riscv/it8xxx2_evb/it8xxx2_evb_defconfig +++ b/boards/riscv/it8xxx2_evb/it8xxx2_evb_defconfig @@ -15,6 +15,8 @@ CONFIG_SERIAL=y CONFIG_UART_INTERRUPT_DRIVEN=y CONFIG_UART_CONSOLE=y CONFIG_UART_NS16550=y +CONFIG_WATCHDOG=y +CONFIG_WDT_ITE_IT8XXX2=y CONFIG_SYS_CLOCK_HW_CYCLES_PER_SEC=32768 CONFIG_SYS_CLOCK_TICKS_PER_SEC=1000 diff --git a/drivers/watchdog/CMakeLists.txt b/drivers/watchdog/CMakeLists.txt index 7b2af17894f..176972aec82 100644 --- a/drivers/watchdog/CMakeLists.txt +++ b/drivers/watchdog/CMakeLists.txt @@ -16,4 +16,5 @@ zephyr_sources_ifdef(CONFIG_WDT_GECKO wdt_gecko.c) zephyr_sources_ifdef(CONFIG_WDT_SIFIVE wdt_sifive.c) zephyr_sources_ifdef(CONFIG_WDT_NPCX wdt_npcx.c) zephyr_sources_ifdef(CONFIG_WDT_CC32XX wdt_cc32xx.c) +zephyr_sources_ifdef(CONFIG_WDT_ITE_IT8XXX2 wdt_ite_it8xxx2.c) zephyr_sources_ifdef(CONFIG_USERSPACE wdt_handlers.c) diff --git a/drivers/watchdog/Kconfig b/drivers/watchdog/Kconfig index 023e58e830d..640f73ab42a 100644 --- a/drivers/watchdog/Kconfig +++ b/drivers/watchdog/Kconfig @@ -56,4 +56,5 @@ source "drivers/watchdog/Kconfig.npcx" source "drivers/watchdog/Kconfig.cc32xx" +source "drivers/watchdog/Kconfig.it8xxx2" endif diff --git a/drivers/watchdog/Kconfig.it8xxx2 b/drivers/watchdog/Kconfig.it8xxx2 new file mode 100644 index 00000000000..63aae1a66eb --- /dev/null +++ b/drivers/watchdog/Kconfig.it8xxx2 @@ -0,0 +1,19 @@ +# Copyright (c) 2021 ITE Corporation. All Rights Reserved. +# SPDX-License-Identifier: Apache-2.0 + +config WDT_ITE_IT8XXX2 + bool "ITE it8xxx2 Watchdog Timer (WDT) driver" + depends on SOC_IT8XXX2 + default y + help + This option enables the Watchdog Timer driver for ITE it8xxx2. + 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" + 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. diff --git a/drivers/watchdog/wdt_ite_it8xxx2.c b/drivers/watchdog/wdt_ite_it8xxx2.c new file mode 100644 index 00000000000..db51328a7dd --- /dev/null +++ b/drivers/watchdog/wdt_ite_it8xxx2.c @@ -0,0 +1,273 @@ +/* + * Copyright (c) 2021 ITE Corporation. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +#define DT_DRV_COMPAT ite_it8xxx2_watchdog + +#include +#include +#include + +#include +#define LOG_LEVEL CONFIG_WDT_LOG_LEVEL +LOG_MODULE_REGISTER(wdt_ite_it8xxx2); + +#define IT8XXX2_WATCHDOG_MAGIC_BYTE 0x5c +#define WARNING_TIMER_PERIOD_MS_TO_1024HZ_COUNT(ms) ((ms) * 1024 / 1000) + +/* enter critical period or not */ +static int wdt_warning_fired; + +/* device config */ +struct wdt_it8xxx2_config { + /* wdt register base address */ + uintptr_t base; +}; + +/* driver data */ +struct wdt_it8xxx2_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; +}; + +/* driver convenience defines */ +#define DRV_CONFIG(dev) ((const struct wdt_it8xxx2_config *)(dev)->config) +#define DRV_DATA(dev) ((struct wdt_it8xxx2_data *)(dev)->data) +#define DRV_REG(dev) (struct wdt_it8xxx2_regs *)(DRV_CONFIG(dev)->base) + +static int wdt_it8xxx2_install_timeout(const struct device *dev, + const struct wdt_timeout_cfg *config) +{ + struct wdt_it8xxx2_data *data = DRV_DATA(dev); + struct wdt_it8xxx2_regs *const inst = DRV_REG(dev); + + /* if watchdog is already running */ + if ((inst->ETWCFG) & IT8XXX2_WDT_LEWDCNTL) { + return -EBUSY; + } + + /* no window watchdog support */ + if (config->window.min != 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_it8xxx2_setup(const struct device *dev, uint8_t options) +{ + struct wdt_it8xxx2_data *data = DRV_DATA(dev); + struct wdt_it8xxx2_regs *const inst = DRV_REG(dev); + 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)); + + /* 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 ((inst->ETWCFG) & IT8XXX2_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; + } + + if ((options & WDT_OPT_PAUSE_HALTED_BY_DBG) != 0) { + LOG_ERR("WDT_OPT_PAUSE_HALTED_BY_DBG is not supported"); + return -ENOTSUP; + } + + /* pre-warning timer1 is 16-bit counter down timer */ + inst->ET1CNTLHR = (cnt0 >> 8) & 0xff; + inst->ET1CNTLLR = cnt0 & 0xff; + + /* 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)); + + /* set watchdog timer count */ + inst->EWDCNTHR = (cnt1 >> 8) & 0xff; + inst->EWDCNTLR = cnt1 & 0xff; + + /* allow to write timer1 count register */ + inst->ETWCFG &= ~IT8XXX2_WDT_LET1CNTL; + + /* + * bit5 = 1: enable key match function to touch watchdog + * bit4 = 1: select watchdog clock source from prescaler + * bit3 = 1: lock watchdog count register + * bit1 = 1: lock timer1 prescaler register + * bit0 = 1: lock watchdog and timer1 config register + */ + inst->ETWCFG = (IT8XXX2_WDT_EWDKEYEN | + IT8XXX2_WDT_EWDSRC | + IT8XXX2_WDT_LEWDCNTL | + IT8XXX2_WDT_LET1PS | + IT8XXX2_WDT_LETWCFG); + + 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_it8xxx2_feed(const struct device *dev, int channel_id) +{ + struct wdt_it8xxx2_data *data = DRV_DATA(dev); + struct wdt_it8xxx2_regs *const inst = DRV_REG(dev); + uint16_t cnt0 = WARNING_TIMER_PERIOD_MS_TO_1024HZ_COUNT(data->timeout); + + ARG_UNUSED(channel_id); + + /* reset pre-warning timer1 */ + inst->ETWCTRL |= IT8XXX2_WDT_ET1RST; + + /* restart watchdog timer */ + inst->EWDKEYR = IT8XXX2_WATCHDOG_MAGIC_BYTE; + + /* reset pre-warning timer1 to default if time is touched */ + if (wdt_warning_fired) { + wdt_warning_fired = 0; + + /* pre-warning timer1 is 16-bit counter down timer */ + inst->ET1CNTLHR = (cnt0 >> 8) & 0xff; + inst->ET1CNTLLR = cnt0 & 0xff; + + /* 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_it8xxx2_disable(const struct device *dev) +{ + struct wdt_it8xxx2_data *data = DRV_DATA(dev); + struct wdt_it8xxx2_regs *const inst = DRV_REG(dev); + + /* stop watchdog timer counting */ + inst->ETWCTRL |= IT8XXX2_WDT_EWDSCEN; + + /* 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_it8xxx2_isr(const struct device *dev) +{ + struct wdt_it8xxx2_data *data = DRV_DATA(dev); + struct wdt_it8xxx2_regs *const inst = DRV_REG(dev); + uint16_t cnt0 = WARNING_TIMER_PERIOD_MS_TO_1024HZ_COUNT(data->timeout); + + /* clear pre-warning timer1 interrupt status */ + ite_intc_isr_clear(DT_INST_IRQN(0)); + + /* reset pre-warning timer1 */ + inst->ETWCTRL |= IT8XXX2_WDT_ET1RST; + + /* callback function, ex. print warning message */ + if (data->callback) { + data->callback(dev, 0); + } + + /* + * Reduce interval of warning timer, so we can print more + * warning messages during critical period. + */ + if (!wdt_warning_fired++) { + /* pre-warning timer1 is 16-bit counter down timer */ + inst->ET1CNTLHR = (cnt0 >> 8) & 0xff; + inst->ET1CNTLLR = cnt0 & 0xff; + + /* clear pre-warning timer1 interrupt status */ + ite_intc_isr_clear(DT_INST_IRQN(0)); + } + + LOG_DBG("WDT ISR"); +} + +static const struct wdt_driver_api wdt_it8xxx2_api = { + .setup = wdt_it8xxx2_setup, + .disable = wdt_it8xxx2_disable, + .install_timeout = wdt_it8xxx2_install_timeout, + .feed = wdt_it8xxx2_feed, +}; + +static int wdt_it8xxx2_init(const struct device *dev) +{ + struct wdt_it8xxx2_regs *const inst = DRV_REG(dev); + + if (IS_ENABLED(CONFIG_WDT_DISABLE_AT_BOOT)) { + wdt_it8xxx2_disable(dev); + } + + /* unlock access to watchdog registers */ + inst->ETWCFG = 0x00; + + /* set WDT and timer1 to use 1.024kHz clock */ + inst->ET1PSR = IT8XXX2_WDT_ETPS_1P024_KHZ; + + /* set WDT key match enabled and WDT clock to use ET1PSR */ + inst->ETWCFG = (IT8XXX2_WDT_EWDKEYEN | + IT8XXX2_WDT_EWDSRC); + + /* watchdog can be stopped */ + inst->ETWCTRL |= IT8XXX2_WDT_EWDSCMS; + + IRQ_CONNECT(DT_INST_IRQN(0), 0, wdt_it8xxx2_isr, + DEVICE_DT_INST_GET(0), 0); + return 0; +} + +static const struct wdt_it8xxx2_config wdt_it8xxx2_cfg_0 = { + .base = DT_INST_REG_ADDR(0), +}; + +static struct wdt_it8xxx2_data wdt_it8xxx2_dev_data; + +DEVICE_DT_INST_DEFINE(0, wdt_it8xxx2_init, NULL, + &wdt_it8xxx2_dev_data, &wdt_it8xxx2_cfg_0, + PRE_KERNEL_1, CONFIG_KERNEL_INIT_PRIORITY_DEFAULT, + &wdt_it8xxx2_api); diff --git a/dts/bindings/watchdog/ite,it8xxx2-watchdog.yaml b/dts/bindings/watchdog/ite,it8xxx2-watchdog.yaml new file mode 100644 index 00000000000..0d0668ccfe7 --- /dev/null +++ b/dts/bindings/watchdog/ite,it8xxx2-watchdog.yaml @@ -0,0 +1,18 @@ +# Copyright (c) 2021 ITE Corporation. All Rights Reserved. +# SPDX-License-Identifier: Apache-2.0 + +description: ITE watchdog timer + +include: base.yaml + +compatible: "ite,it8xxx2-watchdog" + +properties: + reg: + required: true + + label: + required: true + + interrupts: + required: true diff --git a/dts/riscv/it8xxx2.dtsi b/dts/riscv/it8xxx2.dtsi index 8b1a9d257ac..4a34d760641 100644 --- a/dts/riscv/it8xxx2.dtsi +++ b/dts/riscv/it8xxx2.dtsi @@ -256,7 +256,16 @@ interrupts = <39 IRQ_TYPE_EDGE_RISING>; interrupt-parent = <&intc>; }; - timer: timer@f01f00 { + + twd0: watchdog@f01f00 { + compatible = "ite,it8xxx2-watchdog"; + reg = <0x00f01f00 0x0062>; + label = "TWD_0"; + interrupts = <30 IRQ_TYPE_EDGE_RISING>; + interrupt-parent = <&intc>; + }; + + timer: timer@f01f10 { compatible = "ite,it8xxx2-timer"; reg = <0x00f01f00 0x0062>; label = "sys_clock"; diff --git a/soc/riscv/riscv-ite/common/chip_chipregs.h b/soc/riscv/riscv-ite/common/chip_chipregs.h index 7ce4849d3bb..cb9bcd395c6 100644 --- a/soc/riscv/riscv-ite/common/chip_chipregs.h +++ b/soc/riscv/riscv-ite/common/chip_chipregs.h @@ -1122,30 +1122,81 @@ * (1Fxxh) External Timer & External Watchdog (ETWD) * */ -#define ETWCFG ECREG(EC_REG_BASE_ADDR + 0x1F01) -#define EWDKEYEN BIT(5) -#define EWDSRC BIT(4) -#define LEWDCNTL BIT(3) -#define LET1CNTL BIT(2) -#define LET1PS BIT(1) -#define LETWCFG BIT(0) -#define ET1PSR ECREG(EC_REG_BASE_ADDR + 0x1F02) -#define ET1CNTLHR ECREG(EC_REG_BASE_ADDR + 0x1F03) -#define ET1CNTLLR ECREG(EC_REG_BASE_ADDR + 0x1F04) -#define ETWCTRL ECREG(EC_REG_BASE_ADDR + 0x1F05) -#define EWDSCEN BIT(5) -#define EWDSCMS BIT(4) -#define ET2TC BIT(3) -#define ET2RST BIT(2) -#define ET1TC BIT(1) -#define ET1RST BIT(0) -#define EWDCNTLR ECREG(EC_REG_BASE_ADDR + 0x1F06) -#define EWDKEYR ECREG(EC_REG_BASE_ADDR + 0x1F07) -#define EWDCNTHR ECREG(EC_REG_BASE_ADDR + 0x1F09) -#define ET2PSR ECREG(EC_REG_BASE_ADDR + 0x1F0A) -#define ET2CNTLHR ECREG(EC_REG_BASE_ADDR + 0x1F0B) -#define ET2CNTLLR ECREG(EC_REG_BASE_ADDR + 0x1F0C) -#define ET2CNTLH2R ECREG(EC_REG_BASE_ADDR + 0x1F0E) +#ifndef __ASSEMBLER__ +struct wdt_it8xxx2_regs { + /* 0x000: Reserved1 */ + volatile uint8_t reserved1; + /* 0x001: External Timer1/WDT Configuration */ + volatile uint8_t ETWCFG; + /* 0x002: External Timer1 Prescaler */ + volatile uint8_t ET1PSR; + /* 0x003: External Timer1 Counter High Byte */ + volatile uint8_t ET1CNTLHR; + /* 0x004: External Timer1 Counter Low Byte */ + volatile uint8_t ET1CNTLLR; + /* 0x005: External Timer1/WDT Control */ + volatile uint8_t ETWCTRL; + /* 0x006: External WDT Counter Low Byte */ + volatile uint8_t EWDCNTLR; + /* 0x007: External WDT Key */ + volatile uint8_t EWDKEYR; + /* 0x008: Reserved2 */ + volatile uint8_t reserved2; + /* 0x009: External WDT Counter High Byte */ + volatile uint8_t EWDCNTHR; + /* 0x00A: External Timer2 Prescaler */ + volatile uint8_t ET2PSR; + /* 0x00B: External Timer2 Counter High Byte */ + volatile uint8_t ET2CNTLHR; + /* 0x00C: External Timer2 Counter Low Byte */ + volatile uint8_t ET2CNTLLR; + /* 0x00D: Reserved3 */ + volatile uint8_t reserved3; + /* 0x00E: External Timer2 Counter High Byte2 */ + volatile uint8_t ET2CNTLH2R; + /* 0x00F~0x03F: Reserved4 */ + volatile uint8_t reserved4[49]; + /* 0x040: External Timer1 Counter Observation Low Byte */ + volatile uint8_t ET1CNTOLR; + /* 0x041: External Timer1 Counter Observation High Byte */ + volatile uint8_t ET1CNTOHR; + /* 0x042~0x043: Reserved5 */ + volatile uint8_t reserved5[2]; + /* 0x044: External Timer1 Counter Observation Low Byte */ + volatile uint8_t ET2CNTOLR; + /* 0x045: External Timer1 Counter Observation High Byte */ + volatile uint8_t ET2CNTOHR; + /* 0x046: External Timer1 Counter Observation High Byte2 */ + volatile uint8_t ET2CNTOH2R; + /* 0x047~0x05F: Reserved6 */ + volatile uint8_t reserved6[25]; + /* 0x060: External WDT Counter Observation Low Byte */ + volatile uint8_t EWDCNTOLR; + /* 0x061: External WDT Counter Observation High Byte */ + volatile uint8_t EWDCNTOHR; +}; +#endif /* !__ASSEMBLER__ */ + +/* WDT register fields */ +/* 0x001: External Timer1/WDT Configuration */ +#define IT8XXX2_WDT_EWDKEYEN BIT(5) +#define IT8XXX2_WDT_EWDSRC BIT(4) +#define IT8XXX2_WDT_LEWDCNTL BIT(3) +#define IT8XXX2_WDT_LET1CNTL BIT(2) +#define IT8XXX2_WDT_LET1PS BIT(1) +#define IT8XXX2_WDT_LETWCFG BIT(0) +/* 0x002: External Timer1 Prescaler */ +#define IT8XXX2_WDT_ETPS_32P768_KHZ 0x00 +#define IT8XXX2_WDT_ETPS_1P024_KHZ 0x01 +#define IT8XXX2_WDT_ETPS_32_HZ 0x02 +/* 0x005: External Timer1/WDT Control */ +#define IT8XXX2_WDT_EWDSCEN BIT(5) +#define IT8XXX2_WDT_EWDSCMS BIT(4) +#define IT8XXX2_WDT_ET2TC BIT(3) +#define IT8XXX2_WDT_ET2RST BIT(2) +#define IT8XXX2_WDT_ET1TC BIT(1) +#define IT8XXX2_WDT_ET1RST BIT(0) + #define ET3CTRL ECREG(EC_REG_BASE_ADDR + 0x1F10) #define ET3PSR ECREG(EC_REG_BASE_ADDR + 0x1F11) #define ET3CNTLLR ECREG(EC_REG_BASE_ADDR + 0x1F14) @@ -1753,22 +1804,6 @@ struct adc_it8xxx2_regs { #define IT83XX_I2C_RAMH2A(base) ECREG(base+0x50) #define IT83XX_I2C_CMD_ADDH2(base) ECREG(base+0x52) -/* --- External Timer and Watchdog (ETWD) --- */ -#define IT83XX_ETWD_BASE 0x00F01F00 - -#define IT83XX_ETWD_ETWCFG ECREG(IT83XX_ETWD_BASE + 0x01) -#define IT83XX_ETWD_ET1PSR ECREG(IT83XX_ETWD_BASE + 0x02) -#define IT83XX_ETWD_ET1CNTLHR ECREG(IT83XX_ETWD_BASE + 0x03) -#define IT83XX_ETWD_ET1CNTLLR ECREG(IT83XX_ETWD_BASE + 0x04) -#define IT83XX_ETWD_ETWCTRL ECREG(IT83XX_ETWD_BASE + 0x05) -#define IT83XX_ETWD_EWDCNTLLR ECREG(IT83XX_ETWD_BASE + 0x06) -#define IT83XX_ETWD_EWDKEYR ECREG(IT83XX_ETWD_BASE + 0x07) -#define IT83XX_ETWD_EWDCNTLHR ECREG(IT83XX_ETWD_BASE + 0x09) -#define IT83XX_ETWD_ETXCTRL(n) ECREG(IT83XX_ETWD_BASE + 0x10 + (n << 3)) -#define IT83XX_ETWD_ETXPSR(n) ECREG(IT83XX_ETWD_BASE + 0x11 + (n << 3)) -#define IT83XX_ETWD_ETXCNTLR(n) ECREG_u32(IT83XX_ETWD_BASE + 0x14 + (n << 3)) -#define IT83XX_ETWD_ETXCNTOR(n) ECREG_u32(IT83XX_ETWD_BASE + 0x48 + (n << 2)) - /* --- General Control (GCTRL) --- */ #define IT83XX_GCTRL_BASE 0x00F02000