xref: /optee_os/core/drivers/stm32_iwdg.c (revision 235baec92a7a7bbfa8e3935e0c6868240c8272ce)
10bdd7f5bSEtienne Carriere // SPDX-License-Identifier: BSD-3-Clause
20bdd7f5bSEtienne Carriere /*
33d5793d2SAntonio Borneo  * Copyright (c) 2017-2025, STMicroelectronics - All Rights Reserved
40bdd7f5bSEtienne Carriere  */
50bdd7f5bSEtienne Carriere 
60bdd7f5bSEtienne Carriere #include <assert.h>
70bdd7f5bSEtienne Carriere #include <drivers/clk.h>
80bdd7f5bSEtienne Carriere #include <drivers/clk_dt.h>
90bdd7f5bSEtienne Carriere #include <drivers/wdt.h>
100bdd7f5bSEtienne Carriere #include <io.h>
110bdd7f5bSEtienne Carriere #include <keep.h>
120bdd7f5bSEtienne Carriere #include <kernel/boot.h>
130bdd7f5bSEtienne Carriere #include <kernel/delay.h>
140bdd7f5bSEtienne Carriere #include <kernel/dt.h>
159e3c57c8SEtienne Carriere #include <kernel/dt_driver.h>
160bdd7f5bSEtienne Carriere #include <kernel/interrupt.h>
170bdd7f5bSEtienne Carriere #include <kernel/misc.h>
180bdd7f5bSEtienne Carriere #include <kernel/panic.h>
190bdd7f5bSEtienne Carriere #include <kernel/pm.h>
200bdd7f5bSEtienne Carriere #include <kernel/spinlock.h>
21eb47832fSAntonio Borneo #include <kernel/tee_time.h>
220bdd7f5bSEtienne Carriere #include <libfdt.h>
230bdd7f5bSEtienne Carriere #include <mm/core_memprot.h>
240bdd7f5bSEtienne Carriere #include <sm/sm.h>
250bdd7f5bSEtienne Carriere #include <stm32_util.h>
260bdd7f5bSEtienne Carriere #include <string.h>
270bdd7f5bSEtienne Carriere #include <trace.h>
280bdd7f5bSEtienne Carriere 
290bdd7f5bSEtienne Carriere /* IWDG Compatibility */
303d5793d2SAntonio Borneo #define IWDG_TIMEOUT_US		U(10000)
310bdd7f5bSEtienne Carriere #define IWDG_CNT_MASK		GENMASK_32(11, 0)
323d5793d2SAntonio Borneo #define IWDG_ONF_MIN_VER	U(0x31)
33e4b8d29aSEtienne Carriere #define IWDG_ICR_MIN_VER	U(0x40)
340bdd7f5bSEtienne Carriere 
350bdd7f5bSEtienne Carriere /* IWDG registers offsets */
360bdd7f5bSEtienne Carriere #define IWDG_KR_OFFSET		U(0x00)
370bdd7f5bSEtienne Carriere #define IWDG_PR_OFFSET		U(0x04)
380bdd7f5bSEtienne Carriere #define IWDG_RLR_OFFSET		U(0x08)
390bdd7f5bSEtienne Carriere #define IWDG_SR_OFFSET		U(0x0C)
400bdd7f5bSEtienne Carriere #define IWDG_EWCR_OFFSET	U(0x14)
41e4b8d29aSEtienne Carriere #define IWDG_ICR_OFFSET		U(0x18)
423d5793d2SAntonio Borneo #define IWDG_VERR_OFFSET	U(0x3F4)
430bdd7f5bSEtienne Carriere 
443d5793d2SAntonio Borneo #define IWDG_KR_WPROT_KEY	U(0x0000)
450bdd7f5bSEtienne Carriere #define IWDG_KR_ACCESS_KEY	U(0x5555)
460bdd7f5bSEtienne Carriere #define IWDG_KR_RELOAD_KEY	U(0xAAAA)
470bdd7f5bSEtienne Carriere #define IWDG_KR_START_KEY	U(0xCCCC)
480bdd7f5bSEtienne Carriere 
490bdd7f5bSEtienne Carriere /* Use a fixed prescaler divider of 256 */
500bdd7f5bSEtienne Carriere #define IWDG_PRESCALER_256	U(256)
510bdd7f5bSEtienne Carriere #define IWDG_PR_DIV_256		U(0x06)
520bdd7f5bSEtienne Carriere #define IWDG_PR_DIV_MASK	GENMASK_32(3, 0)
530bdd7f5bSEtienne Carriere 
540bdd7f5bSEtienne Carriere #define IWDG_SR_PVU		BIT(0)
550bdd7f5bSEtienne Carriere #define IWDG_SR_RVU		BIT(1)
560bdd7f5bSEtienne Carriere #define IWDG_SR_WVU		BIT(2)
570bdd7f5bSEtienne Carriere #define IWDG_SR_EWU		BIT(3)
580bdd7f5bSEtienne Carriere #define IWDG_SR_UPDATE_MASK	(IWDG_SR_PVU | IWDG_SR_RVU | IWDG_SR_WVU | \
590bdd7f5bSEtienne Carriere 				 IWDG_SR_EWU)
603d5793d2SAntonio Borneo #define IWDG_SR_ONF		BIT(8)
61e4b8d29aSEtienne Carriere #define IWDG_SR_EWIF		BIT(14)
62e4b8d29aSEtienne Carriere #define IWDG_SR_EWIF_V40	BIT(15)
630bdd7f5bSEtienne Carriere 
640bdd7f5bSEtienne Carriere #define IWDG_EWCR_EWIE		BIT(15)
650bdd7f5bSEtienne Carriere #define IWDG_EWCR_EWIC		BIT(14)
660bdd7f5bSEtienne Carriere 
67e4b8d29aSEtienne Carriere #define IWDG_ICR_EWIC		BIT(15)
68e4b8d29aSEtienne Carriere 
693d5793d2SAntonio Borneo #define IWDG_VERR_REV_MASK	GENMASK_32(7, 0)
703d5793d2SAntonio Borneo 
71e4b8d29aSEtienne Carriere /* Define default early timeout delay to 5 sec before timeout */
72e4b8d29aSEtienne Carriere #define IWDG_ETIMEOUT_SEC	U(5)
73e4b8d29aSEtienne Carriere 
740bdd7f5bSEtienne Carriere /*
750bdd7f5bSEtienne Carriere  * Values for struct stm32_iwdg_device::flags
760bdd7f5bSEtienne Carriere  * IWDG_FLAGS_ENABLED			Watchdog has been enabled
770bdd7f5bSEtienne Carriere  */
78c501c3e1SLionel Debieve #define IWDG_FLAGS_ENABLED			BIT(0)
790bdd7f5bSEtienne Carriere 
800bdd7f5bSEtienne Carriere /*
810bdd7f5bSEtienne Carriere  * IWDG watch instance data
820bdd7f5bSEtienne Carriere  * @base - IWDG interface IOMEM base address
83b2f17e87SEtienne Carriere  * @clk_pclk - Bus clock
840bdd7f5bSEtienne Carriere  * @clk_lsi - IWDG source clock
85e4b8d29aSEtienne Carriere  * @itr_chip - Interrupt chip device
86e4b8d29aSEtienne Carriere  * @itr_num - Interrupt number for the IWDG instance
87e4b8d29aSEtienne Carriere  * @itr_handler - Interrupt handler
880bdd7f5bSEtienne Carriere  * @flags - Property flags for the IWDG instance
890bdd7f5bSEtienne Carriere  * @timeout - Watchdog elaspure timeout
903d5793d2SAntonio Borneo  * @hw_version - Watchdog HW version
91eb47832fSAntonio Borneo  * @last_refresh - Time of last watchdog refresh
920bdd7f5bSEtienne Carriere  * @wdt_chip - Wathcdog chip instance
930bdd7f5bSEtienne Carriere  */
940bdd7f5bSEtienne Carriere struct stm32_iwdg_device {
950bdd7f5bSEtienne Carriere 	struct io_pa_va base;
96b2f17e87SEtienne Carriere 	struct clk *clk_pclk;
970bdd7f5bSEtienne Carriere 	struct clk *clk_lsi;
98e4b8d29aSEtienne Carriere 	struct itr_chip *itr_chip;
99e4b8d29aSEtienne Carriere 	size_t itr_num;
100e4b8d29aSEtienne Carriere 	struct itr_handler *itr_handler;
1010bdd7f5bSEtienne Carriere 	uint32_t flags;
1020bdd7f5bSEtienne Carriere 	unsigned long timeout;
1033d5793d2SAntonio Borneo 	unsigned int hw_version;
104eb47832fSAntonio Borneo 	TEE_Time last_refresh;
1050bdd7f5bSEtienne Carriere 	struct wdt_chip wdt_chip;
1060bdd7f5bSEtienne Carriere };
1070bdd7f5bSEtienne Carriere 
108e4b8d29aSEtienne Carriere static uint32_t sr_ewif_mask(struct stm32_iwdg_device *iwdg)
109e4b8d29aSEtienne Carriere {
110e4b8d29aSEtienne Carriere 	if (iwdg->hw_version >= IWDG_ICR_MIN_VER)
111e4b8d29aSEtienne Carriere 		return IWDG_SR_EWIF_V40;
112e4b8d29aSEtienne Carriere 	else
113e4b8d29aSEtienne Carriere 		return IWDG_SR_EWIF;
114e4b8d29aSEtienne Carriere }
115e4b8d29aSEtienne Carriere 
1160bdd7f5bSEtienne Carriere static vaddr_t get_base(struct stm32_iwdg_device *iwdg)
1170bdd7f5bSEtienne Carriere {
1180bdd7f5bSEtienne Carriere 	return io_pa_or_va(&iwdg->base, 1);
1190bdd7f5bSEtienne Carriere }
1200bdd7f5bSEtienne Carriere 
1212f9b82faSEtienne Carriere static void iwdg_wdt_set_enabled(struct stm32_iwdg_device *iwdg)
1222f9b82faSEtienne Carriere {
1232f9b82faSEtienne Carriere 	iwdg->flags |= IWDG_FLAGS_ENABLED;
1242f9b82faSEtienne Carriere }
1252f9b82faSEtienne Carriere 
1262f9b82faSEtienne Carriere static bool iwdg_wdt_is_enabled(struct stm32_iwdg_device *iwdg)
1270bdd7f5bSEtienne Carriere {
1280bdd7f5bSEtienne Carriere 	return iwdg->flags & IWDG_FLAGS_ENABLED;
1290bdd7f5bSEtienne Carriere }
1300bdd7f5bSEtienne Carriere 
1310bdd7f5bSEtienne Carriere /* Return counter value to related to input timeout in seconds, or 0 on error */
1320bdd7f5bSEtienne Carriere static uint32_t iwdg_timeout_cnt(struct stm32_iwdg_device *iwdg,
1330bdd7f5bSEtienne Carriere 				 unsigned long to_sec)
1340bdd7f5bSEtienne Carriere {
1350bdd7f5bSEtienne Carriere 	uint64_t reload = (uint64_t)to_sec * clk_get_rate(iwdg->clk_lsi);
1360bdd7f5bSEtienne Carriere 	uint64_t cnt = (reload / IWDG_PRESCALER_256) - 1;
1370bdd7f5bSEtienne Carriere 
1380bdd7f5bSEtienne Carriere 	/* Be safe and expect any counter to be above 2 */
1390bdd7f5bSEtienne Carriere 	if (cnt > IWDG_CNT_MASK || cnt < 3)
1400bdd7f5bSEtienne Carriere 		return 0;
1410bdd7f5bSEtienne Carriere 
1420bdd7f5bSEtienne Carriere 	return cnt;
1430bdd7f5bSEtienne Carriere }
1440bdd7f5bSEtienne Carriere 
1450bdd7f5bSEtienne Carriere /* Wait IWDG programming completes */
1460bdd7f5bSEtienne Carriere static TEE_Result iwdg_wait_sync(struct stm32_iwdg_device *iwdg)
1470bdd7f5bSEtienne Carriere {
1480bdd7f5bSEtienne Carriere 	uint64_t timeout_ref = timeout_init_us(IWDG_TIMEOUT_US);
1490bdd7f5bSEtienne Carriere 	vaddr_t iwdg_base = get_base(iwdg);
1500bdd7f5bSEtienne Carriere 
1510bdd7f5bSEtienne Carriere 	while (io_read32(iwdg_base + IWDG_SR_OFFSET) & IWDG_SR_UPDATE_MASK)
1520bdd7f5bSEtienne Carriere 		if (timeout_elapsed(timeout_ref))
1530bdd7f5bSEtienne Carriere 			break;
1540bdd7f5bSEtienne Carriere 
155077bbb8aSEtienne Carriere 	if (io_read32(iwdg_base + IWDG_SR_OFFSET) & IWDG_SR_UPDATE_MASK)
1560bdd7f5bSEtienne Carriere 		return TEE_ERROR_GENERIC;
1570bdd7f5bSEtienne Carriere 
1580bdd7f5bSEtienne Carriere 	return TEE_SUCCESS;
1590bdd7f5bSEtienne Carriere }
1600bdd7f5bSEtienne Carriere 
161e4b8d29aSEtienne Carriere static enum itr_return stm32_iwdg_it_handler(struct itr_handler *h)
162e4b8d29aSEtienne Carriere {
163e4b8d29aSEtienne Carriere 	unsigned int __maybe_unused cpu = get_core_pos();
164e4b8d29aSEtienne Carriere 	struct stm32_iwdg_device *iwdg = h->data;
165e4b8d29aSEtienne Carriere 	vaddr_t iwdg_base = get_base(iwdg);
166e4b8d29aSEtienne Carriere 
167e4b8d29aSEtienne Carriere 	DMSG("CPU %u IT Watchdog %#"PRIxPA, cpu, iwdg->base.pa);
168e4b8d29aSEtienne Carriere 
169e4b8d29aSEtienne Carriere 	/* Check for spurious interrupt */
170e4b8d29aSEtienne Carriere 	if (!(io_read32(iwdg_base + IWDG_SR_OFFSET) & sr_ewif_mask(iwdg)))
171e4b8d29aSEtienne Carriere 		return ITRR_NONE;
172e4b8d29aSEtienne Carriere 
173e4b8d29aSEtienne Carriere 	/*
174e4b8d29aSEtienne Carriere 	 * Writing IWDG_EWCR_EWIT triggers a watchdog refresh.
175e4b8d29aSEtienne Carriere 	 * To prevent the watchdog refresh, write-protect all the registers;
176e4b8d29aSEtienne Carriere 	 * this makes read-only all IWDG_EWCR fields except IWDG_EWCR_EWIC.
177e4b8d29aSEtienne Carriere 	 */
178e4b8d29aSEtienne Carriere 	io_write32(iwdg_base + IWDG_KR_OFFSET, IWDG_KR_WPROT_KEY);
179e4b8d29aSEtienne Carriere 
180e4b8d29aSEtienne Carriere 	/* Disable early interrupt */
181e4b8d29aSEtienne Carriere 	if (iwdg->hw_version >= IWDG_ICR_MIN_VER)
182e4b8d29aSEtienne Carriere 		io_setbits32(iwdg_base + IWDG_ICR_OFFSET, IWDG_ICR_EWIC);
183e4b8d29aSEtienne Carriere 	else
184e4b8d29aSEtienne Carriere 		io_setbits32(iwdg_base + IWDG_EWCR_OFFSET, IWDG_EWCR_EWIC);
185e4b8d29aSEtienne Carriere 
186e4b8d29aSEtienne Carriere 	panic("Watchdog");
187e4b8d29aSEtienne Carriere 
188e4b8d29aSEtienne Carriere 	return ITRR_HANDLED;
189e4b8d29aSEtienne Carriere }
190e4b8d29aSEtienne Carriere DECLARE_KEEP_PAGER(stm32_iwdg_it_handler);
191e4b8d29aSEtienne Carriere 
1920bdd7f5bSEtienne Carriere static TEE_Result configure_timeout(struct stm32_iwdg_device *iwdg)
1930bdd7f5bSEtienne Carriere {
1940bdd7f5bSEtienne Carriere 	TEE_Result res = TEE_ERROR_GENERIC;
1950bdd7f5bSEtienne Carriere 	vaddr_t iwdg_base = get_base(iwdg);
1960bdd7f5bSEtienne Carriere 	uint32_t rlr_value = 0;
197e4b8d29aSEtienne Carriere 	uint32_t ewie_value = 0;
198e4b8d29aSEtienne Carriere 	uint32_t early_timeout = 0;
1990bdd7f5bSEtienne Carriere 
2002f9b82faSEtienne Carriere 	assert(iwdg_wdt_is_enabled(iwdg));
2010bdd7f5bSEtienne Carriere 
2020bdd7f5bSEtienne Carriere 	rlr_value = iwdg_timeout_cnt(iwdg, iwdg->timeout);
2030bdd7f5bSEtienne Carriere 	if (!rlr_value)
2040bdd7f5bSEtienne Carriere 		return TEE_ERROR_GENERIC;
2050bdd7f5bSEtienne Carriere 
206e4b8d29aSEtienne Carriere 	if (iwdg->itr_handler) {
207e4b8d29aSEtienne Carriere 		if (iwdg->timeout >= 2 * IWDG_ETIMEOUT_SEC)
208e4b8d29aSEtienne Carriere 			early_timeout = IWDG_ETIMEOUT_SEC;
209e4b8d29aSEtienne Carriere 		else
210e4b8d29aSEtienne Carriere 			early_timeout = iwdg->timeout / 4;
211e4b8d29aSEtienne Carriere 		ewie_value = iwdg_timeout_cnt(iwdg, early_timeout);
212e4b8d29aSEtienne Carriere 		interrupt_enable(iwdg->itr_chip, iwdg->itr_num);
213e4b8d29aSEtienne Carriere 	}
214e4b8d29aSEtienne Carriere 
2150bdd7f5bSEtienne Carriere 	io_write32(iwdg_base + IWDG_KR_OFFSET, IWDG_KR_ACCESS_KEY);
2160bdd7f5bSEtienne Carriere 	io_write32(iwdg_base + IWDG_PR_OFFSET, IWDG_PR_DIV_256);
2170bdd7f5bSEtienne Carriere 	io_write32(iwdg_base + IWDG_RLR_OFFSET, rlr_value);
218e4b8d29aSEtienne Carriere 	if (ewie_value &&
219e4b8d29aSEtienne Carriere 	    !(io_read32(iwdg_base + IWDG_EWCR_OFFSET) & IWDG_EWCR_EWIE))
220e4b8d29aSEtienne Carriere 		io_write32(iwdg_base + IWDG_EWCR_OFFSET,
221e4b8d29aSEtienne Carriere 			   ewie_value | IWDG_EWCR_EWIE);
2220bdd7f5bSEtienne Carriere 
2230bdd7f5bSEtienne Carriere 	res = iwdg_wait_sync(iwdg);
2240bdd7f5bSEtienne Carriere 
225e4b8d29aSEtienne Carriere 	io_write32(iwdg_base + IWDG_KR_OFFSET, IWDG_KR_RELOAD_KEY);
226e4b8d29aSEtienne Carriere 
2270bdd7f5bSEtienne Carriere 	return res;
2280bdd7f5bSEtienne Carriere }
2290bdd7f5bSEtienne Carriere 
2300bdd7f5bSEtienne Carriere static void iwdg_start(struct stm32_iwdg_device *iwdg)
2310bdd7f5bSEtienne Carriere {
232eb47832fSAntonio Borneo 	TEE_Result res = TEE_ERROR_GENERIC;
233eb47832fSAntonio Borneo 
234eb47832fSAntonio Borneo 	res = tee_time_get_sys_time(&iwdg->last_refresh);
235eb47832fSAntonio Borneo 	if (res)
236eb47832fSAntonio Borneo 		panic();
237eb47832fSAntonio Borneo 
2380bdd7f5bSEtienne Carriere 	io_write32(get_base(iwdg) + IWDG_KR_OFFSET, IWDG_KR_START_KEY);
2390bdd7f5bSEtienne Carriere 
2402f9b82faSEtienne Carriere 	iwdg_wdt_set_enabled(iwdg);
2410bdd7f5bSEtienne Carriere }
2420bdd7f5bSEtienne Carriere 
2430bdd7f5bSEtienne Carriere static void iwdg_refresh(struct stm32_iwdg_device *iwdg)
2440bdd7f5bSEtienne Carriere {
245eb47832fSAntonio Borneo 	TEE_Result res = TEE_ERROR_GENERIC;
246eb47832fSAntonio Borneo 
247eb47832fSAntonio Borneo 	res = tee_time_get_sys_time(&iwdg->last_refresh);
248eb47832fSAntonio Borneo 	if (res)
249eb47832fSAntonio Borneo 		panic();
250eb47832fSAntonio Borneo 
2510bdd7f5bSEtienne Carriere 	io_write32(get_base(iwdg) + IWDG_KR_OFFSET, IWDG_KR_RELOAD_KEY);
2520bdd7f5bSEtienne Carriere }
2530bdd7f5bSEtienne Carriere 
2540bdd7f5bSEtienne Carriere /* Operators for watchdog OP-TEE interface */
2550bdd7f5bSEtienne Carriere static struct stm32_iwdg_device *wdt_chip_to_iwdg(struct wdt_chip *chip)
2560bdd7f5bSEtienne Carriere {
2570bdd7f5bSEtienne Carriere 	return container_of(chip, struct stm32_iwdg_device, wdt_chip);
2580bdd7f5bSEtienne Carriere }
2590bdd7f5bSEtienne Carriere 
260fc9063ddSEtienne Carriere static TEE_Result iwdg_wdt_init(struct wdt_chip *chip,
261fc9063ddSEtienne Carriere 				unsigned long *min_timeout,
262fc9063ddSEtienne Carriere 				unsigned long *max_timeout)
263fc9063ddSEtienne Carriere {
264fc9063ddSEtienne Carriere 	struct stm32_iwdg_device *iwdg = wdt_chip_to_iwdg(chip);
265fc9063ddSEtienne Carriere 	unsigned long rate = clk_get_rate(iwdg->clk_lsi);
266fc9063ddSEtienne Carriere 
267fc9063ddSEtienne Carriere 	if (!rate)
268fc9063ddSEtienne Carriere 		return TEE_ERROR_GENERIC;
269fc9063ddSEtienne Carriere 
270fc9063ddSEtienne Carriere 	/* Be safe and expect any counter to be above 2 */
271fc9063ddSEtienne Carriere 	*min_timeout = 3 * IWDG_PRESCALER_256 / rate;
272fc9063ddSEtienne Carriere 	*max_timeout = (IWDG_CNT_MASK + 1) * IWDG_PRESCALER_256 / rate;
273fc9063ddSEtienne Carriere 
274fc9063ddSEtienne Carriere 	return TEE_SUCCESS;
275fc9063ddSEtienne Carriere }
276fc9063ddSEtienne Carriere 
2770bdd7f5bSEtienne Carriere static void iwdg_wdt_start(struct wdt_chip *chip)
2780bdd7f5bSEtienne Carriere {
2790bdd7f5bSEtienne Carriere 	struct stm32_iwdg_device *iwdg = wdt_chip_to_iwdg(chip);
2800bdd7f5bSEtienne Carriere 
2810bdd7f5bSEtienne Carriere 	iwdg_start(iwdg);
2820bdd7f5bSEtienne Carriere 
2830bdd7f5bSEtienne Carriere 	if (configure_timeout(iwdg))
2840bdd7f5bSEtienne Carriere 		panic();
2850bdd7f5bSEtienne Carriere }
2860bdd7f5bSEtienne Carriere 
2870bdd7f5bSEtienne Carriere static void iwdg_wdt_refresh(struct wdt_chip *chip)
2880bdd7f5bSEtienne Carriere {
2890bdd7f5bSEtienne Carriere 	struct stm32_iwdg_device *iwdg = wdt_chip_to_iwdg(chip);
2900bdd7f5bSEtienne Carriere 
2910bdd7f5bSEtienne Carriere 	iwdg_refresh(iwdg);
2920bdd7f5bSEtienne Carriere }
2930bdd7f5bSEtienne Carriere 
2940bdd7f5bSEtienne Carriere static TEE_Result iwdg_wdt_set_timeout(struct wdt_chip *chip,
2950bdd7f5bSEtienne Carriere 				       unsigned long timeout)
2960bdd7f5bSEtienne Carriere {
2970bdd7f5bSEtienne Carriere 	struct stm32_iwdg_device *iwdg = wdt_chip_to_iwdg(chip);
2980bdd7f5bSEtienne Carriere 
2990bdd7f5bSEtienne Carriere 	if (!iwdg_timeout_cnt(iwdg, timeout))
3000bdd7f5bSEtienne Carriere 		return TEE_ERROR_BAD_PARAMETERS;
3010bdd7f5bSEtienne Carriere 
3020bdd7f5bSEtienne Carriere 	iwdg->timeout = timeout;
3030bdd7f5bSEtienne Carriere 
3042f9b82faSEtienne Carriere 	if (iwdg_wdt_is_enabled(iwdg)) {
3050bdd7f5bSEtienne Carriere 		TEE_Result res = TEE_ERROR_GENERIC;
3060bdd7f5bSEtienne Carriere 
3070bdd7f5bSEtienne Carriere 		res = configure_timeout(iwdg);
3080bdd7f5bSEtienne Carriere 		if (res)
3090bdd7f5bSEtienne Carriere 			return res;
3100bdd7f5bSEtienne Carriere 	}
3110bdd7f5bSEtienne Carriere 
3120bdd7f5bSEtienne Carriere 	return TEE_SUCCESS;
3130bdd7f5bSEtienne Carriere }
3140bdd7f5bSEtienne Carriere 
315eb47832fSAntonio Borneo static TEE_Result iwdg_wdt_get_timeleft(struct wdt_chip *chip, bool *is_started,
316eb47832fSAntonio Borneo 					unsigned long *timeleft)
317eb47832fSAntonio Borneo {
318eb47832fSAntonio Borneo 	struct stm32_iwdg_device *iwdg = wdt_chip_to_iwdg(chip);
319eb47832fSAntonio Borneo 	TEE_Result res = TEE_ERROR_GENERIC;
320eb47832fSAntonio Borneo 	TEE_Time time = { };
321eb47832fSAntonio Borneo 	TEE_Time now = { };
322eb47832fSAntonio Borneo 
323eb47832fSAntonio Borneo 	*is_started = iwdg_wdt_is_enabled(iwdg);
324eb47832fSAntonio Borneo 
325eb47832fSAntonio Borneo 	if (!*is_started)
326eb47832fSAntonio Borneo 		return TEE_SUCCESS;
327eb47832fSAntonio Borneo 
328eb47832fSAntonio Borneo 	res = tee_time_get_sys_time(&now);
329eb47832fSAntonio Borneo 	if (res)
330eb47832fSAntonio Borneo 		panic();
331eb47832fSAntonio Borneo 
332eb47832fSAntonio Borneo 	time.seconds = iwdg->timeout;
333eb47832fSAntonio Borneo 	TEE_TIME_ADD(iwdg->last_refresh, time, time);
334eb47832fSAntonio Borneo 	if (TEE_TIME_LE(time, now)) {
335eb47832fSAntonio Borneo 		*timeleft = 0;
336eb47832fSAntonio Borneo 	} else {
337eb47832fSAntonio Borneo 		TEE_TIME_SUB(time, now, time);
338eb47832fSAntonio Borneo 		*timeleft = time.seconds;
339eb47832fSAntonio Borneo 	}
340eb47832fSAntonio Borneo 
341eb47832fSAntonio Borneo 	return TEE_SUCCESS;
342eb47832fSAntonio Borneo }
343eb47832fSAntonio Borneo 
3440bdd7f5bSEtienne Carriere static const struct wdt_ops stm32_iwdg_ops = {
345fc9063ddSEtienne Carriere 	.init = iwdg_wdt_init,
3460bdd7f5bSEtienne Carriere 	.start = iwdg_wdt_start,
3470bdd7f5bSEtienne Carriere 	.ping = iwdg_wdt_refresh,
3480bdd7f5bSEtienne Carriere 	.set_timeout = iwdg_wdt_set_timeout,
349eb47832fSAntonio Borneo 	.get_timeleft = iwdg_wdt_get_timeleft,
3500bdd7f5bSEtienne Carriere };
3510bdd7f5bSEtienne Carriere DECLARE_KEEP_PAGER(stm32_iwdg_ops);
3520bdd7f5bSEtienne Carriere 
3530bdd7f5bSEtienne Carriere /* Driver initialization */
3540bdd7f5bSEtienne Carriere static TEE_Result stm32_iwdg_parse_fdt(struct stm32_iwdg_device *iwdg,
3550bdd7f5bSEtienne Carriere 				       const void *fdt, int node)
3560bdd7f5bSEtienne Carriere {
3570bdd7f5bSEtienne Carriere 	TEE_Result res = TEE_ERROR_GENERIC;
3580bdd7f5bSEtienne Carriere 	struct dt_node_info dt_info = { };
3590bdd7f5bSEtienne Carriere 	const fdt32_t *cuint = NULL;
3600bdd7f5bSEtienne Carriere 
361f354a5d8SGatien Chevallier 	fdt_fill_device_info(fdt, &dt_info, node);
3620bdd7f5bSEtienne Carriere 
3630bdd7f5bSEtienne Carriere 	if (dt_info.reg == DT_INFO_INVALID_REG ||
3640bdd7f5bSEtienne Carriere 	    dt_info.reg_size == DT_INFO_INVALID_REG_SIZE)
3650bdd7f5bSEtienne Carriere 		panic();
3660bdd7f5bSEtienne Carriere 
367b2f17e87SEtienne Carriere 	res = clk_dt_get_by_name(fdt, node, "pclk", &iwdg->clk_pclk);
3680bdd7f5bSEtienne Carriere 	if (res)
3690bdd7f5bSEtienne Carriere 		return res;
3700bdd7f5bSEtienne Carriere 
3710bdd7f5bSEtienne Carriere 	res = clk_dt_get_by_name(fdt, node, "lsi", &iwdg->clk_lsi);
3720bdd7f5bSEtienne Carriere 	if (res)
3730bdd7f5bSEtienne Carriere 		return res;
3740bdd7f5bSEtienne Carriere 
375e4b8d29aSEtienne Carriere 	res = interrupt_dt_get(fdt, node, &iwdg->itr_chip, &iwdg->itr_num);
376e4b8d29aSEtienne Carriere 	if (res && res != TEE_ERROR_ITEM_NOT_FOUND)
377e4b8d29aSEtienne Carriere 		return res;
378e4b8d29aSEtienne Carriere 	if (!res) {
379e4b8d29aSEtienne Carriere 		res = interrupt_create_handler(iwdg->itr_chip, iwdg->itr_num,
380e4b8d29aSEtienne Carriere 					       stm32_iwdg_it_handler, iwdg, 0,
381e4b8d29aSEtienne Carriere 					       &iwdg->itr_handler);
382e4b8d29aSEtienne Carriere 		if (res)
383e4b8d29aSEtienne Carriere 			return res;
384e4b8d29aSEtienne Carriere 	}
385e4b8d29aSEtienne Carriere 
3860bdd7f5bSEtienne Carriere 	/* Get IOMEM address */
3870bdd7f5bSEtienne Carriere 	iwdg->base.pa = dt_info.reg;
3880bdd7f5bSEtienne Carriere 	io_pa_or_va_secure(&iwdg->base, dt_info.reg_size);
3890bdd7f5bSEtienne Carriere 	assert(iwdg->base.va);
3900bdd7f5bSEtienne Carriere 
3910bdd7f5bSEtienne Carriere 	/* Get and check timeout value */
3920bdd7f5bSEtienne Carriere 	cuint = fdt_getprop(fdt, node, "timeout-sec", NULL);
393e4b8d29aSEtienne Carriere 	if (!cuint) {
394e4b8d29aSEtienne Carriere 		res = TEE_ERROR_BAD_PARAMETERS;
395e4b8d29aSEtienne Carriere 		goto err_itr;
396e4b8d29aSEtienne Carriere 	}
3970bdd7f5bSEtienne Carriere 
3980bdd7f5bSEtienne Carriere 	iwdg->timeout = (int)fdt32_to_cpu(*cuint);
399e4b8d29aSEtienne Carriere 	if (!iwdg->timeout) {
400e4b8d29aSEtienne Carriere 		res = TEE_ERROR_BAD_PARAMETERS;
401e4b8d29aSEtienne Carriere 		goto err_itr;
402e4b8d29aSEtienne Carriere 	}
4030bdd7f5bSEtienne Carriere 
4040bdd7f5bSEtienne Carriere 	if (!iwdg_timeout_cnt(iwdg, iwdg->timeout)) {
4050bdd7f5bSEtienne Carriere 		EMSG("Timeout %lu not applicable", iwdg->timeout);
406e4b8d29aSEtienne Carriere 		res = TEE_ERROR_BAD_PARAMETERS;
407e4b8d29aSEtienne Carriere 		goto err_itr;
4080bdd7f5bSEtienne Carriere 	}
4090bdd7f5bSEtienne Carriere 
4100bdd7f5bSEtienne Carriere 	return TEE_SUCCESS;
411e4b8d29aSEtienne Carriere 
412e4b8d29aSEtienne Carriere err_itr:
413e4b8d29aSEtienne Carriere 	interrupt_remove_free_handler(iwdg->itr_handler);
414e4b8d29aSEtienne Carriere 
415e4b8d29aSEtienne Carriere 	return res;
4160bdd7f5bSEtienne Carriere }
4170bdd7f5bSEtienne Carriere 
4183d5793d2SAntonio Borneo static void iwdg_wdt_get_version_and_status(struct stm32_iwdg_device *iwdg)
4193d5793d2SAntonio Borneo {
4203d5793d2SAntonio Borneo 	vaddr_t iwdg_base = get_base(iwdg);
4213d5793d2SAntonio Borneo 	uint32_t rlr_value = 0;
4223d5793d2SAntonio Borneo 
4233d5793d2SAntonio Borneo 	iwdg->hw_version = io_read32(iwdg_base + IWDG_VERR_OFFSET) &
4243d5793d2SAntonio Borneo 			   IWDG_VERR_REV_MASK;
4253d5793d2SAntonio Borneo 
4263d5793d2SAntonio Borneo 	/* Test if watchdog is already running */
4273d5793d2SAntonio Borneo 	if (iwdg->hw_version >= IWDG_ONF_MIN_VER) {
4283d5793d2SAntonio Borneo 		if (io_read32(iwdg_base + IWDG_SR_OFFSET) & IWDG_SR_ONF)
4293d5793d2SAntonio Borneo 			iwdg_wdt_set_enabled(iwdg);
4303d5793d2SAntonio Borneo 	} else {
4313d5793d2SAntonio Borneo 		/*
4323d5793d2SAntonio Borneo 		 * Workaround for old versions without IWDG_SR_ONF bit:
4333d5793d2SAntonio Borneo 		 * - write in IWDG_RLR_OFFSET
4343d5793d2SAntonio Borneo 		 * - wait for sync
4353d5793d2SAntonio Borneo 		 * - if sync succeeds, then iwdg is running
4363d5793d2SAntonio Borneo 		 */
4373d5793d2SAntonio Borneo 		io_write32(iwdg_base + IWDG_KR_OFFSET, IWDG_KR_ACCESS_KEY);
4383d5793d2SAntonio Borneo 
4393d5793d2SAntonio Borneo 		rlr_value = io_read32(iwdg_base + IWDG_RLR_OFFSET);
4403d5793d2SAntonio Borneo 		io_write32(iwdg_base + IWDG_RLR_OFFSET, rlr_value);
4413d5793d2SAntonio Borneo 
4423d5793d2SAntonio Borneo 		if (!iwdg_wait_sync(iwdg))
4433d5793d2SAntonio Borneo 			iwdg_wdt_set_enabled(iwdg);
4443d5793d2SAntonio Borneo 
4453d5793d2SAntonio Borneo 		io_write32(iwdg_base + IWDG_KR_OFFSET, IWDG_KR_WPROT_KEY);
4463d5793d2SAntonio Borneo 	}
4473d5793d2SAntonio Borneo 
4483d5793d2SAntonio Borneo 	DMSG("Watchdog is %sabled", iwdg_wdt_is_enabled(iwdg) ? "en" : "dis");
4493d5793d2SAntonio Borneo }
4503d5793d2SAntonio Borneo 
4510bdd7f5bSEtienne Carriere static TEE_Result stm32_iwdg_setup(struct stm32_iwdg_device *iwdg,
4520bdd7f5bSEtienne Carriere 				   const void *fdt, int node)
4530bdd7f5bSEtienne Carriere {
4540bdd7f5bSEtienne Carriere 	TEE_Result res = TEE_SUCCESS;
4550bdd7f5bSEtienne Carriere 
4560bdd7f5bSEtienne Carriere 	res = stm32_iwdg_parse_fdt(iwdg, fdt, node);
4570bdd7f5bSEtienne Carriere 	if (res)
4580bdd7f5bSEtienne Carriere 		return res;
4590bdd7f5bSEtienne Carriere 
46036d2a417SEtienne Carriere 	/* Enable watchdog source and bus clocks once for all */
4615c7ebea7SAntonio Borneo 	if (clk_enable(iwdg->clk_lsi))
4625c7ebea7SAntonio Borneo 		panic();
4635c7ebea7SAntonio Borneo 
4645c7ebea7SAntonio Borneo 	if (clk_enable(iwdg->clk_pclk))
4655c7ebea7SAntonio Borneo 		panic();
4660bdd7f5bSEtienne Carriere 
4673d5793d2SAntonio Borneo 	iwdg_wdt_get_version_and_status(iwdg);
4680bdd7f5bSEtienne Carriere 
4693d5793d2SAntonio Borneo 	if (iwdg_wdt_is_enabled(iwdg)) {
4700bdd7f5bSEtienne Carriere 		/* Configure timeout if watchdog is already enabled */
4710bdd7f5bSEtienne Carriere 		res = configure_timeout(iwdg);
4720bdd7f5bSEtienne Carriere 		if (res)
4735c7ebea7SAntonio Borneo 			panic();
4740bdd7f5bSEtienne Carriere 
4750bdd7f5bSEtienne Carriere 		iwdg_refresh(iwdg);
4760bdd7f5bSEtienne Carriere 	}
4770bdd7f5bSEtienne Carriere 
4780bdd7f5bSEtienne Carriere 	return TEE_SUCCESS;
4790bdd7f5bSEtienne Carriere }
4800bdd7f5bSEtienne Carriere 
481*235baec9SEtienne Carriere static TEE_Result stm32_iwdg_pm(enum pm_op op, unsigned int pm_hint __unused,
482*235baec9SEtienne Carriere 				const struct pm_callback_handle *pm_handle)
483*235baec9SEtienne Carriere {
484*235baec9SEtienne Carriere 	struct stm32_iwdg_device *iwdg = PM_CALLBACK_GET_HANDLE(pm_handle);
485*235baec9SEtienne Carriere 
486*235baec9SEtienne Carriere 	if (op == PM_OP_RESUME) {
487*235baec9SEtienne Carriere 		clk_enable(iwdg->clk_lsi);
488*235baec9SEtienne Carriere 		clk_enable(iwdg->clk_pclk);
489*235baec9SEtienne Carriere 	} else {
490*235baec9SEtienne Carriere 		clk_disable(iwdg->clk_lsi);
491*235baec9SEtienne Carriere 		clk_disable(iwdg->clk_pclk);
492*235baec9SEtienne Carriere 	}
493*235baec9SEtienne Carriere 
494*235baec9SEtienne Carriere 	return TEE_SUCCESS;
495*235baec9SEtienne Carriere }
496*235baec9SEtienne Carriere DECLARE_KEEP_PAGER(stm32_iwdg_pm);
497*235baec9SEtienne Carriere 
4980bdd7f5bSEtienne Carriere static TEE_Result stm32_iwdg_probe(const void *fdt, int node,
4990bdd7f5bSEtienne Carriere 				   const void *compat_data __unused)
5000bdd7f5bSEtienne Carriere {
5010bdd7f5bSEtienne Carriere 	struct stm32_iwdg_device *iwdg = NULL;
5020bdd7f5bSEtienne Carriere 	TEE_Result res = TEE_SUCCESS;
5030bdd7f5bSEtienne Carriere 
5040bdd7f5bSEtienne Carriere 	iwdg = calloc(1, sizeof(*iwdg));
5050bdd7f5bSEtienne Carriere 	if (!iwdg)
5060bdd7f5bSEtienne Carriere 		return TEE_ERROR_OUT_OF_MEMORY;
5070bdd7f5bSEtienne Carriere 
5080bdd7f5bSEtienne Carriere 	res = stm32_iwdg_setup(iwdg, fdt, node);
5090bdd7f5bSEtienne Carriere 	if (res)
510*235baec9SEtienne Carriere 		goto out_free;
5110bdd7f5bSEtienne Carriere 
512a096e2d9SEtienne Carriere 	iwdg->wdt_chip.ops = &stm32_iwdg_ops;
513a096e2d9SEtienne Carriere 
514*235baec9SEtienne Carriere 	register_pm_core_service_cb(stm32_iwdg_pm, iwdg, "stm32-iwdg");
515a096e2d9SEtienne Carriere 
516*235baec9SEtienne Carriere 	res = watchdog_register(&iwdg->wdt_chip);
5170bdd7f5bSEtienne Carriere 	if (res)
518*235baec9SEtienne Carriere 		goto out_pm;
519*235baec9SEtienne Carriere 
520*235baec9SEtienne Carriere 	return TEE_SUCCESS;
521*235baec9SEtienne Carriere 
522*235baec9SEtienne Carriere out_pm:
523*235baec9SEtienne Carriere 	unregister_pm_core_service_cb(stm32_iwdg_pm, iwdg);
524*235baec9SEtienne Carriere out_free:
5250bdd7f5bSEtienne Carriere 	free(iwdg);
526a096e2d9SEtienne Carriere 
5270bdd7f5bSEtienne Carriere 	return res;
5280bdd7f5bSEtienne Carriere }
5290bdd7f5bSEtienne Carriere 
5300bdd7f5bSEtienne Carriere static const struct dt_device_match stm32_iwdg_match_table[] = {
5310bdd7f5bSEtienne Carriere 	{ .compatible = "st,stm32mp1-iwdg" },
5320bdd7f5bSEtienne Carriere 	{ }
5330bdd7f5bSEtienne Carriere };
5340bdd7f5bSEtienne Carriere 
5350bdd7f5bSEtienne Carriere DEFINE_DT_DRIVER(stm32_iwdg_dt_driver) = {
5360bdd7f5bSEtienne Carriere 	.name = "stm32-iwdg",
5370bdd7f5bSEtienne Carriere 	.match_table = stm32_iwdg_match_table,
5380bdd7f5bSEtienne Carriere 	.probe = stm32_iwdg_probe,
5390bdd7f5bSEtienne Carriere };
540