zephyr/drivers/watchdog/wdt_gecko.c
Tomasz Bursztyka 4dcfb5531c 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-09-02 13:48:13 +02:00

311 lines
7.4 KiB
C

/*
* Copyright (c) 2019 Interay Solutions B.V.
* Copyright (c) 2019 Oane Kingma
*
* SPDX-License-Identifier: Apache-2.0
*/
#define DT_DRV_COMPAT silabs_gecko_wdog
#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);
#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)
/* 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;
CMU_Clock_TypeDef clock;
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)->name)
#define DEV_DATA(dev) \
((struct wdt_gecko_data *)(dev)->data)
#define DEV_CFG(dev) \
((const struct wdt_gecko_cfg *)(dev)->config)
static uint32_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(uint32_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(uint32_t window, uint32_t period)
{
int idx = 0;
uint32_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(const struct device *dev, uint8_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(const 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(const struct device *dev,
const struct wdt_timeout_cfg *cfg)
{
struct wdt_gecko_data *data = DEV_DATA(dev);
data->wdog_config = (WDOG_Init_TypeDef)WDOG_INIT_DEFAULT;
uint32_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;
}
#if defined(_WDOG_CTRL_CLKSEL_MASK)
data->wdog_config.clkSel = wdogClkSelULFRCO;
#endif
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(const 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(const 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;
uint32_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(const 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);
#if !defined(_SILICON_LABS_32B_SERIES_2)
/* Ensure LE modules are clocked */
CMU_ClockEnable(config->clock, true);
#else
CMU_ClockSelectSet(config->clock, cmuSelect_ULFRCO);
#endif
/* 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_REG_ADDR(index),\
.clock = CLOCK_ID(DT_INST_PROP(index, peripheral_id)), \
.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_LABEL(index),\
&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_IRQN(index), \
DT_INST_IRQ(index, priority),\
wdt_gecko_isr, DEVICE_GET(wdt_##index), 0); \
irq_enable(DT_INST_IRQN(index)); \
}
DT_INST_FOREACH_STATUS_OKAY(GECKO_WDT_INIT)