Skip to content

Commit

Permalink
ITE driver/watchdog: add watchdog timer for it8xxx2
Browse files Browse the repository at this point in the history
Add watchdog timer for it8xxx2.

Signed-off-by: Ruibin Chang <ruibin.chang@ite.com.tw>
  • Loading branch information
RuibinChang authored and carlescufi committed Jun 18, 2021
1 parent 22cfbd3 commit d456684
Show file tree
Hide file tree
Showing 9 changed files with 401 additions and 41 deletions.
2 changes: 2 additions & 0 deletions CODEOWNERS
Validating CODEOWNERS rules …
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
2 changes: 2 additions & 0 deletions boards/riscv/it8xxx2_evb/it8xxx2_evb_defconfig
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
1 change: 1 addition & 0 deletions drivers/watchdog/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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)
1 change: 1 addition & 0 deletions drivers/watchdog/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -56,4 +56,5 @@ source "drivers/watchdog/Kconfig.npcx"

source "drivers/watchdog/Kconfig.cc32xx"

source "drivers/watchdog/Kconfig.it8xxx2"
endif
19 changes: 19 additions & 0 deletions drivers/watchdog/Kconfig.it8xxx2
Original file line number Diff line number Diff line change
@@ -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.
273 changes: 273 additions & 0 deletions drivers/watchdog/wdt_ite_it8xxx2.c
Original file line number Diff line number Diff line change
@@ -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 <drivers/watchdog.h>
#include <errno.h>
#include <soc.h>

#include <logging/log.h>
#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);
18 changes: 18 additions & 0 deletions dts/bindings/watchdog/ite,it8xxx2-watchdog.yaml
Original file line number Diff line number Diff line change
@@ -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
11 changes: 10 additions & 1 deletion dts/riscv/it8xxx2.dtsi
Original file line number Diff line number Diff line change
Expand Up @@ -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";
Expand Down
Loading

0 comments on commit d456684

Please sign in to comment.