xref: /optee_os/core/drivers/atmel_wdt.c (revision d8af061197721f7285db39ada5d85dd4c806898d)
1 // SPDX-License-Identifier: BSD-2-Clause
2 /*
3  * Copyright 2022 Microchip
4  */
5 
6 #include <assert.h>
7 #include <drivers/clk.h>
8 #include <drivers/clk_dt.h>
9 #include <drivers/wdt.h>
10 #include <io.h>
11 #include <kernel/delay.h>
12 #include <kernel/dt.h>
13 #include <kernel/dt_driver.h>
14 #include <kernel/interrupt.h>
15 #include <kernel/pm.h>
16 #include <matrix.h>
17 #include <mm/core_mmu.h>
18 #include <platform_config.h>
19 #include <tee_api_types.h>
20 
21 #define WDT_CR			0x0
22 #define WDT_CR_KEY		SHIFT_U32(0xA5, 24)
23 #define WDT_CR_WDRSTT		BIT(0)
24 
25 #define WDT_MR			0x4
26 #define WDT_MR_WDV		GENMASK_32(11, 0)
27 #define WDT_MR_WDV_SET(val)	((val) & WDT_MR_WDV)
28 #define WDT_MR_WDFIEN		BIT(12)
29 #define WDT_MR_WDRSTEN		BIT(13)
30 #define WDT_MR_WDDIS		BIT(15) /* Watchdog Disable of WDT on bit 15 */
31 #define WDT_MR_WDDIS_DWDT	BIT(12) /* Watchdog Disable of DWDT on bit 12 */
32 #define WDT_MR_WDD_SHIFT	16
33 #define WDT_MR_WDD_MASK		GENMASK_32(11, 0)
34 #define WDT_MR_WDD		SHIFT_U32(WDT_MR_WDD_MASK, WDT_MR_WDD_SHIFT)
35 #define WDT_MR_WDD_SET(val) \
36 			SHIFT_U32(((val) & WDT_MR_WDD_MASK), WDT_MR_WDD_SHIFT)
37 #define WDT_MR_WDDBGHLT		BIT(28)
38 #define WDT_MR_WDIDLEHLT	BIT(29)
39 
40 #define WDT_SR			0x8
41 #define WDT_SR_DUNF		BIT(0)
42 #define WDT_SR_DERR		BIT(1)
43 
44 /* DWDT: Watchdog Timer Mode Register */
45 #define WDT_MR_PERIODRST	BIT(4)
46 #define WDT_MR_RPTHRST		BIT(5)
47 
48 /* DWDT: Watchdog Timer Value Register (Read-only) */
49 #define WDT_VR			0x8
50 #define WDT_VR_COUNTER_SHIFT	0
51 #define WDT_VR_COUNTER_MASK	GENMASK_32(11, 0)
52 
53 /* DWDT: Watchdog Timer Window Level Register */
54 #define WDT_WL			0xc
55 #define WDT_WL_RPTH_SHIFT	16
56 #define WDT_WL_RPTH_MASK	GENMASK_32(27, 16)
57 #define WDT_WL_PERIOD_SHIFT	0
58 #define WDT_WL_PERIOD_MASK	GENMASK_32(11, 0)
59 
60 /* DWDT: Watchdog Timer Interrupt Level Register */
61 #define WDT_IL			0x10
62 #define WDT_IL_LVLTH_SHIFT	0
63 #define WDT_IL_LVLTH_MASK	GENMASK_32(11, 0)
64 
65 /* DWDT: Watchdog Timer Interrupt Enable/Disable/Status/Mask Register */
66 #define WDT_IER			0x14
67 #define WDT_IDR			0x18
68 #define WDT_ISR			0x1c
69 #define WDT_IMR			0x20
70 #define WDT_NSRPTHINT		BIT(4)
71 #define WDT_NSPERINT		BIT(3)
72 #define WDT_LVLINT		BIT(2)
73 #define WDT_RPTHINT		BIT(1)
74 #define WDT_PERINT		BIT(0)
75 
76 /*
77  * The watchdog is clocked by a 32768Hz clock/128 and the counter is on
78  * 12 bits.
79  */
80 #define SLOW_CLOCK_FREQ		(32768)
81 #define WDT_CLOCK_FREQ		(SLOW_CLOCK_FREQ / 128)
82 #define WDT_MIN_TIMEOUT		1
83 #define WDT_MAX_TIMEOUT		(BIT(12) / WDT_CLOCK_FREQ)
84 
85 #define WDT_DEFAULT_TIMEOUT	WDT_MAX_TIMEOUT
86 
87 /*
88  * We must wait at least 3 clocks period before accessing registers MR and CR.
89  * Ensure that we see at least 4 edges
90  */
91 #define WDT_REG_ACCESS_UDELAY	(1000000ULL / SLOW_CLOCK_FREQ * 4)
92 
93 #define SEC_TO_WDT(sec)		(((sec) * WDT_CLOCK_FREQ) - 1)
94 
95 #define WDT_ENABLED(mr, dis_mask)	(!((mr) & (dis_mask)))
96 
97 enum wdt_type {
98 	WDT_TYPE_WDT,	/* Watchdog Timer */
99 	WDT_TYPE_DWDT,	/* Dual Watchdog Timer */
100 };
101 
102 struct wdt_compat {
103 	bool wdt_ps; /* Is Peripheral SHDWC Programmable Secure */
104 	enum wdt_type type; /* Type of Watchdog Timer */
105 	uint32_t dis_mask; /* Mask of Watchdog Disable in Mode Register */
106 };
107 
108 struct atmel_wdt {
109 	struct wdt_chip chip;
110 	enum wdt_type type;
111 	uint32_t dis_mask;
112 	vaddr_t base;
113 	uint32_t mr;
114 	bool enabled;
115 };
116 
117 static void atmel_wdt_write_sleep(struct atmel_wdt *wdt, uint32_t reg,
118 				  uint32_t val)
119 {
120 	udelay(WDT_REG_ACCESS_UDELAY);
121 
122 	io_write32(wdt->base + reg, val);
123 }
124 
125 static TEE_Result atmel_wdt_settimeout(struct wdt_chip *chip,
126 				       unsigned long timeout)
127 {
128 	struct atmel_wdt *wdt = container_of(chip, struct atmel_wdt, chip);
129 
130 	if (wdt->type == WDT_TYPE_WDT) {
131 		wdt->mr &= ~WDT_MR_WDV;
132 		wdt->mr |= WDT_MR_WDV_SET(SEC_TO_WDT(timeout));
133 
134 		/* WDV and WDD only be updated when the watchdog is running */
135 		if (WDT_ENABLED(wdt->mr, wdt->dis_mask))
136 			atmel_wdt_write_sleep(wdt, WDT_MR, wdt->mr);
137 	} else {
138 		io_write32(wdt->base + WDT_WL,
139 			   SHIFT_U32(SEC_TO_WDT(timeout), WDT_WL_PERIOD_SHIFT));
140 	}
141 
142 	return TEE_SUCCESS;
143 }
144 
145 static void atmel_wdt_ping(struct wdt_chip *chip)
146 {
147 	struct atmel_wdt *wdt = container_of(chip, struct atmel_wdt, chip);
148 
149 	atmel_wdt_write_sleep(wdt, WDT_CR, WDT_CR_KEY | WDT_CR_WDRSTT);
150 }
151 
152 static void atmel_wdt_start(struct atmel_wdt *wdt)
153 {
154 	wdt->mr &= ~wdt->dis_mask;
155 	atmel_wdt_write_sleep(wdt, WDT_MR, wdt->mr);
156 }
157 
158 static void atmel_wdt_enable(struct wdt_chip *chip)
159 {
160 	struct atmel_wdt *wdt = container_of(chip, struct atmel_wdt, chip);
161 
162 	wdt->enabled = true;
163 	atmel_wdt_start(wdt);
164 }
165 
166 static void atmel_wdt_stop(struct atmel_wdt *wdt)
167 {
168 	wdt->mr |= wdt->dis_mask;
169 	atmel_wdt_write_sleep(wdt, WDT_MR, wdt->mr);
170 }
171 
172 static void atmel_wdt_disable(struct wdt_chip *chip)
173 {
174 	struct atmel_wdt *wdt = container_of(chip, struct atmel_wdt, chip);
175 
176 	wdt->enabled = false;
177 	atmel_wdt_stop(wdt);
178 }
179 
180 static enum itr_return atmel_wdt_itr_cb(struct itr_handler *h)
181 {
182 	struct atmel_wdt *wdt = h->data;
183 	uint32_t sr = 0;
184 
185 	if (wdt->type == WDT_TYPE_WDT) {
186 		sr = io_read32(wdt->base + WDT_SR);
187 
188 		if (sr & WDT_SR_DUNF)
189 			DMSG("Watchdog Underflow");
190 		if (sr & WDT_SR_DERR)
191 			DMSG("Watchdog Error");
192 	} else if (wdt->type == WDT_TYPE_DWDT) {
193 		sr = io_read32(wdt->base + WDT_ISR);
194 
195 		if (sr & WDT_NSRPTHINT)
196 			DMSG("NS Watchdog Repeat Threshold Interrupt");
197 		if (sr & WDT_NSPERINT)
198 			DMSG("NS Watchdog Period Interrupt");
199 		if (sr & WDT_LVLINT)
200 			DMSG("Watchdog Level Threshold Interrupt");
201 		if (sr & WDT_RPTHINT)
202 			DMSG("Watchdog Repeat Threshold Interrupt");
203 		if (sr & WDT_PERINT)
204 			DMSG("Watchdog Period Interrupt");
205 	}
206 
207 	panic("Watchdog interrupt");
208 
209 	return ITRR_HANDLED;
210 }
211 
212 static TEE_Result atmel_wdt_init(struct wdt_chip *chip __unused,
213 				 unsigned long *min_timeout,
214 				 unsigned long *max_timeout)
215 {
216 	*min_timeout = WDT_MIN_TIMEOUT;
217 	*max_timeout = WDT_MAX_TIMEOUT;
218 
219 	return TEE_SUCCESS;
220 }
221 
222 static const struct wdt_ops atmel_wdt_ops = {
223 	.init = atmel_wdt_init,
224 	.start = atmel_wdt_enable,
225 	.stop = atmel_wdt_disable,
226 	.ping = atmel_wdt_ping,
227 	.set_timeout = atmel_wdt_settimeout,
228 };
229 
230 static void atmel_wdt_init_hw(struct atmel_wdt *wdt)
231 {
232 	uint32_t mr = 0;
233 
234 	/*
235 	 * If we are resuming, we disabled the watchdog on suspend but the
236 	 * bootloader might have enabled the watchdog. If so, disable it
237 	 * properly.
238 	 */
239 	if (!WDT_ENABLED(wdt->mr, wdt->dis_mask)) {
240 		mr = io_read32(wdt->base + WDT_MR);
241 		if (WDT_ENABLED(mr, wdt->dis_mask))
242 			io_write32(wdt->base + WDT_MR, mr | wdt->dis_mask);
243 	}
244 
245 	if (wdt->type == WDT_TYPE_WDT) {
246 		/* Enable interrupt, and disable watchdog in debug and idle */
247 		wdt->mr |= WDT_MR_WDFIEN | WDT_MR_WDDBGHLT | WDT_MR_WDIDLEHLT;
248 		/* Enable watchdog reset */
249 		wdt->mr |= WDT_MR_WDRSTEN;
250 		wdt->mr |= WDT_MR_WDD_SET(SEC_TO_WDT(WDT_MAX_TIMEOUT));
251 		wdt->mr |= WDT_MR_WDV_SET(SEC_TO_WDT(WDT_DEFAULT_TIMEOUT));
252 	} else if (wdt->type == WDT_TYPE_DWDT) {
253 		/* Enable interrupt */
254 		io_write32(wdt->base + WDT_ISR, WDT_PERINT);
255 		/* Disable watchdog in debug and idle */
256 		wdt->mr |= WDT_MR_WDDBGHLT | WDT_MR_WDIDLEHLT;
257 		/* Enable watchdog period reset */
258 		wdt->mr |= WDT_MR_PERIODRST;
259 		io_write32(wdt->base + WDT_WL,
260 			   SHIFT_U32(SEC_TO_WDT(WDT_DEFAULT_TIMEOUT),
261 				     WDT_WL_PERIOD_SHIFT));
262 	} else {
263 		panic("Invalid Watchdog");
264 	}
265 
266 	/*
267 	 * If the watchdog was enabled, write the configuration which will ping
268 	 * the watchdog.
269 	 */
270 	if (WDT_ENABLED(wdt->mr, wdt->dis_mask))
271 		io_write32(wdt->base + WDT_MR, wdt->mr);
272 }
273 
274 #ifdef CFG_PM_ARM32
275 static TEE_Result atmel_wdt_pm(enum pm_op op, uint32_t pm_hint __unused,
276 			       const struct pm_callback_handle *hdl)
277 {
278 	struct atmel_wdt *wdt = hdl->handle;
279 
280 	switch (op) {
281 	case PM_OP_RESUME:
282 		atmel_wdt_init_hw(wdt);
283 		if (wdt->enabled)
284 			atmel_wdt_start(wdt);
285 		break;
286 	case PM_OP_SUSPEND:
287 		if (wdt->enabled)
288 			atmel_wdt_stop(wdt);
289 		break;
290 	default:
291 		panic("Invalid PM operation");
292 	}
293 
294 	return TEE_SUCCESS;
295 }
296 
297 static void atmel_wdt_register_pm(struct atmel_wdt *wdt)
298 {
299 	register_pm_driver_cb(atmel_wdt_pm, wdt, "atmel_wdt");
300 }
301 #else
302 static void atmel_wdt_register_pm(struct atmel_wdt *wdt __unused)
303 {
304 }
305 #endif
306 
307 static TEE_Result wdt_node_probe(const void *fdt, int node,
308 				 const void *compat_data)
309 {
310 	const struct wdt_compat *compat = compat_data;
311 	size_t size = 0;
312 	struct atmel_wdt *wdt;
313 	uint32_t irq_type = 0;
314 	uint32_t irq_prio = 0;
315 	int it = DT_INFO_INVALID_INTERRUPT;
316 	struct itr_handler *it_hdlr = NULL;
317 	TEE_Result res = TEE_ERROR_GENERIC;
318 
319 	if (fdt_get_status(fdt, node) != DT_STATUS_OK_SEC)
320 		return TEE_ERROR_BAD_PARAMETERS;
321 
322 	if (compat->wdt_ps)
323 		matrix_configure_periph_secure(AT91C_ID_WDT);
324 
325 	wdt = calloc(1, sizeof(*wdt));
326 	if (!wdt)
327 		return TEE_ERROR_OUT_OF_MEMORY;
328 
329 	wdt->chip.ops = &atmel_wdt_ops;
330 	wdt->type = compat->type;
331 	wdt->dis_mask = compat->dis_mask;
332 
333 	it = dt_get_irq_type_prio(fdt, node, &irq_type, &irq_prio);
334 	if (it == DT_INFO_INVALID_INTERRUPT)
335 		goto err_free;
336 
337 	res = interrupt_alloc_add_conf_handler(interrupt_get_main_chip(),
338 					       it, atmel_wdt_itr_cb, 0, wdt,
339 					       irq_type, irq_prio, &it_hdlr);
340 	if (res)
341 		goto err_free;
342 
343 	if (dt_map_dev(fdt, node, &wdt->base, &size, DT_MAP_AUTO) < 0)
344 		goto err_remove_handler;
345 
346 	/* Get current state of the watchdog */
347 	wdt->mr = io_read32(wdt->base + WDT_MR) & wdt->dis_mask;
348 
349 	atmel_wdt_init_hw(wdt);
350 	interrupt_enable(it_hdlr->chip, it_hdlr->it);
351 
352 	res = watchdog_register(&wdt->chip);
353 	if (res)
354 		goto err_disable_unmap;
355 
356 	atmel_wdt_register_pm(wdt);
357 
358 	return TEE_SUCCESS;
359 
360 err_disable_unmap:
361 	interrupt_disable(it_hdlr->chip, it_hdlr->it);
362 	core_mmu_remove_mapping(MEM_AREA_IO_SEC, (void *)wdt->base, size);
363 err_remove_handler:
364 	interrupt_remove_free_handler(it_hdlr);
365 err_free:
366 	free(wdt);
367 
368 	return TEE_ERROR_GENERIC;
369 }
370 
371 static const struct wdt_compat sama5d2_compat = {
372 	.wdt_ps = true,
373 	.type = WDT_TYPE_WDT,
374 	.dis_mask = WDT_MR_WDDIS,
375 };
376 
377 static const struct wdt_compat sama7g5_compat = {
378 	.wdt_ps = false,
379 	.type = WDT_TYPE_DWDT,
380 	.dis_mask = WDT_MR_WDDIS_DWDT,
381 };
382 
383 static const struct dt_device_match atmel_wdt_match_table[] = {
384 	{
385 		.compatible = "atmel,sama5d4-wdt",
386 		.compat_data = &sama5d2_compat,
387 	},
388 	{
389 		.compatible = "microchip,sama7g5-wdt",
390 		.compat_data = &sama7g5_compat,
391 	},
392 	{ }
393 };
394 
395 DEFINE_DT_DRIVER(atmel_wdt_dt_driver) = {
396 	.name = "atmel_wdt",
397 	.type = DT_DRIVER_NOTYPE,
398 	.match_table = atmel_wdt_match_table,
399 	.probe = wdt_node_probe,
400 };
401