1 // SPDX-License-Identifier: BSD-2-Clause
2 /*
3 * Copyright 2022 Microchip
4 */
5
6 #include <assert.h>
7 #include <drivers/wdt.h>
8 #include <io.h>
9 #include <kernel/delay.h>
10 #include <kernel/dt.h>
11 #include <kernel/dt_driver.h>
12 #include <kernel/interrupt.h>
13 #include <kernel/panic.h>
14 #include <kernel/pm.h>
15 #include <matrix.h>
16 #include <mm/core_mmu.h>
17 #include <tee_api_defines.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) /* Watchdog Disable of WDT on bit 15 */
29 #define WDT_MR_WDDIS_DWDT BIT(12) /* Watchdog Disable of DWDT on bit 12 */
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 /* DWDT: Watchdog Timer Mode Register */
43 #define WDT_MR_PERIODRST BIT(4)
44 #define WDT_MR_RPTHRST BIT(5)
45
46 /* DWDT: Watchdog Timer Value Register (Read-only) */
47 #define WDT_VR 0x8
48 #define WDT_VR_COUNTER_SHIFT 0
49 #define WDT_VR_COUNTER_MASK GENMASK_32(11, 0)
50
51 /* DWDT: Watchdog Timer Window Level Register */
52 #define WDT_WL 0xc
53 #define WDT_WL_RPTH_SHIFT 16
54 #define WDT_WL_RPTH_MASK GENMASK_32(27, 16)
55 #define WDT_WL_PERIOD_SHIFT 0
56 #define WDT_WL_PERIOD_MASK GENMASK_32(11, 0)
57
58 /* DWDT: Watchdog Timer Interrupt Level Register */
59 #define WDT_IL 0x10
60 #define WDT_IL_LVLTH_SHIFT 0
61 #define WDT_IL_LVLTH_MASK GENMASK_32(11, 0)
62
63 /* DWDT: Watchdog Timer Interrupt Enable/Disable/Status/Mask Register */
64 #define WDT_IER 0x14
65 #define WDT_IDR 0x18
66 #define WDT_ISR 0x1c
67 #define WDT_IMR 0x20
68 #define WDT_NSRPTHINT BIT(4)
69 #define WDT_NSPERINT BIT(3)
70 #define WDT_LVLINT BIT(2)
71 #define WDT_RPTHINT BIT(1)
72 #define WDT_PERINT BIT(0)
73
74 /*
75 * The watchdog is clocked by a 32768Hz clock/128 and the counter is on
76 * 12 bits.
77 */
78 #define SLOW_CLOCK_FREQ (32768)
79 #define WDT_CLOCK_FREQ (SLOW_CLOCK_FREQ / 128)
80 #define WDT_MIN_TIMEOUT 1
81 #define WDT_MAX_TIMEOUT (BIT(12) / WDT_CLOCK_FREQ)
82
83 #define WDT_DEFAULT_TIMEOUT WDT_MAX_TIMEOUT
84
85 /*
86 * We must wait at least 3 clocks period before accessing registers MR and CR.
87 * Ensure that we see at least 4 edges
88 */
89 #define WDT_REG_ACCESS_UDELAY (1000000ULL / SLOW_CLOCK_FREQ * 4)
90
91 #define SEC_TO_WDT(sec) (((sec) * WDT_CLOCK_FREQ) - 1)
92
93 #define WDT_ENABLED(mr, dis_mask) (!((mr) & (dis_mask)))
94
95 enum wdt_type {
96 WDT_TYPE_WDT, /* Watchdog Timer */
97 WDT_TYPE_DWDT, /* Dual Watchdog Timer */
98 };
99
100 struct wdt_compat {
101 bool wdt_ps; /* Is Peripheral SHDWC Programmable Secure */
102 enum wdt_type type; /* Type of Watchdog Timer */
103 uint32_t dis_mask; /* Mask of Watchdog Disable in Mode Register */
104 };
105
106 struct atmel_wdt {
107 struct wdt_chip chip;
108 enum wdt_type type;
109 uint32_t dis_mask;
110 vaddr_t base;
111 uint32_t mr;
112 bool enabled;
113 };
114
atmel_wdt_write_sleep(struct atmel_wdt * wdt,uint32_t reg,uint32_t val)115 static void atmel_wdt_write_sleep(struct atmel_wdt *wdt, uint32_t reg,
116 uint32_t val)
117 {
118 udelay(WDT_REG_ACCESS_UDELAY);
119
120 io_write32(wdt->base + reg, val);
121 }
122
atmel_wdt_settimeout(struct wdt_chip * chip,unsigned long timeout)123 static TEE_Result atmel_wdt_settimeout(struct wdt_chip *chip,
124 unsigned long timeout)
125 {
126 struct atmel_wdt *wdt = container_of(chip, struct atmel_wdt, chip);
127
128 if (wdt->type == WDT_TYPE_WDT) {
129 wdt->mr &= ~WDT_MR_WDV;
130 wdt->mr |= WDT_MR_WDV_SET(SEC_TO_WDT(timeout));
131
132 /* WDV and WDD only be updated when the watchdog is running */
133 if (WDT_ENABLED(wdt->mr, wdt->dis_mask))
134 atmel_wdt_write_sleep(wdt, WDT_MR, wdt->mr);
135 } else {
136 io_write32(wdt->base + WDT_WL,
137 SHIFT_U32(SEC_TO_WDT(timeout), WDT_WL_PERIOD_SHIFT));
138 }
139
140 return TEE_SUCCESS;
141 }
142
atmel_wdt_ping(struct wdt_chip * chip)143 static void atmel_wdt_ping(struct wdt_chip *chip)
144 {
145 struct atmel_wdt *wdt = container_of(chip, struct atmel_wdt, chip);
146
147 atmel_wdt_write_sleep(wdt, WDT_CR, WDT_CR_KEY | WDT_CR_WDRSTT);
148 }
149
atmel_wdt_start(struct atmel_wdt * wdt)150 static void atmel_wdt_start(struct atmel_wdt *wdt)
151 {
152 wdt->mr &= ~wdt->dis_mask;
153 atmel_wdt_write_sleep(wdt, WDT_MR, wdt->mr);
154 }
155
atmel_wdt_enable(struct wdt_chip * chip)156 static void atmel_wdt_enable(struct wdt_chip *chip)
157 {
158 struct atmel_wdt *wdt = container_of(chip, struct atmel_wdt, chip);
159
160 wdt->enabled = true;
161 atmel_wdt_start(wdt);
162 }
163
atmel_wdt_stop(struct atmel_wdt * wdt)164 static void atmel_wdt_stop(struct atmel_wdt *wdt)
165 {
166 wdt->mr |= wdt->dis_mask;
167 atmel_wdt_write_sleep(wdt, WDT_MR, wdt->mr);
168 }
169
atmel_wdt_disable(struct wdt_chip * chip)170 static void atmel_wdt_disable(struct wdt_chip *chip)
171 {
172 struct atmel_wdt *wdt = container_of(chip, struct atmel_wdt, chip);
173
174 wdt->enabled = false;
175 atmel_wdt_stop(wdt);
176 }
177
atmel_wdt_itr_cb(struct itr_handler * h)178 static enum itr_return atmel_wdt_itr_cb(struct itr_handler *h)
179 {
180 struct atmel_wdt *wdt = h->data;
181 uint32_t sr = 0;
182
183 if (wdt->type == WDT_TYPE_WDT) {
184 sr = io_read32(wdt->base + WDT_SR);
185
186 if (sr & WDT_SR_DUNF)
187 DMSG("Watchdog Underflow");
188 if (sr & WDT_SR_DERR)
189 DMSG("Watchdog Error");
190 } else if (wdt->type == WDT_TYPE_DWDT) {
191 sr = io_read32(wdt->base + WDT_ISR);
192
193 if (sr & WDT_NSRPTHINT)
194 DMSG("NS Watchdog Repeat Threshold Interrupt");
195 if (sr & WDT_NSPERINT)
196 DMSG("NS Watchdog Period Interrupt");
197 if (sr & WDT_LVLINT)
198 DMSG("Watchdog Level Threshold Interrupt");
199 if (sr & WDT_RPTHINT)
200 DMSG("Watchdog Repeat Threshold Interrupt");
201 if (sr & WDT_PERINT)
202 DMSG("Watchdog Period Interrupt");
203 }
204
205 panic("Watchdog interrupt");
206
207 return ITRR_HANDLED;
208 }
209
atmel_wdt_init(struct wdt_chip * chip __unused,unsigned long * min_timeout,unsigned long * max_timeout)210 static TEE_Result atmel_wdt_init(struct wdt_chip *chip __unused,
211 unsigned long *min_timeout,
212 unsigned long *max_timeout)
213 {
214 *min_timeout = WDT_MIN_TIMEOUT;
215 *max_timeout = WDT_MAX_TIMEOUT;
216
217 return TEE_SUCCESS;
218 }
219
220 static const struct wdt_ops atmel_wdt_ops = {
221 .init = atmel_wdt_init,
222 .start = atmel_wdt_enable,
223 .stop = atmel_wdt_disable,
224 .ping = atmel_wdt_ping,
225 .set_timeout = atmel_wdt_settimeout,
226 };
227
atmel_wdt_init_hw(struct atmel_wdt * wdt)228 static void atmel_wdt_init_hw(struct atmel_wdt *wdt)
229 {
230 uint32_t mr = 0;
231
232 /*
233 * If we are resuming, we disabled the watchdog on suspend but the
234 * bootloader might have enabled the watchdog. If so, disable it
235 * properly.
236 */
237 if (!WDT_ENABLED(wdt->mr, wdt->dis_mask)) {
238 mr = io_read32(wdt->base + WDT_MR);
239 if (WDT_ENABLED(mr, wdt->dis_mask))
240 io_write32(wdt->base + WDT_MR, mr | wdt->dis_mask);
241 }
242
243 if (wdt->type == WDT_TYPE_WDT) {
244 /* Enable interrupt, and disable watchdog in debug and idle */
245 wdt->mr |= WDT_MR_WDFIEN | WDT_MR_WDDBGHLT | WDT_MR_WDIDLEHLT;
246 /* Enable watchdog reset */
247 wdt->mr |= WDT_MR_WDRSTEN;
248 wdt->mr |= WDT_MR_WDD_SET(SEC_TO_WDT(WDT_MAX_TIMEOUT));
249 wdt->mr |= WDT_MR_WDV_SET(SEC_TO_WDT(WDT_DEFAULT_TIMEOUT));
250 } else if (wdt->type == WDT_TYPE_DWDT) {
251 /* Enable interrupt */
252 io_write32(wdt->base + WDT_ISR, WDT_PERINT);
253 /* Disable watchdog in debug and idle */
254 wdt->mr |= WDT_MR_WDDBGHLT | WDT_MR_WDIDLEHLT;
255 /* Enable watchdog period reset */
256 wdt->mr |= WDT_MR_PERIODRST;
257 io_write32(wdt->base + WDT_WL,
258 SHIFT_U32(SEC_TO_WDT(WDT_DEFAULT_TIMEOUT),
259 WDT_WL_PERIOD_SHIFT));
260 } else {
261 panic("Invalid Watchdog");
262 }
263
264 /*
265 * If the watchdog was enabled, write the configuration which will ping
266 * the watchdog.
267 */
268 if (WDT_ENABLED(wdt->mr, wdt->dis_mask))
269 io_write32(wdt->base + WDT_MR, wdt->mr);
270 }
271
272 #ifdef CFG_PM_ARM32
atmel_wdt_pm(enum pm_op op,uint32_t pm_hint __unused,const struct pm_callback_handle * hdl)273 static TEE_Result atmel_wdt_pm(enum pm_op op, uint32_t pm_hint __unused,
274 const struct pm_callback_handle *hdl)
275 {
276 struct atmel_wdt *wdt = hdl->handle;
277
278 switch (op) {
279 case PM_OP_RESUME:
280 atmel_wdt_init_hw(wdt);
281 if (wdt->enabled)
282 atmel_wdt_start(wdt);
283 break;
284 case PM_OP_SUSPEND:
285 if (wdt->enabled)
286 atmel_wdt_stop(wdt);
287 break;
288 default:
289 panic("Invalid PM operation");
290 }
291
292 return TEE_SUCCESS;
293 }
294
atmel_wdt_register_pm(struct atmel_wdt * wdt)295 static void atmel_wdt_register_pm(struct atmel_wdt *wdt)
296 {
297 register_pm_driver_cb(atmel_wdt_pm, wdt, "atmel_wdt");
298 }
299 #else
atmel_wdt_register_pm(struct atmel_wdt * wdt __unused)300 static void atmel_wdt_register_pm(struct atmel_wdt *wdt __unused)
301 {
302 }
303 #endif
304
wdt_node_probe(const void * fdt,int node,const void * compat_data)305 static TEE_Result wdt_node_probe(const void *fdt, int node,
306 const void *compat_data)
307 {
308 const struct wdt_compat *compat = compat_data;
309 size_t size = 0;
310 struct atmel_wdt *wdt;
311 uint32_t irq_type = 0;
312 uint32_t irq_prio = 0;
313 int it = DT_INFO_INVALID_INTERRUPT;
314 struct itr_handler *it_hdlr = NULL;
315 TEE_Result res = TEE_ERROR_GENERIC;
316
317 if (fdt_get_status(fdt, node) != DT_STATUS_OK_SEC)
318 return TEE_ERROR_BAD_PARAMETERS;
319
320 if (compat->wdt_ps)
321 matrix_configure_periph_secure(AT91C_ID_WDT);
322
323 wdt = calloc(1, sizeof(*wdt));
324 if (!wdt)
325 return TEE_ERROR_OUT_OF_MEMORY;
326
327 wdt->chip.ops = &atmel_wdt_ops;
328 wdt->type = compat->type;
329 wdt->dis_mask = compat->dis_mask;
330
331 it = dt_get_irq_type_prio(fdt, node, &irq_type, &irq_prio);
332 if (it == DT_INFO_INVALID_INTERRUPT)
333 goto err_free;
334
335 res = interrupt_alloc_add_conf_handler(interrupt_get_main_chip(),
336 it, atmel_wdt_itr_cb, 0, wdt,
337 irq_type, irq_prio, &it_hdlr);
338 if (res)
339 goto err_free;
340
341 if (dt_map_dev(fdt, node, &wdt->base, &size, DT_MAP_AUTO) < 0)
342 goto err_remove_handler;
343
344 /* Get current state of the watchdog */
345 wdt->mr = io_read32(wdt->base + WDT_MR) & wdt->dis_mask;
346
347 atmel_wdt_init_hw(wdt);
348 interrupt_enable(it_hdlr->chip, it_hdlr->it);
349
350 res = watchdog_register(&wdt->chip);
351 if (res)
352 goto err_disable_unmap;
353
354 atmel_wdt_register_pm(wdt);
355
356 return TEE_SUCCESS;
357
358 err_disable_unmap:
359 interrupt_disable(it_hdlr->chip, it_hdlr->it);
360 core_mmu_remove_mapping(MEM_AREA_IO_SEC, (void *)wdt->base, size);
361 err_remove_handler:
362 interrupt_remove_free_handler(it_hdlr);
363 err_free:
364 free(wdt);
365
366 return TEE_ERROR_GENERIC;
367 }
368
369 static const struct wdt_compat sama5d2_compat = {
370 .wdt_ps = true,
371 .type = WDT_TYPE_WDT,
372 .dis_mask = WDT_MR_WDDIS,
373 };
374
375 static const struct wdt_compat sama7g5_compat = {
376 .wdt_ps = false,
377 .type = WDT_TYPE_DWDT,
378 .dis_mask = WDT_MR_WDDIS_DWDT,
379 };
380
381 static const struct dt_device_match atmel_wdt_match_table[] = {
382 {
383 .compatible = "atmel,sama5d4-wdt",
384 .compat_data = &sama5d2_compat,
385 },
386 {
387 .compatible = "microchip,sama7g5-wdt",
388 .compat_data = &sama7g5_compat,
389 },
390 { }
391 };
392
393 DEFINE_DT_DRIVER(atmel_wdt_dt_driver) = {
394 .name = "atmel_wdt",
395 .type = DT_DRIVER_NOTYPE,
396 .match_table = atmel_wdt_match_table,
397 .probe = wdt_node_probe,
398 };
399