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