diff --git a/CODEOWNERS b/CODEOWNERS index 40f8686192d..a99b1a8f82f 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -205,6 +205,7 @@ /drivers/i2c/*sam0* @Sizurka /drivers/i2c/i2c_dw* @dcpleung /drivers/*/*xec* @franciscomunoz @albertofloyd @scottwcpg +/drivers/watchdog/*gecko* @oanerer /drivers/watchdog/wdt_handlers.c @andrewboie /drivers/wifi/ @jukkar @tbursztyka @pfalcon /drivers/wifi/eswifi/ @loicpoulain diff --git a/drivers/watchdog/CMakeLists.txt b/drivers/watchdog/CMakeLists.txt index dd1b3058b13..7522d02cde5 100644 --- a/drivers/watchdog/CMakeLists.txt +++ b/drivers/watchdog/CMakeLists.txt @@ -10,5 +10,5 @@ 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) - +zephyr_sources_ifdef(CONFIG_WDT_GECKO wdt_gecko.c) zephyr_sources_ifdef(CONFIG_USERSPACE wdt_handlers.c) diff --git a/drivers/watchdog/Kconfig b/drivers/watchdog/Kconfig index 1a0ce7c03a5..bbea644a881 100644 --- a/drivers/watchdog/Kconfig +++ b/drivers/watchdog/Kconfig @@ -51,4 +51,6 @@ source "drivers/watchdog/Kconfig.mcux" source "drivers/watchdog/Kconfig.xec" +source "drivers/watchdog/Kconfig.gecko" + endif diff --git a/drivers/watchdog/Kconfig.gecko b/drivers/watchdog/Kconfig.gecko new file mode 100644 index 00000000000..3de06c3b621 --- /dev/null +++ b/drivers/watchdog/Kconfig.gecko @@ -0,0 +1,14 @@ +# Watchdog configuration options +# +# Copyright (c) 2019 Interay Solutions B.V. +# Copyright (c) 2019 Oane Kingma +# +# SPDX-License-Identifier: Apache-2.0 + +config WDT_GECKO + bool "Gecko series Watchdog (WDOG) Driver" + depends on SOC_FAMILY_EXX32 + select SOC_GECKO_WDOG + default y + help + Enable WDOG driver for Silicon Labs Gecko MCUs. diff --git a/drivers/watchdog/wdt_gecko.c b/drivers/watchdog/wdt_gecko.c new file mode 100644 index 00000000000..1c29afd0cfa --- /dev/null +++ b/drivers/watchdog/wdt_gecko.c @@ -0,0 +1,300 @@ +/* + * Copyright (c) 2019 Interay Solutions B.V. + * Copyright (c) 2019 Oane Kingma + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include +#include +#include + +#include +LOG_MODULE_REGISTER(wdt_gecko, CONFIG_WDT_LOG_LEVEL); + +/* Defines maximum WDOG_CTRL.PERSEL value which is used by the watchdog module + * to select its timeout period. + */ +#define WDT_GECKO_MAX_PERIOD_SELECT_VALUE 15 + +/* Device constant configuration parameters */ +struct wdt_gecko_cfg { + WDOG_TypeDef *base; + void (*irq_cfg_func)(void); +}; + +struct wdt_gecko_data { + wdt_callback_t callback; + WDOG_Init_TypeDef wdog_config; + bool timeout_installed; +}; + +#define DEV_NAME(dev) ((dev)->config->name) +#define DEV_DATA(dev) \ + ((struct wdt_gecko_data *)(dev)->driver_data) +#define DEV_CFG(dev) \ + ((struct wdt_gecko_cfg *)(dev)->config->config_info) + +static u32_t wdt_gecko_get_timeout_from_persel(int perSel) +{ + return (8 << perSel) + 1; +} + +/* Find the rounded up value of cycles for supplied timeout. When using ULFRCO + * (default), 1 cycle is 1 ms +/- 12%. + */ +static int wdt_gecko_get_persel_from_timeout(u32_t timeout) +{ + int idx; + + for (idx = 0; idx < WDT_GECKO_MAX_PERIOD_SELECT_VALUE; idx++) { + if (wdt_gecko_get_timeout_from_persel(idx) >= timeout) { + break; + } + } + + return idx; +} + +static int wdt_gecko_convert_window(u32_t window, u32_t period) +{ + int idx = 0; + u32_t incr_val, comp_val; + + incr_val = period / 8; + comp_val = 0; /* Initially 0, disable */ + + /* Valid window settings range from 12.5% of the calculated + * timeout period up to 87.5% (= 7 * 12.5%) + */ + while (idx < 7) { + if (window > comp_val) { + comp_val += incr_val; + idx++; + continue; + } + + break; + } + + return idx; +} + +static int wdt_gecko_setup(struct device *dev, u8_t options) +{ + const struct wdt_gecko_cfg *config = DEV_CFG(dev); + struct wdt_gecko_data *data = DEV_DATA(dev); + WDOG_TypeDef *wdog = config->base; + + if (!data->timeout_installed) { + LOG_ERR("No valid timeouts installed"); + return -EINVAL; + } + + data->wdog_config.em2Run = + (options & WDT_OPT_PAUSE_IN_SLEEP) == 0U; + data->wdog_config.em3Run = + (options & WDT_OPT_PAUSE_IN_SLEEP) == 0U; + + data->wdog_config.debugRun = + (options & WDT_OPT_PAUSE_HALTED_BY_DBG) == 0U; + + if (data->callback != NULL) { + /* Interrupt mode for window */ + /* Clear possible lingering interrupts */ + WDOGn_IntClear(wdog, WDOG_IEN_TOUT); + /* Enable timeout interrupt */ + WDOGn_IntEnable(wdog, WDOG_IEN_TOUT); + } else { + /* Disable timeout interrupt */ + WDOGn_IntDisable(wdog, WDOG_IEN_TOUT); + } + + /* Watchdog is started after initialization */ + WDOGn_Init(wdog, &data->wdog_config); + LOG_DBG("Setup the watchdog"); + + return 0; +} + +static int wdt_gecko_disable(struct device *dev) +{ + const struct wdt_gecko_cfg *config = DEV_CFG(dev); + struct wdt_gecko_data *data = DEV_DATA(dev); + WDOG_TypeDef *wdog = config->base; + + WDOGn_Enable(wdog, false); + data->timeout_installed = false; + LOG_DBG("Disabled the watchdog"); + + return 0; +} + +static int wdt_gecko_install_timeout(struct device *dev, + const struct wdt_timeout_cfg *cfg) +{ + struct wdt_gecko_data *data = DEV_DATA(dev); + WDOG_Init_TypeDef init_defaults = WDOG_INIT_DEFAULT; + u32_t installed_timeout; + + if (data->timeout_installed) { + LOG_ERR("No more timeouts can be installed"); + return -ENOMEM; + } + + if ((cfg->window.max < wdt_gecko_get_timeout_from_persel(0)) || + (cfg->window.max > wdt_gecko_get_timeout_from_persel( + WDT_GECKO_MAX_PERIOD_SELECT_VALUE))) { + LOG_ERR("Upper limit timeout out of range"); + return -EINVAL; + } + + data->wdog_config = init_defaults; + + data->wdog_config.perSel = (WDOG_PeriodSel_TypeDef) + wdt_gecko_get_persel_from_timeout(cfg->window.max); + + installed_timeout = wdt_gecko_get_timeout_from_persel( + data->wdog_config.perSel); + LOG_INF("Installed timeout value: %u", installed_timeout); + + if (cfg->window.min > 0) { + /* Window mode. Use rounded up timeout value to + * calculate minimum window setting. + */ + data->wdog_config.winSel = (WDOG_WinSel_TypeDef) + wdt_gecko_convert_window(cfg->window.min, + installed_timeout); + + LOG_INF("Installed window value: %u", + (installed_timeout / 8) * data->wdog_config.winSel); + } else { + /* Normal mode */ + data->wdog_config.winSel = wdogIllegalWindowDisable; + } + + /* Set mode of watchdog and callback */ + switch (cfg->flags) { + case WDT_FLAG_RESET_SOC: + case WDT_FLAG_RESET_CPU_CORE: + if (cfg->callback != NULL) { + LOG_ERR("Reset mode with callback not supported\n"); + return -ENOTSUP; + } + data->wdog_config.resetDisable = false; + LOG_DBG("Configuring reset CPU/SoC mode\n"); + break; + + case WDT_FLAG_RESET_NONE: + data->wdog_config.resetDisable = true; + data->callback = cfg->callback; + LOG_DBG("Configuring non-reset mode\n"); + break; + + default: + LOG_ERR("Unsupported watchdog config flag"); + return -EINVAL; + } + + data->timeout_installed = true; + + return 0; +} + +static int wdt_gecko_feed(struct device *dev, int channel_id) +{ + const struct wdt_gecko_cfg *config = DEV_CFG(dev); + WDOG_TypeDef *wdog = config->base; + + if (channel_id != 0) { + LOG_ERR("Invalid channel id"); + return -EINVAL; + } + + WDOGn_Feed(wdog); + LOG_DBG("Fed the watchdog"); + + return 0; +} + +static void wdt_gecko_isr(void *arg) +{ + struct device *dev = (struct device *)arg; + const struct wdt_gecko_cfg *config = DEV_CFG(dev); + struct wdt_gecko_data *data = DEV_DATA(dev); + WDOG_TypeDef *wdog = config->base; + u32_t flags; + + /* Clear IRQ flags */ + flags = WDOGn_IntGet(wdog); + WDOGn_IntClear(wdog, flags); + + if (data->callback != NULL) { + data->callback(dev, 0); + } +} + +static int wdt_gecko_init(struct device *dev) +{ + const struct wdt_gecko_cfg *config = DEV_CFG(dev); + +#ifdef CONFIG_WDT_DISABLE_AT_BOOT + /* Ignore any errors */ + wdt_gecko_disable(dev); +#endif + + /* Enable ULFRCO (1KHz) oscillator */ + CMU_OscillatorEnable(cmuOsc_ULFRCO, true, false); + + /* Ensure LE modules are clocked */ + CMU_ClockEnable(cmuClock_CORELE, true); + + /* Enable IRQs */ + config->irq_cfg_func(); + + LOG_INF("Device %s initialized", DEV_NAME(dev)); + + return 0; +} + +static const struct wdt_driver_api wdt_gecko_driver_api = { + .setup = wdt_gecko_setup, + .disable = wdt_gecko_disable, + .install_timeout = wdt_gecko_install_timeout, + .feed = wdt_gecko_feed, +}; + +#define GECKO_WDT_INIT(index) \ + \ + static void wdt_gecko_cfg_func_##index(void); \ + \ + static const struct wdt_gecko_cfg wdt_gecko_cfg_##index = { \ + .base = (WDOG_TypeDef *) \ + DT_INST_##index##_SILABS_GECKO_WDOG_BASE_ADDRESS,\ + .irq_cfg_func = wdt_gecko_cfg_func_##index, \ + }; \ + static struct wdt_gecko_data wdt_gecko_data_##index; \ + \ + DEVICE_AND_API_INIT(wdt_##index, \ + DT_INST_##index##_SILABS_GECKO_WDOG_LABEL,\ + &wdt_gecko_init, &wdt_gecko_data_##index,\ + &wdt_gecko_cfg_##index, POST_KERNEL, \ + CONFIG_KERNEL_INIT_PRIORITY_DEFAULT, \ + &wdt_gecko_driver_api); \ + \ + static void wdt_gecko_cfg_func_##index(void) \ + { \ + IRQ_CONNECT(DT_INST_##index##_SILABS_GECKO_WDOG_IRQ_0, \ + DT_INST_##index##_SILABS_GECKO_WDOG_IRQ_0_PRIORITY,\ + wdt_gecko_isr, DEVICE_GET(wdt_##index), 0); \ + irq_enable(DT_INST_##index##_SILABS_GECKO_WDOG_IRQ_0); \ + } + +#ifdef DT_INST_0_SILABS_GECKO_WDOG +GECKO_WDT_INIT(0) +#endif /* DT_INST_0_SILABS_GECKO_WDOG */ + +#ifdef DT_INST_1_SILABS_GECKO_WDOG +GECKO_WDT_INIT(1) +#endif /* DT_INST_1_SILABS_GECKO_WDOG */