1 /*
2 * Driver for Xilinx AXI Timebase WDT core, as described in
3 * Xilinx document PG128.
4 *
5 * Note that the window mode of operation of the core is
6 * currently not supported. Also, only a full SoC reset is
7 * supported as a watchdog expiry action.
8 *
9 * Copyright © 2023 Calian Ltd. All rights reserved.
10 * SPDX-License-Identifier: Apache-2.0
11 */
12
13 #define DT_DRV_COMPAT xlnx_xps_timebase_wdt_1_00_a
14
15 #include <errno.h>
16 #include <zephyr/kernel.h>
17 #include <zephyr/device.h>
18 #include <zephyr/logging/log.h>
19 #include <zephyr/drivers/watchdog.h>
20 #include <zephyr/drivers/hwinfo.h>
21
22 enum xilinx_wdt_axi_register {
23 REG_TWCSR0 = 0x00, /* Control/Status Register 0 */
24 REG_TWCSR1 = 0x04, /* Control/Status Register 1 */
25 REG_TBR = 0x08, /* Timebase Register */
26 REG_MWR = 0x0C, /* Master Write Control Register */
27 };
28
29 enum xilinx_wdt_csr0_bits {
30 CSR0_WRS = BIT(3),
31 CSR0_WDS = BIT(2),
32 CSR0_EWDT1 = BIT(1),
33 CSR0_EWDT2 = BIT(0),
34 };
35
36 enum xilinx_wdt_csr1_bits {
37 CSR1_EWDT2 = BIT(0),
38 };
39
40 enum {
41 TIMER_WIDTH_MIN = 8,
42 };
43
44 LOG_MODULE_REGISTER(wdt_xilinx_axi, CONFIG_WDT_LOG_LEVEL);
45
46 struct xilinx_wdt_axi_config {
47 mem_addr_t base;
48 uint32_t clock_rate;
49 uint8_t timer_width_max;
50 bool enable_once;
51 };
52
53 struct xilinx_wdt_axi_data {
54 struct k_spinlock lock;
55 bool timeout_active;
56 bool wdt_started;
57 };
58
59 static const struct device *first_wdt_dev;
60
wdt_xilinx_axi_setup(const struct device * dev,uint8_t options)61 static int wdt_xilinx_axi_setup(const struct device *dev, uint8_t options)
62 {
63 const struct xilinx_wdt_axi_config *config = dev->config;
64 struct xilinx_wdt_axi_data *data = dev->data;
65 k_spinlock_key_t key = k_spin_lock(&data->lock);
66 int ret;
67
68 if (!data->timeout_active) {
69 ret = -EINVAL;
70 goto out;
71 }
72
73 if (data->wdt_started) {
74 ret = -EBUSY;
75 goto out;
76 }
77
78 /* We don't actually know or control at the driver level whether
79 * the WDT pauses in CPU sleep or when halted by the debugger,
80 * so we don't check anything with the options.
81 */
82 sys_write32(CSR0_EWDT1 | CSR0_WDS, config->base + REG_TWCSR0);
83 sys_write32(CSR1_EWDT2, config->base + REG_TWCSR1);
84 data->wdt_started = true;
85 ret = 0;
86
87 out:
88 k_spin_unlock(&data->lock, key);
89 return ret;
90 }
91
wdt_xilinx_axi_disable(const struct device * dev)92 static int wdt_xilinx_axi_disable(const struct device *dev)
93 {
94 const struct xilinx_wdt_axi_config *config = dev->config;
95 struct xilinx_wdt_axi_data *data = dev->data;
96 k_spinlock_key_t key = k_spin_lock(&data->lock);
97 int ret;
98
99 if (config->enable_once) {
100 ret = -EPERM;
101 goto out;
102 }
103
104 if (!data->wdt_started) {
105 ret = -EFAULT;
106 goto out;
107 }
108
109 sys_write32(CSR0_WDS, config->base + REG_TWCSR0);
110 sys_write32(0, config->base + REG_TWCSR1);
111 data->wdt_started = false;
112 ret = 0;
113
114 out:
115 k_spin_unlock(&data->lock, key);
116 return ret;
117 }
118
wdt_xilinx_axi_install_timeout(const struct device * dev,const struct wdt_timeout_cfg * cfg)119 static int wdt_xilinx_axi_install_timeout(const struct device *dev,
120 const struct wdt_timeout_cfg *cfg)
121 {
122 const struct xilinx_wdt_axi_config *config = dev->config;
123 struct xilinx_wdt_axi_data *data = dev->data;
124 k_spinlock_key_t key = k_spin_lock(&data->lock);
125 uint32_t timer_width;
126 bool good_timer_width = false;
127 int ret;
128
129 if (data->timeout_active) {
130 ret = -ENOMEM;
131 goto out;
132 }
133
134 if (!(cfg->flags & WDT_FLAG_RESET_SOC)) {
135 ret = -ENOTSUP;
136 goto out;
137 }
138
139 if (cfg->window.min != 0) {
140 ret = -EINVAL;
141 goto out;
142 }
143
144 for (timer_width = TIMER_WIDTH_MIN; timer_width <= config->timer_width_max; timer_width++) {
145 /* Note: WDT expiry happens after 2 wraps of the timer (first raises an interrupt
146 * which is not used, second triggers a reset) so add 1 to timer_width.
147 */
148 const uint64_t expiry_cycles = ((uint64_t)1) << (timer_width + 1);
149 const uint64_t expiry_msec = expiry_cycles * 1000 / config->clock_rate;
150
151 if (expiry_msec >= cfg->window.max) {
152 LOG_INF("Set timer width to %u, actual expiry %u msec", timer_width,
153 (unsigned int)expiry_msec);
154 good_timer_width = true;
155 break;
156 }
157 }
158
159 if (!good_timer_width) {
160 LOG_ERR("Cannot support timeout value of %u msec", cfg->window.max);
161 ret = -EINVAL;
162 goto out;
163 }
164
165 sys_write32(timer_width, config->base + REG_MWR);
166 data->timeout_active = true;
167 ret = 0;
168
169 out:
170 k_spin_unlock(&data->lock, key);
171 return ret;
172 }
173
wdt_xilinx_axi_feed(const struct device * dev,int channel_id)174 static int wdt_xilinx_axi_feed(const struct device *dev, int channel_id)
175 {
176 const struct xilinx_wdt_axi_config *config = dev->config;
177 struct xilinx_wdt_axi_data *data = dev->data;
178 k_spinlock_key_t key = k_spin_lock(&data->lock);
179 uint32_t twcsr0 = sys_read32(config->base + REG_TWCSR0);
180 int ret;
181
182 if (channel_id != 0 || !data->timeout_active) {
183 ret = -EINVAL;
184 goto out;
185 }
186
187 twcsr0 |= CSR0_WDS;
188 if (data->wdt_started) {
189 twcsr0 |= CSR0_EWDT1;
190 }
191
192 sys_write32(twcsr0, config->base + REG_TWCSR0);
193 ret = 0;
194
195 out:
196 k_spin_unlock(&data->lock, key);
197 return ret;
198 }
199
wdt_xilinx_axi_init(const struct device * dev)200 static int wdt_xilinx_axi_init(const struct device *dev)
201 {
202 if (IS_ENABLED(CONFIG_WDT_XILINX_AXI_HWINFO_API)) {
203 if (first_wdt_dev) {
204 LOG_WRN("Multiple WDT instances, only first will implement HWINFO");
205 } else {
206 first_wdt_dev = dev;
207 }
208 }
209
210 return 0;
211 }
212
213 #ifdef CONFIG_WDT_XILINX_AXI_HWINFO_API
214
z_impl_hwinfo_get_reset_cause(uint32_t * cause)215 int z_impl_hwinfo_get_reset_cause(uint32_t *cause)
216 {
217 if (!first_wdt_dev) {
218 return -ENOSYS;
219 }
220
221 const struct xilinx_wdt_axi_config *config = first_wdt_dev->config;
222
223 if ((sys_read32(config->base + REG_TWCSR0) & CSR0_WRS) != 0) {
224 *cause = RESET_WATCHDOG;
225 } else {
226 *cause = 0;
227 }
228
229 return 0;
230 }
231
z_impl_hwinfo_clear_reset_cause(void)232 int z_impl_hwinfo_clear_reset_cause(void)
233 {
234 if (!first_wdt_dev) {
235 return -ENOSYS;
236 }
237
238 const struct xilinx_wdt_axi_config *config = first_wdt_dev->config;
239 struct xilinx_wdt_axi_data *data = first_wdt_dev->data;
240
241 k_spinlock_key_t key = k_spin_lock(&data->lock);
242 uint32_t twcsr0 = sys_read32(config->base + REG_TWCSR0);
243
244 if ((twcsr0 & CSR0_WRS) != 0) {
245 twcsr0 |= CSR0_WRS;
246 sys_write32(twcsr0, config->base + REG_TWCSR0);
247 }
248
249 k_spin_unlock(&data->lock, key);
250
251 return 0;
252 }
253
z_impl_hwinfo_get_supported_reset_cause(uint32_t * supported)254 int z_impl_hwinfo_get_supported_reset_cause(uint32_t *supported)
255 {
256 if (!first_wdt_dev) {
257 return -ENOSYS;
258 }
259
260 *supported = RESET_WATCHDOG;
261 return 0;
262 }
263
264 #endif
265
266 static DEVICE_API(wdt, wdt_xilinx_api) = {
267 .setup = wdt_xilinx_axi_setup,
268 .disable = wdt_xilinx_axi_disable,
269 .install_timeout = wdt_xilinx_axi_install_timeout,
270 .feed = wdt_xilinx_axi_feed,
271 };
272
273 #define WDT_XILINX_AXI_INIT(inst) \
274 static struct xilinx_wdt_axi_data wdt_xilinx_axi_##inst##_dev_data; \
275 \
276 static const struct xilinx_wdt_axi_config wdt_xilinx_axi_##inst##_cfg = { \
277 .base = DT_INST_REG_ADDR(inst), \
278 .clock_rate = DT_INST_PROP_BY_PHANDLE(inst, clocks, clock_frequency), \
279 .timer_width_max = DT_INST_PROP(inst, xlnx_wdt_interval), \
280 .enable_once = DT_INST_PROP(inst, xlnx_wdt_enable_once), \
281 }; \
282 \
283 DEVICE_DT_INST_DEFINE(inst, &wdt_xilinx_axi_init, NULL, &wdt_xilinx_axi_##inst##_dev_data, \
284 &wdt_xilinx_axi_##inst##_cfg, PRE_KERNEL_1, \
285 CONFIG_KERNEL_INIT_PRIORITY_DEVICE, &wdt_xilinx_api);
286
287 DT_INST_FOREACH_STATUS_OKAY(WDT_XILINX_AXI_INIT)
288