xref: /optee_os/core/drivers/atmel_wdt.c (revision ea9329ec8928593bf753ae45b4d8f6c27f7b3323)
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 	unsigned long rate;
114 	uint32_t mr;
115 	bool enabled;
116 };
117 
118 static void atmel_wdt_write_sleep(struct atmel_wdt *wdt, uint32_t reg,
119 				  uint32_t val)
120 {
121 	udelay(WDT_REG_ACCESS_UDELAY);
122 
123 	io_write32(wdt->base + reg, val);
124 }
125 
126 static TEE_Result atmel_wdt_settimeout(struct wdt_chip *chip,
127 				       unsigned long timeout)
128 {
129 	struct atmel_wdt *wdt = container_of(chip, struct atmel_wdt, chip);
130 
131 	if (wdt->type == WDT_TYPE_WDT) {
132 		wdt->mr &= ~WDT_MR_WDV;
133 		wdt->mr |= WDT_MR_WDV_SET(SEC_TO_WDT(timeout));
134 
135 		/* WDV and WDD only be updated when the watchdog is running */
136 		if (WDT_ENABLED(wdt->mr, wdt->dis_mask))
137 			atmel_wdt_write_sleep(wdt, WDT_MR, wdt->mr);
138 	} else {
139 		io_write32(wdt->base + WDT_WL,
140 			   SHIFT_U32(SEC_TO_WDT(timeout), WDT_WL_PERIOD_SHIFT));
141 	}
142 
143 	return TEE_SUCCESS;
144 }
145 
146 static void atmel_wdt_ping(struct wdt_chip *chip)
147 {
148 	struct atmel_wdt *wdt = container_of(chip, struct atmel_wdt, chip);
149 
150 	atmel_wdt_write_sleep(wdt, WDT_CR, WDT_CR_KEY | WDT_CR_WDRSTT);
151 }
152 
153 static void atmel_wdt_start(struct atmel_wdt *wdt)
154 {
155 	wdt->mr &= ~wdt->dis_mask;
156 	atmel_wdt_write_sleep(wdt, WDT_MR, wdt->mr);
157 }
158 
159 static void atmel_wdt_enable(struct wdt_chip *chip)
160 {
161 	struct atmel_wdt *wdt = container_of(chip, struct atmel_wdt, chip);
162 
163 	wdt->enabled = true;
164 	atmel_wdt_start(wdt);
165 }
166 
167 static void atmel_wdt_stop(struct atmel_wdt *wdt)
168 {
169 	wdt->mr |= wdt->dis_mask;
170 	atmel_wdt_write_sleep(wdt, WDT_MR, wdt->mr);
171 }
172 
173 static void atmel_wdt_disable(struct wdt_chip *chip)
174 {
175 	struct atmel_wdt *wdt = container_of(chip, struct atmel_wdt, chip);
176 
177 	wdt->enabled = false;
178 	atmel_wdt_stop(wdt);
179 }
180 
181 static enum itr_return atmel_wdt_itr_cb(struct itr_handler *h)
182 {
183 	struct atmel_wdt *wdt = h->data;
184 	uint32_t sr = 0;
185 
186 	if (wdt->type == WDT_TYPE_WDT) {
187 		sr = io_read32(wdt->base + WDT_SR);
188 
189 		if (sr & WDT_SR_DUNF)
190 			DMSG("Watchdog Underflow");
191 		if (sr & WDT_SR_DERR)
192 			DMSG("Watchdog Error");
193 	} else if (wdt->type == WDT_TYPE_DWDT) {
194 		sr = io_read32(wdt->base + WDT_ISR);
195 
196 		if (sr & WDT_NSRPTHINT)
197 			DMSG("NS Watchdog Repeat Threshold Interrupt");
198 		if (sr & WDT_NSPERINT)
199 			DMSG("NS Watchdog Period Interrupt");
200 		if (sr & WDT_LVLINT)
201 			DMSG("Watchdog Level Threshold Interrupt");
202 		if (sr & WDT_RPTHINT)
203 			DMSG("Watchdog Repeat Threshold Interrupt");
204 		if (sr & WDT_PERINT)
205 			DMSG("Watchdog Period Interrupt");
206 	}
207 
208 	panic("Watchdog interrupt");
209 
210 	return ITRR_HANDLED;
211 }
212 
213 static TEE_Result atmel_wdt_init(struct wdt_chip *chip __unused,
214 				 unsigned long *min_timeout,
215 				 unsigned long *max_timeout)
216 {
217 	*min_timeout = WDT_MIN_TIMEOUT;
218 	*max_timeout = WDT_MAX_TIMEOUT;
219 
220 	return TEE_SUCCESS;
221 }
222 
223 static const struct wdt_ops atmel_wdt_ops = {
224 	.init = atmel_wdt_init,
225 	.start = atmel_wdt_enable,
226 	.stop = atmel_wdt_disable,
227 	.ping = atmel_wdt_ping,
228 	.set_timeout = atmel_wdt_settimeout,
229 };
230 
231 static void atmel_wdt_init_hw(struct atmel_wdt *wdt)
232 {
233 	uint32_t mr = 0;
234 
235 	/*
236 	 * If we are resuming, we disabled the watchdog on suspend but the
237 	 * bootloader might have enabled the watchdog. If so, disable it
238 	 * properly.
239 	 */
240 	if (!WDT_ENABLED(wdt->mr, wdt->dis_mask)) {
241 		mr = io_read32(wdt->base + WDT_MR);
242 		if (WDT_ENABLED(mr, wdt->dis_mask))
243 			io_write32(wdt->base + WDT_MR, mr | wdt->dis_mask);
244 	}
245 
246 	if (wdt->type == WDT_TYPE_WDT) {
247 		/* Enable interrupt, and disable watchdog in debug and idle */
248 		wdt->mr |= WDT_MR_WDFIEN | WDT_MR_WDDBGHLT | WDT_MR_WDIDLEHLT;
249 		/* Enable watchdog reset */
250 		wdt->mr |= WDT_MR_WDRSTEN;
251 		wdt->mr |= WDT_MR_WDD_SET(SEC_TO_WDT(WDT_MAX_TIMEOUT));
252 		wdt->mr |= WDT_MR_WDV_SET(SEC_TO_WDT(WDT_DEFAULT_TIMEOUT));
253 	} else if (wdt->type == WDT_TYPE_DWDT) {
254 		/* Enable interrupt */
255 		io_write32(wdt->base + WDT_ISR, WDT_PERINT);
256 		/* Disable watchdog in debug and idle */
257 		wdt->mr |= WDT_MR_WDDBGHLT | WDT_MR_WDIDLEHLT;
258 		/* Enable watchdog period reset */
259 		wdt->mr |= WDT_MR_PERIODRST;
260 		io_write32(wdt->base + WDT_WL,
261 			   SHIFT_U32(SEC_TO_WDT(WDT_DEFAULT_TIMEOUT),
262 				     WDT_WL_PERIOD_SHIFT));
263 	} else {
264 		panic("Invalid Watchdog");
265 	}
266 
267 	/*
268 	 * If the watchdog was enabled, write the configuration which will ping
269 	 * the watchdog.
270 	 */
271 	if (WDT_ENABLED(wdt->mr, wdt->dis_mask))
272 		io_write32(wdt->base + WDT_MR, wdt->mr);
273 }
274 
275 #ifdef CFG_PM_ARM32
276 static TEE_Result atmel_wdt_pm(enum pm_op op, uint32_t pm_hint __unused,
277 			       const struct pm_callback_handle *hdl)
278 {
279 	struct atmel_wdt *wdt = hdl->handle;
280 
281 	switch (op) {
282 	case PM_OP_RESUME:
283 		atmel_wdt_init_hw(wdt);
284 		if (wdt->enabled)
285 			atmel_wdt_start(wdt);
286 		break;
287 	case PM_OP_SUSPEND:
288 		if (wdt->enabled)
289 			atmel_wdt_stop(wdt);
290 		break;
291 	default:
292 		panic("Invalid PM operation");
293 	}
294 
295 	return TEE_SUCCESS;
296 }
297 
298 static void atmel_wdt_register_pm(struct atmel_wdt *wdt)
299 {
300 	register_pm_driver_cb(atmel_wdt_pm, wdt, "atmel_wdt");
301 }
302 #else
303 static void atmel_wdt_register_pm(struct atmel_wdt *wdt __unused)
304 {
305 }
306 #endif
307 
308 static TEE_Result wdt_node_probe(const void *fdt, int node,
309 				 const void *compat_data)
310 {
311 	const struct wdt_compat *compat = compat_data;
312 	size_t size = 0;
313 	struct atmel_wdt *wdt;
314 	uint32_t irq_type = 0;
315 	uint32_t irq_prio = 0;
316 	int it = DT_INFO_INVALID_INTERRUPT;
317 	struct itr_handler *it_hdlr = NULL;
318 	TEE_Result res = TEE_ERROR_GENERIC;
319 
320 	if (fdt_get_status(fdt, node) != DT_STATUS_OK_SEC)
321 		return TEE_ERROR_BAD_PARAMETERS;
322 
323 	if (compat->wdt_ps)
324 		matrix_configure_periph_secure(AT91C_ID_WDT);
325 
326 	wdt = calloc(1, sizeof(*wdt));
327 	if (!wdt)
328 		return TEE_ERROR_OUT_OF_MEMORY;
329 
330 	wdt->chip.ops = &atmel_wdt_ops;
331 	wdt->type = compat->type;
332 	wdt->dis_mask = compat->dis_mask;
333 
334 	it = dt_get_irq_type_prio(fdt, node, &irq_type, &irq_prio);
335 	if (it == DT_INFO_INVALID_INTERRUPT)
336 		goto err_free;
337 
338 	res = interrupt_alloc_add_conf_handler(interrupt_get_main_chip(),
339 					       it, atmel_wdt_itr_cb, 0, wdt,
340 					       irq_type, irq_prio, &it_hdlr);
341 	if (res)
342 		goto err_free;
343 
344 	if (dt_map_dev(fdt, node, &wdt->base, &size, DT_MAP_AUTO) < 0)
345 		goto err_remove_handler;
346 
347 	/* Get current state of the watchdog */
348 	wdt->mr = io_read32(wdt->base + WDT_MR) & wdt->dis_mask;
349 
350 	atmel_wdt_init_hw(wdt);
351 	interrupt_enable(it_hdlr->chip, it_hdlr->it);
352 
353 	res = watchdog_register(&wdt->chip);
354 	if (res)
355 		goto err_disable_unmap;
356 
357 	atmel_wdt_register_pm(wdt);
358 
359 	return TEE_SUCCESS;
360 
361 err_disable_unmap:
362 	interrupt_disable(it_hdlr->chip, it_hdlr->it);
363 	core_mmu_remove_mapping(MEM_AREA_IO_SEC, (void *)wdt->base, size);
364 err_remove_handler:
365 	interrupt_remove_free_handler(it_hdlr);
366 err_free:
367 	free(wdt);
368 
369 	return TEE_ERROR_GENERIC;
370 }
371 
372 static const struct wdt_compat sama5d2_compat = {
373 	.wdt_ps = true,
374 	.type = WDT_TYPE_WDT,
375 	.dis_mask = WDT_MR_WDDIS,
376 };
377 
378 static const struct wdt_compat sama7g5_compat = {
379 	.wdt_ps = false,
380 	.type = WDT_TYPE_DWDT,
381 	.dis_mask = WDT_MR_WDDIS_DWDT,
382 };
383 
384 static const struct dt_device_match atmel_wdt_match_table[] = {
385 	{
386 		.compatible = "atmel,sama5d4-wdt",
387 		.compat_data = &sama5d2_compat,
388 	},
389 	{
390 		.compatible = "microchip,sama7g5-wdt",
391 		.compat_data = &sama7g5_compat,
392 	},
393 	{ }
394 };
395 
396 DEFINE_DT_DRIVER(atmel_wdt_dt_driver) = {
397 	.name = "atmel_wdt",
398 	.type = DT_DRIVER_NOTYPE,
399 	.match_table = atmel_wdt_match_table,
400 	.probe = wdt_node_probe,
401 };
402