Skip to content

Commit a688b4d

Browse files
katsustergalak
authored andcommitted
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 <[email protected]>
1 parent e956639 commit a688b4d

File tree

5 files changed

+304
-0
lines changed

5 files changed

+304
-0
lines changed

CODEOWNERS

+1
Original file line numberDiff line numberDiff line change
@@ -292,6 +292,7 @@
292292
/drivers/i2c/i2c_dw* @dcpleung
293293
/drivers/*/*xec* @franciscomunoz @albertofloyd @scottwcpg
294294
/drivers/watchdog/*gecko* @oanerer
295+
/drivers/watchdog/*sifive* @katsuster
295296
/drivers/watchdog/wdt_handlers.c @andrewboie
296297
/drivers/wifi/ @jukkar @tbursztyka @pfalcon
297298
/drivers/wifi/esp/ @mniestroj

drivers/watchdog/CMakeLists.txt

+1
Original file line numberDiff line numberDiff line change
@@ -13,4 +13,5 @@ zephyr_sources_ifdef(CONFIG_WDT_MCUX_WDOG32 wdt_mcux_wdog32.c)
1313
zephyr_sources_ifdef(CONFIG_WDT_MCUX_WWDT wdt_mcux_wwdt.c)
1414
zephyr_sources_ifdef(CONFIG_WDT_XEC wdt_mchp_xec.c)
1515
zephyr_sources_ifdef(CONFIG_WDT_GECKO wdt_gecko.c)
16+
zephyr_sources_ifdef(CONFIG_WDT_SIFIVE wdt_sifive.c)
1617
zephyr_sources_ifdef(CONFIG_USERSPACE wdt_handlers.c)

drivers/watchdog/Kconfig

+2
Original file line numberDiff line numberDiff line change
@@ -50,4 +50,6 @@ source "drivers/watchdog/Kconfig.xec"
5050

5151
source "drivers/watchdog/Kconfig.gecko"
5252

53+
source "drivers/watchdog/Kconfig.sifive"
54+
5355
endif

drivers/watchdog/Kconfig.sifive

+11
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
# SiFive Freedom WDT configuration
2+
3+
# Copyright (C) 2020 Katsuhiro Suzuki
4+
# SPDX-License-Identifier: Apache-2.0
5+
6+
config WDT_SIFIVE
7+
bool "SiFive Watchdog (WDT) Driver"
8+
depends on SOC_RISCV_SIFIVE_FREEDOM
9+
default y
10+
help
11+
This option enables WDT driver for SiFive Freedom.

drivers/watchdog/wdt_sifive.c

+289
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,289 @@
1+
/*
2+
* Copyright (C) 2020 Katsuhiro Suzuki
3+
*
4+
* SPDX-License-Identifier: Apache-2.0
5+
*/
6+
7+
/**
8+
* @brief Watchdog (WDT) Driver for SiFive Freedom
9+
*/
10+
11+
#define DT_DRV_COMPAT sifive_wdt
12+
13+
#include <kernel.h>
14+
#include <soc.h>
15+
#include <drivers/watchdog.h>
16+
17+
#define LOG_LEVEL CONFIG_WDT_LOG_LEVEL
18+
#include <logging/log.h>
19+
LOG_MODULE_REGISTER(wdt_sifive);
20+
21+
#define WDOGCFG_SCALE_MAX 0xf
22+
#define WDOGCFG_SCALE_SHIFT 0
23+
#define WDOGCFG_SCALE_MASK (WDOGCFG_SCALE_MAX << WDOGCFG_SCALE_SHIFT)
24+
#define WDOGCFG_RSTEN BIT(8)
25+
#define WDOGCFG_ZEROCMP BIT(9)
26+
#define WDOGCFG_ENALWAYS BIT(12)
27+
#define WDOGCFG_COREAWAKE BIT(13)
28+
#define WDOGCFG_IP0 BIT(28)
29+
30+
#define WDOGCMP_MAX 0xffff
31+
32+
#define WDOG_KEY 0x51f15e
33+
#define WDOG_FEED 0xd09f00d
34+
35+
#define WDOG_CLK 32768
36+
37+
struct wdt_sifive_reg {
38+
/* offset: 0x000 */
39+
uint32_t wdogcfg;
40+
uint32_t dummy0;
41+
uint32_t wdogcount;
42+
uint32_t dummy1;
43+
/* offset: 0x010 */
44+
uint32_t wdogs;
45+
uint32_t dummy2;
46+
uint32_t wdogfeed;
47+
uint32_t wdogkey;
48+
/* offset: 0x020 */
49+
uint32_t wdogcmp0;
50+
};
51+
52+
struct wdt_sifive_device_config {
53+
uintptr_t regs;
54+
};
55+
56+
struct wdt_sifive_dev_data {
57+
wdt_callback_t cb;
58+
bool enable_cb;
59+
bool timeout_valid;
60+
};
61+
62+
#define DEV_CFG(dev) \
63+
((const struct wdt_sifive_device_config *const)(dev)->config)
64+
#define DEV_REG(dev) \
65+
((struct wdt_sifive_reg *)(DEV_CFG(dev))->regs)
66+
67+
/**
68+
* @brief Set maximum length of timeout to watchdog
69+
*
70+
* @param dev Watchdog device struct
71+
*/
72+
static void wdt_sifive_set_max_timeout(const struct device *dev)
73+
{
74+
volatile struct wdt_sifive_reg *wdt = DEV_REG(dev);
75+
uint32_t t;
76+
77+
t = wdt->wdogcfg;
78+
t |= WDOGCFG_SCALE_MASK;
79+
80+
wdt->wdogkey = WDOG_KEY;
81+
wdt->wdogcfg = t;
82+
wdt->wdogkey = WDOG_KEY;
83+
wdt->wdogcmp0 = WDOGCMP_MAX;
84+
}
85+
86+
static void wdt_sifive_isr(const struct device *dev)
87+
{
88+
volatile struct wdt_sifive_reg *wdt = DEV_REG(dev);
89+
struct wdt_sifive_dev_data *data = dev->data;
90+
uint32_t t;
91+
92+
wdt_sifive_set_max_timeout(dev);
93+
94+
t = wdt->wdogcfg;
95+
t &= ~WDOGCFG_IP0;
96+
97+
wdt->wdogkey = WDOG_KEY;
98+
wdt->wdogcfg = t;
99+
100+
if (data->enable_cb && data->cb) {
101+
data->enable_cb = false;
102+
data->cb(dev, 0);
103+
}
104+
}
105+
106+
static int wdt_sifive_disable(const struct device *dev)
107+
{
108+
struct wdt_sifive_dev_data *data = dev->data;
109+
110+
wdt_sifive_set_max_timeout(dev);
111+
112+
data->enable_cb = false;
113+
114+
return 0;
115+
}
116+
117+
static int wdt_sifive_setup(const struct device *dev, uint8_t options)
118+
{
119+
volatile struct wdt_sifive_reg *wdt = DEV_REG(dev);
120+
struct wdt_sifive_dev_data *data = dev->data;
121+
uint32_t t, mode;
122+
123+
if (!data->timeout_valid) {
124+
LOG_ERR("No valid timeouts installed");
125+
return -EINVAL;
126+
}
127+
128+
mode = WDOGCFG_ENALWAYS;
129+
if ((options & WDT_OPT_PAUSE_IN_SLEEP) ==
130+
WDT_OPT_PAUSE_IN_SLEEP) {
131+
mode = WDOGCFG_COREAWAKE;
132+
}
133+
if ((options & WDT_OPT_PAUSE_HALTED_BY_DBG) ==
134+
WDT_OPT_PAUSE_HALTED_BY_DBG) {
135+
mode = WDOGCFG_COREAWAKE;
136+
}
137+
138+
t = wdt->wdogcfg;
139+
t &= ~(WDOGCFG_ENALWAYS | WDOGCFG_COREAWAKE);
140+
t |= mode;
141+
142+
wdt->wdogkey = WDOG_KEY;
143+
wdt->wdogcfg = t;
144+
145+
return 0;
146+
}
147+
148+
/**
149+
* @brief Calculates the watchdog counter value (wdogcmp0) and
150+
* scaler (wdogscale) to be installed in the watchdog timer
151+
*
152+
* @param timeout Timeout value in milliseconds.
153+
* @param clk Clock of watchdog in Hz.
154+
* @param scaler Pointer to return scaler power of 2
155+
*
156+
* @return Watchdog counter value
157+
*/
158+
static int wdt_sifive_convtime(uint32_t timeout, int clk, int *scaler)
159+
{
160+
uint64_t cnt;
161+
int i;
162+
163+
cnt = (uint64_t)timeout * clk / 1000;
164+
for (i = 0; i < 16; i++) {
165+
if (cnt <= WDOGCMP_MAX) {
166+
break;
167+
}
168+
169+
cnt >>= 1;
170+
}
171+
172+
if (i == 16) {
173+
/* Maximum counter and scaler */
174+
LOG_ERR("Invalid timeout value allowed range");
175+
176+
*scaler = WDOGCFG_SCALE_MAX;
177+
return WDOGCMP_MAX;
178+
}
179+
180+
*scaler = i;
181+
182+
return cnt;
183+
}
184+
185+
static int wdt_sifive_install_timeout(const struct device *dev,
186+
const struct wdt_timeout_cfg *cfg)
187+
{
188+
volatile struct wdt_sifive_reg *wdt = DEV_REG(dev);
189+
struct wdt_sifive_dev_data *data = dev->data;
190+
uint32_t mode = 0, t;
191+
int cmp, scaler;
192+
193+
if (data->timeout_valid) {
194+
LOG_ERR("No more timeouts can be installed");
195+
return -ENOMEM;
196+
}
197+
if (cfg->window.min != 0U || cfg->window.max == 0U) {
198+
return -EINVAL;
199+
}
200+
201+
/*
202+
* Freedom watchdog does not support window timeout config.
203+
* So use max field of window.
204+
*/
205+
cmp = wdt_sifive_convtime(cfg->window.max, WDOG_CLK, &scaler);
206+
if (cmp < 0 || WDOGCMP_MAX < cmp) {
207+
LOG_ERR("Unsupported watchdog timeout\n");
208+
return -EINVAL;
209+
}
210+
211+
switch (cfg->flags) {
212+
case WDT_FLAG_RESET_SOC:
213+
/* WDT supports global SoC reset but cannot callback. */
214+
mode = WDOGCFG_RSTEN | WDOGCFG_ZEROCMP;
215+
break;
216+
case WDT_FLAG_RESET_NONE:
217+
/* No reset */
218+
mode = WDOGCFG_ZEROCMP;
219+
break;
220+
case WDT_FLAG_RESET_CPU_CORE:
221+
default:
222+
LOG_ERR("Unsupported watchdog config flags\n");
223+
224+
wdt_sifive_disable(dev);
225+
return -ENOTSUP;
226+
}
227+
228+
t = wdt->wdogcfg;
229+
t &= ~(WDOGCFG_RSTEN | WDOGCFG_ZEROCMP | WDOGCFG_SCALE_MASK);
230+
t |= mode | scaler;
231+
232+
wdt->wdogkey = WDOG_KEY;
233+
wdt->wdogcfg = t;
234+
wdt->wdogkey = WDOG_KEY;
235+
wdt->wdogcmp0 = cmp;
236+
237+
data->cb = cfg->callback;
238+
data->enable_cb = true;
239+
data->timeout_valid = true;
240+
241+
return 0;
242+
}
243+
244+
static int wdt_sifive_feed(const struct device *dev, int channel_id)
245+
{
246+
volatile struct wdt_sifive_reg *wdt = DEV_REG(dev);
247+
248+
wdt->wdogkey = WDOG_KEY;
249+
wdt->wdogfeed = WDOG_FEED;
250+
251+
return 0;
252+
}
253+
254+
static const struct wdt_driver_api wdt_sifive_api = {
255+
.setup = wdt_sifive_setup,
256+
.disable = wdt_sifive_disable,
257+
.install_timeout = wdt_sifive_install_timeout,
258+
.feed = wdt_sifive_feed,
259+
};
260+
261+
DEVICE_DT_INST_DECLARE(0);
262+
263+
static void wdt_sifive_irq_config(void)
264+
{
265+
IRQ_CONNECT(DT_INST_IRQN(0),
266+
DT_INST_IRQ(0, priority), wdt_sifive_isr,
267+
DEVICE_DT_INST_GET(0), 0);
268+
irq_enable(DT_INST_IRQN(0));
269+
}
270+
271+
static int wdt_sifive_init(const struct device *dev)
272+
{
273+
#ifdef CONFIG_WDT_DISABLE_AT_BOOT
274+
wdt_sifive_disable(dev);
275+
#endif
276+
wdt_sifive_irq_config();
277+
278+
return 0;
279+
}
280+
281+
static struct wdt_sifive_dev_data wdt_sifive_data;
282+
283+
static const struct wdt_sifive_device_config wdt_sifive_cfg = {
284+
.regs = DT_INST_REG_ADDR(0),
285+
};
286+
287+
DEVICE_DT_INST_DEFINE(0, wdt_sifive_init, device_pm_control_nop,
288+
&wdt_sifive_data, &wdt_sifive_cfg, PRE_KERNEL_1,
289+
CONFIG_KERNEL_INIT_PRIORITY_DEVICE, &wdt_sifive_api);

0 commit comments

Comments
 (0)