2019-11-04 16:08:24 +01:00
|
|
|
/*
|
|
|
|
* Copyright (c) 2019 Interay Solutions B.V.
|
|
|
|
* Copyright (c) 2019 Oane Kingma
|
|
|
|
*
|
|
|
|
* SPDX-License-Identifier: Apache-2.0
|
|
|
|
*/
|
|
|
|
|
2020-03-24 14:49:56 -05:00
|
|
|
#define DT_DRV_COMPAT silabs_gecko_wdog
|
|
|
|
|
2019-11-04 16:08:24 +01:00
|
|
|
#include <soc.h>
|
2022-05-06 10:25:46 +02:00
|
|
|
#include <zephyr/drivers/watchdog.h>
|
2022-10-12 12:05:10 +02:00
|
|
|
#include <zephyr/irq.h>
|
2019-11-04 16:08:24 +01:00
|
|
|
#include <em_wdog.h>
|
|
|
|
#include <em_cmu.h>
|
|
|
|
|
2022-05-06 10:25:46 +02:00
|
|
|
#include <zephyr/logging/log.h>
|
2022-10-17 10:24:11 +02:00
|
|
|
#include <zephyr/irq.h>
|
2019-11-04 16:08:24 +01:00
|
|
|
LOG_MODULE_REGISTER(wdt_gecko, CONFIG_WDT_LOG_LEVEL);
|
|
|
|
|
2020-07-14 11:56:11 +02:00
|
|
|
#ifdef cmuClock_CORELE
|
|
|
|
#define CLOCK_DEF(id) cmuClock_CORELE
|
|
|
|
#else
|
|
|
|
#define CLOCK_DEF(id) cmuClock_WDOG##id
|
|
|
|
#endif /* cmuClock_CORELE */
|
|
|
|
#define CLOCK_ID(id) CLOCK_DEF(id)
|
|
|
|
|
2019-11-04 16:08:24 +01:00
|
|
|
/* 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;
|
2020-07-14 11:56:11 +02:00
|
|
|
CMU_Clock_TypeDef clock;
|
2019-11-04 16:08:24 +01:00
|
|
|
void (*irq_cfg_func)(void);
|
|
|
|
};
|
|
|
|
|
|
|
|
struct wdt_gecko_data {
|
|
|
|
wdt_callback_t callback;
|
|
|
|
WDOG_Init_TypeDef wdog_config;
|
|
|
|
bool timeout_installed;
|
|
|
|
};
|
|
|
|
|
2020-05-27 11:26:57 -05:00
|
|
|
static uint32_t wdt_gecko_get_timeout_from_persel(int perSel)
|
2019-11-04 16:08:24 +01:00
|
|
|
{
|
|
|
|
return (8 << perSel) + 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Find the rounded up value of cycles for supplied timeout. When using ULFRCO
|
|
|
|
* (default), 1 cycle is 1 ms +/- 12%.
|
|
|
|
*/
|
2020-05-27 11:26:57 -05:00
|
|
|
static int wdt_gecko_get_persel_from_timeout(uint32_t timeout)
|
2019-11-04 16:08:24 +01:00
|
|
|
{
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2020-05-27 11:26:57 -05:00
|
|
|
static int wdt_gecko_convert_window(uint32_t window, uint32_t period)
|
2019-11-04 16:08:24 +01:00
|
|
|
{
|
|
|
|
int idx = 0;
|
2020-05-27 11:26:57 -05:00
|
|
|
uint32_t incr_val, comp_val;
|
2019-11-04 16:08:24 +01:00
|
|
|
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2020-04-30 20:33:38 +02:00
|
|
|
static int wdt_gecko_setup(const struct device *dev, uint8_t options)
|
2019-11-04 16:08:24 +01:00
|
|
|
{
|
2022-01-18 17:00:11 +01:00
|
|
|
const struct wdt_gecko_cfg *config = dev->config;
|
|
|
|
struct wdt_gecko_data *data = dev->data;
|
2019-11-04 16:08:24 +01:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2020-04-30 20:33:38 +02:00
|
|
|
static int wdt_gecko_disable(const struct device *dev)
|
2019-11-04 16:08:24 +01:00
|
|
|
{
|
2022-01-18 17:00:11 +01:00
|
|
|
const struct wdt_gecko_cfg *config = dev->config;
|
|
|
|
struct wdt_gecko_data *data = dev->data;
|
2019-11-04 16:08:24 +01:00
|
|
|
WDOG_TypeDef *wdog = config->base;
|
|
|
|
|
|
|
|
WDOGn_Enable(wdog, false);
|
|
|
|
data->timeout_installed = false;
|
|
|
|
LOG_DBG("Disabled the watchdog");
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2020-04-30 20:33:38 +02:00
|
|
|
static int wdt_gecko_install_timeout(const struct device *dev,
|
2019-11-04 16:08:24 +01:00
|
|
|
const struct wdt_timeout_cfg *cfg)
|
|
|
|
{
|
2022-01-18 17:00:11 +01:00
|
|
|
struct wdt_gecko_data *data = dev->data;
|
2020-04-02 00:19:44 +02:00
|
|
|
data->wdog_config = (WDOG_Init_TypeDef)WDOG_INIT_DEFAULT;
|
2020-05-27 11:26:57 -05:00
|
|
|
uint32_t installed_timeout;
|
2019-11-04 16:08:24 +01:00
|
|
|
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2020-04-02 00:19:44 +02:00
|
|
|
#if defined(_WDOG_CTRL_CLKSEL_MASK)
|
|
|
|
data->wdog_config.clkSel = wdogClkSelULFRCO;
|
|
|
|
#endif
|
2019-11-04 16:08:24 +01:00
|
|
|
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2020-04-30 20:33:38 +02:00
|
|
|
static int wdt_gecko_feed(const struct device *dev, int channel_id)
|
2019-11-04 16:08:24 +01:00
|
|
|
{
|
2022-01-18 17:00:11 +01:00
|
|
|
const struct wdt_gecko_cfg *config = dev->config;
|
2019-11-04 16:08:24 +01:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
isr: Normalize usage of device instance through ISR
The goal of this patch is to replace the 'void *' parameter by 'struct
device *' if they use such variable or just 'const void *' on all
relevant ISRs
This will avoid not-so-nice const qualifier tweaks when device instances
will be constant.
Note that only the ISR passed to IRQ_CONNECT are of interest here.
In order to do so, the script fix_isr.py below is necessary:
from pathlib import Path
import subprocess
import pickle
import mmap
import sys
import re
import os
cocci_template = """
@r_fix_isr_0
@
type ret_type;
identifier P;
identifier D;
@@
-ret_type <!fn!>(void *P)
+ret_type <!fn!>(const struct device *P)
{
...
(
const struct device *D = (const struct device *)P;
|
const struct device *D = P;
)
...
}
@r_fix_isr_1
@
type ret_type;
identifier P;
identifier D;
@@
-ret_type <!fn!>(void *P)
+ret_type <!fn!>(const struct device *P)
{
...
const struct device *D;
...
(
D = (const struct device *)P;
|
D = P;
)
...
}
@r_fix_isr_2
@
type ret_type;
identifier A;
@@
-ret_type <!fn!>(void *A)
+ret_type <!fn!>(const void *A)
{
...
}
@r_fix_isr_3
@
const struct device *D;
@@
-<!fn!>((void *)D);
+<!fn!>(D);
@r_fix_isr_4
@
type ret_type;
identifier D;
identifier P;
@@
-ret_type <!fn!>(const struct device *P)
+ret_type <!fn!>(const struct device *D)
{
...
(
-const struct device *D = (const struct device *)P;
|
-const struct device *D = P;
)
...
}
@r_fix_isr_5
@
type ret_type;
identifier D;
identifier P;
@@
-ret_type <!fn!>(const struct device *P)
+ret_type <!fn!>(const struct device *D)
{
...
-const struct device *D;
...
(
-D = (const struct device *)P;
|
-D = P;
)
...
}
"""
def find_isr(fn):
db = []
data = None
start = 0
try:
with open(fn, 'r+') as f:
data = str(mmap.mmap(f.fileno(), 0).read())
except Exception as e:
return db
while True:
isr = ""
irq = data.find('IRQ_CONNECT', start)
while irq > -1:
p = 1
arg = 1
p_o = data.find('(', irq)
if p_o < 0:
irq = -1
break;
pos = p_o + 1
while p > 0:
if data[pos] == ')':
p -= 1
elif data[pos] == '(':
p += 1
elif data[pos] == ',' and p == 1:
arg += 1
if arg == 3:
isr += data[pos]
pos += 1
isr = isr.strip(',\\n\\t ')
if isr not in db and len(isr) > 0:
db.append(isr)
start = pos
break
if irq < 0:
break
return db
def patch_isr(fn, isr_list):
if len(isr_list) <= 0:
return
for isr in isr_list:
tmplt = cocci_template.replace('<!fn!>', isr)
with open('/tmp/isr_fix.cocci', 'w') as f:
f.write(tmplt)
cmd = ['spatch', '--sp-file', '/tmp/isr_fix.cocci', '--in-place', fn]
subprocess.run(cmd)
def process_files(path):
if path.is_file() and path.suffix in ['.h', '.c']:
p = str(path.parent) + '/' + path.name
isr_list = find_isr(p)
patch_isr(p, isr_list)
elif path.is_dir():
for p in path.iterdir():
process_files(p)
if len(sys.argv) < 2:
print("You need to provide a dir/file path")
sys.exit(1)
process_files(Path(sys.argv[1]))
And is run: ./fix_isr.py <zephyr root directory>
Finally, some files needed manual fixes such.
Fixes #27399
Signed-off-by: Tomasz Bursztyka <tomasz.bursztyka@linux.intel.com>
2020-06-17 14:58:56 +02:00
|
|
|
static void wdt_gecko_isr(const struct device *dev)
|
2019-11-04 16:08:24 +01:00
|
|
|
{
|
2022-01-18 17:00:11 +01:00
|
|
|
const struct wdt_gecko_cfg *config = dev->config;
|
|
|
|
struct wdt_gecko_data *data = dev->data;
|
2019-11-04 16:08:24 +01:00
|
|
|
WDOG_TypeDef *wdog = config->base;
|
2020-05-27 11:26:57 -05:00
|
|
|
uint32_t flags;
|
2019-11-04 16:08:24 +01:00
|
|
|
|
|
|
|
/* Clear IRQ flags */
|
|
|
|
flags = WDOGn_IntGet(wdog);
|
|
|
|
WDOGn_IntClear(wdog, flags);
|
|
|
|
|
|
|
|
if (data->callback != NULL) {
|
|
|
|
data->callback(dev, 0);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-04-30 20:33:38 +02:00
|
|
|
static int wdt_gecko_init(const struct device *dev)
|
2019-11-04 16:08:24 +01:00
|
|
|
{
|
2022-01-18 17:00:11 +01:00
|
|
|
const struct wdt_gecko_cfg *config = dev->config;
|
2019-11-04 16:08:24 +01:00
|
|
|
|
|
|
|
#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 */
|
2020-07-14 11:56:11 +02:00
|
|
|
CMU_ClockEnable(config->clock, true);
|
2022-12-30 16:10:44 +01:00
|
|
|
|
|
|
|
#if defined(_SILICON_LABS_32B_SERIES_2)
|
2020-07-14 11:56:11 +02:00
|
|
|
CMU_ClockSelectSet(config->clock, cmuSelect_ULFRCO);
|
2023-01-09 15:08:08 +01:00
|
|
|
/* Enable Watchdog clock. */
|
|
|
|
CMU_ClockEnable(cmuClock_WDOG0, true);
|
2020-07-14 11:56:11 +02:00
|
|
|
#endif
|
2019-11-04 16:08:24 +01:00
|
|
|
|
|
|
|
/* Enable IRQs */
|
|
|
|
config->irq_cfg_func();
|
|
|
|
|
2022-03-15 20:13:24 +01:00
|
|
|
LOG_INF("Device %s initialized", dev->name);
|
2019-11-04 16:08:24 +01:00
|
|
|
|
|
|
|
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 *) \
|
2020-03-24 14:49:56 -05:00
|
|
|
DT_INST_REG_ADDR(index),\
|
2020-07-14 11:56:11 +02:00
|
|
|
.clock = CLOCK_ID(DT_INST_PROP(index, peripheral_id)), \
|
2019-11-04 16:08:24 +01:00
|
|
|
.irq_cfg_func = wdt_gecko_cfg_func_##index, \
|
|
|
|
}; \
|
|
|
|
static struct wdt_gecko_data wdt_gecko_data_##index; \
|
|
|
|
\
|
2020-12-10 08:49:59 -06:00
|
|
|
DEVICE_DT_INST_DEFINE(index, \
|
2021-04-28 12:07:15 +02:00
|
|
|
&wdt_gecko_init, NULL, \
|
2020-12-10 08:49:59 -06:00
|
|
|
&wdt_gecko_data_##index, \
|
2019-11-04 16:08:24 +01:00
|
|
|
&wdt_gecko_cfg_##index, POST_KERNEL, \
|
|
|
|
CONFIG_KERNEL_INIT_PRIORITY_DEFAULT, \
|
|
|
|
&wdt_gecko_driver_api); \
|
|
|
|
\
|
|
|
|
static void wdt_gecko_cfg_func_##index(void) \
|
|
|
|
{ \
|
2020-03-24 14:49:56 -05:00
|
|
|
IRQ_CONNECT(DT_INST_IRQN(index), \
|
|
|
|
DT_INST_IRQ(index, priority),\
|
2020-12-10 08:49:59 -06:00
|
|
|
wdt_gecko_isr, DEVICE_DT_INST_GET(index), 0); \
|
2020-03-24 14:49:56 -05:00
|
|
|
irq_enable(DT_INST_IRQN(index)); \
|
2019-11-04 16:08:24 +01:00
|
|
|
}
|
|
|
|
|
2020-05-06 11:23:07 -07:00
|
|
|
DT_INST_FOREACH_STATUS_OKAY(GECKO_WDT_INIT)
|