drivers/watchdog: Add support for SiLabs Gecko Watchdog
Watchdog type is found on e.g. Pearl/Jade Gecko, often more than 1 is present. Driver supports timeout and (minimum) window configuration and reset or timeout interrupt support for now. Signed-off-by: Oane Kingma <o.kingma@interay.com>
This commit is contained in:
parent
59208ac13b
commit
dc5c242223
5 changed files with 318 additions and 1 deletions
|
@ -205,6 +205,7 @@
|
||||||
/drivers/i2c/*sam0* @Sizurka
|
/drivers/i2c/*sam0* @Sizurka
|
||||||
/drivers/i2c/i2c_dw* @dcpleung
|
/drivers/i2c/i2c_dw* @dcpleung
|
||||||
/drivers/*/*xec* @franciscomunoz @albertofloyd @scottwcpg
|
/drivers/*/*xec* @franciscomunoz @albertofloyd @scottwcpg
|
||||||
|
/drivers/watchdog/*gecko* @oanerer
|
||||||
/drivers/watchdog/wdt_handlers.c @andrewboie
|
/drivers/watchdog/wdt_handlers.c @andrewboie
|
||||||
/drivers/wifi/ @jukkar @tbursztyka @pfalcon
|
/drivers/wifi/ @jukkar @tbursztyka @pfalcon
|
||||||
/drivers/wifi/eswifi/ @loicpoulain
|
/drivers/wifi/eswifi/ @loicpoulain
|
||||||
|
|
|
@ -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_WDOG wdt_mcux_wdog.c)
|
||||||
zephyr_sources_ifdef(CONFIG_WDT_MCUX_WDOG32 wdt_mcux_wdog32.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_XEC wdt_mchp_xec.c)
|
||||||
|
zephyr_sources_ifdef(CONFIG_WDT_GECKO wdt_gecko.c)
|
||||||
zephyr_sources_ifdef(CONFIG_USERSPACE wdt_handlers.c)
|
zephyr_sources_ifdef(CONFIG_USERSPACE wdt_handlers.c)
|
||||||
|
|
|
@ -51,4 +51,6 @@ source "drivers/watchdog/Kconfig.mcux"
|
||||||
|
|
||||||
source "drivers/watchdog/Kconfig.xec"
|
source "drivers/watchdog/Kconfig.xec"
|
||||||
|
|
||||||
|
source "drivers/watchdog/Kconfig.gecko"
|
||||||
|
|
||||||
endif
|
endif
|
||||||
|
|
14
drivers/watchdog/Kconfig.gecko
Normal file
14
drivers/watchdog/Kconfig.gecko
Normal file
|
@ -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.
|
300
drivers/watchdog/wdt_gecko.c
Normal file
300
drivers/watchdog/wdt_gecko.c
Normal file
|
@ -0,0 +1,300 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2019 Interay Solutions B.V.
|
||||||
|
* Copyright (c) 2019 Oane Kingma
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <soc.h>
|
||||||
|
#include <drivers/watchdog.h>
|
||||||
|
#include <em_wdog.h>
|
||||||
|
#include <em_cmu.h>
|
||||||
|
|
||||||
|
#include <logging/log.h>
|
||||||
|
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 */
|
Loading…
Add table
Add a link
Reference in a new issue