15cbd8b3aSClément Léger // SPDX-License-Identifier: BSD-2-Clause
25cbd8b3aSClément Léger /*
35cbd8b3aSClément Léger * Copyright 2022 Microchip
45cbd8b3aSClément Léger */
55cbd8b3aSClément Léger
65cbd8b3aSClément Léger #include <assert.h>
75cbd8b3aSClément Léger #include <drivers/wdt.h>
85cbd8b3aSClément Léger #include <io.h>
95cbd8b3aSClément Léger #include <kernel/delay.h>
105cbd8b3aSClément Léger #include <kernel/dt.h>
119e3c57c8SEtienne Carriere #include <kernel/dt_driver.h>
1233a0c835SEtienne Carriere #include <kernel/interrupt.h>
13*3b616eeaSTony Han #include <kernel/panic.h>
145cbd8b3aSClément Léger #include <kernel/pm.h>
155cbd8b3aSClément Léger #include <matrix.h>
16c64c658bSEtienne Carriere #include <mm/core_mmu.h>
17*3b616eeaSTony Han #include <tee_api_defines.h>
185cbd8b3aSClément Léger
195cbd8b3aSClément Léger #define WDT_CR 0x0
205cbd8b3aSClément Léger #define WDT_CR_KEY SHIFT_U32(0xA5, 24)
215cbd8b3aSClément Léger #define WDT_CR_WDRSTT BIT(0)
225cbd8b3aSClément Léger
235cbd8b3aSClément Léger #define WDT_MR 0x4
245cbd8b3aSClément Léger #define WDT_MR_WDV GENMASK_32(11, 0)
255cbd8b3aSClément Léger #define WDT_MR_WDV_SET(val) ((val) & WDT_MR_WDV)
265cbd8b3aSClément Léger #define WDT_MR_WDFIEN BIT(12)
275cbd8b3aSClément Léger #define WDT_MR_WDRSTEN BIT(13)
28ea9329ecSTony Han #define WDT_MR_WDDIS BIT(15) /* Watchdog Disable of WDT on bit 15 */
29ea9329ecSTony Han #define WDT_MR_WDDIS_DWDT BIT(12) /* Watchdog Disable of DWDT on bit 12 */
305cbd8b3aSClément Léger #define WDT_MR_WDD_SHIFT 16
315cbd8b3aSClément Léger #define WDT_MR_WDD_MASK GENMASK_32(11, 0)
325cbd8b3aSClément Léger #define WDT_MR_WDD SHIFT_U32(WDT_MR_WDD_MASK, WDT_MR_WDD_SHIFT)
335cbd8b3aSClément Léger #define WDT_MR_WDD_SET(val) \
345cbd8b3aSClément Léger SHIFT_U32(((val) & WDT_MR_WDD_MASK), WDT_MR_WDD_SHIFT)
355cbd8b3aSClément Léger #define WDT_MR_WDDBGHLT BIT(28)
365cbd8b3aSClément Léger #define WDT_MR_WDIDLEHLT BIT(29)
375cbd8b3aSClément Léger
385cbd8b3aSClément Léger #define WDT_SR 0x8
395cbd8b3aSClément Léger #define WDT_SR_DUNF BIT(0)
405cbd8b3aSClément Léger #define WDT_SR_DERR BIT(1)
415cbd8b3aSClément Léger
42ea9329ecSTony Han /* DWDT: Watchdog Timer Mode Register */
43ea9329ecSTony Han #define WDT_MR_PERIODRST BIT(4)
44ea9329ecSTony Han #define WDT_MR_RPTHRST BIT(5)
45ea9329ecSTony Han
46ea9329ecSTony Han /* DWDT: Watchdog Timer Value Register (Read-only) */
47ea9329ecSTony Han #define WDT_VR 0x8
48ea9329ecSTony Han #define WDT_VR_COUNTER_SHIFT 0
49ea9329ecSTony Han #define WDT_VR_COUNTER_MASK GENMASK_32(11, 0)
50ea9329ecSTony Han
51ea9329ecSTony Han /* DWDT: Watchdog Timer Window Level Register */
52ea9329ecSTony Han #define WDT_WL 0xc
53ea9329ecSTony Han #define WDT_WL_RPTH_SHIFT 16
54ea9329ecSTony Han #define WDT_WL_RPTH_MASK GENMASK_32(27, 16)
55ea9329ecSTony Han #define WDT_WL_PERIOD_SHIFT 0
56ea9329ecSTony Han #define WDT_WL_PERIOD_MASK GENMASK_32(11, 0)
57ea9329ecSTony Han
58ea9329ecSTony Han /* DWDT: Watchdog Timer Interrupt Level Register */
59ea9329ecSTony Han #define WDT_IL 0x10
60ea9329ecSTony Han #define WDT_IL_LVLTH_SHIFT 0
61ea9329ecSTony Han #define WDT_IL_LVLTH_MASK GENMASK_32(11, 0)
62ea9329ecSTony Han
63ea9329ecSTony Han /* DWDT: Watchdog Timer Interrupt Enable/Disable/Status/Mask Register */
64ea9329ecSTony Han #define WDT_IER 0x14
65ea9329ecSTony Han #define WDT_IDR 0x18
66ea9329ecSTony Han #define WDT_ISR 0x1c
67ea9329ecSTony Han #define WDT_IMR 0x20
68ea9329ecSTony Han #define WDT_NSRPTHINT BIT(4)
69ea9329ecSTony Han #define WDT_NSPERINT BIT(3)
70ea9329ecSTony Han #define WDT_LVLINT BIT(2)
71ea9329ecSTony Han #define WDT_RPTHINT BIT(1)
72ea9329ecSTony Han #define WDT_PERINT BIT(0)
73ea9329ecSTony Han
745cbd8b3aSClément Léger /*
755cbd8b3aSClément Léger * The watchdog is clocked by a 32768Hz clock/128 and the counter is on
765cbd8b3aSClément Léger * 12 bits.
775cbd8b3aSClément Léger */
785cbd8b3aSClément Léger #define SLOW_CLOCK_FREQ (32768)
795cbd8b3aSClément Léger #define WDT_CLOCK_FREQ (SLOW_CLOCK_FREQ / 128)
805cbd8b3aSClément Léger #define WDT_MIN_TIMEOUT 1
815cbd8b3aSClément Léger #define WDT_MAX_TIMEOUT (BIT(12) / WDT_CLOCK_FREQ)
825cbd8b3aSClément Léger
835cbd8b3aSClément Léger #define WDT_DEFAULT_TIMEOUT WDT_MAX_TIMEOUT
845cbd8b3aSClément Léger
855cbd8b3aSClément Léger /*
865cbd8b3aSClément Léger * We must wait at least 3 clocks period before accessing registers MR and CR.
875cbd8b3aSClément Léger * Ensure that we see at least 4 edges
885cbd8b3aSClément Léger */
895cbd8b3aSClément Léger #define WDT_REG_ACCESS_UDELAY (1000000ULL / SLOW_CLOCK_FREQ * 4)
905cbd8b3aSClément Léger
915cbd8b3aSClément Léger #define SEC_TO_WDT(sec) (((sec) * WDT_CLOCK_FREQ) - 1)
925cbd8b3aSClément Léger
93ea9329ecSTony Han #define WDT_ENABLED(mr, dis_mask) (!((mr) & (dis_mask)))
94ea9329ecSTony Han
95ea9329ecSTony Han enum wdt_type {
96ea9329ecSTony Han WDT_TYPE_WDT, /* Watchdog Timer */
97ea9329ecSTony Han WDT_TYPE_DWDT, /* Dual Watchdog Timer */
98ea9329ecSTony Han };
99ea9329ecSTony Han
100ea9329ecSTony Han struct wdt_compat {
101ea9329ecSTony Han bool wdt_ps; /* Is Peripheral SHDWC Programmable Secure */
102ea9329ecSTony Han enum wdt_type type; /* Type of Watchdog Timer */
103ea9329ecSTony Han uint32_t dis_mask; /* Mask of Watchdog Disable in Mode Register */
104ea9329ecSTony Han };
1055cbd8b3aSClément Léger
1065cbd8b3aSClément Léger struct atmel_wdt {
1075cbd8b3aSClément Léger struct wdt_chip chip;
108ea9329ecSTony Han enum wdt_type type;
109ea9329ecSTony Han uint32_t dis_mask;
1105cbd8b3aSClément Léger vaddr_t base;
1115cbd8b3aSClément Léger uint32_t mr;
1125cbd8b3aSClément Léger bool enabled;
1135cbd8b3aSClément Léger };
1145cbd8b3aSClément Léger
atmel_wdt_write_sleep(struct atmel_wdt * wdt,uint32_t reg,uint32_t val)1155cbd8b3aSClément Léger static void atmel_wdt_write_sleep(struct atmel_wdt *wdt, uint32_t reg,
1165cbd8b3aSClément Léger uint32_t val)
1175cbd8b3aSClément Léger {
1185cbd8b3aSClément Léger udelay(WDT_REG_ACCESS_UDELAY);
1195cbd8b3aSClément Léger
1205cbd8b3aSClément Léger io_write32(wdt->base + reg, val);
1215cbd8b3aSClément Léger }
1225cbd8b3aSClément Léger
atmel_wdt_settimeout(struct wdt_chip * chip,unsigned long timeout)1235cbd8b3aSClément Léger static TEE_Result atmel_wdt_settimeout(struct wdt_chip *chip,
1245cbd8b3aSClément Léger unsigned long timeout)
1255cbd8b3aSClément Léger {
1265cbd8b3aSClément Léger struct atmel_wdt *wdt = container_of(chip, struct atmel_wdt, chip);
1275cbd8b3aSClément Léger
128ea9329ecSTony Han if (wdt->type == WDT_TYPE_WDT) {
1295cbd8b3aSClément Léger wdt->mr &= ~WDT_MR_WDV;
1305cbd8b3aSClément Léger wdt->mr |= WDT_MR_WDV_SET(SEC_TO_WDT(timeout));
1315cbd8b3aSClément Léger
132ea9329ecSTony Han /* WDV and WDD only be updated when the watchdog is running */
133ea9329ecSTony Han if (WDT_ENABLED(wdt->mr, wdt->dis_mask))
1345cbd8b3aSClément Léger atmel_wdt_write_sleep(wdt, WDT_MR, wdt->mr);
135ea9329ecSTony Han } else {
136ea9329ecSTony Han io_write32(wdt->base + WDT_WL,
137ea9329ecSTony Han SHIFT_U32(SEC_TO_WDT(timeout), WDT_WL_PERIOD_SHIFT));
138ea9329ecSTony Han }
1395cbd8b3aSClément Léger
1405cbd8b3aSClément Léger return TEE_SUCCESS;
1415cbd8b3aSClément Léger }
1425cbd8b3aSClément Léger
atmel_wdt_ping(struct wdt_chip * chip)1435cbd8b3aSClément Léger static void atmel_wdt_ping(struct wdt_chip *chip)
1445cbd8b3aSClément Léger {
1455cbd8b3aSClément Léger struct atmel_wdt *wdt = container_of(chip, struct atmel_wdt, chip);
1465cbd8b3aSClément Léger
1475cbd8b3aSClément Léger atmel_wdt_write_sleep(wdt, WDT_CR, WDT_CR_KEY | WDT_CR_WDRSTT);
1485cbd8b3aSClément Léger }
1495cbd8b3aSClément Léger
atmel_wdt_start(struct atmel_wdt * wdt)1505cbd8b3aSClément Léger static void atmel_wdt_start(struct atmel_wdt *wdt)
1515cbd8b3aSClément Léger {
152ea9329ecSTony Han wdt->mr &= ~wdt->dis_mask;
1535cbd8b3aSClément Léger atmel_wdt_write_sleep(wdt, WDT_MR, wdt->mr);
1545cbd8b3aSClément Léger }
1555cbd8b3aSClément Léger
atmel_wdt_enable(struct wdt_chip * chip)1565cbd8b3aSClément Léger static void atmel_wdt_enable(struct wdt_chip *chip)
1575cbd8b3aSClément Léger {
1585cbd8b3aSClément Léger struct atmel_wdt *wdt = container_of(chip, struct atmel_wdt, chip);
1595cbd8b3aSClément Léger
1605cbd8b3aSClément Léger wdt->enabled = true;
1615cbd8b3aSClément Léger atmel_wdt_start(wdt);
1625cbd8b3aSClément Léger }
1635cbd8b3aSClément Léger
atmel_wdt_stop(struct atmel_wdt * wdt)1645cbd8b3aSClément Léger static void atmel_wdt_stop(struct atmel_wdt *wdt)
1655cbd8b3aSClément Léger {
166ea9329ecSTony Han wdt->mr |= wdt->dis_mask;
1675cbd8b3aSClément Léger atmel_wdt_write_sleep(wdt, WDT_MR, wdt->mr);
1685cbd8b3aSClément Léger }
1695cbd8b3aSClément Léger
atmel_wdt_disable(struct wdt_chip * chip)1705cbd8b3aSClément Léger static void atmel_wdt_disable(struct wdt_chip *chip)
1715cbd8b3aSClément Léger {
1725cbd8b3aSClément Léger struct atmel_wdt *wdt = container_of(chip, struct atmel_wdt, chip);
1735cbd8b3aSClément Léger
1745cbd8b3aSClément Léger wdt->enabled = false;
1755cbd8b3aSClément Léger atmel_wdt_stop(wdt);
1765cbd8b3aSClément Léger }
1775cbd8b3aSClément Léger
atmel_wdt_itr_cb(struct itr_handler * h)1785cbd8b3aSClément Léger static enum itr_return atmel_wdt_itr_cb(struct itr_handler *h)
1795cbd8b3aSClément Léger {
1805cbd8b3aSClément Léger struct atmel_wdt *wdt = h->data;
181ea9329ecSTony Han uint32_t sr = 0;
182ea9329ecSTony Han
183ea9329ecSTony Han if (wdt->type == WDT_TYPE_WDT) {
184ea9329ecSTony Han sr = io_read32(wdt->base + WDT_SR);
1855cbd8b3aSClément Léger
1865cbd8b3aSClément Léger if (sr & WDT_SR_DUNF)
187ea9329ecSTony Han DMSG("Watchdog Underflow");
1885cbd8b3aSClément Léger if (sr & WDT_SR_DERR)
189ea9329ecSTony Han DMSG("Watchdog Error");
190ea9329ecSTony Han } else if (wdt->type == WDT_TYPE_DWDT) {
191ea9329ecSTony Han sr = io_read32(wdt->base + WDT_ISR);
192ea9329ecSTony Han
193ea9329ecSTony Han if (sr & WDT_NSRPTHINT)
194ea9329ecSTony Han DMSG("NS Watchdog Repeat Threshold Interrupt");
195ea9329ecSTony Han if (sr & WDT_NSPERINT)
196ea9329ecSTony Han DMSG("NS Watchdog Period Interrupt");
197ea9329ecSTony Han if (sr & WDT_LVLINT)
198ea9329ecSTony Han DMSG("Watchdog Level Threshold Interrupt");
199ea9329ecSTony Han if (sr & WDT_RPTHINT)
200ea9329ecSTony Han DMSG("Watchdog Repeat Threshold Interrupt");
201ea9329ecSTony Han if (sr & WDT_PERINT)
202ea9329ecSTony Han DMSG("Watchdog Period Interrupt");
203ea9329ecSTony Han }
2045cbd8b3aSClément Léger
2055cbd8b3aSClément Léger panic("Watchdog interrupt");
2065cbd8b3aSClément Léger
2075cbd8b3aSClément Léger return ITRR_HANDLED;
2085cbd8b3aSClément Léger }
2095cbd8b3aSClément Léger
atmel_wdt_init(struct wdt_chip * chip __unused,unsigned long * min_timeout,unsigned long * max_timeout)2105cbd8b3aSClément Léger static TEE_Result atmel_wdt_init(struct wdt_chip *chip __unused,
2115cbd8b3aSClément Léger unsigned long *min_timeout,
2125cbd8b3aSClément Léger unsigned long *max_timeout)
2135cbd8b3aSClément Léger {
2145cbd8b3aSClément Léger *min_timeout = WDT_MIN_TIMEOUT;
2155cbd8b3aSClément Léger *max_timeout = WDT_MAX_TIMEOUT;
2165cbd8b3aSClément Léger
2175cbd8b3aSClément Léger return TEE_SUCCESS;
2185cbd8b3aSClément Léger }
2195cbd8b3aSClément Léger
2205cbd8b3aSClément Léger static const struct wdt_ops atmel_wdt_ops = {
2215cbd8b3aSClément Léger .init = atmel_wdt_init,
2225cbd8b3aSClément Léger .start = atmel_wdt_enable,
2235cbd8b3aSClément Léger .stop = atmel_wdt_disable,
2245cbd8b3aSClément Léger .ping = atmel_wdt_ping,
2255cbd8b3aSClément Léger .set_timeout = atmel_wdt_settimeout,
2265cbd8b3aSClément Léger };
2275cbd8b3aSClément Léger
atmel_wdt_init_hw(struct atmel_wdt * wdt)2285cbd8b3aSClément Léger static void atmel_wdt_init_hw(struct atmel_wdt *wdt)
2295cbd8b3aSClément Léger {
2305cbd8b3aSClément Léger uint32_t mr = 0;
2315cbd8b3aSClément Léger
2325cbd8b3aSClément Léger /*
2335cbd8b3aSClément Léger * If we are resuming, we disabled the watchdog on suspend but the
2345cbd8b3aSClément Léger * bootloader might have enabled the watchdog. If so, disable it
2355cbd8b3aSClément Léger * properly.
2365cbd8b3aSClément Léger */
237ea9329ecSTony Han if (!WDT_ENABLED(wdt->mr, wdt->dis_mask)) {
2385cbd8b3aSClément Léger mr = io_read32(wdt->base + WDT_MR);
239ea9329ecSTony Han if (WDT_ENABLED(mr, wdt->dis_mask))
240ea9329ecSTony Han io_write32(wdt->base + WDT_MR, mr | wdt->dis_mask);
2415cbd8b3aSClément Léger }
2425cbd8b3aSClément Léger
243ea9329ecSTony Han if (wdt->type == WDT_TYPE_WDT) {
2445cbd8b3aSClément Léger /* Enable interrupt, and disable watchdog in debug and idle */
2455cbd8b3aSClément Léger wdt->mr |= WDT_MR_WDFIEN | WDT_MR_WDDBGHLT | WDT_MR_WDIDLEHLT;
246d5d94b35SClément Léger /* Enable watchdog reset */
247d5d94b35SClément Léger wdt->mr |= WDT_MR_WDRSTEN;
2485cbd8b3aSClément Léger wdt->mr |= WDT_MR_WDD_SET(SEC_TO_WDT(WDT_MAX_TIMEOUT));
2495cbd8b3aSClément Léger wdt->mr |= WDT_MR_WDV_SET(SEC_TO_WDT(WDT_DEFAULT_TIMEOUT));
250ea9329ecSTony Han } else if (wdt->type == WDT_TYPE_DWDT) {
251ea9329ecSTony Han /* Enable interrupt */
252ea9329ecSTony Han io_write32(wdt->base + WDT_ISR, WDT_PERINT);
253ea9329ecSTony Han /* Disable watchdog in debug and idle */
254ea9329ecSTony Han wdt->mr |= WDT_MR_WDDBGHLT | WDT_MR_WDIDLEHLT;
255ea9329ecSTony Han /* Enable watchdog period reset */
256ea9329ecSTony Han wdt->mr |= WDT_MR_PERIODRST;
257ea9329ecSTony Han io_write32(wdt->base + WDT_WL,
258ea9329ecSTony Han SHIFT_U32(SEC_TO_WDT(WDT_DEFAULT_TIMEOUT),
259ea9329ecSTony Han WDT_WL_PERIOD_SHIFT));
260ea9329ecSTony Han } else {
261ea9329ecSTony Han panic("Invalid Watchdog");
262ea9329ecSTony Han }
2635cbd8b3aSClément Léger
2645cbd8b3aSClément Léger /*
2655cbd8b3aSClément Léger * If the watchdog was enabled, write the configuration which will ping
2665cbd8b3aSClément Léger * the watchdog.
2675cbd8b3aSClément Léger */
268ea9329ecSTony Han if (WDT_ENABLED(wdt->mr, wdt->dis_mask))
2695cbd8b3aSClément Léger io_write32(wdt->base + WDT_MR, wdt->mr);
2705cbd8b3aSClément Léger }
2715cbd8b3aSClément Léger
2725cbd8b3aSClément Léger #ifdef CFG_PM_ARM32
atmel_wdt_pm(enum pm_op op,uint32_t pm_hint __unused,const struct pm_callback_handle * hdl)2735cbd8b3aSClément Léger static TEE_Result atmel_wdt_pm(enum pm_op op, uint32_t pm_hint __unused,
2745cbd8b3aSClément Léger const struct pm_callback_handle *hdl)
2755cbd8b3aSClément Léger {
2765cbd8b3aSClément Léger struct atmel_wdt *wdt = hdl->handle;
2775cbd8b3aSClément Léger
2785cbd8b3aSClément Léger switch (op) {
2795cbd8b3aSClément Léger case PM_OP_RESUME:
2805cbd8b3aSClément Léger atmel_wdt_init_hw(wdt);
2815cbd8b3aSClément Léger if (wdt->enabled)
2825cbd8b3aSClément Léger atmel_wdt_start(wdt);
2835cbd8b3aSClément Léger break;
2845cbd8b3aSClément Léger case PM_OP_SUSPEND:
2855cbd8b3aSClément Léger if (wdt->enabled)
2865cbd8b3aSClément Léger atmel_wdt_stop(wdt);
2875cbd8b3aSClément Léger break;
2885cbd8b3aSClément Léger default:
2895cbd8b3aSClément Léger panic("Invalid PM operation");
2905cbd8b3aSClément Léger }
2915cbd8b3aSClément Léger
2925cbd8b3aSClément Léger return TEE_SUCCESS;
2935cbd8b3aSClément Léger }
2945cbd8b3aSClément Léger
atmel_wdt_register_pm(struct atmel_wdt * wdt)2955cbd8b3aSClément Léger static void atmel_wdt_register_pm(struct atmel_wdt *wdt)
2965cbd8b3aSClément Léger {
2975cbd8b3aSClément Léger register_pm_driver_cb(atmel_wdt_pm, wdt, "atmel_wdt");
2985cbd8b3aSClément Léger }
2995cbd8b3aSClément Léger #else
atmel_wdt_register_pm(struct atmel_wdt * wdt __unused)3005cbd8b3aSClément Léger static void atmel_wdt_register_pm(struct atmel_wdt *wdt __unused)
3015cbd8b3aSClément Léger {
3025cbd8b3aSClément Léger }
3035cbd8b3aSClément Léger #endif
3045cbd8b3aSClément Léger
wdt_node_probe(const void * fdt,int node,const void * compat_data)3055cbd8b3aSClément Léger static TEE_Result wdt_node_probe(const void *fdt, int node,
306ea9329ecSTony Han const void *compat_data)
3075cbd8b3aSClément Léger {
308ea9329ecSTony Han const struct wdt_compat *compat = compat_data;
3095cbd8b3aSClément Léger size_t size = 0;
3105cbd8b3aSClément Léger struct atmel_wdt *wdt;
3115cbd8b3aSClément Léger uint32_t irq_type = 0;
3125cbd8b3aSClément Léger uint32_t irq_prio = 0;
3135cbd8b3aSClément Léger int it = DT_INFO_INVALID_INTERRUPT;
314c64c658bSEtienne Carriere struct itr_handler *it_hdlr = NULL;
315c64c658bSEtienne Carriere TEE_Result res = TEE_ERROR_GENERIC;
3165cbd8b3aSClément Léger
317f354a5d8SGatien Chevallier if (fdt_get_status(fdt, node) != DT_STATUS_OK_SEC)
3185cbd8b3aSClément Léger return TEE_ERROR_BAD_PARAMETERS;
3195cbd8b3aSClément Léger
320ea9329ecSTony Han if (compat->wdt_ps)
3215cbd8b3aSClément Léger matrix_configure_periph_secure(AT91C_ID_WDT);
3225cbd8b3aSClément Léger
3235cbd8b3aSClément Léger wdt = calloc(1, sizeof(*wdt));
3245cbd8b3aSClément Léger if (!wdt)
3255cbd8b3aSClément Léger return TEE_ERROR_OUT_OF_MEMORY;
3265cbd8b3aSClément Léger
3275cbd8b3aSClément Léger wdt->chip.ops = &atmel_wdt_ops;
328ea9329ecSTony Han wdt->type = compat->type;
329ea9329ecSTony Han wdt->dis_mask = compat->dis_mask;
3305cbd8b3aSClément Léger
3315cbd8b3aSClément Léger it = dt_get_irq_type_prio(fdt, node, &irq_type, &irq_prio);
3325cbd8b3aSClément Léger if (it == DT_INFO_INVALID_INTERRUPT)
333c64c658bSEtienne Carriere goto err_free;
3345cbd8b3aSClément Léger
335c64c658bSEtienne Carriere res = interrupt_alloc_add_conf_handler(interrupt_get_main_chip(),
336c64c658bSEtienne Carriere it, atmel_wdt_itr_cb, 0, wdt,
337c64c658bSEtienne Carriere irq_type, irq_prio, &it_hdlr);
338c64c658bSEtienne Carriere if (res)
339c64c658bSEtienne Carriere goto err_free;
3405cbd8b3aSClément Léger
341a5d5bbc8SVesa Jääskeläinen if (dt_map_dev(fdt, node, &wdt->base, &size, DT_MAP_AUTO) < 0)
342c64c658bSEtienne Carriere goto err_remove_handler;
3435cbd8b3aSClément Léger
3445cbd8b3aSClément Léger /* Get current state of the watchdog */
345ea9329ecSTony Han wdt->mr = io_read32(wdt->base + WDT_MR) & wdt->dis_mask;
3465cbd8b3aSClément Léger
3475cbd8b3aSClément Léger atmel_wdt_init_hw(wdt);
348c64c658bSEtienne Carriere interrupt_enable(it_hdlr->chip, it_hdlr->it);
349c64c658bSEtienne Carriere
350c64c658bSEtienne Carriere res = watchdog_register(&wdt->chip);
351c64c658bSEtienne Carriere if (res)
352c64c658bSEtienne Carriere goto err_disable_unmap;
353c64c658bSEtienne Carriere
3545cbd8b3aSClément Léger atmel_wdt_register_pm(wdt);
3555cbd8b3aSClément Léger
356c64c658bSEtienne Carriere return TEE_SUCCESS;
3575cbd8b3aSClément Léger
358c64c658bSEtienne Carriere err_disable_unmap:
359c64c658bSEtienne Carriere interrupt_disable(it_hdlr->chip, it_hdlr->it);
360c64c658bSEtienne Carriere core_mmu_remove_mapping(MEM_AREA_IO_SEC, (void *)wdt->base, size);
361c64c658bSEtienne Carriere err_remove_handler:
362c64c658bSEtienne Carriere interrupt_remove_free_handler(it_hdlr);
363c64c658bSEtienne Carriere err_free:
3645cbd8b3aSClément Léger free(wdt);
3655cbd8b3aSClément Léger
3665cbd8b3aSClément Léger return TEE_ERROR_GENERIC;
3675cbd8b3aSClément Léger }
3685cbd8b3aSClément Léger
369ea9329ecSTony Han static const struct wdt_compat sama5d2_compat = {
370ea9329ecSTony Han .wdt_ps = true,
371ea9329ecSTony Han .type = WDT_TYPE_WDT,
372ea9329ecSTony Han .dis_mask = WDT_MR_WDDIS,
373ea9329ecSTony Han };
374ea9329ecSTony Han
375ea9329ecSTony Han static const struct wdt_compat sama7g5_compat = {
376ea9329ecSTony Han .wdt_ps = false,
377ea9329ecSTony Han .type = WDT_TYPE_DWDT,
378ea9329ecSTony Han .dis_mask = WDT_MR_WDDIS_DWDT,
379ea9329ecSTony Han };
380ea9329ecSTony Han
3815cbd8b3aSClément Léger static const struct dt_device_match atmel_wdt_match_table[] = {
382ea9329ecSTony Han {
383ea9329ecSTony Han .compatible = "atmel,sama5d4-wdt",
384ea9329ecSTony Han .compat_data = &sama5d2_compat,
385ea9329ecSTony Han },
386ea9329ecSTony Han {
387ea9329ecSTony Han .compatible = "microchip,sama7g5-wdt",
388ea9329ecSTony Han .compat_data = &sama7g5_compat,
389ea9329ecSTony Han },
3905cbd8b3aSClément Léger { }
3915cbd8b3aSClément Léger };
3925cbd8b3aSClément Léger
3935cbd8b3aSClément Léger DEFINE_DT_DRIVER(atmel_wdt_dt_driver) = {
3945cbd8b3aSClément Léger .name = "atmel_wdt",
3955cbd8b3aSClément Léger .type = DT_DRIVER_NOTYPE,
3965cbd8b3aSClément Léger .match_table = atmel_wdt_match_table,
3975cbd8b3aSClément Léger .probe = wdt_node_probe,
3985cbd8b3aSClément Léger };
399