1 /*
2 * Copyright (c) 2023 Andes Technology Corporation.
3 *
4 * SPDX-License-Identifier: Apache-2.0
5 */
6
7 #define DT_DRV_COMPAT andestech_atcwdt200
8
9 #include <zephyr/kernel.h>
10 #include <zephyr/drivers/watchdog.h>
11
12 #define LOG_LEVEL CONFIG_WDT_LOG_LEVEL
13 #include <zephyr/logging/log.h>
14 #include <zephyr/irq.h>
15 #include <zephyr/drivers/counter.h>
16 #include <zephyr/drivers/syscon.h>
17 LOG_MODULE_REGISTER(wdt_andes);
18
19 /* Watchdog register */
20 #define REG_IDR 0x00
21 #define REG_CTRL 0x10
22 #define REG_RESTAR 0x14
23 #define REG_WREN 0x18
24 #define REG_STATUS 0x1c
25
26 #define WDT_CTRL(addr) (addr + REG_CTRL)
27 #define WDT_RESTAR(addr) (addr + REG_RESTAR)
28 #define WDT_WREN(addr) (addr + REG_WREN)
29 #define WDT_STATUS(addr) (addr + REG_STATUS)
30
31 /* Atcwdt200 magic number */
32 /* 0x10 Control Register */
33
34 #define WDT_CTRL_RSTTIME_POW_2_7 0x000
35 #define WDT_CTRL_RSTTIME_POW_2_8 0x100
36 #define WDT_CTRL_RSTTIME_POW_2_9 0x200
37 #define WDT_CTRL_RSTTIME_POW_2_10 0x300
38 #define WDT_CTRL_RSTTIME_POW_2_11 0x400
39 #define WDT_CTRL_RSTTIME_POW_2_12 0x500
40 #define WDT_CTRL_RSTTIME_POW_2_13 0x600
41 #define WDT_CTRL_RSTTIME_POW_2_14 0x700
42
43 #define WDT_CTRL_INTTIME_POW_2_6 0x000
44 #define WDT_CTRL_INTTIME_POW_2_8 0x010
45 #define WDT_CTRL_INTTIME_POW_2_10 0x020
46 #define WDT_CTRL_INTTIME_POW_2_11 0x030
47 #define WDT_CTRL_INTTIME_POW_2_12 0x040
48 #define WDT_CTRL_INTTIME_POW_2_13 0x050
49 #define WDT_CTRL_INTTIME_POW_2_14 0x060
50 #define WDT_CTRL_INTTIME_POW_2_15 0x070
51 #define WDT_CTRL_INTTIME_POW_2_17 0x080
52 #define WDT_CTRL_INTTIME_POW_2_19 0x090
53 #define WDT_CTRL_INTTIME_POW_2_21 0x0A0
54 #define WDT_CTRL_INTTIME_POW_2_23 0x0B0
55 #define WDT_CTRL_INTTIME_POW_2_25 0x0C0
56 #define WDT_CTRL_INTTIME_POW_2_27 0x0D0
57 #define WDT_CTRL_INTTIME_POW_2_29 0x0E0
58 #define WDT_CTRL_INTTIME_POW_2_31 0x0F0
59
60 #define WDT_CTRL_RSTEN 0x8
61 #define WDT_CTRL_INTEN 0x4
62 #define WDT_CTRL_APBCLK 0x2
63 #define WDT_CTRL_EXTCLK 0x0
64 #define WDT_CTRL_EN 0x1
65
66 /* Magic Number for Restart Register */
67 #define WDT_RESTART_NUM 0xcafe
68
69 /* Magic Number for Write Enable Register */
70 #define WDT_WREN_NUM 0x5aa5
71
72 /* 0x1C Status Register */
73 #define WDT_ST_INTEXPIRED 0x1
74 #define WDT_ST_INTEXPIRED_CLR 0x1
75
76 #define WDOGCFG_PERIOD_MIN BIT(7)
77 #define WDOGCFG_PERIOD_MAX BIT(14)
78 #define EXT_CLOCK_FREQ BIT(15)
79
80 static const struct device *const pit_counter_dev =
81 DEVICE_DT_GET(DT_NODELABEL(pit0));
82
83 struct counter_alarm_cfg alarm_cfg;
84
85 struct wdt_atcwdt200_config {
86 uintptr_t base;
87 };
88
89 struct wdt_atcwdt200_dev_data {
90 bool timeout_valid;
91 counter_alarm_callback_t counter_callback;
92 struct k_spinlock lock;
93 };
94
95 static int wdt_atcwdt200_disable(const struct device *dev);
96
wdt_counter_cb(const struct device * counter_dev,uint8_t chan_id,uint32_t counter,void * user_data)97 static void wdt_counter_cb(const struct device *counter_dev, uint8_t chan_id,
98 uint32_t counter,
99 void *user_data)
100 {
101 const struct device *dev = DEVICE_DT_INST_GET(0);
102 struct wdt_atcwdt200_dev_data *wdt_data = dev->data;
103 uint32_t wdt_addr = ((const struct wdt_atcwdt200_config *)(dev->config))->base;
104 k_spinlock_key_t key;
105
106 key = k_spin_lock(&wdt_data->lock);
107
108 sys_write32(WDT_WREN_NUM, WDT_WREN(wdt_addr));
109 sys_write32(WDT_RESTART_NUM, WDT_RESTAR(wdt_addr));
110
111 counter_set_channel_alarm(counter_dev, 2, &alarm_cfg);
112
113 k_spin_unlock(&wdt_data->lock, key);
114 }
115
116 /**
117 * @brief Set maximum length of timeout to watchdog
118 *
119 * @param dev Watchdog device struct
120 */
wdt_atcwdt200_set_max_timeout(const struct device * dev)121 static void wdt_atcwdt200_set_max_timeout(const struct device *dev)
122 {
123 struct wdt_atcwdt200_dev_data *data = dev->data;
124 k_spinlock_key_t key;
125 uint32_t wdt_addr = ((const struct wdt_atcwdt200_config *)(dev->config))->base;
126 uint32_t reg, counter_freq;
127
128 key = k_spin_lock(&data->lock);
129
130 counter_freq = counter_get_frequency(pit_counter_dev);
131
132 alarm_cfg.flags = 0;
133 alarm_cfg.callback = wdt_counter_cb;
134 alarm_cfg.user_data = &alarm_cfg;
135 alarm_cfg.ticks = ((WDOGCFG_PERIOD_MAX * counter_freq) / EXT_CLOCK_FREQ) >> 1;
136
137 reg = WDT_CTRL_RSTTIME_POW_2_14;
138
139 sys_write32(WDT_WREN_NUM, WDT_WREN(wdt_addr));
140 sys_write32(reg, WDT_CTRL(wdt_addr));
141
142 data->timeout_valid = true;
143
144 k_spin_unlock(&data->lock, key);
145 }
146
wdt_atcwdt200_disable(const struct device * dev)147 static int wdt_atcwdt200_disable(const struct device *dev)
148 {
149 struct wdt_atcwdt200_dev_data *data = dev->data;
150 uint32_t wdt_addr = ((const struct wdt_atcwdt200_config *)(dev->config))->base;
151 k_spinlock_key_t key;
152 uint32_t reg;
153
154 key = k_spin_lock(&data->lock);
155
156 reg = sys_read32(WDT_CTRL(wdt_addr));
157 reg &= ~(WDT_CTRL_RSTEN | WDT_CTRL_EN);
158
159 sys_write32(WDT_WREN_NUM, WDT_WREN(wdt_addr));
160 sys_write32(reg, WDT_CTRL(wdt_addr));
161
162 k_spin_unlock(&data->lock, key);
163
164 wdt_atcwdt200_set_max_timeout(dev);
165 counter_cancel_channel_alarm(pit_counter_dev, 2);
166
167 return 0;
168 }
169
wdt_atcwdt200_setup(const struct device * dev,uint8_t options)170 static int wdt_atcwdt200_setup(const struct device *dev, uint8_t options)
171 {
172 struct wdt_atcwdt200_dev_data *data = dev->data;
173 uint32_t wdt_addr = ((const struct wdt_atcwdt200_config *)(dev->config))->base;
174 k_spinlock_key_t key;
175 uint32_t reg;
176 uint32_t ret = 0;
177
178 if (!data->timeout_valid) {
179 LOG_ERR("No valid timeouts installed");
180 return -EINVAL;
181 }
182
183 key = k_spin_lock(&data->lock);
184
185 reg = sys_read32(WDT_CTRL(wdt_addr));
186 reg |= (WDT_CTRL_RSTEN | WDT_CTRL_EN);
187
188 if ((options & WDT_OPT_PAUSE_HALTED_BY_DBG) ==
189 WDT_OPT_PAUSE_HALTED_BY_DBG) {
190 counter_cancel_channel_alarm(pit_counter_dev, 2);
191 sys_write32(WDT_WREN_NUM, WDT_WREN(wdt_addr));
192 sys_write32(reg, WDT_CTRL(wdt_addr));
193 goto out;
194 } else {
195 ret = counter_set_channel_alarm(pit_counter_dev, 2, &alarm_cfg);
196 if (ret != 0) {
197 ret = -EINVAL;
198 goto out;
199 }
200
201 sys_write32(WDT_WREN_NUM, WDT_WREN(wdt_addr));
202 sys_write32(reg, WDT_CTRL(wdt_addr));
203 }
204
205 out:
206 k_spin_unlock(&data->lock, key);
207 return ret;
208 }
209
210 /**
211 * @brief Calculates the watchdog counter value (wdogcmp0) and
212 * scaler (wdogscale) to be installed in the watchdog timer
213 *
214 * @param timeout Timeout value in milliseconds.
215 * @param scaler Pointer to return scaler power of 2
216 *
217 * @return Watchdog counter value
218 */
wdt_atcwdt200_convtime(uint32_t timeout,uint32_t * scaler)219 static uint32_t wdt_atcwdt200_convtime(uint32_t timeout, uint32_t *scaler)
220 {
221 int i;
222 uint32_t rst_period, cnt;
223
224 cnt = (uint32_t)((timeout * EXT_CLOCK_FREQ) / 1000);
225 rst_period = cnt;
226
227 for (i = 0; i < 14 && cnt > 0; i++) {
228 cnt >>= 1;
229 }
230
231 *scaler = i;
232
233 return rst_period;
234 }
235
wdt_atcwdt200_install_timeout(const struct device * dev,const struct wdt_timeout_cfg * cfg)236 static int wdt_atcwdt200_install_timeout(const struct device *dev,
237 const struct wdt_timeout_cfg *cfg)
238 {
239 struct wdt_atcwdt200_dev_data *data = dev->data;
240 uint32_t wdt_addr = ((const struct wdt_atcwdt200_config *)(dev->config))->base;
241 k_spinlock_key_t key;
242 uint32_t rst_period, reg, counter_freq, scaler;
243
244 if (cfg->window.min != 0U || cfg->window.max == 0U) {
245 return -EINVAL;
246 }
247
248 counter_freq = counter_get_frequency(pit_counter_dev);
249 rst_period = wdt_atcwdt200_convtime(cfg->window.max, &scaler);
250
251 if (rst_period < 0 || WDOGCFG_PERIOD_MAX < rst_period) {
252 LOG_ERR("Unsupported watchdog timeout\n");
253 return -EINVAL;
254 }
255
256 wdt_atcwdt200_disable(dev);
257
258 key = k_spin_lock(&data->lock);
259
260 switch (cfg->flags) {
261 case WDT_FLAG_RESET_SOC:
262 if (scaler < 7) {
263 reg = WDT_CTRL_RSTTIME_POW_2_7;
264 } else {
265 scaler = scaler - 7;
266 reg = scaler << 8;
267 }
268
269 alarm_cfg.flags = 0;
270 alarm_cfg.callback = wdt_counter_cb;
271 alarm_cfg.user_data = &alarm_cfg;
272 alarm_cfg.ticks = (((cfg->window.max * counter_freq) / 1000) >> 1);
273
274 break;
275 case WDT_FLAG_RESET_NONE:
276 case WDT_FLAG_RESET_CPU_CORE:
277 default:
278 LOG_ERR("Unsupported watchdog config flags\n");
279 k_spin_unlock(&data->lock, key);
280 return -ENOTSUP;
281 }
282
283 sys_write32(WDT_WREN_NUM, WDT_WREN(wdt_addr));
284 sys_write32(reg, WDT_CTRL(wdt_addr));
285
286 k_spin_unlock(&data->lock, key);
287 return 0;
288 }
289
wdt_atcwdt200_feed(const struct device * dev,int channel_id)290 static int wdt_atcwdt200_feed(const struct device *dev, int channel_id)
291 {
292 uint32_t wdt_addr = ((const struct wdt_atcwdt200_config *)(dev->config))->base;
293
294 ARG_UNUSED(channel_id);
295
296 sys_write32(WDT_WREN_NUM, WDT_WREN(wdt_addr));
297 sys_write32(WDT_RESTART_NUM, WDT_RESTAR(wdt_addr));
298
299 return 0;
300 }
301
302 static const struct wdt_driver_api wdt_atcwdt200_api = {
303 .setup = wdt_atcwdt200_setup,
304 .disable = wdt_atcwdt200_disable,
305 .install_timeout = wdt_atcwdt200_install_timeout,
306 .feed = wdt_atcwdt200_feed,
307 };
308
wdt_atcwdt200_init(const struct device * dev)309 static int wdt_atcwdt200_init(const struct device *dev)
310 {
311 struct wdt_atcwdt200_dev_data *data = dev->data;
312
313 data->timeout_valid = false;
314 data->counter_callback = wdt_counter_cb;
315
316 counter_start(pit_counter_dev);
317
318 #ifdef CONFIG_WDT_DISABLE_AT_BOOT
319 wdt_atcwdt200_disable(dev);
320 #else
321 data->timeout_valid = true;
322 wdt_atcwdt200_set_max_timeout(dev);
323 wdt_atcwdt200_setup(dev, 0x0);
324 #endif
325 return 0;
326 }
327
328 static struct wdt_atcwdt200_dev_data wdt_atcwdt200_data;
329
330 static const struct wdt_atcwdt200_config wdt_atcwdt200_cfg = {
331 .base = DT_INST_REG_ADDR(0),
332 };
333
334 DEVICE_DT_INST_DEFINE(0, wdt_atcwdt200_init, NULL,
335 &wdt_atcwdt200_data, &wdt_atcwdt200_cfg, PRE_KERNEL_2,
336 CONFIG_KERNEL_INIT_PRIORITY_DEVICE, &wdt_atcwdt200_api);
337