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