1 /*
2 * Copyright (C) 2024 Vogl Electronic GmbH
3 *
4 * SPDX-License-Identifier: Apache-2.0
5 */
6
7 #define DT_DRV_COMPAT litex_watchdog
8
9 #include <zephyr/kernel.h>
10 #include <zephyr/drivers/watchdog.h>
11 #include <zephyr/device.h>
12 #include <zephyr/sys_clock.h>
13
14 #include <zephyr/logging/log.h>
15 LOG_MODULE_REGISTER(wdt_litex, CONFIG_WDT_LOG_LEVEL);
16
17 #include <soc.h>
18
19 struct wdt_litex_data {
20 wdt_callback_t callback;
21 uint32_t timeout;
22 bool reset_soc_mode;
23 bool pause_halted;
24 };
25
26 struct wdt_litex_config {
27 uint32_t control_addr;
28 uint32_t cycles_addr;
29 uint32_t cycles_size;
30 uint32_t remaining_addr;
31 uint32_t ev_status_addr;
32 uint32_t ev_pending_addr;
33 uint32_t ev_enable_addr;
34 void (*irq_cfg_func)(void);
35 };
36
37 #define CONTROL_FEED_BIT BIT(0)
38 #define CONTROL_ENABLE_BIT BIT(8)
39 #define CONTROL_RESET_BIT BIT(16)
40 #define CONTROL_PAUSE_HALTED_BIT BIT(24)
41
wdt_litex_is_enabled(const struct device * dev)42 static bool wdt_litex_is_enabled(const struct device *dev)
43 {
44 const struct wdt_litex_config *config = dev->config;
45
46 return litex_read8(config->control_addr) & BIT(0);
47 }
48
wdt_litex_irq_enable(const struct device * dev)49 static void wdt_litex_irq_enable(const struct device *dev)
50 {
51 const struct wdt_litex_config *config = dev->config;
52 struct wdt_litex_data *data = dev->data;
53
54 if (!data->callback) {
55 return;
56 }
57
58 litex_write8(BIT(0), config->ev_pending_addr);
59
60 litex_write8(BIT(0), config->ev_enable_addr);
61 }
62
wdt_litex_enable(const struct device * dev)63 static void wdt_litex_enable(const struct device *dev)
64 {
65 const struct wdt_litex_config *config = dev->config;
66 struct wdt_litex_data *data = dev->data;
67 uint32_t control;
68
69 if (config->cycles_size <= 4) {
70 litex_write32(k_ms_to_cyc_floor32(data->timeout), config->cycles_addr);
71 } else {
72 litex_write64(k_ms_to_cyc_floor64(data->timeout), config->cycles_addr);
73 }
74
75 control = CONTROL_FEED_BIT | CONTROL_ENABLE_BIT;
76
77 if (data->reset_soc_mode) {
78 control |= CONTROL_RESET_BIT;
79 }
80 if (data->pause_halted) {
81 control |= CONTROL_PAUSE_HALTED_BIT;
82 }
83
84 litex_write32(control, config->control_addr);
85
86 wdt_litex_irq_enable(dev);
87 }
88
wdt_litex_disable(const struct device * dev)89 static int wdt_litex_disable(const struct device *dev)
90 {
91 const struct wdt_litex_config *config = dev->config;
92
93 litex_write8(0, config->ev_enable_addr);
94
95 if (!wdt_litex_is_enabled(dev)) {
96 return -EFAULT;
97 }
98 litex_write16(CONTROL_ENABLE_BIT, config->control_addr);
99
100 return 0;
101 }
102
wdt_litex_feed(const struct device * dev,int channel_id)103 static int wdt_litex_feed(const struct device *dev, int channel_id)
104 {
105 const struct wdt_litex_config *config = dev->config;
106
107 if (channel_id != 0) {
108 return -EINVAL;
109 }
110
111 litex_write8(CONTROL_FEED_BIT, config->control_addr);
112
113 return 0;
114 }
115
wdt_litex_setup(const struct device * dev,uint8_t options)116 static int wdt_litex_setup(const struct device *dev, uint8_t options)
117 {
118 struct wdt_litex_data *data = dev->data;
119
120 data->pause_halted = !!(options & WDT_OPT_PAUSE_HALTED_BY_DBG);
121
122 if (options & WDT_OPT_PAUSE_IN_SLEEP) {
123 return -ENOTSUP;
124 }
125
126 if (wdt_litex_is_enabled(dev)) {
127 return -EBUSY;
128 }
129
130 wdt_litex_enable(dev);
131 wdt_litex_feed(dev, 0);
132
133 return 0;
134 }
135
wdt_litex_install_timeout(const struct device * dev,const struct wdt_timeout_cfg * cfg)136 static int wdt_litex_install_timeout(const struct device *dev, const struct wdt_timeout_cfg *cfg)
137 {
138 const struct wdt_litex_config *config = dev->config;
139 struct wdt_litex_data *data = dev->data;
140
141 if (cfg->window.min != 0U || cfg->window.max == 0U) {
142 return -EINVAL;
143 }
144
145 if (cfg->window.max > (config->cycles_size <= 4 ? k_cyc_to_ms_floor32(UINT32_MAX)
146 : k_cyc_to_ms_floor64(UINT64_MAX))) {
147 return -EINVAL;
148 }
149
150 if (wdt_litex_is_enabled(dev)) {
151 return -EBUSY;
152 }
153
154 data->timeout = cfg->window.max;
155 data->callback = cfg->callback;
156
157 /* Set mode of watchdog and callback */
158 switch (cfg->flags) {
159 case WDT_FLAG_RESET_SOC:
160 LOG_DBG("Configuring reset SOC mode");
161 data->reset_soc_mode = true;
162 break;
163
164 case WDT_FLAG_RESET_NONE:
165 LOG_DBG("Configuring non-reset mode");
166 data->reset_soc_mode = false;
167 break;
168
169 default:
170 LOG_ERR("Unsupported watchdog config flag");
171 return -EINVAL;
172 }
173
174 return 0;
175 }
176
wdt_litex_isr(void * arg)177 static void wdt_litex_isr(void *arg)
178 {
179 const struct device *dev = (const struct device *)arg;
180 const struct wdt_litex_config *config = dev->config;
181 struct wdt_litex_data *data = dev->data;
182 unsigned int key = irq_lock();
183
184 if (data->callback) {
185 data->callback(dev, 0);
186 }
187
188 litex_write8(BIT(0), config->ev_pending_addr);
189
190 irq_unlock(key);
191 }
192
wdt_litex_init(const struct device * dev)193 static int wdt_litex_init(const struct device *dev)
194 {
195 const struct wdt_litex_config *const config = dev->config;
196
197 config->irq_cfg_func();
198
199 #ifndef CONFIG_WDT_DISABLE_AT_BOOT
200 wdt_litex_enable(dev);
201 #endif
202
203 return 0;
204 }
205
206 static DEVICE_API(wdt, wdt_api) = {
207 .setup = wdt_litex_setup,
208 .disable = wdt_litex_disable,
209 .install_timeout = wdt_litex_install_timeout,
210 .feed = wdt_litex_feed,
211 };
212
213 #define LITEX_WDT_INIT(n) \
214 static void wdt_litex_cfg_func_##n(void); \
215 \
216 static struct wdt_litex_data wdt_litex_data##n; \
217 static struct wdt_litex_config wdt_litex_config##n = { \
218 .control_addr = DT_INST_REG_ADDR_BY_NAME(n, control), \
219 .cycles_addr = DT_INST_REG_ADDR_BY_NAME(n, cycles), \
220 .cycles_size = DT_INST_REG_SIZE_BY_NAME(n, cycles), \
221 .remaining_addr = DT_INST_REG_ADDR_BY_NAME(n, remaining), \
222 .ev_status_addr = DT_INST_REG_ADDR_BY_NAME(n, ev_status), \
223 .ev_pending_addr = DT_INST_REG_ADDR_BY_NAME(n, ev_pending), \
224 .ev_enable_addr = DT_INST_REG_ADDR_BY_NAME(n, ev_enable), \
225 .irq_cfg_func = wdt_litex_cfg_func_##n, \
226 }; \
227 \
228 DEVICE_DT_INST_DEFINE(n, wdt_litex_init, NULL, &wdt_litex_data##n, &wdt_litex_config##n, \
229 PRE_KERNEL_1, CONFIG_KERNEL_INIT_PRIORITY_DEVICE, &wdt_api) \
230 \
231 static void wdt_litex_cfg_func_##n(void) \
232 { \
233 IRQ_CONNECT(DT_INST_IRQN(n), DT_INST_IRQ(n, priority), wdt_litex_isr, \
234 DEVICE_DT_INST_GET(n), 0); \
235 irq_enable(DT_INST_IRQN(n)); \
236 }
237
238 DT_INST_FOREACH_STATUS_OKAY(LITEX_WDT_INIT)
239