xref: /optee_os/core/drivers/atmel_wdt.c (revision 5f7f88c6b9d618d1e068166bbf2b07757350791d)
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 <sama5d2.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)
31 #define WDT_MR_WDD_SHIFT	16
32 #define WDT_MR_WDD_MASK		GENMASK_32(11, 0)
33 #define WDT_MR_WDD		SHIFT_U32(WDT_MR_WDD_MASK, WDT_MR_WDD_SHIFT)
34 #define WDT_MR_WDD_SET(val) \
35 			SHIFT_U32(((val) & WDT_MR_WDD_MASK), WDT_MR_WDD_SHIFT)
36 #define WDT_MR_WDDBGHLT		BIT(28)
37 #define WDT_MR_WDIDLEHLT	BIT(29)
38 
39 #define WDT_SR			0x8
40 #define WDT_SR_DUNF		BIT(0)
41 #define WDT_SR_DERR		BIT(1)
42 
43 /*
44  * The watchdog is clocked by a 32768Hz clock/128 and the counter is on
45  * 12 bits.
46  */
47 #define SLOW_CLOCK_FREQ		(32768)
48 #define WDT_CLOCK_FREQ		(SLOW_CLOCK_FREQ / 128)
49 #define WDT_MIN_TIMEOUT		1
50 #define WDT_MAX_TIMEOUT		(BIT(12) / WDT_CLOCK_FREQ)
51 
52 #define WDT_DEFAULT_TIMEOUT	WDT_MAX_TIMEOUT
53 
54 /*
55  * We must wait at least 3 clocks period before accessing registers MR and CR.
56  * Ensure that we see at least 4 edges
57  */
58 #define WDT_REG_ACCESS_UDELAY	(1000000ULL / SLOW_CLOCK_FREQ * 4)
59 
60 #define SEC_TO_WDT(sec)		(((sec) * WDT_CLOCK_FREQ) - 1)
61 
62 #define WDT_ENABLED(mr)		(!((mr) & WDT_MR_WDDIS))
63 
64 struct atmel_wdt {
65 	struct wdt_chip chip;
66 	vaddr_t base;
67 	unsigned long rate;
68 	uint32_t mr;
69 	bool enabled;
70 };
71 
72 static void atmel_wdt_write_sleep(struct atmel_wdt *wdt, uint32_t reg,
73 				  uint32_t val)
74 {
75 	udelay(WDT_REG_ACCESS_UDELAY);
76 
77 	io_write32(wdt->base + reg, val);
78 }
79 
80 static TEE_Result atmel_wdt_settimeout(struct wdt_chip *chip,
81 				       unsigned long timeout)
82 {
83 	struct atmel_wdt *wdt = container_of(chip, struct atmel_wdt, chip);
84 
85 	wdt->mr &= ~WDT_MR_WDV;
86 	wdt->mr |= WDT_MR_WDV_SET(SEC_TO_WDT(timeout));
87 
88 	/* WDV and WDD can only be updated when the watchdog is running */
89 	if (WDT_ENABLED(wdt->mr))
90 		atmel_wdt_write_sleep(wdt, WDT_MR, wdt->mr);
91 
92 	return TEE_SUCCESS;
93 }
94 
95 static void atmel_wdt_ping(struct wdt_chip *chip)
96 {
97 	struct atmel_wdt *wdt = container_of(chip, struct atmel_wdt, chip);
98 
99 	atmel_wdt_write_sleep(wdt, WDT_CR, WDT_CR_KEY | WDT_CR_WDRSTT);
100 }
101 
102 static void atmel_wdt_start(struct atmel_wdt *wdt)
103 {
104 	wdt->mr &= ~WDT_MR_WDDIS;
105 	atmel_wdt_write_sleep(wdt, WDT_MR, wdt->mr);
106 }
107 
108 static void atmel_wdt_enable(struct wdt_chip *chip)
109 {
110 	struct atmel_wdt *wdt = container_of(chip, struct atmel_wdt, chip);
111 
112 	wdt->enabled = true;
113 	atmel_wdt_start(wdt);
114 }
115 
116 static void atmel_wdt_stop(struct atmel_wdt *wdt)
117 {
118 	wdt->mr |= WDT_MR_WDDIS;
119 	atmel_wdt_write_sleep(wdt, WDT_MR, wdt->mr);
120 }
121 
122 static void atmel_wdt_disable(struct wdt_chip *chip)
123 {
124 	struct atmel_wdt *wdt = container_of(chip, struct atmel_wdt, chip);
125 
126 	wdt->enabled = false;
127 	atmel_wdt_stop(wdt);
128 }
129 
130 static enum itr_return atmel_wdt_itr_cb(struct itr_handler *h)
131 {
132 	struct atmel_wdt *wdt = h->data;
133 	uint32_t sr = io_read32(wdt->base + WDT_SR);
134 
135 	if (sr & WDT_SR_DUNF)
136 		DMSG("Watchdog Underflow !");
137 	if (sr & WDT_SR_DERR)
138 		DMSG("Watchdog Error !");
139 
140 	panic("Watchdog interrupt");
141 
142 	return ITRR_HANDLED;
143 }
144 
145 static TEE_Result atmel_wdt_init(struct wdt_chip *chip __unused,
146 				 unsigned long *min_timeout,
147 				 unsigned long *max_timeout)
148 {
149 	*min_timeout = WDT_MIN_TIMEOUT;
150 	*max_timeout = WDT_MAX_TIMEOUT;
151 
152 	return TEE_SUCCESS;
153 }
154 
155 static const struct wdt_ops atmel_wdt_ops = {
156 	.init = atmel_wdt_init,
157 	.start = atmel_wdt_enable,
158 	.stop = atmel_wdt_disable,
159 	.ping = atmel_wdt_ping,
160 	.set_timeout = atmel_wdt_settimeout,
161 };
162 
163 static void atmel_wdt_init_hw(struct atmel_wdt *wdt)
164 {
165 	uint32_t mr = 0;
166 
167 	/*
168 	 * If we are resuming, we disabled the watchdog on suspend but the
169 	 * bootloader might have enabled the watchdog. If so, disable it
170 	 * properly.
171 	 */
172 	if (!WDT_ENABLED(wdt->mr)) {
173 		mr = io_read32(wdt->base + WDT_MR);
174 		if (WDT_ENABLED(mr))
175 			io_write32(wdt->base + WDT_MR, mr | WDT_MR_WDDIS);
176 	}
177 
178 	/* Enable interrupt, and disable watchdog in debug and idle */
179 	wdt->mr |= WDT_MR_WDFIEN | WDT_MR_WDDBGHLT | WDT_MR_WDIDLEHLT;
180 	/* Enable watchdog reset */
181 	wdt->mr |= WDT_MR_WDRSTEN;
182 	wdt->mr |= WDT_MR_WDD_SET(SEC_TO_WDT(WDT_MAX_TIMEOUT));
183 	wdt->mr |= WDT_MR_WDV_SET(SEC_TO_WDT(WDT_DEFAULT_TIMEOUT));
184 
185 	/*
186 	 * If the watchdog was enabled, write the configuration which will ping
187 	 * the watchdog.
188 	 */
189 	if (WDT_ENABLED(wdt->mr))
190 		io_write32(wdt->base + WDT_MR, wdt->mr);
191 }
192 
193 #ifdef CFG_PM_ARM32
194 static TEE_Result atmel_wdt_pm(enum pm_op op, uint32_t pm_hint __unused,
195 			       const struct pm_callback_handle *hdl)
196 {
197 	struct atmel_wdt *wdt = hdl->handle;
198 
199 	switch (op) {
200 	case PM_OP_RESUME:
201 		atmel_wdt_init_hw(wdt);
202 		if (wdt->enabled)
203 			atmel_wdt_start(wdt);
204 		break;
205 	case PM_OP_SUSPEND:
206 		if (wdt->enabled)
207 			atmel_wdt_stop(wdt);
208 		break;
209 	default:
210 		panic("Invalid PM operation");
211 	}
212 
213 	return TEE_SUCCESS;
214 }
215 
216 static void atmel_wdt_register_pm(struct atmel_wdt *wdt)
217 {
218 	register_pm_driver_cb(atmel_wdt_pm, wdt, "atmel_wdt");
219 }
220 #else
221 static void atmel_wdt_register_pm(struct atmel_wdt *wdt __unused)
222 {
223 }
224 #endif
225 
226 static TEE_Result wdt_node_probe(const void *fdt, int node,
227 				 const void *compat_data __unused)
228 {
229 	size_t size = 0;
230 	struct atmel_wdt *wdt;
231 	uint32_t irq_type = 0;
232 	uint32_t irq_prio = 0;
233 	int it = DT_INFO_INVALID_INTERRUPT;
234 	struct itr_handler *it_hdlr = NULL;
235 	TEE_Result res = TEE_ERROR_GENERIC;
236 
237 	if (fdt_get_status(fdt, node) != DT_STATUS_OK_SEC)
238 		return TEE_ERROR_BAD_PARAMETERS;
239 
240 	matrix_configure_periph_secure(AT91C_ID_WDT);
241 
242 	wdt = calloc(1, sizeof(*wdt));
243 	if (!wdt)
244 		return TEE_ERROR_OUT_OF_MEMORY;
245 
246 	wdt->chip.ops = &atmel_wdt_ops;
247 
248 	it = dt_get_irq_type_prio(fdt, node, &irq_type, &irq_prio);
249 	if (it == DT_INFO_INVALID_INTERRUPT)
250 		goto err_free;
251 
252 	res = interrupt_alloc_add_conf_handler(interrupt_get_main_chip(),
253 					       it, atmel_wdt_itr_cb, 0, wdt,
254 					       irq_type, irq_prio, &it_hdlr);
255 	if (res)
256 		goto err_free;
257 
258 	if (dt_map_dev(fdt, node, &wdt->base, &size, DT_MAP_AUTO) < 0)
259 		goto err_remove_handler;
260 
261 	/* Get current state of the watchdog */
262 	wdt->mr = io_read32(wdt->base + WDT_MR) & WDT_MR_WDDIS;
263 
264 	atmel_wdt_init_hw(wdt);
265 	interrupt_enable(it_hdlr->chip, it_hdlr->it);
266 
267 	res = watchdog_register(&wdt->chip);
268 	if (res)
269 		goto err_disable_unmap;
270 
271 	atmel_wdt_register_pm(wdt);
272 
273 	return TEE_SUCCESS;
274 
275 err_disable_unmap:
276 	interrupt_disable(it_hdlr->chip, it_hdlr->it);
277 	core_mmu_remove_mapping(MEM_AREA_IO_SEC, (void *)wdt->base, size);
278 err_remove_handler:
279 	interrupt_remove_free_handler(it_hdlr);
280 err_free:
281 	free(wdt);
282 
283 	return TEE_ERROR_GENERIC;
284 }
285 
286 static const struct dt_device_match atmel_wdt_match_table[] = {
287 	{ .compatible = "atmel,sama5d4-wdt" },
288 	{ }
289 };
290 
291 DEFINE_DT_DRIVER(atmel_wdt_dt_driver) = {
292 	.name = "atmel_wdt",
293 	.type = DT_DRIVER_NOTYPE,
294 	.match_table = atmel_wdt_match_table,
295 	.probe = wdt_node_probe,
296 };
297