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