drivers: watchdog: implement SiFive FE310 watchdog driver
This patch adds watchdog driver for HiFive1 rev.B that has SiFive Freedom E310 SoC. Signed-off-by: Katsuhiro Suzuki <katsuhiro@katsuster.net>
This commit is contained in:
parent
e956639dd6
commit
a688b4da73
|
@ -292,6 +292,7 @@
|
|||
/drivers/i2c/i2c_dw* @dcpleung
|
||||
/drivers/*/*xec* @franciscomunoz @albertofloyd @scottwcpg
|
||||
/drivers/watchdog/*gecko* @oanerer
|
||||
/drivers/watchdog/*sifive* @katsuster
|
||||
/drivers/watchdog/wdt_handlers.c @andrewboie
|
||||
/drivers/wifi/ @jukkar @tbursztyka @pfalcon
|
||||
/drivers/wifi/esp/ @mniestroj
|
||||
|
|
|
@ -13,4 +13,5 @@ zephyr_sources_ifdef(CONFIG_WDT_MCUX_WDOG32 wdt_mcux_wdog32.c)
|
|||
zephyr_sources_ifdef(CONFIG_WDT_MCUX_WWDT wdt_mcux_wwdt.c)
|
||||
zephyr_sources_ifdef(CONFIG_WDT_XEC wdt_mchp_xec.c)
|
||||
zephyr_sources_ifdef(CONFIG_WDT_GECKO wdt_gecko.c)
|
||||
zephyr_sources_ifdef(CONFIG_WDT_SIFIVE wdt_sifive.c)
|
||||
zephyr_sources_ifdef(CONFIG_USERSPACE wdt_handlers.c)
|
||||
|
|
|
@ -50,4 +50,6 @@ source "drivers/watchdog/Kconfig.xec"
|
|||
|
||||
source "drivers/watchdog/Kconfig.gecko"
|
||||
|
||||
source "drivers/watchdog/Kconfig.sifive"
|
||||
|
||||
endif
|
||||
|
|
11
drivers/watchdog/Kconfig.sifive
Normal file
11
drivers/watchdog/Kconfig.sifive
Normal file
|
@ -0,0 +1,11 @@
|
|||
# SiFive Freedom WDT configuration
|
||||
|
||||
# Copyright (C) 2020 Katsuhiro Suzuki
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
config WDT_SIFIVE
|
||||
bool "SiFive Watchdog (WDT) Driver"
|
||||
depends on SOC_RISCV_SIFIVE_FREEDOM
|
||||
default y
|
||||
help
|
||||
This option enables WDT driver for SiFive Freedom.
|
289
drivers/watchdog/wdt_sifive.c
Normal file
289
drivers/watchdog/wdt_sifive.c
Normal file
|
@ -0,0 +1,289 @@
|
|||
/*
|
||||
* Copyright (C) 2020 Katsuhiro Suzuki
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
/**
|
||||
* @brief Watchdog (WDT) Driver for SiFive Freedom
|
||||
*/
|
||||
|
||||
#define DT_DRV_COMPAT sifive_wdt
|
||||
|
||||
#include <kernel.h>
|
||||
#include <soc.h>
|
||||
#include <drivers/watchdog.h>
|
||||
|
||||
#define LOG_LEVEL CONFIG_WDT_LOG_LEVEL
|
||||
#include <logging/log.h>
|
||||
LOG_MODULE_REGISTER(wdt_sifive);
|
||||
|
||||
#define WDOGCFG_SCALE_MAX 0xf
|
||||
#define WDOGCFG_SCALE_SHIFT 0
|
||||
#define WDOGCFG_SCALE_MASK (WDOGCFG_SCALE_MAX << WDOGCFG_SCALE_SHIFT)
|
||||
#define WDOGCFG_RSTEN BIT(8)
|
||||
#define WDOGCFG_ZEROCMP BIT(9)
|
||||
#define WDOGCFG_ENALWAYS BIT(12)
|
||||
#define WDOGCFG_COREAWAKE BIT(13)
|
||||
#define WDOGCFG_IP0 BIT(28)
|
||||
|
||||
#define WDOGCMP_MAX 0xffff
|
||||
|
||||
#define WDOG_KEY 0x51f15e
|
||||
#define WDOG_FEED 0xd09f00d
|
||||
|
||||
#define WDOG_CLK 32768
|
||||
|
||||
struct wdt_sifive_reg {
|
||||
/* offset: 0x000 */
|
||||
uint32_t wdogcfg;
|
||||
uint32_t dummy0;
|
||||
uint32_t wdogcount;
|
||||
uint32_t dummy1;
|
||||
/* offset: 0x010 */
|
||||
uint32_t wdogs;
|
||||
uint32_t dummy2;
|
||||
uint32_t wdogfeed;
|
||||
uint32_t wdogkey;
|
||||
/* offset: 0x020 */
|
||||
uint32_t wdogcmp0;
|
||||
};
|
||||
|
||||
struct wdt_sifive_device_config {
|
||||
uintptr_t regs;
|
||||
};
|
||||
|
||||
struct wdt_sifive_dev_data {
|
||||
wdt_callback_t cb;
|
||||
bool enable_cb;
|
||||
bool timeout_valid;
|
||||
};
|
||||
|
||||
#define DEV_CFG(dev) \
|
||||
((const struct wdt_sifive_device_config *const)(dev)->config)
|
||||
#define DEV_REG(dev) \
|
||||
((struct wdt_sifive_reg *)(DEV_CFG(dev))->regs)
|
||||
|
||||
/**
|
||||
* @brief Set maximum length of timeout to watchdog
|
||||
*
|
||||
* @param dev Watchdog device struct
|
||||
*/
|
||||
static void wdt_sifive_set_max_timeout(const struct device *dev)
|
||||
{
|
||||
volatile struct wdt_sifive_reg *wdt = DEV_REG(dev);
|
||||
uint32_t t;
|
||||
|
||||
t = wdt->wdogcfg;
|
||||
t |= WDOGCFG_SCALE_MASK;
|
||||
|
||||
wdt->wdogkey = WDOG_KEY;
|
||||
wdt->wdogcfg = t;
|
||||
wdt->wdogkey = WDOG_KEY;
|
||||
wdt->wdogcmp0 = WDOGCMP_MAX;
|
||||
}
|
||||
|
||||
static void wdt_sifive_isr(const struct device *dev)
|
||||
{
|
||||
volatile struct wdt_sifive_reg *wdt = DEV_REG(dev);
|
||||
struct wdt_sifive_dev_data *data = dev->data;
|
||||
uint32_t t;
|
||||
|
||||
wdt_sifive_set_max_timeout(dev);
|
||||
|
||||
t = wdt->wdogcfg;
|
||||
t &= ~WDOGCFG_IP0;
|
||||
|
||||
wdt->wdogkey = WDOG_KEY;
|
||||
wdt->wdogcfg = t;
|
||||
|
||||
if (data->enable_cb && data->cb) {
|
||||
data->enable_cb = false;
|
||||
data->cb(dev, 0);
|
||||
}
|
||||
}
|
||||
|
||||
static int wdt_sifive_disable(const struct device *dev)
|
||||
{
|
||||
struct wdt_sifive_dev_data *data = dev->data;
|
||||
|
||||
wdt_sifive_set_max_timeout(dev);
|
||||
|
||||
data->enable_cb = false;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int wdt_sifive_setup(const struct device *dev, uint8_t options)
|
||||
{
|
||||
volatile struct wdt_sifive_reg *wdt = DEV_REG(dev);
|
||||
struct wdt_sifive_dev_data *data = dev->data;
|
||||
uint32_t t, mode;
|
||||
|
||||
if (!data->timeout_valid) {
|
||||
LOG_ERR("No valid timeouts installed");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
mode = WDOGCFG_ENALWAYS;
|
||||
if ((options & WDT_OPT_PAUSE_IN_SLEEP) ==
|
||||
WDT_OPT_PAUSE_IN_SLEEP) {
|
||||
mode = WDOGCFG_COREAWAKE;
|
||||
}
|
||||
if ((options & WDT_OPT_PAUSE_HALTED_BY_DBG) ==
|
||||
WDT_OPT_PAUSE_HALTED_BY_DBG) {
|
||||
mode = WDOGCFG_COREAWAKE;
|
||||
}
|
||||
|
||||
t = wdt->wdogcfg;
|
||||
t &= ~(WDOGCFG_ENALWAYS | WDOGCFG_COREAWAKE);
|
||||
t |= mode;
|
||||
|
||||
wdt->wdogkey = WDOG_KEY;
|
||||
wdt->wdogcfg = t;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Calculates the watchdog counter value (wdogcmp0) and
|
||||
* scaler (wdogscale) to be installed in the watchdog timer
|
||||
*
|
||||
* @param timeout Timeout value in milliseconds.
|
||||
* @param clk Clock of watchdog in Hz.
|
||||
* @param scaler Pointer to return scaler power of 2
|
||||
*
|
||||
* @return Watchdog counter value
|
||||
*/
|
||||
static int wdt_sifive_convtime(uint32_t timeout, int clk, int *scaler)
|
||||
{
|
||||
uint64_t cnt;
|
||||
int i;
|
||||
|
||||
cnt = (uint64_t)timeout * clk / 1000;
|
||||
for (i = 0; i < 16; i++) {
|
||||
if (cnt <= WDOGCMP_MAX) {
|
||||
break;
|
||||
}
|
||||
|
||||
cnt >>= 1;
|
||||
}
|
||||
|
||||
if (i == 16) {
|
||||
/* Maximum counter and scaler */
|
||||
LOG_ERR("Invalid timeout value allowed range");
|
||||
|
||||
*scaler = WDOGCFG_SCALE_MAX;
|
||||
return WDOGCMP_MAX;
|
||||
}
|
||||
|
||||
*scaler = i;
|
||||
|
||||
return cnt;
|
||||
}
|
||||
|
||||
static int wdt_sifive_install_timeout(const struct device *dev,
|
||||
const struct wdt_timeout_cfg *cfg)
|
||||
{
|
||||
volatile struct wdt_sifive_reg *wdt = DEV_REG(dev);
|
||||
struct wdt_sifive_dev_data *data = dev->data;
|
||||
uint32_t mode = 0, t;
|
||||
int cmp, scaler;
|
||||
|
||||
if (data->timeout_valid) {
|
||||
LOG_ERR("No more timeouts can be installed");
|
||||
return -ENOMEM;
|
||||
}
|
||||
if (cfg->window.min != 0U || cfg->window.max == 0U) {
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/*
|
||||
* Freedom watchdog does not support window timeout config.
|
||||
* So use max field of window.
|
||||
*/
|
||||
cmp = wdt_sifive_convtime(cfg->window.max, WDOG_CLK, &scaler);
|
||||
if (cmp < 0 || WDOGCMP_MAX < cmp) {
|
||||
LOG_ERR("Unsupported watchdog timeout\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
switch (cfg->flags) {
|
||||
case WDT_FLAG_RESET_SOC:
|
||||
/* WDT supports global SoC reset but cannot callback. */
|
||||
mode = WDOGCFG_RSTEN | WDOGCFG_ZEROCMP;
|
||||
break;
|
||||
case WDT_FLAG_RESET_NONE:
|
||||
/* No reset */
|
||||
mode = WDOGCFG_ZEROCMP;
|
||||
break;
|
||||
case WDT_FLAG_RESET_CPU_CORE:
|
||||
default:
|
||||
LOG_ERR("Unsupported watchdog config flags\n");
|
||||
|
||||
wdt_sifive_disable(dev);
|
||||
return -ENOTSUP;
|
||||
}
|
||||
|
||||
t = wdt->wdogcfg;
|
||||
t &= ~(WDOGCFG_RSTEN | WDOGCFG_ZEROCMP | WDOGCFG_SCALE_MASK);
|
||||
t |= mode | scaler;
|
||||
|
||||
wdt->wdogkey = WDOG_KEY;
|
||||
wdt->wdogcfg = t;
|
||||
wdt->wdogkey = WDOG_KEY;
|
||||
wdt->wdogcmp0 = cmp;
|
||||
|
||||
data->cb = cfg->callback;
|
||||
data->enable_cb = true;
|
||||
data->timeout_valid = true;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int wdt_sifive_feed(const struct device *dev, int channel_id)
|
||||
{
|
||||
volatile struct wdt_sifive_reg *wdt = DEV_REG(dev);
|
||||
|
||||
wdt->wdogkey = WDOG_KEY;
|
||||
wdt->wdogfeed = WDOG_FEED;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct wdt_driver_api wdt_sifive_api = {
|
||||
.setup = wdt_sifive_setup,
|
||||
.disable = wdt_sifive_disable,
|
||||
.install_timeout = wdt_sifive_install_timeout,
|
||||
.feed = wdt_sifive_feed,
|
||||
};
|
||||
|
||||
DEVICE_DT_INST_DECLARE(0);
|
||||
|
||||
static void wdt_sifive_irq_config(void)
|
||||
{
|
||||
IRQ_CONNECT(DT_INST_IRQN(0),
|
||||
DT_INST_IRQ(0, priority), wdt_sifive_isr,
|
||||
DEVICE_DT_INST_GET(0), 0);
|
||||
irq_enable(DT_INST_IRQN(0));
|
||||
}
|
||||
|
||||
static int wdt_sifive_init(const struct device *dev)
|
||||
{
|
||||
#ifdef CONFIG_WDT_DISABLE_AT_BOOT
|
||||
wdt_sifive_disable(dev);
|
||||
#endif
|
||||
wdt_sifive_irq_config();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct wdt_sifive_dev_data wdt_sifive_data;
|
||||
|
||||
static const struct wdt_sifive_device_config wdt_sifive_cfg = {
|
||||
.regs = DT_INST_REG_ADDR(0),
|
||||
};
|
||||
|
||||
DEVICE_DT_INST_DEFINE(0, wdt_sifive_init, device_pm_control_nop,
|
||||
&wdt_sifive_data, &wdt_sifive_cfg, PRE_KERNEL_1,
|
||||
CONFIG_KERNEL_INIT_PRIORITY_DEVICE, &wdt_sifive_api);
|
Loading…
Reference in a new issue