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
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -51,4 +51,6 @@ source "drivers/watchdog/Kconfig.mcux"
|
|||
|
||||
source "drivers/watchdog/Kconfig.xec"
|
||||
|
||||
source "drivers/watchdog/Kconfig.gecko"
|
||||
|
||||
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…
Reference in a new issue