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