xref: /optee_os/core/drivers/atmel_wdt.c (revision ea9329ec8928593bf753ae45b4d8f6c27f7b3323)
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/clk.h>
85cbd8b3aSClément Léger #include <drivers/clk_dt.h>
95cbd8b3aSClément Léger #include <drivers/wdt.h>
105cbd8b3aSClément Léger #include <io.h>
115cbd8b3aSClément Léger #include <kernel/delay.h>
125cbd8b3aSClément Léger #include <kernel/dt.h>
139e3c57c8SEtienne Carriere #include <kernel/dt_driver.h>
1433a0c835SEtienne Carriere #include <kernel/interrupt.h>
155cbd8b3aSClément Léger #include <kernel/pm.h>
165cbd8b3aSClément Léger #include <matrix.h>
17c64c658bSEtienne Carriere #include <mm/core_mmu.h>
186370f75dSTony Han #include <platform_config.h>
195cbd8b3aSClément Léger #include <tee_api_types.h>
205cbd8b3aSClément Léger 
215cbd8b3aSClément Léger #define WDT_CR			0x0
225cbd8b3aSClément Léger #define WDT_CR_KEY		SHIFT_U32(0xA5, 24)
235cbd8b3aSClément Léger #define WDT_CR_WDRSTT		BIT(0)
245cbd8b3aSClément Léger 
255cbd8b3aSClément Léger #define WDT_MR			0x4
265cbd8b3aSClément Léger #define WDT_MR_WDV		GENMASK_32(11, 0)
275cbd8b3aSClément Léger #define WDT_MR_WDV_SET(val)	((val) & WDT_MR_WDV)
285cbd8b3aSClément Léger #define WDT_MR_WDFIEN		BIT(12)
295cbd8b3aSClément Léger #define WDT_MR_WDRSTEN		BIT(13)
30*ea9329ecSTony Han #define WDT_MR_WDDIS		BIT(15) /* Watchdog Disable of WDT on bit 15 */
31*ea9329ecSTony Han #define WDT_MR_WDDIS_DWDT	BIT(12) /* Watchdog Disable of DWDT on bit 12 */
325cbd8b3aSClément Léger #define WDT_MR_WDD_SHIFT	16
335cbd8b3aSClément Léger #define WDT_MR_WDD_MASK		GENMASK_32(11, 0)
345cbd8b3aSClément Léger #define WDT_MR_WDD		SHIFT_U32(WDT_MR_WDD_MASK, WDT_MR_WDD_SHIFT)
355cbd8b3aSClément Léger #define WDT_MR_WDD_SET(val) \
365cbd8b3aSClément Léger 			SHIFT_U32(((val) & WDT_MR_WDD_MASK), WDT_MR_WDD_SHIFT)
375cbd8b3aSClément Léger #define WDT_MR_WDDBGHLT		BIT(28)
385cbd8b3aSClément Léger #define WDT_MR_WDIDLEHLT	BIT(29)
395cbd8b3aSClément Léger 
405cbd8b3aSClément Léger #define WDT_SR			0x8
415cbd8b3aSClément Léger #define WDT_SR_DUNF		BIT(0)
425cbd8b3aSClément Léger #define WDT_SR_DERR		BIT(1)
435cbd8b3aSClément Léger 
44*ea9329ecSTony Han /* DWDT: Watchdog Timer Mode Register */
45*ea9329ecSTony Han #define WDT_MR_PERIODRST	BIT(4)
46*ea9329ecSTony Han #define WDT_MR_RPTHRST		BIT(5)
47*ea9329ecSTony Han 
48*ea9329ecSTony Han /* DWDT: Watchdog Timer Value Register (Read-only) */
49*ea9329ecSTony Han #define WDT_VR			0x8
50*ea9329ecSTony Han #define WDT_VR_COUNTER_SHIFT	0
51*ea9329ecSTony Han #define WDT_VR_COUNTER_MASK	GENMASK_32(11, 0)
52*ea9329ecSTony Han 
53*ea9329ecSTony Han /* DWDT: Watchdog Timer Window Level Register */
54*ea9329ecSTony Han #define WDT_WL			0xc
55*ea9329ecSTony Han #define WDT_WL_RPTH_SHIFT	16
56*ea9329ecSTony Han #define WDT_WL_RPTH_MASK	GENMASK_32(27, 16)
57*ea9329ecSTony Han #define WDT_WL_PERIOD_SHIFT	0
58*ea9329ecSTony Han #define WDT_WL_PERIOD_MASK	GENMASK_32(11, 0)
59*ea9329ecSTony Han 
60*ea9329ecSTony Han /* DWDT: Watchdog Timer Interrupt Level Register */
61*ea9329ecSTony Han #define WDT_IL			0x10
62*ea9329ecSTony Han #define WDT_IL_LVLTH_SHIFT	0
63*ea9329ecSTony Han #define WDT_IL_LVLTH_MASK	GENMASK_32(11, 0)
64*ea9329ecSTony Han 
65*ea9329ecSTony Han /* DWDT: Watchdog Timer Interrupt Enable/Disable/Status/Mask Register */
66*ea9329ecSTony Han #define WDT_IER			0x14
67*ea9329ecSTony Han #define WDT_IDR			0x18
68*ea9329ecSTony Han #define WDT_ISR			0x1c
69*ea9329ecSTony Han #define WDT_IMR			0x20
70*ea9329ecSTony Han #define WDT_NSRPTHINT		BIT(4)
71*ea9329ecSTony Han #define WDT_NSPERINT		BIT(3)
72*ea9329ecSTony Han #define WDT_LVLINT		BIT(2)
73*ea9329ecSTony Han #define WDT_RPTHINT		BIT(1)
74*ea9329ecSTony Han #define WDT_PERINT		BIT(0)
75*ea9329ecSTony Han 
765cbd8b3aSClément Léger /*
775cbd8b3aSClément Léger  * The watchdog is clocked by a 32768Hz clock/128 and the counter is on
785cbd8b3aSClément Léger  * 12 bits.
795cbd8b3aSClément Léger  */
805cbd8b3aSClément Léger #define SLOW_CLOCK_FREQ		(32768)
815cbd8b3aSClément Léger #define WDT_CLOCK_FREQ		(SLOW_CLOCK_FREQ / 128)
825cbd8b3aSClément Léger #define WDT_MIN_TIMEOUT		1
835cbd8b3aSClément Léger #define WDT_MAX_TIMEOUT		(BIT(12) / WDT_CLOCK_FREQ)
845cbd8b3aSClément Léger 
855cbd8b3aSClément Léger #define WDT_DEFAULT_TIMEOUT	WDT_MAX_TIMEOUT
865cbd8b3aSClément Léger 
875cbd8b3aSClément Léger /*
885cbd8b3aSClément Léger  * We must wait at least 3 clocks period before accessing registers MR and CR.
895cbd8b3aSClément Léger  * Ensure that we see at least 4 edges
905cbd8b3aSClément Léger  */
915cbd8b3aSClément Léger #define WDT_REG_ACCESS_UDELAY	(1000000ULL / SLOW_CLOCK_FREQ * 4)
925cbd8b3aSClément Léger 
935cbd8b3aSClément Léger #define SEC_TO_WDT(sec)		(((sec) * WDT_CLOCK_FREQ) - 1)
945cbd8b3aSClément Léger 
95*ea9329ecSTony Han #define WDT_ENABLED(mr, dis_mask)	(!((mr) & (dis_mask)))
96*ea9329ecSTony Han 
97*ea9329ecSTony Han enum wdt_type {
98*ea9329ecSTony Han 	WDT_TYPE_WDT,	/* Watchdog Timer */
99*ea9329ecSTony Han 	WDT_TYPE_DWDT,	/* Dual Watchdog Timer */
100*ea9329ecSTony Han };
101*ea9329ecSTony Han 
102*ea9329ecSTony Han struct wdt_compat {
103*ea9329ecSTony Han 	bool wdt_ps; /* Is Peripheral SHDWC Programmable Secure */
104*ea9329ecSTony Han 	enum wdt_type type; /* Type of Watchdog Timer */
105*ea9329ecSTony Han 	uint32_t dis_mask; /* Mask of Watchdog Disable in Mode Register */
106*ea9329ecSTony Han };
1075cbd8b3aSClément Léger 
1085cbd8b3aSClément Léger struct atmel_wdt {
1095cbd8b3aSClément Léger 	struct wdt_chip chip;
110*ea9329ecSTony Han 	enum wdt_type type;
111*ea9329ecSTony Han 	uint32_t dis_mask;
1125cbd8b3aSClément Léger 	vaddr_t base;
1135cbd8b3aSClément Léger 	unsigned long rate;
1145cbd8b3aSClément Léger 	uint32_t mr;
1155cbd8b3aSClément Léger 	bool enabled;
1165cbd8b3aSClément Léger };
1175cbd8b3aSClément Léger 
1185cbd8b3aSClément Léger static void atmel_wdt_write_sleep(struct atmel_wdt *wdt, uint32_t reg,
1195cbd8b3aSClément Léger 				  uint32_t val)
1205cbd8b3aSClément Léger {
1215cbd8b3aSClément Léger 	udelay(WDT_REG_ACCESS_UDELAY);
1225cbd8b3aSClément Léger 
1235cbd8b3aSClément Léger 	io_write32(wdt->base + reg, val);
1245cbd8b3aSClément Léger }
1255cbd8b3aSClément Léger 
1265cbd8b3aSClément Léger static TEE_Result atmel_wdt_settimeout(struct wdt_chip *chip,
1275cbd8b3aSClément Léger 				       unsigned long timeout)
1285cbd8b3aSClément Léger {
1295cbd8b3aSClément Léger 	struct atmel_wdt *wdt = container_of(chip, struct atmel_wdt, chip);
1305cbd8b3aSClément Léger 
131*ea9329ecSTony Han 	if (wdt->type == WDT_TYPE_WDT) {
1325cbd8b3aSClément Léger 		wdt->mr &= ~WDT_MR_WDV;
1335cbd8b3aSClément Léger 		wdt->mr |= WDT_MR_WDV_SET(SEC_TO_WDT(timeout));
1345cbd8b3aSClément Léger 
135*ea9329ecSTony Han 		/* WDV and WDD only be updated when the watchdog is running */
136*ea9329ecSTony Han 		if (WDT_ENABLED(wdt->mr, wdt->dis_mask))
1375cbd8b3aSClément Léger 			atmel_wdt_write_sleep(wdt, WDT_MR, wdt->mr);
138*ea9329ecSTony Han 	} else {
139*ea9329ecSTony Han 		io_write32(wdt->base + WDT_WL,
140*ea9329ecSTony Han 			   SHIFT_U32(SEC_TO_WDT(timeout), WDT_WL_PERIOD_SHIFT));
141*ea9329ecSTony Han 	}
1425cbd8b3aSClément Léger 
1435cbd8b3aSClément Léger 	return TEE_SUCCESS;
1445cbd8b3aSClément Léger }
1455cbd8b3aSClément Léger 
1465cbd8b3aSClément Léger static void atmel_wdt_ping(struct wdt_chip *chip)
1475cbd8b3aSClément Léger {
1485cbd8b3aSClément Léger 	struct atmel_wdt *wdt = container_of(chip, struct atmel_wdt, chip);
1495cbd8b3aSClément Léger 
1505cbd8b3aSClément Léger 	atmel_wdt_write_sleep(wdt, WDT_CR, WDT_CR_KEY | WDT_CR_WDRSTT);
1515cbd8b3aSClément Léger }
1525cbd8b3aSClément Léger 
1535cbd8b3aSClément Léger static void atmel_wdt_start(struct atmel_wdt *wdt)
1545cbd8b3aSClément Léger {
155*ea9329ecSTony Han 	wdt->mr &= ~wdt->dis_mask;
1565cbd8b3aSClément Léger 	atmel_wdt_write_sleep(wdt, WDT_MR, wdt->mr);
1575cbd8b3aSClément Léger }
1585cbd8b3aSClément Léger 
1595cbd8b3aSClément Léger static void atmel_wdt_enable(struct wdt_chip *chip)
1605cbd8b3aSClément Léger {
1615cbd8b3aSClément Léger 	struct atmel_wdt *wdt = container_of(chip, struct atmel_wdt, chip);
1625cbd8b3aSClément Léger 
1635cbd8b3aSClément Léger 	wdt->enabled = true;
1645cbd8b3aSClément Léger 	atmel_wdt_start(wdt);
1655cbd8b3aSClément Léger }
1665cbd8b3aSClément Léger 
1675cbd8b3aSClément Léger static void atmel_wdt_stop(struct atmel_wdt *wdt)
1685cbd8b3aSClément Léger {
169*ea9329ecSTony Han 	wdt->mr |= wdt->dis_mask;
1705cbd8b3aSClément Léger 	atmel_wdt_write_sleep(wdt, WDT_MR, wdt->mr);
1715cbd8b3aSClément Léger }
1725cbd8b3aSClément Léger 
1735cbd8b3aSClément Léger static void atmel_wdt_disable(struct wdt_chip *chip)
1745cbd8b3aSClément Léger {
1755cbd8b3aSClément Léger 	struct atmel_wdt *wdt = container_of(chip, struct atmel_wdt, chip);
1765cbd8b3aSClément Léger 
1775cbd8b3aSClément Léger 	wdt->enabled = false;
1785cbd8b3aSClément Léger 	atmel_wdt_stop(wdt);
1795cbd8b3aSClément Léger }
1805cbd8b3aSClément Léger 
1815cbd8b3aSClément Léger static enum itr_return atmel_wdt_itr_cb(struct itr_handler *h)
1825cbd8b3aSClément Léger {
1835cbd8b3aSClément Léger 	struct atmel_wdt *wdt = h->data;
184*ea9329ecSTony Han 	uint32_t sr = 0;
185*ea9329ecSTony Han 
186*ea9329ecSTony Han 	if (wdt->type == WDT_TYPE_WDT) {
187*ea9329ecSTony Han 		sr = io_read32(wdt->base + WDT_SR);
1885cbd8b3aSClément Léger 
1895cbd8b3aSClément Léger 		if (sr & WDT_SR_DUNF)
190*ea9329ecSTony Han 			DMSG("Watchdog Underflow");
1915cbd8b3aSClément Léger 		if (sr & WDT_SR_DERR)
192*ea9329ecSTony Han 			DMSG("Watchdog Error");
193*ea9329ecSTony Han 	} else if (wdt->type == WDT_TYPE_DWDT) {
194*ea9329ecSTony Han 		sr = io_read32(wdt->base + WDT_ISR);
195*ea9329ecSTony Han 
196*ea9329ecSTony Han 		if (sr & WDT_NSRPTHINT)
197*ea9329ecSTony Han 			DMSG("NS Watchdog Repeat Threshold Interrupt");
198*ea9329ecSTony Han 		if (sr & WDT_NSPERINT)
199*ea9329ecSTony Han 			DMSG("NS Watchdog Period Interrupt");
200*ea9329ecSTony Han 		if (sr & WDT_LVLINT)
201*ea9329ecSTony Han 			DMSG("Watchdog Level Threshold Interrupt");
202*ea9329ecSTony Han 		if (sr & WDT_RPTHINT)
203*ea9329ecSTony Han 			DMSG("Watchdog Repeat Threshold Interrupt");
204*ea9329ecSTony Han 		if (sr & WDT_PERINT)
205*ea9329ecSTony Han 			DMSG("Watchdog Period Interrupt");
206*ea9329ecSTony Han 	}
2075cbd8b3aSClément Léger 
2085cbd8b3aSClément Léger 	panic("Watchdog interrupt");
2095cbd8b3aSClément Léger 
2105cbd8b3aSClément Léger 	return ITRR_HANDLED;
2115cbd8b3aSClément Léger }
2125cbd8b3aSClément Léger 
2135cbd8b3aSClément Léger static TEE_Result atmel_wdt_init(struct wdt_chip *chip __unused,
2145cbd8b3aSClément Léger 				 unsigned long *min_timeout,
2155cbd8b3aSClément Léger 				 unsigned long *max_timeout)
2165cbd8b3aSClément Léger {
2175cbd8b3aSClément Léger 	*min_timeout = WDT_MIN_TIMEOUT;
2185cbd8b3aSClément Léger 	*max_timeout = WDT_MAX_TIMEOUT;
2195cbd8b3aSClément Léger 
2205cbd8b3aSClément Léger 	return TEE_SUCCESS;
2215cbd8b3aSClément Léger }
2225cbd8b3aSClément Léger 
2235cbd8b3aSClément Léger static const struct wdt_ops atmel_wdt_ops = {
2245cbd8b3aSClément Léger 	.init = atmel_wdt_init,
2255cbd8b3aSClément Léger 	.start = atmel_wdt_enable,
2265cbd8b3aSClément Léger 	.stop = atmel_wdt_disable,
2275cbd8b3aSClément Léger 	.ping = atmel_wdt_ping,
2285cbd8b3aSClément Léger 	.set_timeout = atmel_wdt_settimeout,
2295cbd8b3aSClément Léger };
2305cbd8b3aSClément Léger 
2315cbd8b3aSClément Léger static void atmel_wdt_init_hw(struct atmel_wdt *wdt)
2325cbd8b3aSClément Léger {
2335cbd8b3aSClément Léger 	uint32_t mr = 0;
2345cbd8b3aSClément Léger 
2355cbd8b3aSClément Léger 	/*
2365cbd8b3aSClément Léger 	 * If we are resuming, we disabled the watchdog on suspend but the
2375cbd8b3aSClément Léger 	 * bootloader might have enabled the watchdog. If so, disable it
2385cbd8b3aSClément Léger 	 * properly.
2395cbd8b3aSClément Léger 	 */
240*ea9329ecSTony Han 	if (!WDT_ENABLED(wdt->mr, wdt->dis_mask)) {
2415cbd8b3aSClément Léger 		mr = io_read32(wdt->base + WDT_MR);
242*ea9329ecSTony Han 		if (WDT_ENABLED(mr, wdt->dis_mask))
243*ea9329ecSTony Han 			io_write32(wdt->base + WDT_MR, mr | wdt->dis_mask);
2445cbd8b3aSClément Léger 	}
2455cbd8b3aSClément Léger 
246*ea9329ecSTony Han 	if (wdt->type == WDT_TYPE_WDT) {
2475cbd8b3aSClément Léger 		/* Enable interrupt, and disable watchdog in debug and idle */
2485cbd8b3aSClément Léger 		wdt->mr |= WDT_MR_WDFIEN | WDT_MR_WDDBGHLT | WDT_MR_WDIDLEHLT;
249d5d94b35SClément Léger 		/* Enable watchdog reset */
250d5d94b35SClément Léger 		wdt->mr |= WDT_MR_WDRSTEN;
2515cbd8b3aSClément Léger 		wdt->mr |= WDT_MR_WDD_SET(SEC_TO_WDT(WDT_MAX_TIMEOUT));
2525cbd8b3aSClément Léger 		wdt->mr |= WDT_MR_WDV_SET(SEC_TO_WDT(WDT_DEFAULT_TIMEOUT));
253*ea9329ecSTony Han 	} else if (wdt->type == WDT_TYPE_DWDT) {
254*ea9329ecSTony Han 		/* Enable interrupt */
255*ea9329ecSTony Han 		io_write32(wdt->base + WDT_ISR, WDT_PERINT);
256*ea9329ecSTony Han 		/* Disable watchdog in debug and idle */
257*ea9329ecSTony Han 		wdt->mr |= WDT_MR_WDDBGHLT | WDT_MR_WDIDLEHLT;
258*ea9329ecSTony Han 		/* Enable watchdog period reset */
259*ea9329ecSTony Han 		wdt->mr |= WDT_MR_PERIODRST;
260*ea9329ecSTony Han 		io_write32(wdt->base + WDT_WL,
261*ea9329ecSTony Han 			   SHIFT_U32(SEC_TO_WDT(WDT_DEFAULT_TIMEOUT),
262*ea9329ecSTony Han 				     WDT_WL_PERIOD_SHIFT));
263*ea9329ecSTony Han 	} else {
264*ea9329ecSTony Han 		panic("Invalid Watchdog");
265*ea9329ecSTony Han 	}
2665cbd8b3aSClément Léger 
2675cbd8b3aSClément Léger 	/*
2685cbd8b3aSClément Léger 	 * If the watchdog was enabled, write the configuration which will ping
2695cbd8b3aSClément Léger 	 * the watchdog.
2705cbd8b3aSClément Léger 	 */
271*ea9329ecSTony Han 	if (WDT_ENABLED(wdt->mr, wdt->dis_mask))
2725cbd8b3aSClément Léger 		io_write32(wdt->base + WDT_MR, wdt->mr);
2735cbd8b3aSClément Léger }
2745cbd8b3aSClément Léger 
2755cbd8b3aSClément Léger #ifdef CFG_PM_ARM32
2765cbd8b3aSClément Léger static TEE_Result atmel_wdt_pm(enum pm_op op, uint32_t pm_hint __unused,
2775cbd8b3aSClément Léger 			       const struct pm_callback_handle *hdl)
2785cbd8b3aSClément Léger {
2795cbd8b3aSClément Léger 	struct atmel_wdt *wdt = hdl->handle;
2805cbd8b3aSClément Léger 
2815cbd8b3aSClément Léger 	switch (op) {
2825cbd8b3aSClément Léger 	case PM_OP_RESUME:
2835cbd8b3aSClément Léger 		atmel_wdt_init_hw(wdt);
2845cbd8b3aSClément Léger 		if (wdt->enabled)
2855cbd8b3aSClément Léger 			atmel_wdt_start(wdt);
2865cbd8b3aSClément Léger 		break;
2875cbd8b3aSClément Léger 	case PM_OP_SUSPEND:
2885cbd8b3aSClément Léger 		if (wdt->enabled)
2895cbd8b3aSClément Léger 			atmel_wdt_stop(wdt);
2905cbd8b3aSClément Léger 		break;
2915cbd8b3aSClément Léger 	default:
2925cbd8b3aSClément Léger 		panic("Invalid PM operation");
2935cbd8b3aSClément Léger 	}
2945cbd8b3aSClément Léger 
2955cbd8b3aSClément Léger 	return TEE_SUCCESS;
2965cbd8b3aSClément Léger }
2975cbd8b3aSClément Léger 
2985cbd8b3aSClément Léger static void atmel_wdt_register_pm(struct atmel_wdt *wdt)
2995cbd8b3aSClément Léger {
3005cbd8b3aSClément Léger 	register_pm_driver_cb(atmel_wdt_pm, wdt, "atmel_wdt");
3015cbd8b3aSClément Léger }
3025cbd8b3aSClément Léger #else
3035cbd8b3aSClément Léger static void atmel_wdt_register_pm(struct atmel_wdt *wdt __unused)
3045cbd8b3aSClément Léger {
3055cbd8b3aSClément Léger }
3065cbd8b3aSClément Léger #endif
3075cbd8b3aSClément Léger 
3085cbd8b3aSClément Léger static TEE_Result wdt_node_probe(const void *fdt, int node,
309*ea9329ecSTony Han 				 const void *compat_data)
3105cbd8b3aSClément Léger {
311*ea9329ecSTony Han 	const struct wdt_compat *compat = compat_data;
3125cbd8b3aSClément Léger 	size_t size = 0;
3135cbd8b3aSClément Léger 	struct atmel_wdt *wdt;
3145cbd8b3aSClément Léger 	uint32_t irq_type = 0;
3155cbd8b3aSClément Léger 	uint32_t irq_prio = 0;
3165cbd8b3aSClément Léger 	int it = DT_INFO_INVALID_INTERRUPT;
317c64c658bSEtienne Carriere 	struct itr_handler *it_hdlr = NULL;
318c64c658bSEtienne Carriere 	TEE_Result res = TEE_ERROR_GENERIC;
3195cbd8b3aSClément Léger 
320f354a5d8SGatien Chevallier 	if (fdt_get_status(fdt, node) != DT_STATUS_OK_SEC)
3215cbd8b3aSClément Léger 		return TEE_ERROR_BAD_PARAMETERS;
3225cbd8b3aSClément Léger 
323*ea9329ecSTony Han 	if (compat->wdt_ps)
3245cbd8b3aSClément Léger 		matrix_configure_periph_secure(AT91C_ID_WDT);
3255cbd8b3aSClément Léger 
3265cbd8b3aSClément Léger 	wdt = calloc(1, sizeof(*wdt));
3275cbd8b3aSClément Léger 	if (!wdt)
3285cbd8b3aSClément Léger 		return TEE_ERROR_OUT_OF_MEMORY;
3295cbd8b3aSClément Léger 
3305cbd8b3aSClément Léger 	wdt->chip.ops = &atmel_wdt_ops;
331*ea9329ecSTony Han 	wdt->type = compat->type;
332*ea9329ecSTony Han 	wdt->dis_mask = compat->dis_mask;
3335cbd8b3aSClément Léger 
3345cbd8b3aSClément Léger 	it = dt_get_irq_type_prio(fdt, node, &irq_type, &irq_prio);
3355cbd8b3aSClément Léger 	if (it == DT_INFO_INVALID_INTERRUPT)
336c64c658bSEtienne Carriere 		goto err_free;
3375cbd8b3aSClément Léger 
338c64c658bSEtienne Carriere 	res = interrupt_alloc_add_conf_handler(interrupt_get_main_chip(),
339c64c658bSEtienne Carriere 					       it, atmel_wdt_itr_cb, 0, wdt,
340c64c658bSEtienne Carriere 					       irq_type, irq_prio, &it_hdlr);
341c64c658bSEtienne Carriere 	if (res)
342c64c658bSEtienne Carriere 		goto err_free;
3435cbd8b3aSClément Léger 
344a5d5bbc8SVesa Jääskeläinen 	if (dt_map_dev(fdt, node, &wdt->base, &size, DT_MAP_AUTO) < 0)
345c64c658bSEtienne Carriere 		goto err_remove_handler;
3465cbd8b3aSClément Léger 
3475cbd8b3aSClément Léger 	/* Get current state of the watchdog */
348*ea9329ecSTony Han 	wdt->mr = io_read32(wdt->base + WDT_MR) & wdt->dis_mask;
3495cbd8b3aSClément Léger 
3505cbd8b3aSClément Léger 	atmel_wdt_init_hw(wdt);
351c64c658bSEtienne Carriere 	interrupt_enable(it_hdlr->chip, it_hdlr->it);
352c64c658bSEtienne Carriere 
353c64c658bSEtienne Carriere 	res = watchdog_register(&wdt->chip);
354c64c658bSEtienne Carriere 	if (res)
355c64c658bSEtienne Carriere 		goto err_disable_unmap;
356c64c658bSEtienne Carriere 
3575cbd8b3aSClément Léger 	atmel_wdt_register_pm(wdt);
3585cbd8b3aSClément Léger 
359c64c658bSEtienne Carriere 	return TEE_SUCCESS;
3605cbd8b3aSClément Léger 
361c64c658bSEtienne Carriere err_disable_unmap:
362c64c658bSEtienne Carriere 	interrupt_disable(it_hdlr->chip, it_hdlr->it);
363c64c658bSEtienne Carriere 	core_mmu_remove_mapping(MEM_AREA_IO_SEC, (void *)wdt->base, size);
364c64c658bSEtienne Carriere err_remove_handler:
365c64c658bSEtienne Carriere 	interrupt_remove_free_handler(it_hdlr);
366c64c658bSEtienne Carriere err_free:
3675cbd8b3aSClément Léger 	free(wdt);
3685cbd8b3aSClément Léger 
3695cbd8b3aSClément Léger 	return TEE_ERROR_GENERIC;
3705cbd8b3aSClément Léger }
3715cbd8b3aSClément Léger 
372*ea9329ecSTony Han static const struct wdt_compat sama5d2_compat = {
373*ea9329ecSTony Han 	.wdt_ps = true,
374*ea9329ecSTony Han 	.type = WDT_TYPE_WDT,
375*ea9329ecSTony Han 	.dis_mask = WDT_MR_WDDIS,
376*ea9329ecSTony Han };
377*ea9329ecSTony Han 
378*ea9329ecSTony Han static const struct wdt_compat sama7g5_compat = {
379*ea9329ecSTony Han 	.wdt_ps = false,
380*ea9329ecSTony Han 	.type = WDT_TYPE_DWDT,
381*ea9329ecSTony Han 	.dis_mask = WDT_MR_WDDIS_DWDT,
382*ea9329ecSTony Han };
383*ea9329ecSTony Han 
3845cbd8b3aSClément Léger static const struct dt_device_match atmel_wdt_match_table[] = {
385*ea9329ecSTony Han 	{
386*ea9329ecSTony Han 		.compatible = "atmel,sama5d4-wdt",
387*ea9329ecSTony Han 		.compat_data = &sama5d2_compat,
388*ea9329ecSTony Han 	},
389*ea9329ecSTony Han 	{
390*ea9329ecSTony Han 		.compatible = "microchip,sama7g5-wdt",
391*ea9329ecSTony Han 		.compat_data = &sama7g5_compat,
392*ea9329ecSTony Han 	},
3935cbd8b3aSClément Léger 	{ }
3945cbd8b3aSClément Léger };
3955cbd8b3aSClément Léger 
3965cbd8b3aSClément Léger DEFINE_DT_DRIVER(atmel_wdt_dt_driver) = {
3975cbd8b3aSClément Léger 	.name = "atmel_wdt",
3985cbd8b3aSClément Léger 	.type = DT_DRIVER_NOTYPE,
3995cbd8b3aSClément Léger 	.match_table = atmel_wdt_match_table,
4005cbd8b3aSClément Léger 	.probe = wdt_node_probe,
4015cbd8b3aSClément Léger };
402