diff --git a/drivers/watchdog/CMakeLists.txt b/drivers/watchdog/CMakeLists.txt index 3cf87b661ac..d683d949366 100644 --- a/drivers/watchdog/CMakeLists.txt +++ b/drivers/watchdog/CMakeLists.txt @@ -9,3 +9,4 @@ zephyr_sources_ifdef(CONFIG_WDT_SAM0 wdt_sam0.c) zephyr_sources_ifdef(CONFIG_WDT_NRFX wdt_nrfx.c) zephyr_sources_ifdef(CONFIG_WDT_MCUX_WDOG wdt_mcux_wdog.c) zephyr_sources_ifdef(CONFIG_WDT_MCUX_WDOG32 wdt_mcux_wdog32.c) +zephyr_sources_ifdef(CONFIG_WDT_XEC wdt_mchp_xec.c) diff --git a/drivers/watchdog/Kconfig b/drivers/watchdog/Kconfig index bcf22ff1aab..135e29cc429 100644 --- a/drivers/watchdog/Kconfig +++ b/drivers/watchdog/Kconfig @@ -57,4 +57,6 @@ source "drivers/watchdog/Kconfig.nrfx" source "drivers/watchdog/Kconfig.mcux" +source "drivers/watchdog/Kconfig.xec" + endif diff --git a/drivers/watchdog/Kconfig.xec b/drivers/watchdog/Kconfig.xec new file mode 100644 index 00000000000..c45b93433fe --- /dev/null +++ b/drivers/watchdog/Kconfig.xec @@ -0,0 +1,14 @@ +# Kconfig - Microchip XEC Watchdog Timer configuration +# +# Copyright (c) 2019 Intel Corporation +# +# SPDX-License-Identifier: Apache-2.0 +# + +config WDT_XEC + bool "Microchip XEC series Watchdog Timer (WDT) driver" + depends on SOC_FAMILY_MEC + select HAS_DTS_WDT + default y + help + Enable WDT driver for Microchip XEC MCU series. diff --git a/drivers/watchdog/wdt_mchp_xec.c b/drivers/watchdog/wdt_mchp_xec.c new file mode 100644 index 00000000000..452a5af0e36 --- /dev/null +++ b/drivers/watchdog/wdt_mchp_xec.c @@ -0,0 +1,183 @@ +/* wdt_xec.c - Microchip XEC watchdog driver */ + +/* + * Copyright (c) 2019 Intel Corporation. + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#define LOG_LEVEL CONFIG_WDT_LOG_LEVEL +#include +LOG_MODULE_REGISTER(wdt_mchp_xec); + +#include +#include +#include +#include + +#define WDT_XEC_REG_BASE \ + ((WDT_Type *)(DT_INST_0_MICROCHIP_XEC_WATCHDOG_BASE_ADDRESS)) + +struct wdt_xec_data { + wdt_callback_t cb; + bool timeout_installed; +}; + + +DEVICE_DECLARE(wdt_xec); + +static int wdt_xec_setup(struct device *dev, u8_t options) +{ + WDT_Type *wdt_regs = WDT_XEC_REG_BASE; + struct wdt_xec_data *data = dev->driver_data; + + if (wdt_regs->CTRL & MCHP_WDT_CTRL_EN) { + return -EBUSY; + } + + if (!data->timeout_installed) { + LOG_ERR("No valid WDT timeout installed"); + return -EINVAL; + } + + if (options & WDT_OPT_PAUSE_IN_SLEEP) { + LOG_WRN("WDT_OPT_PAUSE_IN_SLEEP is not supported"); + return -ENOTSUP; + } + + if (options & WDT_OPT_PAUSE_HALTED_BY_DBG) { + wdt_regs->CTRL |= MCHP_WDT_CTRL_JTAG_STALL_EN; + } else { + wdt_regs->CTRL &= ~MCHP_WDT_CTRL_JTAG_STALL_EN; + } + + wdt_regs->CTRL |= MCHP_WDT_CTRL_EN; + + LOG_DBG("WDT Setup and enabled"); + + return 0; +} + +static int wdt_xec_disable(struct device *dev) +{ + WDT_Type *wdt_regs = WDT_XEC_REG_BASE; + struct wdt_xec_data *data = dev->driver_data; + + if (!(wdt_regs->CTRL & MCHP_WDT_CTRL_EN)) { + return -EALREADY; + } + + wdt_regs->CTRL &= ~MCHP_WDT_CTRL_EN; + data->timeout_installed = false; + + LOG_DBG("WDT Disabled"); + + return 0; +} + +static int wdt_xec_install_timeout(struct device *dev, + const struct wdt_timeout_cfg *config) +{ + WDT_Type *wdt_regs = WDT_XEC_REG_BASE; + struct wdt_xec_data *data = dev->driver_data; + + if (wdt_regs->CTRL & MCHP_WDT_CTRL_EN) { + return -EBUSY; + } + + if (config->window.min > 0U) { + data->timeout_installed = false; + return -EINVAL; + } + + wdt_regs->LOAD = 0; + + data->cb = config->callback; + if (data->cb) { + wdt_regs->CTRL |= MCHP_WDT_CTRL_MODE_IRQ; + wdt_regs->IEN |= MCHP_WDT_IEN_EVENT_IRQ_EN; + + LOG_DBG("WDT callback enabled"); + } else { + /* Setting WDT_FLAG_RESET_SOC or not will have no effect: + * even after the cb, if anything is done, SoC will reset + */ + wdt_regs->CTRL &= ~MCHP_WDT_CTRL_MODE_IRQ; + wdt_regs->IEN &= ~MCHP_WDT_IEN_EVENT_IRQ_EN; + + LOG_DBG("WDT Reset enabled"); + } + + /* Since it almost takes 1ms to decrement the load register + * (See datasheet 18.6.1.4: 33/32.768 KHz = 1.007ms) + * Let's use the given window directly. + */ + wdt_regs->LOAD = config->window.max; + + data->timeout_installed = true; + + return 0; +} + +static int wdt_xec_feed(struct device *dev, int channel_id) +{ + WDT_Type *wdt_regs = WDT_XEC_REG_BASE; + + ARG_UNUSED(dev); + ARG_UNUSED(channel_id); + + if (!(wdt_regs->CTRL & MCHP_WDT_CTRL_EN)) { + return -EINVAL; + } + + LOG_DBG("WDT Kicking"); + + wdt_regs->KICK = 1; + + return 0; +} + +static void wdt_xec_isr(struct device *dev) +{ + WDT_Type *wdt_regs = WDT_XEC_REG_BASE; + struct wdt_xec_data *data = dev->driver_data; + + LOG_DBG("WDT ISR"); + + if (data->cb) { + data->cb(dev, 0); + } + + MCHP_GIRQ_SRC(MCHP_WDT_GIRQ) = MCHP_WDT_GIRQ_VAL; + wdt_regs->IEN &= ~MCHP_WDT_IEN_EVENT_IRQ_EN; +} + +static const struct wdt_driver_api wdt_xec_api = { + .setup = wdt_xec_setup, + .disable = wdt_xec_disable, + .install_timeout = wdt_xec_install_timeout, + .feed = wdt_xec_feed, +}; + +static int wdt_xec_init(struct device *dev) +{ + if (IS_ENABLED(CONFIG_WDT_DISABLE_AT_BOOT)) { + wdt_xec_disable(dev); + } + + MCHP_GIRQ_ENSET(MCHP_WDT_GIRQ) = MCHP_WDT_GIRQ_VAL; + + IRQ_CONNECT(DT_INST_0_MICROCHIP_XEC_WATCHDOG_IRQ_0, + DT_INST_0_MICROCHIP_XEC_WATCHDOG_IRQ_0_PRIORITY, + wdt_xec_isr, DEVICE_GET(wdt_xec), 0); + irq_enable(DT_INST_0_MICROCHIP_XEC_WATCHDOG_IRQ_0); + + return 0; +} + +static struct wdt_xec_data wdt_xec_dev_data; + +DEVICE_AND_API_INIT(wdt_xec, DT_INST_0_MICROCHIP_XEC_WATCHDOG_LABEL, + wdt_xec_init, &wdt_xec_dev_data, NULL, + PRE_KERNEL_1, CONFIG_KERNEL_INIT_PRIORITY_DEVICE, + &wdt_xec_api);