xref: /optee_os/core/drivers/stm32_iwdg.c (revision f6ee86ec9430b4c912b7b91158d2b0cd88f6a174)
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>
9dff60fe8SClément Le Goffic #include <drivers/rstctrl.h>
100bdd7f5bSEtienne Carriere #include <drivers/wdt.h>
110bdd7f5bSEtienne Carriere #include <io.h>
120bdd7f5bSEtienne Carriere #include <keep.h>
130bdd7f5bSEtienne Carriere #include <kernel/boot.h>
140bdd7f5bSEtienne Carriere #include <kernel/delay.h>
150bdd7f5bSEtienne Carriere #include <kernel/dt.h>
169e3c57c8SEtienne Carriere #include <kernel/dt_driver.h>
170bdd7f5bSEtienne Carriere #include <kernel/interrupt.h>
180bdd7f5bSEtienne Carriere #include <kernel/misc.h>
190bdd7f5bSEtienne Carriere #include <kernel/panic.h>
200bdd7f5bSEtienne Carriere #include <kernel/pm.h>
210bdd7f5bSEtienne Carriere #include <kernel/spinlock.h>
22eb47832fSAntonio Borneo #include <kernel/tee_time.h>
230bdd7f5bSEtienne Carriere #include <libfdt.h>
240bdd7f5bSEtienne Carriere #include <mm/core_memprot.h>
250bdd7f5bSEtienne Carriere #include <sm/sm.h>
26*f6ee86ecSClément Le Goffic #include <stdint.h>
270bdd7f5bSEtienne Carriere #include <stm32_util.h>
280bdd7f5bSEtienne Carriere #include <string.h>
290bdd7f5bSEtienne Carriere #include <trace.h>
300bdd7f5bSEtienne Carriere 
310bdd7f5bSEtienne Carriere /* IWDG Compatibility */
323d5793d2SAntonio Borneo #define IWDG_TIMEOUT_US		U(10000)
330bdd7f5bSEtienne Carriere #define IWDG_CNT_MASK		GENMASK_32(11, 0)
343d5793d2SAntonio Borneo #define IWDG_ONF_MIN_VER	U(0x31)
35e4b8d29aSEtienne Carriere #define IWDG_ICR_MIN_VER	U(0x40)
360bdd7f5bSEtienne Carriere 
370bdd7f5bSEtienne Carriere /* IWDG registers offsets */
380bdd7f5bSEtienne Carriere #define IWDG_KR_OFFSET		U(0x00)
390bdd7f5bSEtienne Carriere #define IWDG_PR_OFFSET		U(0x04)
400bdd7f5bSEtienne Carriere #define IWDG_RLR_OFFSET		U(0x08)
410bdd7f5bSEtienne Carriere #define IWDG_SR_OFFSET		U(0x0C)
420bdd7f5bSEtienne Carriere #define IWDG_EWCR_OFFSET	U(0x14)
43e4b8d29aSEtienne Carriere #define IWDG_ICR_OFFSET		U(0x18)
443d5793d2SAntonio Borneo #define IWDG_VERR_OFFSET	U(0x3F4)
450bdd7f5bSEtienne Carriere 
463d5793d2SAntonio Borneo #define IWDG_KR_WPROT_KEY	U(0x0000)
470bdd7f5bSEtienne Carriere #define IWDG_KR_ACCESS_KEY	U(0x5555)
480bdd7f5bSEtienne Carriere #define IWDG_KR_RELOAD_KEY	U(0xAAAA)
490bdd7f5bSEtienne Carriere #define IWDG_KR_START_KEY	U(0xCCCC)
500bdd7f5bSEtienne Carriere 
510bdd7f5bSEtienne Carriere /* Use a fixed prescaler divider of 256 */
520bdd7f5bSEtienne Carriere #define IWDG_PRESCALER_256	U(256)
530bdd7f5bSEtienne Carriere #define IWDG_PR_DIV_256		U(0x06)
540bdd7f5bSEtienne Carriere #define IWDG_PR_DIV_MASK	GENMASK_32(3, 0)
550bdd7f5bSEtienne Carriere 
560bdd7f5bSEtienne Carriere #define IWDG_SR_PVU		BIT(0)
570bdd7f5bSEtienne Carriere #define IWDG_SR_RVU		BIT(1)
580bdd7f5bSEtienne Carriere #define IWDG_SR_WVU		BIT(2)
590bdd7f5bSEtienne Carriere #define IWDG_SR_EWU		BIT(3)
600bdd7f5bSEtienne Carriere #define IWDG_SR_UPDATE_MASK	(IWDG_SR_PVU | IWDG_SR_RVU | IWDG_SR_WVU | \
610bdd7f5bSEtienne Carriere 				 IWDG_SR_EWU)
623d5793d2SAntonio Borneo #define IWDG_SR_ONF		BIT(8)
63e4b8d29aSEtienne Carriere #define IWDG_SR_EWIF		BIT(14)
64e4b8d29aSEtienne Carriere #define IWDG_SR_EWIF_V40	BIT(15)
650bdd7f5bSEtienne Carriere 
660bdd7f5bSEtienne Carriere #define IWDG_EWCR_EWIE		BIT(15)
670bdd7f5bSEtienne Carriere #define IWDG_EWCR_EWIC		BIT(14)
680bdd7f5bSEtienne Carriere 
69e4b8d29aSEtienne Carriere #define IWDG_ICR_EWIC		BIT(15)
70e4b8d29aSEtienne Carriere 
713d5793d2SAntonio Borneo #define IWDG_VERR_REV_MASK	GENMASK_32(7, 0)
723d5793d2SAntonio Borneo 
73e4b8d29aSEtienne Carriere /* Define default early timeout delay to 5 sec before timeout */
74e4b8d29aSEtienne Carriere #define IWDG_ETIMEOUT_SEC	U(5)
75e4b8d29aSEtienne Carriere 
760bdd7f5bSEtienne Carriere /*
770bdd7f5bSEtienne Carriere  * Values for struct stm32_iwdg_device::flags
780bdd7f5bSEtienne Carriere  * IWDG_FLAGS_ENABLED			Watchdog has been enabled
790bdd7f5bSEtienne Carriere  */
80c501c3e1SLionel Debieve #define IWDG_FLAGS_ENABLED			BIT(0)
810bdd7f5bSEtienne Carriere 
820bdd7f5bSEtienne Carriere /*
830bdd7f5bSEtienne Carriere  * IWDG watch instance data
840bdd7f5bSEtienne Carriere  * @base - IWDG interface IOMEM base address
85b2f17e87SEtienne Carriere  * @clk_pclk - Bus clock
860bdd7f5bSEtienne Carriere  * @clk_lsi - IWDG source clock
87e4b8d29aSEtienne Carriere  * @itr_chip - Interrupt chip device
88e4b8d29aSEtienne Carriere  * @itr_num - Interrupt number for the IWDG instance
89e4b8d29aSEtienne Carriere  * @itr_handler - Interrupt handler
90dff60fe8SClément Le Goffic  * @reset - Reset controller device used to control the ability of the watchdog
91dff60fe8SClément Le Goffic  *          to reset the system
920bdd7f5bSEtienne Carriere  * @flags - Property flags for the IWDG instance
930bdd7f5bSEtienne Carriere  * @timeout - Watchdog elaspure timeout
94*f6ee86ecSClément Le Goffic  * @saved_nb_int - Saved number of interrupts before panic
95*f6ee86ecSClément Le Goffic  * @nb_int - Remaining number of interrupts before panic
963d5793d2SAntonio Borneo  * @hw_version - Watchdog HW version
97eb47832fSAntonio Borneo  * @last_refresh - Time of last watchdog refresh
980bdd7f5bSEtienne Carriere  * @wdt_chip - Wathcdog chip instance
99*f6ee86ecSClément Le Goffic  * @max_hw_timeout - Maximum hardware timeout
1000bdd7f5bSEtienne Carriere  */
1010bdd7f5bSEtienne Carriere struct stm32_iwdg_device {
1020bdd7f5bSEtienne Carriere 	struct io_pa_va base;
103b2f17e87SEtienne Carriere 	struct clk *clk_pclk;
1040bdd7f5bSEtienne Carriere 	struct clk *clk_lsi;
105e4b8d29aSEtienne Carriere 	struct itr_chip *itr_chip;
106e4b8d29aSEtienne Carriere 	size_t itr_num;
107e4b8d29aSEtienne Carriere 	struct itr_handler *itr_handler;
108dff60fe8SClément Le Goffic 	struct rstctrl *reset;
1090bdd7f5bSEtienne Carriere 	uint32_t flags;
1100bdd7f5bSEtienne Carriere 	unsigned long timeout;
111*f6ee86ecSClément Le Goffic 	unsigned long early_timeout;
112*f6ee86ecSClément Le Goffic 	unsigned long saved_nb_int;
113*f6ee86ecSClément Le Goffic 	unsigned long nb_int;
1143d5793d2SAntonio Borneo 	unsigned int hw_version;
115eb47832fSAntonio Borneo 	TEE_Time last_refresh;
1160bdd7f5bSEtienne Carriere 	struct wdt_chip wdt_chip;
117*f6ee86ecSClément Le Goffic 	unsigned long max_hw_timeout;
1180bdd7f5bSEtienne Carriere };
1190bdd7f5bSEtienne Carriere 
120e4b8d29aSEtienne Carriere static uint32_t sr_ewif_mask(struct stm32_iwdg_device *iwdg)
121e4b8d29aSEtienne Carriere {
122e4b8d29aSEtienne Carriere 	if (iwdg->hw_version >= IWDG_ICR_MIN_VER)
123e4b8d29aSEtienne Carriere 		return IWDG_SR_EWIF_V40;
124e4b8d29aSEtienne Carriere 	else
125e4b8d29aSEtienne Carriere 		return IWDG_SR_EWIF;
126e4b8d29aSEtienne Carriere }
127e4b8d29aSEtienne Carriere 
1280bdd7f5bSEtienne Carriere static vaddr_t get_base(struct stm32_iwdg_device *iwdg)
1290bdd7f5bSEtienne Carriere {
1300bdd7f5bSEtienne Carriere 	return io_pa_or_va(&iwdg->base, 1);
1310bdd7f5bSEtienne Carriere }
1320bdd7f5bSEtienne Carriere 
1332f9b82faSEtienne Carriere static void iwdg_wdt_set_enabled(struct stm32_iwdg_device *iwdg)
1342f9b82faSEtienne Carriere {
1352f9b82faSEtienne Carriere 	iwdg->flags |= IWDG_FLAGS_ENABLED;
1362f9b82faSEtienne Carriere }
1372f9b82faSEtienne Carriere 
1382f9b82faSEtienne Carriere static bool iwdg_wdt_is_enabled(struct stm32_iwdg_device *iwdg)
1390bdd7f5bSEtienne Carriere {
1400bdd7f5bSEtienne Carriere 	return iwdg->flags & IWDG_FLAGS_ENABLED;
1410bdd7f5bSEtienne Carriere }
1420bdd7f5bSEtienne Carriere 
1430bdd7f5bSEtienne Carriere /* Return counter value to related to input timeout in seconds, or 0 on error */
1440bdd7f5bSEtienne Carriere static uint32_t iwdg_timeout_cnt(struct stm32_iwdg_device *iwdg,
1450bdd7f5bSEtienne Carriere 				 unsigned long to_sec)
1460bdd7f5bSEtienne Carriere {
1470bdd7f5bSEtienne Carriere 	uint64_t reload = (uint64_t)to_sec * clk_get_rate(iwdg->clk_lsi);
1480bdd7f5bSEtienne Carriere 	uint64_t cnt = (reload / IWDG_PRESCALER_256) - 1;
1490bdd7f5bSEtienne Carriere 
1500bdd7f5bSEtienne Carriere 	/* Be safe and expect any counter to be above 2 */
1510bdd7f5bSEtienne Carriere 	if (cnt > IWDG_CNT_MASK || cnt < 3)
1520bdd7f5bSEtienne Carriere 		return 0;
1530bdd7f5bSEtienne Carriere 
1540bdd7f5bSEtienne Carriere 	return cnt;
1550bdd7f5bSEtienne Carriere }
1560bdd7f5bSEtienne Carriere 
1570bdd7f5bSEtienne Carriere /* Wait IWDG programming completes */
1580bdd7f5bSEtienne Carriere static TEE_Result iwdg_wait_sync(struct stm32_iwdg_device *iwdg)
1590bdd7f5bSEtienne Carriere {
1600bdd7f5bSEtienne Carriere 	uint64_t timeout_ref = timeout_init_us(IWDG_TIMEOUT_US);
1610bdd7f5bSEtienne Carriere 	vaddr_t iwdg_base = get_base(iwdg);
1620bdd7f5bSEtienne Carriere 
1630bdd7f5bSEtienne Carriere 	while (io_read32(iwdg_base + IWDG_SR_OFFSET) & IWDG_SR_UPDATE_MASK)
1640bdd7f5bSEtienne Carriere 		if (timeout_elapsed(timeout_ref))
1650bdd7f5bSEtienne Carriere 			break;
1660bdd7f5bSEtienne Carriere 
167077bbb8aSEtienne Carriere 	if (io_read32(iwdg_base + IWDG_SR_OFFSET) & IWDG_SR_UPDATE_MASK)
1680bdd7f5bSEtienne Carriere 		return TEE_ERROR_GENERIC;
1690bdd7f5bSEtienne Carriere 
1700bdd7f5bSEtienne Carriere 	return TEE_SUCCESS;
1710bdd7f5bSEtienne Carriere }
1720bdd7f5bSEtienne Carriere 
173dff60fe8SClément Le Goffic static void stm32_iwdg_it_ack(struct stm32_iwdg_device *iwdg)
174dff60fe8SClément Le Goffic {
175dff60fe8SClément Le Goffic 	vaddr_t iwdg_base = get_base(iwdg);
176dff60fe8SClément Le Goffic 
177dff60fe8SClément Le Goffic 	if (iwdg->hw_version >= IWDG_ICR_MIN_VER)
178dff60fe8SClément Le Goffic 		io_setbits32(iwdg_base + IWDG_ICR_OFFSET, IWDG_ICR_EWIC);
179dff60fe8SClément Le Goffic 	else
180dff60fe8SClément Le Goffic 		io_setbits32(iwdg_base + IWDG_EWCR_OFFSET, IWDG_EWCR_EWIC);
181dff60fe8SClément Le Goffic }
182dff60fe8SClément Le Goffic 
183e4b8d29aSEtienne Carriere static enum itr_return stm32_iwdg_it_handler(struct itr_handler *h)
184e4b8d29aSEtienne Carriere {
185e4b8d29aSEtienne Carriere 	unsigned int __maybe_unused cpu = get_core_pos();
186e4b8d29aSEtienne Carriere 	struct stm32_iwdg_device *iwdg = h->data;
187e4b8d29aSEtienne Carriere 	vaddr_t iwdg_base = get_base(iwdg);
188e4b8d29aSEtienne Carriere 
189e4b8d29aSEtienne Carriere 	DMSG("CPU %u IT Watchdog %#"PRIxPA, cpu, iwdg->base.pa);
190e4b8d29aSEtienne Carriere 
191e4b8d29aSEtienne Carriere 	/* Check for spurious interrupt */
192e4b8d29aSEtienne Carriere 	if (!(io_read32(iwdg_base + IWDG_SR_OFFSET) & sr_ewif_mask(iwdg)))
193e4b8d29aSEtienne Carriere 		return ITRR_NONE;
194e4b8d29aSEtienne Carriere 
195e4b8d29aSEtienne Carriere 	/*
196e4b8d29aSEtienne Carriere 	 * Writing IWDG_EWCR_EWIT triggers a watchdog refresh.
197e4b8d29aSEtienne Carriere 	 * To prevent the watchdog refresh, write-protect all the registers;
198e4b8d29aSEtienne Carriere 	 * this makes read-only all IWDG_EWCR fields except IWDG_EWCR_EWIC.
199e4b8d29aSEtienne Carriere 	 */
200e4b8d29aSEtienne Carriere 	io_write32(iwdg_base + IWDG_KR_OFFSET, IWDG_KR_WPROT_KEY);
201e4b8d29aSEtienne Carriere 
202e4b8d29aSEtienne Carriere 	/* Disable early interrupt */
203dff60fe8SClément Le Goffic 	stm32_iwdg_it_ack(iwdg);
204e4b8d29aSEtienne Carriere 
205*f6ee86ecSClément Le Goffic 	if (iwdg->nb_int > 0) {
206*f6ee86ecSClément Le Goffic 		iwdg->nb_int--;
207*f6ee86ecSClément Le Goffic 		io_write32(get_base(iwdg) + IWDG_KR_OFFSET, IWDG_KR_RELOAD_KEY);
208*f6ee86ecSClément Le Goffic 	} else {
209e4b8d29aSEtienne Carriere 		panic("Watchdog");
210*f6ee86ecSClément Le Goffic 	}
211e4b8d29aSEtienne Carriere 
212e4b8d29aSEtienne Carriere 	return ITRR_HANDLED;
213e4b8d29aSEtienne Carriere }
214e4b8d29aSEtienne Carriere DECLARE_KEEP_PAGER(stm32_iwdg_it_handler);
215e4b8d29aSEtienne Carriere 
2160bdd7f5bSEtienne Carriere static TEE_Result configure_timeout(struct stm32_iwdg_device *iwdg)
2170bdd7f5bSEtienne Carriere {
2180bdd7f5bSEtienne Carriere 	TEE_Result res = TEE_ERROR_GENERIC;
2190bdd7f5bSEtienne Carriere 	vaddr_t iwdg_base = get_base(iwdg);
2200bdd7f5bSEtienne Carriere 	uint32_t rlr_value = 0;
221e4b8d29aSEtienne Carriere 	uint32_t ewie_value = 0;
2220bdd7f5bSEtienne Carriere 
2232f9b82faSEtienne Carriere 	assert(iwdg_wdt_is_enabled(iwdg));
2240bdd7f5bSEtienne Carriere 
2250bdd7f5bSEtienne Carriere 	rlr_value = iwdg_timeout_cnt(iwdg, iwdg->timeout);
2260bdd7f5bSEtienne Carriere 	if (!rlr_value)
2270bdd7f5bSEtienne Carriere 		return TEE_ERROR_GENERIC;
2280bdd7f5bSEtienne Carriere 
229e4b8d29aSEtienne Carriere 	if (iwdg->itr_handler) {
230*f6ee86ecSClément Le Goffic 		ewie_value = iwdg_timeout_cnt(iwdg, iwdg->early_timeout);
231e4b8d29aSEtienne Carriere 		interrupt_enable(iwdg->itr_chip, iwdg->itr_num);
232e4b8d29aSEtienne Carriere 	}
233e4b8d29aSEtienne Carriere 
2340bdd7f5bSEtienne Carriere 	io_write32(iwdg_base + IWDG_KR_OFFSET, IWDG_KR_ACCESS_KEY);
2350bdd7f5bSEtienne Carriere 	io_write32(iwdg_base + IWDG_PR_OFFSET, IWDG_PR_DIV_256);
2360bdd7f5bSEtienne Carriere 	io_write32(iwdg_base + IWDG_RLR_OFFSET, rlr_value);
237e4b8d29aSEtienne Carriere 	if (ewie_value &&
238e4b8d29aSEtienne Carriere 	    !(io_read32(iwdg_base + IWDG_EWCR_OFFSET) & IWDG_EWCR_EWIE))
239e4b8d29aSEtienne Carriere 		io_write32(iwdg_base + IWDG_EWCR_OFFSET,
240e4b8d29aSEtienne Carriere 			   ewie_value | IWDG_EWCR_EWIE);
2410bdd7f5bSEtienne Carriere 
2420bdd7f5bSEtienne Carriere 	res = iwdg_wait_sync(iwdg);
2430bdd7f5bSEtienne Carriere 
244e4b8d29aSEtienne Carriere 	io_write32(iwdg_base + IWDG_KR_OFFSET, IWDG_KR_RELOAD_KEY);
245e4b8d29aSEtienne Carriere 
2460bdd7f5bSEtienne Carriere 	return res;
2470bdd7f5bSEtienne Carriere }
2480bdd7f5bSEtienne Carriere 
2490bdd7f5bSEtienne Carriere static void iwdg_start(struct stm32_iwdg_device *iwdg)
2500bdd7f5bSEtienne Carriere {
251eb47832fSAntonio Borneo 	TEE_Result res = TEE_ERROR_GENERIC;
252eb47832fSAntonio Borneo 
253eb47832fSAntonio Borneo 	res = tee_time_get_sys_time(&iwdg->last_refresh);
254eb47832fSAntonio Borneo 	if (res)
255eb47832fSAntonio Borneo 		panic();
256eb47832fSAntonio Borneo 
2570bdd7f5bSEtienne Carriere 	io_write32(get_base(iwdg) + IWDG_KR_OFFSET, IWDG_KR_START_KEY);
2580bdd7f5bSEtienne Carriere 
2592f9b82faSEtienne Carriere 	iwdg_wdt_set_enabled(iwdg);
2600bdd7f5bSEtienne Carriere }
2610bdd7f5bSEtienne Carriere 
2620bdd7f5bSEtienne Carriere static void iwdg_refresh(struct stm32_iwdg_device *iwdg)
2630bdd7f5bSEtienne Carriere {
264eb47832fSAntonio Borneo 	TEE_Result res = TEE_ERROR_GENERIC;
265eb47832fSAntonio Borneo 
266eb47832fSAntonio Borneo 	res = tee_time_get_sys_time(&iwdg->last_refresh);
267eb47832fSAntonio Borneo 	if (res)
268eb47832fSAntonio Borneo 		panic();
269eb47832fSAntonio Borneo 
2700bdd7f5bSEtienne Carriere 	io_write32(get_base(iwdg) + IWDG_KR_OFFSET, IWDG_KR_RELOAD_KEY);
2710bdd7f5bSEtienne Carriere }
2720bdd7f5bSEtienne Carriere 
2730bdd7f5bSEtienne Carriere /* Operators for watchdog OP-TEE interface */
2740bdd7f5bSEtienne Carriere static struct stm32_iwdg_device *wdt_chip_to_iwdg(struct wdt_chip *chip)
2750bdd7f5bSEtienne Carriere {
2760bdd7f5bSEtienne Carriere 	return container_of(chip, struct stm32_iwdg_device, wdt_chip);
2770bdd7f5bSEtienne Carriere }
2780bdd7f5bSEtienne Carriere 
279fc9063ddSEtienne Carriere static TEE_Result iwdg_wdt_init(struct wdt_chip *chip,
280fc9063ddSEtienne Carriere 				unsigned long *min_timeout,
281fc9063ddSEtienne Carriere 				unsigned long *max_timeout)
282fc9063ddSEtienne Carriere {
283fc9063ddSEtienne Carriere 	struct stm32_iwdg_device *iwdg = wdt_chip_to_iwdg(chip);
284fc9063ddSEtienne Carriere 	unsigned long rate = clk_get_rate(iwdg->clk_lsi);
285fc9063ddSEtienne Carriere 
286fc9063ddSEtienne Carriere 	if (!rate)
287fc9063ddSEtienne Carriere 		return TEE_ERROR_GENERIC;
288fc9063ddSEtienne Carriere 
289fc9063ddSEtienne Carriere 	/* Be safe and expect any counter to be above 2 */
290fc9063ddSEtienne Carriere 	*min_timeout = 3 * IWDG_PRESCALER_256 / rate;
291*f6ee86ecSClément Le Goffic 	*max_timeout = INT32_MAX;
292fc9063ddSEtienne Carriere 
293fc9063ddSEtienne Carriere 	return TEE_SUCCESS;
294fc9063ddSEtienne Carriere }
295fc9063ddSEtienne Carriere 
2960bdd7f5bSEtienne Carriere static void iwdg_wdt_start(struct wdt_chip *chip)
2970bdd7f5bSEtienne Carriere {
2980bdd7f5bSEtienne Carriere 	struct stm32_iwdg_device *iwdg = wdt_chip_to_iwdg(chip);
2990bdd7f5bSEtienne Carriere 
3000bdd7f5bSEtienne Carriere 	iwdg_start(iwdg);
301dff60fe8SClément Le Goffic 	if (iwdg->reset && iwdg->itr_handler)
302dff60fe8SClément Le Goffic 		stm32_iwdg_it_ack(iwdg);
3030bdd7f5bSEtienne Carriere 
3040bdd7f5bSEtienne Carriere 	if (configure_timeout(iwdg))
3050bdd7f5bSEtienne Carriere 		panic();
306dff60fe8SClément Le Goffic 
307dff60fe8SClément Le Goffic 	if (iwdg->reset)
308dff60fe8SClément Le Goffic 		if (rstctrl_assert(iwdg->reset))
309dff60fe8SClément Le Goffic 			panic();
310dff60fe8SClément Le Goffic }
311dff60fe8SClément Le Goffic 
312dff60fe8SClément Le Goffic static void iwdg_wdt_stop(struct wdt_chip *chip)
313dff60fe8SClément Le Goffic {
314dff60fe8SClément Le Goffic 	struct stm32_iwdg_device *iwdg = wdt_chip_to_iwdg(chip);
315dff60fe8SClément Le Goffic 
316dff60fe8SClément Le Goffic 	if (iwdg->reset) {
317dff60fe8SClément Le Goffic 		if (rstctrl_deassert(iwdg->reset))
318dff60fe8SClément Le Goffic 			panic();
319dff60fe8SClément Le Goffic 		if (iwdg->itr_handler)
320dff60fe8SClément Le Goffic 			interrupt_disable(iwdg->itr_chip, iwdg->itr_num);
321dff60fe8SClément Le Goffic 	}
3220bdd7f5bSEtienne Carriere }
3230bdd7f5bSEtienne Carriere 
3240bdd7f5bSEtienne Carriere static void iwdg_wdt_refresh(struct wdt_chip *chip)
3250bdd7f5bSEtienne Carriere {
3260bdd7f5bSEtienne Carriere 	struct stm32_iwdg_device *iwdg = wdt_chip_to_iwdg(chip);
3270bdd7f5bSEtienne Carriere 
328*f6ee86ecSClément Le Goffic 	iwdg->nb_int = iwdg->saved_nb_int;
3290bdd7f5bSEtienne Carriere 	iwdg_refresh(iwdg);
3300bdd7f5bSEtienne Carriere }
3310bdd7f5bSEtienne Carriere 
332*f6ee86ecSClément Le Goffic static void stm32_iwdg_handle_timeouts(struct stm32_iwdg_device *iwdg,
333*f6ee86ecSClément Le Goffic 				       unsigned long timeout_sec)
334*f6ee86ecSClément Le Goffic {
335*f6ee86ecSClément Le Goffic 	unsigned long interval = 0;
336*f6ee86ecSClément Le Goffic 	unsigned long rate = 0;
337*f6ee86ecSClément Le Goffic 	unsigned long n = 0;
338*f6ee86ecSClément Le Goffic 	long w = 0;
339*f6ee86ecSClément Le Goffic 
340*f6ee86ecSClément Le Goffic 	rate = clk_get_rate(iwdg->clk_lsi);
341*f6ee86ecSClément Le Goffic 	iwdg->max_hw_timeout = (IWDG_CNT_MASK + 1) * IWDG_PRESCALER_256 / rate;
342*f6ee86ecSClément Le Goffic 
343*f6ee86ecSClément Le Goffic 	if (timeout_sec > iwdg->max_hw_timeout) {
344*f6ee86ecSClément Le Goffic 		IMSG("Timeout exceeds hardware capability, approximate it");
345*f6ee86ecSClément Le Goffic 		interval = iwdg->max_hw_timeout - IWDG_ETIMEOUT_SEC;
346*f6ee86ecSClément Le Goffic 		n = (timeout_sec - IWDG_ETIMEOUT_SEC) / interval;
347*f6ee86ecSClément Le Goffic 		w = ((timeout_sec - IWDG_ETIMEOUT_SEC) / (n + 1)) +
348*f6ee86ecSClément Le Goffic 		IWDG_ETIMEOUT_SEC;
349*f6ee86ecSClément Le Goffic 		iwdg->timeout = w;
350*f6ee86ecSClément Le Goffic 		iwdg->early_timeout = IWDG_ETIMEOUT_SEC;
351*f6ee86ecSClément Le Goffic 	} else {
352*f6ee86ecSClément Le Goffic 		iwdg->timeout = timeout_sec;
353*f6ee86ecSClément Le Goffic 		if (iwdg->timeout >= 2 * IWDG_ETIMEOUT_SEC)
354*f6ee86ecSClément Le Goffic 			iwdg->early_timeout = IWDG_ETIMEOUT_SEC;
355*f6ee86ecSClément Le Goffic 		else
356*f6ee86ecSClément Le Goffic 			iwdg->early_timeout = iwdg->timeout / 4;
357*f6ee86ecSClément Le Goffic 	}
358*f6ee86ecSClément Le Goffic 
359*f6ee86ecSClément Le Goffic 	if (!iwdg->early_timeout)
360*f6ee86ecSClément Le Goffic 		iwdg->early_timeout = 1;
361*f6ee86ecSClément Le Goffic 
362*f6ee86ecSClément Le Goffic 	iwdg->saved_nb_int = n;
363*f6ee86ecSClément Le Goffic 	iwdg->nb_int = n;
364*f6ee86ecSClément Le Goffic }
365*f6ee86ecSClément Le Goffic 
3660bdd7f5bSEtienne Carriere static TEE_Result iwdg_wdt_set_timeout(struct wdt_chip *chip,
3670bdd7f5bSEtienne Carriere 				       unsigned long timeout)
3680bdd7f5bSEtienne Carriere {
3690bdd7f5bSEtienne Carriere 	struct stm32_iwdg_device *iwdg = wdt_chip_to_iwdg(chip);
3700bdd7f5bSEtienne Carriere 
3712f9b82faSEtienne Carriere 	if (iwdg_wdt_is_enabled(iwdg)) {
3720bdd7f5bSEtienne Carriere 		TEE_Result res = TEE_ERROR_GENERIC;
3730bdd7f5bSEtienne Carriere 
374*f6ee86ecSClément Le Goffic 		stm32_iwdg_handle_timeouts(iwdg, timeout);
375*f6ee86ecSClément Le Goffic 
3760bdd7f5bSEtienne Carriere 		res = configure_timeout(iwdg);
3770bdd7f5bSEtienne Carriere 		if (res)
3780bdd7f5bSEtienne Carriere 			return res;
3790bdd7f5bSEtienne Carriere 	}
3800bdd7f5bSEtienne Carriere 
3810bdd7f5bSEtienne Carriere 	return TEE_SUCCESS;
3820bdd7f5bSEtienne Carriere }
3830bdd7f5bSEtienne Carriere 
384eb47832fSAntonio Borneo static TEE_Result iwdg_wdt_get_timeleft(struct wdt_chip *chip, bool *is_started,
385eb47832fSAntonio Borneo 					unsigned long *timeleft)
386eb47832fSAntonio Borneo {
387eb47832fSAntonio Borneo 	struct stm32_iwdg_device *iwdg = wdt_chip_to_iwdg(chip);
388eb47832fSAntonio Borneo 	TEE_Result res = TEE_ERROR_GENERIC;
389eb47832fSAntonio Borneo 	TEE_Time time = { };
390eb47832fSAntonio Borneo 	TEE_Time now = { };
391eb47832fSAntonio Borneo 
392eb47832fSAntonio Borneo 	*is_started = iwdg_wdt_is_enabled(iwdg);
393eb47832fSAntonio Borneo 
394eb47832fSAntonio Borneo 	if (!*is_started)
395eb47832fSAntonio Borneo 		return TEE_SUCCESS;
396eb47832fSAntonio Borneo 
397eb47832fSAntonio Borneo 	res = tee_time_get_sys_time(&now);
398eb47832fSAntonio Borneo 	if (res)
399eb47832fSAntonio Borneo 		panic();
400eb47832fSAntonio Borneo 
401*f6ee86ecSClément Le Goffic 	time.seconds =
402*f6ee86ecSClément Le Goffic 		(iwdg->timeout - iwdg->early_timeout) * iwdg->saved_nb_int
403*f6ee86ecSClément Le Goffic 		+ iwdg->early_timeout;
404eb47832fSAntonio Borneo 	TEE_TIME_ADD(iwdg->last_refresh, time, time);
405eb47832fSAntonio Borneo 	if (TEE_TIME_LE(time, now)) {
406eb47832fSAntonio Borneo 		*timeleft = 0;
407eb47832fSAntonio Borneo 	} else {
408eb47832fSAntonio Borneo 		TEE_TIME_SUB(time, now, time);
409eb47832fSAntonio Borneo 		*timeleft = time.seconds;
410eb47832fSAntonio Borneo 	}
411eb47832fSAntonio Borneo 
412eb47832fSAntonio Borneo 	return TEE_SUCCESS;
413eb47832fSAntonio Borneo }
414eb47832fSAntonio Borneo 
4150bdd7f5bSEtienne Carriere static const struct wdt_ops stm32_iwdg_ops = {
416fc9063ddSEtienne Carriere 	.init = iwdg_wdt_init,
4170bdd7f5bSEtienne Carriere 	.start = iwdg_wdt_start,
418dff60fe8SClément Le Goffic 	.stop = iwdg_wdt_stop,
4190bdd7f5bSEtienne Carriere 	.ping = iwdg_wdt_refresh,
4200bdd7f5bSEtienne Carriere 	.set_timeout = iwdg_wdt_set_timeout,
421eb47832fSAntonio Borneo 	.get_timeleft = iwdg_wdt_get_timeleft,
4220bdd7f5bSEtienne Carriere };
4230bdd7f5bSEtienne Carriere DECLARE_KEEP_PAGER(stm32_iwdg_ops);
4240bdd7f5bSEtienne Carriere 
4250bdd7f5bSEtienne Carriere /* Driver initialization */
4260bdd7f5bSEtienne Carriere static TEE_Result stm32_iwdg_parse_fdt(struct stm32_iwdg_device *iwdg,
4270bdd7f5bSEtienne Carriere 				       const void *fdt, int node)
4280bdd7f5bSEtienne Carriere {
4290bdd7f5bSEtienne Carriere 	TEE_Result res = TEE_ERROR_GENERIC;
4300bdd7f5bSEtienne Carriere 	struct dt_node_info dt_info = { };
4310bdd7f5bSEtienne Carriere 	const fdt32_t *cuint = NULL;
4320bdd7f5bSEtienne Carriere 
433f354a5d8SGatien Chevallier 	fdt_fill_device_info(fdt, &dt_info, node);
4340bdd7f5bSEtienne Carriere 
4350bdd7f5bSEtienne Carriere 	if (dt_info.reg == DT_INFO_INVALID_REG ||
4360bdd7f5bSEtienne Carriere 	    dt_info.reg_size == DT_INFO_INVALID_REG_SIZE)
4370bdd7f5bSEtienne Carriere 		panic();
4380bdd7f5bSEtienne Carriere 
439b2f17e87SEtienne Carriere 	res = clk_dt_get_by_name(fdt, node, "pclk", &iwdg->clk_pclk);
4400bdd7f5bSEtienne Carriere 	if (res)
4410bdd7f5bSEtienne Carriere 		return res;
4420bdd7f5bSEtienne Carriere 
4430bdd7f5bSEtienne Carriere 	res = clk_dt_get_by_name(fdt, node, "lsi", &iwdg->clk_lsi);
4440bdd7f5bSEtienne Carriere 	if (res)
4450bdd7f5bSEtienne Carriere 		return res;
4460bdd7f5bSEtienne Carriere 
447e4b8d29aSEtienne Carriere 	res = interrupt_dt_get(fdt, node, &iwdg->itr_chip, &iwdg->itr_num);
448e4b8d29aSEtienne Carriere 	if (res && res != TEE_ERROR_ITEM_NOT_FOUND)
449e4b8d29aSEtienne Carriere 		return res;
450e4b8d29aSEtienne Carriere 	if (!res) {
451e4b8d29aSEtienne Carriere 		res = interrupt_create_handler(iwdg->itr_chip, iwdg->itr_num,
452e4b8d29aSEtienne Carriere 					       stm32_iwdg_it_handler, iwdg, 0,
453e4b8d29aSEtienne Carriere 					       &iwdg->itr_handler);
454e4b8d29aSEtienne Carriere 		if (res)
455e4b8d29aSEtienne Carriere 			return res;
456e4b8d29aSEtienne Carriere 	}
457e4b8d29aSEtienne Carriere 
458dff60fe8SClément Le Goffic 	res = rstctrl_dt_get_by_index(fdt, node, 0, &iwdg->reset);
459dff60fe8SClément Le Goffic 	if (res && res != TEE_ERROR_ITEM_NOT_FOUND)
460dff60fe8SClément Le Goffic 		goto err_itr;
461dff60fe8SClément Le Goffic 
4620bdd7f5bSEtienne Carriere 	/* Get IOMEM address */
4630bdd7f5bSEtienne Carriere 	iwdg->base.pa = dt_info.reg;
4640bdd7f5bSEtienne Carriere 	io_pa_or_va_secure(&iwdg->base, dt_info.reg_size);
4650bdd7f5bSEtienne Carriere 	assert(iwdg->base.va);
4660bdd7f5bSEtienne Carriere 
4670bdd7f5bSEtienne Carriere 	/* Get and check timeout value */
4680bdd7f5bSEtienne Carriere 	cuint = fdt_getprop(fdt, node, "timeout-sec", NULL);
469e4b8d29aSEtienne Carriere 	if (!cuint) {
470e4b8d29aSEtienne Carriere 		res = TEE_ERROR_BAD_PARAMETERS;
471e4b8d29aSEtienne Carriere 		goto err_itr;
472e4b8d29aSEtienne Carriere 	}
4730bdd7f5bSEtienne Carriere 
4740bdd7f5bSEtienne Carriere 	iwdg->timeout = (int)fdt32_to_cpu(*cuint);
475e4b8d29aSEtienne Carriere 	if (!iwdg->timeout) {
476e4b8d29aSEtienne Carriere 		res = TEE_ERROR_BAD_PARAMETERS;
477e4b8d29aSEtienne Carriere 		goto err_itr;
478e4b8d29aSEtienne Carriere 	}
4790bdd7f5bSEtienne Carriere 
4800bdd7f5bSEtienne Carriere 	return TEE_SUCCESS;
481e4b8d29aSEtienne Carriere 
482e4b8d29aSEtienne Carriere err_itr:
483e4b8d29aSEtienne Carriere 	interrupt_remove_free_handler(iwdg->itr_handler);
484e4b8d29aSEtienne Carriere 
485e4b8d29aSEtienne Carriere 	return res;
4860bdd7f5bSEtienne Carriere }
4870bdd7f5bSEtienne Carriere 
4883d5793d2SAntonio Borneo static void iwdg_wdt_get_version_and_status(struct stm32_iwdg_device *iwdg)
4893d5793d2SAntonio Borneo {
4903d5793d2SAntonio Borneo 	vaddr_t iwdg_base = get_base(iwdg);
4913d5793d2SAntonio Borneo 	uint32_t rlr_value = 0;
4923d5793d2SAntonio Borneo 
4933d5793d2SAntonio Borneo 	iwdg->hw_version = io_read32(iwdg_base + IWDG_VERR_OFFSET) &
4943d5793d2SAntonio Borneo 			   IWDG_VERR_REV_MASK;
4953d5793d2SAntonio Borneo 
4963d5793d2SAntonio Borneo 	/* Test if watchdog is already running */
4973d5793d2SAntonio Borneo 	if (iwdg->hw_version >= IWDG_ONF_MIN_VER) {
4983d5793d2SAntonio Borneo 		if (io_read32(iwdg_base + IWDG_SR_OFFSET) & IWDG_SR_ONF)
4993d5793d2SAntonio Borneo 			iwdg_wdt_set_enabled(iwdg);
5003d5793d2SAntonio Borneo 	} else {
5013d5793d2SAntonio Borneo 		/*
5023d5793d2SAntonio Borneo 		 * Workaround for old versions without IWDG_SR_ONF bit:
5033d5793d2SAntonio Borneo 		 * - write in IWDG_RLR_OFFSET
5043d5793d2SAntonio Borneo 		 * - wait for sync
5053d5793d2SAntonio Borneo 		 * - if sync succeeds, then iwdg is running
5063d5793d2SAntonio Borneo 		 */
5073d5793d2SAntonio Borneo 		io_write32(iwdg_base + IWDG_KR_OFFSET, IWDG_KR_ACCESS_KEY);
5083d5793d2SAntonio Borneo 
5093d5793d2SAntonio Borneo 		rlr_value = io_read32(iwdg_base + IWDG_RLR_OFFSET);
5103d5793d2SAntonio Borneo 		io_write32(iwdg_base + IWDG_RLR_OFFSET, rlr_value);
5113d5793d2SAntonio Borneo 
5123d5793d2SAntonio Borneo 		if (!iwdg_wait_sync(iwdg))
5133d5793d2SAntonio Borneo 			iwdg_wdt_set_enabled(iwdg);
5143d5793d2SAntonio Borneo 
5153d5793d2SAntonio Borneo 		io_write32(iwdg_base + IWDG_KR_OFFSET, IWDG_KR_WPROT_KEY);
5163d5793d2SAntonio Borneo 	}
5173d5793d2SAntonio Borneo 
5183d5793d2SAntonio Borneo 	DMSG("Watchdog is %sabled", iwdg_wdt_is_enabled(iwdg) ? "en" : "dis");
5193d5793d2SAntonio Borneo }
5203d5793d2SAntonio Borneo 
5210bdd7f5bSEtienne Carriere static TEE_Result stm32_iwdg_setup(struct stm32_iwdg_device *iwdg,
5220bdd7f5bSEtienne Carriere 				   const void *fdt, int node)
5230bdd7f5bSEtienne Carriere {
5240bdd7f5bSEtienne Carriere 	TEE_Result res = TEE_SUCCESS;
5250bdd7f5bSEtienne Carriere 
5260bdd7f5bSEtienne Carriere 	res = stm32_iwdg_parse_fdt(iwdg, fdt, node);
5270bdd7f5bSEtienne Carriere 	if (res)
5280bdd7f5bSEtienne Carriere 		return res;
5290bdd7f5bSEtienne Carriere 
53036d2a417SEtienne Carriere 	/* Enable watchdog source and bus clocks once for all */
5315c7ebea7SAntonio Borneo 	if (clk_enable(iwdg->clk_lsi))
5325c7ebea7SAntonio Borneo 		panic();
5335c7ebea7SAntonio Borneo 
5345c7ebea7SAntonio Borneo 	if (clk_enable(iwdg->clk_pclk))
5355c7ebea7SAntonio Borneo 		panic();
5360bdd7f5bSEtienne Carriere 
5373d5793d2SAntonio Borneo 	iwdg_wdt_get_version_and_status(iwdg);
5380bdd7f5bSEtienne Carriere 
539*f6ee86ecSClément Le Goffic 	res = iwdg_wdt_set_timeout(&iwdg->wdt_chip, iwdg->timeout);
5400bdd7f5bSEtienne Carriere 	if (res)
5415c7ebea7SAntonio Borneo 		panic();
5420bdd7f5bSEtienne Carriere 
543*f6ee86ecSClément Le Goffic 	if (iwdg_wdt_is_enabled(iwdg))
544*f6ee86ecSClément Le Goffic 		iwdg_wdt_refresh(&iwdg->wdt_chip);
5450bdd7f5bSEtienne Carriere 
5460bdd7f5bSEtienne Carriere 	return TEE_SUCCESS;
5470bdd7f5bSEtienne Carriere }
5480bdd7f5bSEtienne Carriere 
549235baec9SEtienne Carriere static TEE_Result stm32_iwdg_pm(enum pm_op op, unsigned int pm_hint __unused,
550235baec9SEtienne Carriere 				const struct pm_callback_handle *pm_handle)
551235baec9SEtienne Carriere {
552235baec9SEtienne Carriere 	struct stm32_iwdg_device *iwdg = PM_CALLBACK_GET_HANDLE(pm_handle);
553235baec9SEtienne Carriere 
554235baec9SEtienne Carriere 	if (op == PM_OP_RESUME) {
555235baec9SEtienne Carriere 		clk_enable(iwdg->clk_lsi);
556235baec9SEtienne Carriere 		clk_enable(iwdg->clk_pclk);
557235baec9SEtienne Carriere 	} else {
558235baec9SEtienne Carriere 		clk_disable(iwdg->clk_lsi);
559235baec9SEtienne Carriere 		clk_disable(iwdg->clk_pclk);
560235baec9SEtienne Carriere 	}
561235baec9SEtienne Carriere 
562235baec9SEtienne Carriere 	return TEE_SUCCESS;
563235baec9SEtienne Carriere }
564235baec9SEtienne Carriere DECLARE_KEEP_PAGER(stm32_iwdg_pm);
565235baec9SEtienne Carriere 
5660bdd7f5bSEtienne Carriere static TEE_Result stm32_iwdg_probe(const void *fdt, int node,
5670bdd7f5bSEtienne Carriere 				   const void *compat_data __unused)
5680bdd7f5bSEtienne Carriere {
5690bdd7f5bSEtienne Carriere 	struct stm32_iwdg_device *iwdg = NULL;
5700bdd7f5bSEtienne Carriere 	TEE_Result res = TEE_SUCCESS;
5710bdd7f5bSEtienne Carriere 
5720bdd7f5bSEtienne Carriere 	iwdg = calloc(1, sizeof(*iwdg));
5730bdd7f5bSEtienne Carriere 	if (!iwdg)
5740bdd7f5bSEtienne Carriere 		return TEE_ERROR_OUT_OF_MEMORY;
5750bdd7f5bSEtienne Carriere 
5760bdd7f5bSEtienne Carriere 	res = stm32_iwdg_setup(iwdg, fdt, node);
5770bdd7f5bSEtienne Carriere 	if (res)
578235baec9SEtienne Carriere 		goto out_free;
5790bdd7f5bSEtienne Carriere 
580a096e2d9SEtienne Carriere 	iwdg->wdt_chip.ops = &stm32_iwdg_ops;
581a096e2d9SEtienne Carriere 
582235baec9SEtienne Carriere 	register_pm_core_service_cb(stm32_iwdg_pm, iwdg, "stm32-iwdg");
583a096e2d9SEtienne Carriere 
584235baec9SEtienne Carriere 	res = watchdog_register(&iwdg->wdt_chip);
5850bdd7f5bSEtienne Carriere 	if (res)
586235baec9SEtienne Carriere 		goto out_pm;
587235baec9SEtienne Carriere 
588235baec9SEtienne Carriere 	return TEE_SUCCESS;
589235baec9SEtienne Carriere 
590235baec9SEtienne Carriere out_pm:
591235baec9SEtienne Carriere 	unregister_pm_core_service_cb(stm32_iwdg_pm, iwdg);
592235baec9SEtienne Carriere out_free:
5930bdd7f5bSEtienne Carriere 	free(iwdg);
594a096e2d9SEtienne Carriere 
5950bdd7f5bSEtienne Carriere 	return res;
5960bdd7f5bSEtienne Carriere }
5970bdd7f5bSEtienne Carriere 
5980bdd7f5bSEtienne Carriere static const struct dt_device_match stm32_iwdg_match_table[] = {
5990bdd7f5bSEtienne Carriere 	{ .compatible = "st,stm32mp1-iwdg" },
6000bdd7f5bSEtienne Carriere 	{ }
6010bdd7f5bSEtienne Carriere };
6020bdd7f5bSEtienne Carriere 
6030bdd7f5bSEtienne Carriere DEFINE_DT_DRIVER(stm32_iwdg_dt_driver) = {
6040bdd7f5bSEtienne Carriere 	.name = "stm32-iwdg",
6050bdd7f5bSEtienne Carriere 	.match_table = stm32_iwdg_match_table,
6060bdd7f5bSEtienne Carriere 	.probe = stm32_iwdg_probe,
6070bdd7f5bSEtienne Carriere };
608