drivers: ti: cc13xx/cc26xx: implement watchdog timer
New Zephyr WDT driver for TI CC13xx/CC26xx family. Supports interrupts & MCU soft reset on timeout. Signed-off-by: Stancu Florin <niflostancu@gmail.com>
This commit is contained in:
parent
e5adeef105
commit
236084df70
|
@ -11,6 +11,7 @@ zephyr_library_sources_ifdef(CONFIG_WWDGT_GD32 wdt_wwdgt_gd32.c)
|
||||||
zephyr_library_sources_ifdef(CONFIG_WDOG_CMSDK_APB wdt_cmsdk_apb.c)
|
zephyr_library_sources_ifdef(CONFIG_WDOG_CMSDK_APB wdt_cmsdk_apb.c)
|
||||||
|
|
||||||
zephyr_library_sources_ifdef(CONFIG_WDT_CC32XX wdt_cc32xx.c)
|
zephyr_library_sources_ifdef(CONFIG_WDT_CC32XX wdt_cc32xx.c)
|
||||||
|
zephyr_library_sources_ifdef(CONFIG_WDT_CC13XX_CC26XX wdt_cc13xx_cc26xx.c)
|
||||||
zephyr_library_sources_ifdef(CONFIG_WDT_ESP32 wdt_esp32.c)
|
zephyr_library_sources_ifdef(CONFIG_WDT_ESP32 wdt_esp32.c)
|
||||||
zephyr_library_sources_ifdef(CONFIG_WDT_GECKO wdt_gecko.c)
|
zephyr_library_sources_ifdef(CONFIG_WDT_GECKO wdt_gecko.c)
|
||||||
zephyr_library_sources_ifdef(CONFIG_WDT_ITE_IT8XXX2 wdt_ite_it8xxx2.c)
|
zephyr_library_sources_ifdef(CONFIG_WDT_ITE_IT8XXX2 wdt_ite_it8xxx2.c)
|
||||||
|
|
|
@ -82,6 +82,8 @@ source "drivers/watchdog/Kconfig.npcx"
|
||||||
|
|
||||||
source "drivers/watchdog/Kconfig.cc32xx"
|
source "drivers/watchdog/Kconfig.cc32xx"
|
||||||
|
|
||||||
|
source "drivers/watchdog/Kconfig.cc13xx_cc26xx"
|
||||||
|
|
||||||
source "drivers/watchdog/Kconfig.it8xxx2"
|
source "drivers/watchdog/Kconfig.it8xxx2"
|
||||||
|
|
||||||
source "drivers/watchdog/Kconfig.rpi_pico"
|
source "drivers/watchdog/Kconfig.rpi_pico"
|
||||||
|
|
|
@ -0,0 +1,20 @@
|
||||||
|
# Copyright (c) 2021 Florin Stancu <niflostancu@gmail.com>
|
||||||
|
# SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
|
config WDT_CC13XX_CC26XX
|
||||||
|
bool "Watchdog Driver for CC13xx / CC26xx family of MCUs"
|
||||||
|
default y
|
||||||
|
depends on DT_HAS_TI_CC13XX_CC26XX_WATCHDOG_ENABLED
|
||||||
|
help
|
||||||
|
Enable watchdog for CC13xx / CC26xx family of MCUs
|
||||||
|
|
||||||
|
config WDT_CC13XX_CC26XX_INITIAL_TIMEOUT
|
||||||
|
int "Value for initial WDT timeout in ms"
|
||||||
|
depends on WDT_CC13XX_CC26XX
|
||||||
|
default 2000
|
||||||
|
range 1 2863311
|
||||||
|
help
|
||||||
|
The CC13xx/CC26xx watchdog timer is sourced from the MCU clock
|
||||||
|
using a fixed prescaler of 32.
|
||||||
|
E.g., for the standard 48 MHz MCU clock, the following:
|
||||||
|
0xFFFFFFFF / (48^9 / 32 / 1000) [ms]
|
|
@ -0,0 +1,252 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2022 Florin Stancu <niflostancu@gmail.com>
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
#define DT_DRV_COMPAT ti_cc13xx_cc26xx_watchdog
|
||||||
|
|
||||||
|
#include <zephyr/drivers/watchdog.h>
|
||||||
|
#include <zephyr/irq.h>
|
||||||
|
#include <soc.h>
|
||||||
|
#include <errno.h>
|
||||||
|
|
||||||
|
#define LOG_LEVEL CONFIG_WDT_LOG_LEVEL
|
||||||
|
#include <zephyr/logging/log.h>
|
||||||
|
LOG_MODULE_REGISTER(wdt_cc13xx_cc26xx);
|
||||||
|
|
||||||
|
/* Driverlib includes */
|
||||||
|
#include <driverlib/watchdog.h>
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* TI CC13xx/CC26xx watchdog is a 32-bit timer that runs on the MCU clock
|
||||||
|
* with a fixed 32 divider.
|
||||||
|
*
|
||||||
|
* For the default MCU frequency of 48MHz:
|
||||||
|
* 1ms = (48e6 / 32 / 1000) = 1500 ticks
|
||||||
|
* Max. value = 2^32 / 1500 ~= 2863311 ms
|
||||||
|
*
|
||||||
|
* The watchdog will issue reset only on second in turn time-out (if the timer
|
||||||
|
* or the interrupt aren't reset after the first time-out). By default, regular
|
||||||
|
* interrupt is generated but platform supports also NMI (can be enabled by
|
||||||
|
* setting the `interrupt-nmi` boolean DT property).
|
||||||
|
*/
|
||||||
|
|
||||||
|
#define CPU_FREQ DT_PROP(DT_PATH(cpus, cpu_0), clock_frequency)
|
||||||
|
#define WATCHDOG_DIV_RATIO 32
|
||||||
|
#define WATCHDOG_MS_RATIO (CPU_FREQ / WATCHDOG_DIV_RATIO / 1000)
|
||||||
|
#define WATCHDOG_MAX_RELOAD_MS (0xFFFFFFFFu / WATCHDOG_MS_RATIO)
|
||||||
|
#define WATCHDOG_MS_TO_TICKS(_ms) ((_ms) * WATCHDOG_MS_RATIO)
|
||||||
|
|
||||||
|
struct wdt_cc13xx_cc26xx_data {
|
||||||
|
uint8_t enabled;
|
||||||
|
uint32_t reload;
|
||||||
|
wdt_callback_t cb;
|
||||||
|
uint8_t flags;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct wdt_cc13xx_cc26xx_cfg {
|
||||||
|
uint32_t reg;
|
||||||
|
uint8_t irq_nmi;
|
||||||
|
void (*irq_cfg_func)(void);
|
||||||
|
};
|
||||||
|
|
||||||
|
static int wdt_cc13xx_cc26xx_install_timeout(const struct device *dev,
|
||||||
|
const struct wdt_timeout_cfg *cfg)
|
||||||
|
{
|
||||||
|
struct wdt_cc13xx_cc26xx_data *data = dev->data;
|
||||||
|
|
||||||
|
/* window watchdog not supported */
|
||||||
|
if (cfg->window.min != 0U || cfg->window.max == 0U) {
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
/*
|
||||||
|
* Note: since this SoC doesn't define CONFIG_WDT_MULTISTAGE, we don't need to
|
||||||
|
* specifically check for it and return ENOTSUP
|
||||||
|
*/
|
||||||
|
|
||||||
|
if (cfg->window.max > WATCHDOG_MAX_RELOAD_MS) {
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
data->reload = WATCHDOG_MS_TO_TICKS(cfg->window.max);
|
||||||
|
data->cb = cfg->callback;
|
||||||
|
data->flags = cfg->flags;
|
||||||
|
LOG_DBG("raw reload value: %d", data->reload);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int wdt_cc13xx_cc26xx_setup(const struct device *dev, uint8_t options)
|
||||||
|
{
|
||||||
|
const struct wdt_cc13xx_cc26xx_cfg *config = dev->config;
|
||||||
|
struct wdt_cc13xx_cc26xx_data *data = dev->data;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Note: don't check if watchdog is already enabled, an application might
|
||||||
|
* want to dynamically re-configure its options (e.g., decrease the reload
|
||||||
|
* value for critical sections).
|
||||||
|
*/
|
||||||
|
|
||||||
|
WatchdogUnlock();
|
||||||
|
|
||||||
|
/* clear any previous interrupt flags */
|
||||||
|
WatchdogIntClear();
|
||||||
|
|
||||||
|
/* Stall the WDT counter when halted by debugger */
|
||||||
|
if (options & WDT_OPT_PAUSE_HALTED_BY_DBG) {
|
||||||
|
WatchdogStallEnable();
|
||||||
|
} else {
|
||||||
|
WatchdogStallDisable();
|
||||||
|
}
|
||||||
|
/*
|
||||||
|
* According to TI's datasheets, the WDT is paused in STANDBY mode,
|
||||||
|
* so we simply continue with the setup => don't do this check:
|
||||||
|
* > if (options & WDT_OPT_PAUSE_IN_SLEEP) {
|
||||||
|
* > return -ENOTSUP;
|
||||||
|
* > }
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* raw reload value was computed by `_install_timeout()` */
|
||||||
|
WatchdogReloadSet(data->reload);
|
||||||
|
|
||||||
|
/* use the Device Tree-configured interrupt type */
|
||||||
|
if (config->irq_nmi) {
|
||||||
|
LOG_DBG("NMI enabled");
|
||||||
|
WatchdogIntTypeSet(WATCHDOG_INT_TYPE_NMI);
|
||||||
|
} else {
|
||||||
|
WatchdogIntTypeSet(WATCHDOG_INT_TYPE_INT);
|
||||||
|
}
|
||||||
|
|
||||||
|
switch ((data->flags & WDT_FLAG_RESET_MASK)) {
|
||||||
|
case WDT_FLAG_RESET_NONE:
|
||||||
|
LOG_DBG("reset disabled");
|
||||||
|
WatchdogResetDisable();
|
||||||
|
break;
|
||||||
|
case WDT_FLAG_RESET_SOC:
|
||||||
|
LOG_DBG("reset enabled");
|
||||||
|
WatchdogResetEnable();
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
WatchdogLock();
|
||||||
|
return -ENOTSUP;
|
||||||
|
}
|
||||||
|
|
||||||
|
data->enabled = 1;
|
||||||
|
WatchdogEnable();
|
||||||
|
WatchdogLock();
|
||||||
|
|
||||||
|
LOG_DBG("done");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int wdt_cc13xx_cc26xx_disable(const struct device *dev)
|
||||||
|
{
|
||||||
|
struct wdt_cc13xx_cc26xx_data *data = dev->data;
|
||||||
|
|
||||||
|
if (!WatchdogRunning()) {
|
||||||
|
return -EFAULT;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Node: once started, the watchdog timer cannot be stopped!
|
||||||
|
* All we can do is disable the timeout reset, but the interrupt
|
||||||
|
* will be triggered if it was enabled (though it won't trigger the
|
||||||
|
* user callback due to `enabled` being unsed)!
|
||||||
|
*/
|
||||||
|
data->enabled = 0;
|
||||||
|
WatchdogUnlock();
|
||||||
|
WatchdogResetDisable();
|
||||||
|
WatchdogLock();
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int wdt_cc13xx_cc26xx_feed(const struct device *dev, int channel_id)
|
||||||
|
{
|
||||||
|
struct wdt_cc13xx_cc26xx_data *data = dev->data;
|
||||||
|
|
||||||
|
WatchdogUnlock();
|
||||||
|
WatchdogIntClear();
|
||||||
|
WatchdogReloadSet(data->reload);
|
||||||
|
WatchdogLock();
|
||||||
|
LOG_DBG("feed %i", data->reload);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void wdt_cc13xx_cc26xx_isr(const struct device *dev)
|
||||||
|
{
|
||||||
|
struct wdt_cc13xx_cc26xx_data *data = dev->data;
|
||||||
|
|
||||||
|
/* Simulate the watchdog being disabled: don't call the handler. */
|
||||||
|
if (!data->enabled) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Note: don't clear the interrupt here, leave it for the callback
|
||||||
|
* to decide (by calling `_feed()`)
|
||||||
|
*/
|
||||||
|
|
||||||
|
LOG_DBG("ISR");
|
||||||
|
if (data->cb) {
|
||||||
|
data->cb(dev, 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static int wdt_cc13xx_cc26xx_init(const struct device *dev)
|
||||||
|
{
|
||||||
|
const struct wdt_cc13xx_cc26xx_cfg *config = dev->config;
|
||||||
|
uint8_t options = 0;
|
||||||
|
|
||||||
|
LOG_DBG("init");
|
||||||
|
config->irq_cfg_func();
|
||||||
|
|
||||||
|
if (IS_ENABLED(CONFIG_WDT_DISABLE_AT_BOOT)) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef CONFIG_DEBUG
|
||||||
|
/* when CONFIG_DEBUG is enabled, pause the WDT during debugging */
|
||||||
|
options = WDT_OPT_PAUSE_HALTED_BY_DBG;
|
||||||
|
#endif /* CONFIG_DEBUG */
|
||||||
|
|
||||||
|
return wdt_cc13xx_cc26xx_setup(dev, options);
|
||||||
|
}
|
||||||
|
|
||||||
|
static const struct wdt_driver_api wdt_cc13xx_cc26xx_api = {
|
||||||
|
.setup = wdt_cc13xx_cc26xx_setup,
|
||||||
|
.disable = wdt_cc13xx_cc26xx_disable,
|
||||||
|
.install_timeout = wdt_cc13xx_cc26xx_install_timeout,
|
||||||
|
.feed = wdt_cc13xx_cc26xx_feed,
|
||||||
|
};
|
||||||
|
|
||||||
|
#define CC13XX_CC26XX_WDT_INIT(index) \
|
||||||
|
static void wdt_cc13xx_cc26xx_irq_cfg_##index(void) \
|
||||||
|
{ \
|
||||||
|
if (DT_INST_PROP(index, interrupt_nmi)) { \
|
||||||
|
return; /* NMI interrupt is used */ \
|
||||||
|
} \
|
||||||
|
IRQ_CONNECT(DT_INST_IRQN(index), \
|
||||||
|
DT_INST_IRQ(index, priority), \
|
||||||
|
wdt_cc13xx_cc26xx_isr, DEVICE_DT_INST_GET(index), 0); \
|
||||||
|
irq_enable(DT_INST_IRQN(index)); \
|
||||||
|
} \
|
||||||
|
static struct wdt_cc13xx_cc26xx_data wdt_cc13xx_cc26xx_data_##index = { \
|
||||||
|
.reload = WATCHDOG_MS_TO_TICKS( \
|
||||||
|
CONFIG_WDT_CC13XX_CC26XX_INITIAL_TIMEOUT), \
|
||||||
|
.cb = NULL, \
|
||||||
|
.flags = 0, \
|
||||||
|
}; \
|
||||||
|
static struct wdt_cc13xx_cc26xx_cfg wdt_cc13xx_cc26xx_cfg_##index = { \
|
||||||
|
.reg = DT_INST_REG_ADDR(index), \
|
||||||
|
.irq_nmi = DT_INST_PROP(index, interrupt_nmi), \
|
||||||
|
.irq_cfg_func = wdt_cc13xx_cc26xx_irq_cfg_##index, \
|
||||||
|
}; \
|
||||||
|
DEVICE_DT_INST_DEFINE(index, \
|
||||||
|
wdt_cc13xx_cc26xx_init, NULL, \
|
||||||
|
&wdt_cc13xx_cc26xx_data_##index, \
|
||||||
|
&wdt_cc13xx_cc26xx_cfg_##index, \
|
||||||
|
POST_KERNEL, CONFIG_KERNEL_INIT_PRIORITY_DEFAULT, \
|
||||||
|
&wdt_cc13xx_cc26xx_api);
|
||||||
|
|
||||||
|
DT_INST_FOREACH_STATUS_OKAY(CC13XX_CC26XX_WDT_INIT)
|
|
@ -150,6 +150,13 @@
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
wdt0: watchdog@40080000 {
|
||||||
|
compatible = "ti,cc13xx-cc26xx-watchdog";
|
||||||
|
reg = <0x40080000 0x1000>;
|
||||||
|
interrupts = <14 0>; /* interrupt #30 = 14 + 16 */
|
||||||
|
status = "disabled";
|
||||||
|
};
|
||||||
|
|
||||||
adc0: adc@400cb008 {
|
adc0: adc@400cb008 {
|
||||||
compatible = "ti,cc13xx-cc26xx-adc";
|
compatible = "ti,cc13xx-cc26xx-adc";
|
||||||
reg = <0x400cb008 0x1>;
|
reg = <0x400cb008 0x1>;
|
||||||
|
|
|
@ -0,0 +1,20 @@
|
||||||
|
# Copyright (c) 2022 Florin Stancu <niflostancu@gmail.com>
|
||||||
|
# SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
|
description: TI CC13xx/CC26xx watchdog
|
||||||
|
|
||||||
|
compatible: "ti,cc13xx-cc26xx-watchdog"
|
||||||
|
|
||||||
|
include: base.yaml
|
||||||
|
|
||||||
|
properties:
|
||||||
|
reg:
|
||||||
|
required: true
|
||||||
|
|
||||||
|
interrupts:
|
||||||
|
required: true
|
||||||
|
|
||||||
|
interrupt-nmi:
|
||||||
|
type: boolean
|
||||||
|
description: |
|
||||||
|
Whether the watchdog triggers a Non-Maskable Interrupt or a standard one
|
Loading…
Reference in New Issue