xref: /optee_os/core/drivers/stm32_iwdg.c (revision eb47832f2c177f0c4f6fa3c595d49a72918f46d0)
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>
21*eb47832fSAntonio 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)
330bdd7f5bSEtienne Carriere 
340bdd7f5bSEtienne Carriere /* IWDG registers offsets */
350bdd7f5bSEtienne Carriere #define IWDG_KR_OFFSET		U(0x00)
360bdd7f5bSEtienne Carriere #define IWDG_PR_OFFSET		U(0x04)
370bdd7f5bSEtienne Carriere #define IWDG_RLR_OFFSET		U(0x08)
380bdd7f5bSEtienne Carriere #define IWDG_SR_OFFSET		U(0x0C)
390bdd7f5bSEtienne Carriere #define IWDG_EWCR_OFFSET	U(0x14)
403d5793d2SAntonio Borneo #define IWDG_VERR_OFFSET	U(0x3F4)
410bdd7f5bSEtienne Carriere 
423d5793d2SAntonio Borneo #define IWDG_KR_WPROT_KEY	U(0x0000)
430bdd7f5bSEtienne Carriere #define IWDG_KR_ACCESS_KEY	U(0x5555)
440bdd7f5bSEtienne Carriere #define IWDG_KR_RELOAD_KEY	U(0xAAAA)
450bdd7f5bSEtienne Carriere #define IWDG_KR_START_KEY	U(0xCCCC)
460bdd7f5bSEtienne Carriere 
470bdd7f5bSEtienne Carriere /* Use a fixed prescaler divider of 256 */
480bdd7f5bSEtienne Carriere #define IWDG_PRESCALER_256	U(256)
490bdd7f5bSEtienne Carriere #define IWDG_PR_DIV_256		U(0x06)
500bdd7f5bSEtienne Carriere #define IWDG_PR_DIV_MASK	GENMASK_32(3, 0)
510bdd7f5bSEtienne Carriere 
520bdd7f5bSEtienne Carriere #define IWDG_SR_PVU		BIT(0)
530bdd7f5bSEtienne Carriere #define IWDG_SR_RVU		BIT(1)
540bdd7f5bSEtienne Carriere #define IWDG_SR_WVU		BIT(2)
550bdd7f5bSEtienne Carriere #define IWDG_SR_EWU		BIT(3)
560bdd7f5bSEtienne Carriere #define IWDG_SR_UPDATE_MASK	(IWDG_SR_PVU | IWDG_SR_RVU | IWDG_SR_WVU | \
570bdd7f5bSEtienne Carriere 				 IWDG_SR_EWU)
583d5793d2SAntonio Borneo #define IWDG_SR_ONF		BIT(8)
590bdd7f5bSEtienne Carriere 
600bdd7f5bSEtienne Carriere #define IWDG_EWCR_EWIE		BIT(15)
610bdd7f5bSEtienne Carriere #define IWDG_EWCR_EWIC		BIT(14)
620bdd7f5bSEtienne Carriere 
633d5793d2SAntonio Borneo #define IWDG_VERR_REV_MASK	GENMASK_32(7, 0)
643d5793d2SAntonio Borneo 
650bdd7f5bSEtienne Carriere /*
660bdd7f5bSEtienne Carriere  * Values for struct stm32_iwdg_device::flags
670bdd7f5bSEtienne Carriere  * IWDG_FLAGS_ENABLED			Watchdog has been enabled
680bdd7f5bSEtienne Carriere  */
69c501c3e1SLionel Debieve #define IWDG_FLAGS_ENABLED			BIT(0)
700bdd7f5bSEtienne Carriere 
710bdd7f5bSEtienne Carriere /*
720bdd7f5bSEtienne Carriere  * IWDG watch instance data
730bdd7f5bSEtienne Carriere  * @base - IWDG interface IOMEM base address
74b2f17e87SEtienne Carriere  * @clk_pclk - Bus clock
750bdd7f5bSEtienne Carriere  * @clk_lsi - IWDG source clock
760bdd7f5bSEtienne Carriere  * @flags - Property flags for the IWDG instance
770bdd7f5bSEtienne Carriere  * @timeout - Watchdog elaspure timeout
783d5793d2SAntonio Borneo  * @hw_version - Watchdog HW version
79*eb47832fSAntonio Borneo  * @last_refresh - Time of last watchdog refresh
800bdd7f5bSEtienne Carriere  * @wdt_chip - Wathcdog chip instance
810bdd7f5bSEtienne Carriere  */
820bdd7f5bSEtienne Carriere struct stm32_iwdg_device {
830bdd7f5bSEtienne Carriere 	struct io_pa_va base;
84b2f17e87SEtienne Carriere 	struct clk *clk_pclk;
850bdd7f5bSEtienne Carriere 	struct clk *clk_lsi;
860bdd7f5bSEtienne Carriere 	uint32_t flags;
870bdd7f5bSEtienne Carriere 	unsigned long timeout;
883d5793d2SAntonio Borneo 	unsigned int hw_version;
89*eb47832fSAntonio Borneo 	TEE_Time last_refresh;
900bdd7f5bSEtienne Carriere 	struct wdt_chip wdt_chip;
910bdd7f5bSEtienne Carriere };
920bdd7f5bSEtienne Carriere 
930bdd7f5bSEtienne Carriere static vaddr_t get_base(struct stm32_iwdg_device *iwdg)
940bdd7f5bSEtienne Carriere {
950bdd7f5bSEtienne Carriere 	return io_pa_or_va(&iwdg->base, 1);
960bdd7f5bSEtienne Carriere }
970bdd7f5bSEtienne Carriere 
982f9b82faSEtienne Carriere static void iwdg_wdt_set_enabled(struct stm32_iwdg_device *iwdg)
992f9b82faSEtienne Carriere {
1002f9b82faSEtienne Carriere 	iwdg->flags |= IWDG_FLAGS_ENABLED;
1012f9b82faSEtienne Carriere }
1022f9b82faSEtienne Carriere 
1032f9b82faSEtienne Carriere static bool iwdg_wdt_is_enabled(struct stm32_iwdg_device *iwdg)
1040bdd7f5bSEtienne Carriere {
1050bdd7f5bSEtienne Carriere 	return iwdg->flags & IWDG_FLAGS_ENABLED;
1060bdd7f5bSEtienne Carriere }
1070bdd7f5bSEtienne Carriere 
1080bdd7f5bSEtienne Carriere /* Return counter value to related to input timeout in seconds, or 0 on error */
1090bdd7f5bSEtienne Carriere static uint32_t iwdg_timeout_cnt(struct stm32_iwdg_device *iwdg,
1100bdd7f5bSEtienne Carriere 				 unsigned long to_sec)
1110bdd7f5bSEtienne Carriere {
1120bdd7f5bSEtienne Carriere 	uint64_t reload = (uint64_t)to_sec * clk_get_rate(iwdg->clk_lsi);
1130bdd7f5bSEtienne Carriere 	uint64_t cnt = (reload / IWDG_PRESCALER_256) - 1;
1140bdd7f5bSEtienne Carriere 
1150bdd7f5bSEtienne Carriere 	/* Be safe and expect any counter to be above 2 */
1160bdd7f5bSEtienne Carriere 	if (cnt > IWDG_CNT_MASK || cnt < 3)
1170bdd7f5bSEtienne Carriere 		return 0;
1180bdd7f5bSEtienne Carriere 
1190bdd7f5bSEtienne Carriere 	return cnt;
1200bdd7f5bSEtienne Carriere }
1210bdd7f5bSEtienne Carriere 
1220bdd7f5bSEtienne Carriere /* Wait IWDG programming completes */
1230bdd7f5bSEtienne Carriere static TEE_Result iwdg_wait_sync(struct stm32_iwdg_device *iwdg)
1240bdd7f5bSEtienne Carriere {
1250bdd7f5bSEtienne Carriere 	uint64_t timeout_ref = timeout_init_us(IWDG_TIMEOUT_US);
1260bdd7f5bSEtienne Carriere 	vaddr_t iwdg_base = get_base(iwdg);
1270bdd7f5bSEtienne Carriere 
1280bdd7f5bSEtienne Carriere 	while (io_read32(iwdg_base + IWDG_SR_OFFSET) & IWDG_SR_UPDATE_MASK)
1290bdd7f5bSEtienne Carriere 		if (timeout_elapsed(timeout_ref))
1300bdd7f5bSEtienne Carriere 			break;
1310bdd7f5bSEtienne Carriere 
132077bbb8aSEtienne Carriere 	if (io_read32(iwdg_base + IWDG_SR_OFFSET) & IWDG_SR_UPDATE_MASK)
1330bdd7f5bSEtienne Carriere 		return TEE_ERROR_GENERIC;
1340bdd7f5bSEtienne Carriere 
1350bdd7f5bSEtienne Carriere 	return TEE_SUCCESS;
1360bdd7f5bSEtienne Carriere }
1370bdd7f5bSEtienne Carriere 
1380bdd7f5bSEtienne Carriere static TEE_Result configure_timeout(struct stm32_iwdg_device *iwdg)
1390bdd7f5bSEtienne Carriere {
1400bdd7f5bSEtienne Carriere 	TEE_Result res = TEE_ERROR_GENERIC;
1410bdd7f5bSEtienne Carriere 	vaddr_t iwdg_base = get_base(iwdg);
1420bdd7f5bSEtienne Carriere 	uint32_t rlr_value = 0;
1430bdd7f5bSEtienne Carriere 
1442f9b82faSEtienne Carriere 	assert(iwdg_wdt_is_enabled(iwdg));
1450bdd7f5bSEtienne Carriere 
1460bdd7f5bSEtienne Carriere 	rlr_value = iwdg_timeout_cnt(iwdg, iwdg->timeout);
1470bdd7f5bSEtienne Carriere 	if (!rlr_value)
1480bdd7f5bSEtienne Carriere 		return TEE_ERROR_GENERIC;
1490bdd7f5bSEtienne Carriere 
1500bdd7f5bSEtienne Carriere 	io_write32(iwdg_base + IWDG_KR_OFFSET, IWDG_KR_ACCESS_KEY);
1510bdd7f5bSEtienne Carriere 	io_write32(iwdg_base + IWDG_PR_OFFSET, IWDG_PR_DIV_256);
1520bdd7f5bSEtienne Carriere 	io_write32(iwdg_base + IWDG_RLR_OFFSET, rlr_value);
1530bdd7f5bSEtienne Carriere 	io_write32(iwdg_base + IWDG_KR_OFFSET, IWDG_KR_RELOAD_KEY);
1540bdd7f5bSEtienne Carriere 
1550bdd7f5bSEtienne Carriere 	res = iwdg_wait_sync(iwdg);
1560bdd7f5bSEtienne Carriere 
1570bdd7f5bSEtienne Carriere 	return res;
1580bdd7f5bSEtienne Carriere }
1590bdd7f5bSEtienne Carriere 
1600bdd7f5bSEtienne Carriere static void iwdg_start(struct stm32_iwdg_device *iwdg)
1610bdd7f5bSEtienne Carriere {
162*eb47832fSAntonio Borneo 	TEE_Result res = TEE_ERROR_GENERIC;
163*eb47832fSAntonio Borneo 
164*eb47832fSAntonio Borneo 	res = tee_time_get_sys_time(&iwdg->last_refresh);
165*eb47832fSAntonio Borneo 	if (res)
166*eb47832fSAntonio Borneo 		panic();
167*eb47832fSAntonio Borneo 
1680bdd7f5bSEtienne Carriere 	io_write32(get_base(iwdg) + IWDG_KR_OFFSET, IWDG_KR_START_KEY);
1690bdd7f5bSEtienne Carriere 
1702f9b82faSEtienne Carriere 	iwdg_wdt_set_enabled(iwdg);
1710bdd7f5bSEtienne Carriere }
1720bdd7f5bSEtienne Carriere 
1730bdd7f5bSEtienne Carriere static void iwdg_refresh(struct stm32_iwdg_device *iwdg)
1740bdd7f5bSEtienne Carriere {
175*eb47832fSAntonio Borneo 	TEE_Result res = TEE_ERROR_GENERIC;
176*eb47832fSAntonio Borneo 
177*eb47832fSAntonio Borneo 	res = tee_time_get_sys_time(&iwdg->last_refresh);
178*eb47832fSAntonio Borneo 	if (res)
179*eb47832fSAntonio Borneo 		panic();
180*eb47832fSAntonio Borneo 
1810bdd7f5bSEtienne Carriere 	io_write32(get_base(iwdg) + IWDG_KR_OFFSET, IWDG_KR_RELOAD_KEY);
1820bdd7f5bSEtienne Carriere }
1830bdd7f5bSEtienne Carriere 
1840bdd7f5bSEtienne Carriere /* Operators for watchdog OP-TEE interface */
1850bdd7f5bSEtienne Carriere static struct stm32_iwdg_device *wdt_chip_to_iwdg(struct wdt_chip *chip)
1860bdd7f5bSEtienne Carriere {
1870bdd7f5bSEtienne Carriere 	return container_of(chip, struct stm32_iwdg_device, wdt_chip);
1880bdd7f5bSEtienne Carriere }
1890bdd7f5bSEtienne Carriere 
190fc9063ddSEtienne Carriere static TEE_Result iwdg_wdt_init(struct wdt_chip *chip,
191fc9063ddSEtienne Carriere 				unsigned long *min_timeout,
192fc9063ddSEtienne Carriere 				unsigned long *max_timeout)
193fc9063ddSEtienne Carriere {
194fc9063ddSEtienne Carriere 	struct stm32_iwdg_device *iwdg = wdt_chip_to_iwdg(chip);
195fc9063ddSEtienne Carriere 	unsigned long rate = clk_get_rate(iwdg->clk_lsi);
196fc9063ddSEtienne Carriere 
197fc9063ddSEtienne Carriere 	if (!rate)
198fc9063ddSEtienne Carriere 		return TEE_ERROR_GENERIC;
199fc9063ddSEtienne Carriere 
200fc9063ddSEtienne Carriere 	/* Be safe and expect any counter to be above 2 */
201fc9063ddSEtienne Carriere 	*min_timeout = 3 * IWDG_PRESCALER_256 / rate;
202fc9063ddSEtienne Carriere 	*max_timeout = (IWDG_CNT_MASK + 1) * IWDG_PRESCALER_256 / rate;
203fc9063ddSEtienne Carriere 
204fc9063ddSEtienne Carriere 	return TEE_SUCCESS;
205fc9063ddSEtienne Carriere }
206fc9063ddSEtienne Carriere 
2070bdd7f5bSEtienne Carriere static void iwdg_wdt_start(struct wdt_chip *chip)
2080bdd7f5bSEtienne Carriere {
2090bdd7f5bSEtienne Carriere 	struct stm32_iwdg_device *iwdg = wdt_chip_to_iwdg(chip);
2100bdd7f5bSEtienne Carriere 
2110bdd7f5bSEtienne Carriere 	iwdg_start(iwdg);
2120bdd7f5bSEtienne Carriere 
2130bdd7f5bSEtienne Carriere 	if (configure_timeout(iwdg))
2140bdd7f5bSEtienne Carriere 		panic();
2150bdd7f5bSEtienne Carriere }
2160bdd7f5bSEtienne Carriere 
2170bdd7f5bSEtienne Carriere static void iwdg_wdt_refresh(struct wdt_chip *chip)
2180bdd7f5bSEtienne Carriere {
2190bdd7f5bSEtienne Carriere 	struct stm32_iwdg_device *iwdg = wdt_chip_to_iwdg(chip);
2200bdd7f5bSEtienne Carriere 
2210bdd7f5bSEtienne Carriere 	iwdg_refresh(iwdg);
2220bdd7f5bSEtienne Carriere }
2230bdd7f5bSEtienne Carriere 
2240bdd7f5bSEtienne Carriere static TEE_Result iwdg_wdt_set_timeout(struct wdt_chip *chip,
2250bdd7f5bSEtienne Carriere 				       unsigned long timeout)
2260bdd7f5bSEtienne Carriere {
2270bdd7f5bSEtienne Carriere 	struct stm32_iwdg_device *iwdg = wdt_chip_to_iwdg(chip);
2280bdd7f5bSEtienne Carriere 
2290bdd7f5bSEtienne Carriere 	if (!iwdg_timeout_cnt(iwdg, timeout))
2300bdd7f5bSEtienne Carriere 		return TEE_ERROR_BAD_PARAMETERS;
2310bdd7f5bSEtienne Carriere 
2320bdd7f5bSEtienne Carriere 	iwdg->timeout = timeout;
2330bdd7f5bSEtienne Carriere 
2342f9b82faSEtienne Carriere 	if (iwdg_wdt_is_enabled(iwdg)) {
2350bdd7f5bSEtienne Carriere 		TEE_Result res = TEE_ERROR_GENERIC;
2360bdd7f5bSEtienne Carriere 
2370bdd7f5bSEtienne Carriere 		res = configure_timeout(iwdg);
2380bdd7f5bSEtienne Carriere 		if (res)
2390bdd7f5bSEtienne Carriere 			return res;
2400bdd7f5bSEtienne Carriere 	}
2410bdd7f5bSEtienne Carriere 
2420bdd7f5bSEtienne Carriere 	return TEE_SUCCESS;
2430bdd7f5bSEtienne Carriere }
2440bdd7f5bSEtienne Carriere 
245*eb47832fSAntonio Borneo static TEE_Result iwdg_wdt_get_timeleft(struct wdt_chip *chip, bool *is_started,
246*eb47832fSAntonio Borneo 					unsigned long *timeleft)
247*eb47832fSAntonio Borneo {
248*eb47832fSAntonio Borneo 	struct stm32_iwdg_device *iwdg = wdt_chip_to_iwdg(chip);
249*eb47832fSAntonio Borneo 	TEE_Result res = TEE_ERROR_GENERIC;
250*eb47832fSAntonio Borneo 	TEE_Time time = { };
251*eb47832fSAntonio Borneo 	TEE_Time now = { };
252*eb47832fSAntonio Borneo 
253*eb47832fSAntonio Borneo 	*is_started = iwdg_wdt_is_enabled(iwdg);
254*eb47832fSAntonio Borneo 
255*eb47832fSAntonio Borneo 	if (!*is_started)
256*eb47832fSAntonio Borneo 		return TEE_SUCCESS;
257*eb47832fSAntonio Borneo 
258*eb47832fSAntonio Borneo 	res = tee_time_get_sys_time(&now);
259*eb47832fSAntonio Borneo 	if (res)
260*eb47832fSAntonio Borneo 		panic();
261*eb47832fSAntonio Borneo 
262*eb47832fSAntonio Borneo 	time.seconds = iwdg->timeout;
263*eb47832fSAntonio Borneo 	TEE_TIME_ADD(iwdg->last_refresh, time, time);
264*eb47832fSAntonio Borneo 	if (TEE_TIME_LE(time, now)) {
265*eb47832fSAntonio Borneo 		*timeleft = 0;
266*eb47832fSAntonio Borneo 	} else {
267*eb47832fSAntonio Borneo 		TEE_TIME_SUB(time, now, time);
268*eb47832fSAntonio Borneo 		*timeleft = time.seconds;
269*eb47832fSAntonio Borneo 	}
270*eb47832fSAntonio Borneo 
271*eb47832fSAntonio Borneo 	return TEE_SUCCESS;
272*eb47832fSAntonio Borneo }
273*eb47832fSAntonio Borneo 
2740bdd7f5bSEtienne Carriere static const struct wdt_ops stm32_iwdg_ops = {
275fc9063ddSEtienne Carriere 	.init = iwdg_wdt_init,
2760bdd7f5bSEtienne Carriere 	.start = iwdg_wdt_start,
2770bdd7f5bSEtienne Carriere 	.ping = iwdg_wdt_refresh,
2780bdd7f5bSEtienne Carriere 	.set_timeout = iwdg_wdt_set_timeout,
279*eb47832fSAntonio Borneo 	.get_timeleft = iwdg_wdt_get_timeleft,
2800bdd7f5bSEtienne Carriere };
2810bdd7f5bSEtienne Carriere DECLARE_KEEP_PAGER(stm32_iwdg_ops);
2820bdd7f5bSEtienne Carriere 
2830bdd7f5bSEtienne Carriere /* Driver initialization */
2840bdd7f5bSEtienne Carriere static TEE_Result stm32_iwdg_parse_fdt(struct stm32_iwdg_device *iwdg,
2850bdd7f5bSEtienne Carriere 				       const void *fdt, int node)
2860bdd7f5bSEtienne Carriere {
2870bdd7f5bSEtienne Carriere 	TEE_Result res = TEE_ERROR_GENERIC;
2880bdd7f5bSEtienne Carriere 	struct dt_node_info dt_info = { };
2890bdd7f5bSEtienne Carriere 	const fdt32_t *cuint = NULL;
2900bdd7f5bSEtienne Carriere 
291f354a5d8SGatien Chevallier 	fdt_fill_device_info(fdt, &dt_info, node);
2920bdd7f5bSEtienne Carriere 
2930bdd7f5bSEtienne Carriere 	if (dt_info.reg == DT_INFO_INVALID_REG ||
2940bdd7f5bSEtienne Carriere 	    dt_info.reg_size == DT_INFO_INVALID_REG_SIZE)
2950bdd7f5bSEtienne Carriere 		panic();
2960bdd7f5bSEtienne Carriere 
297b2f17e87SEtienne Carriere 	res = clk_dt_get_by_name(fdt, node, "pclk", &iwdg->clk_pclk);
2980bdd7f5bSEtienne Carriere 	if (res)
2990bdd7f5bSEtienne Carriere 		return res;
3000bdd7f5bSEtienne Carriere 
3010bdd7f5bSEtienne Carriere 	res = clk_dt_get_by_name(fdt, node, "lsi", &iwdg->clk_lsi);
3020bdd7f5bSEtienne Carriere 	if (res)
3030bdd7f5bSEtienne Carriere 		return res;
3040bdd7f5bSEtienne Carriere 
3050bdd7f5bSEtienne Carriere 	/* Get IOMEM address */
3060bdd7f5bSEtienne Carriere 	iwdg->base.pa = dt_info.reg;
3070bdd7f5bSEtienne Carriere 	io_pa_or_va_secure(&iwdg->base, dt_info.reg_size);
3080bdd7f5bSEtienne Carriere 	assert(iwdg->base.va);
3090bdd7f5bSEtienne Carriere 
3100bdd7f5bSEtienne Carriere 	/* Get and check timeout value */
3110bdd7f5bSEtienne Carriere 	cuint = fdt_getprop(fdt, node, "timeout-sec", NULL);
3120bdd7f5bSEtienne Carriere 	if (!cuint)
3130bdd7f5bSEtienne Carriere 		return TEE_ERROR_BAD_PARAMETERS;
3140bdd7f5bSEtienne Carriere 
3150bdd7f5bSEtienne Carriere 	iwdg->timeout = (int)fdt32_to_cpu(*cuint);
3160bdd7f5bSEtienne Carriere 	if (!iwdg->timeout)
3170bdd7f5bSEtienne Carriere 		return TEE_ERROR_BAD_PARAMETERS;
3180bdd7f5bSEtienne Carriere 
3190bdd7f5bSEtienne Carriere 	if (!iwdg_timeout_cnt(iwdg, iwdg->timeout)) {
3200bdd7f5bSEtienne Carriere 		EMSG("Timeout %lu not applicable", iwdg->timeout);
3210bdd7f5bSEtienne Carriere 		return TEE_ERROR_BAD_PARAMETERS;
3220bdd7f5bSEtienne Carriere 	}
3230bdd7f5bSEtienne Carriere 
3240bdd7f5bSEtienne Carriere 	return TEE_SUCCESS;
3250bdd7f5bSEtienne Carriere }
3260bdd7f5bSEtienne Carriere 
3273d5793d2SAntonio Borneo static void iwdg_wdt_get_version_and_status(struct stm32_iwdg_device *iwdg)
3283d5793d2SAntonio Borneo {
3293d5793d2SAntonio Borneo 	vaddr_t iwdg_base = get_base(iwdg);
3303d5793d2SAntonio Borneo 	uint32_t rlr_value = 0;
3313d5793d2SAntonio Borneo 
3323d5793d2SAntonio Borneo 	iwdg->hw_version = io_read32(iwdg_base + IWDG_VERR_OFFSET) &
3333d5793d2SAntonio Borneo 			   IWDG_VERR_REV_MASK;
3343d5793d2SAntonio Borneo 
3353d5793d2SAntonio Borneo 	/* Test if watchdog is already running */
3363d5793d2SAntonio Borneo 	if (iwdg->hw_version >= IWDG_ONF_MIN_VER) {
3373d5793d2SAntonio Borneo 		if (io_read32(iwdg_base + IWDG_SR_OFFSET) & IWDG_SR_ONF)
3383d5793d2SAntonio Borneo 			iwdg_wdt_set_enabled(iwdg);
3393d5793d2SAntonio Borneo 	} else {
3403d5793d2SAntonio Borneo 		/*
3413d5793d2SAntonio Borneo 		 * Workaround for old versions without IWDG_SR_ONF bit:
3423d5793d2SAntonio Borneo 		 * - write in IWDG_RLR_OFFSET
3433d5793d2SAntonio Borneo 		 * - wait for sync
3443d5793d2SAntonio Borneo 		 * - if sync succeeds, then iwdg is running
3453d5793d2SAntonio Borneo 		 */
3463d5793d2SAntonio Borneo 		io_write32(iwdg_base + IWDG_KR_OFFSET, IWDG_KR_ACCESS_KEY);
3473d5793d2SAntonio Borneo 
3483d5793d2SAntonio Borneo 		rlr_value = io_read32(iwdg_base + IWDG_RLR_OFFSET);
3493d5793d2SAntonio Borneo 		io_write32(iwdg_base + IWDG_RLR_OFFSET, rlr_value);
3503d5793d2SAntonio Borneo 
3513d5793d2SAntonio Borneo 		if (!iwdg_wait_sync(iwdg))
3523d5793d2SAntonio Borneo 			iwdg_wdt_set_enabled(iwdg);
3533d5793d2SAntonio Borneo 
3543d5793d2SAntonio Borneo 		io_write32(iwdg_base + IWDG_KR_OFFSET, IWDG_KR_WPROT_KEY);
3553d5793d2SAntonio Borneo 	}
3563d5793d2SAntonio Borneo 
3573d5793d2SAntonio Borneo 	DMSG("Watchdog is %sabled", iwdg_wdt_is_enabled(iwdg) ? "en" : "dis");
3583d5793d2SAntonio Borneo }
3593d5793d2SAntonio Borneo 
3600bdd7f5bSEtienne Carriere static TEE_Result stm32_iwdg_setup(struct stm32_iwdg_device *iwdg,
3610bdd7f5bSEtienne Carriere 				   const void *fdt, int node)
3620bdd7f5bSEtienne Carriere {
3630bdd7f5bSEtienne Carriere 	TEE_Result res = TEE_SUCCESS;
3640bdd7f5bSEtienne Carriere 
3650bdd7f5bSEtienne Carriere 	res = stm32_iwdg_parse_fdt(iwdg, fdt, node);
3660bdd7f5bSEtienne Carriere 	if (res)
3670bdd7f5bSEtienne Carriere 		return res;
3680bdd7f5bSEtienne Carriere 
36936d2a417SEtienne Carriere 	/* Enable watchdog source and bus clocks once for all */
3700bdd7f5bSEtienne Carriere 	clk_enable(iwdg->clk_lsi);
37136d2a417SEtienne Carriere 	clk_enable(iwdg->clk_pclk);
3720bdd7f5bSEtienne Carriere 
3733d5793d2SAntonio Borneo 	iwdg_wdt_get_version_and_status(iwdg);
3740bdd7f5bSEtienne Carriere 
3753d5793d2SAntonio Borneo 	if (iwdg_wdt_is_enabled(iwdg)) {
3760bdd7f5bSEtienne Carriere 		/* Configure timeout if watchdog is already enabled */
3770bdd7f5bSEtienne Carriere 		res = configure_timeout(iwdg);
3780bdd7f5bSEtienne Carriere 		if (res)
3790bdd7f5bSEtienne Carriere 			return res;
3800bdd7f5bSEtienne Carriere 
3810bdd7f5bSEtienne Carriere 		iwdg_refresh(iwdg);
3820bdd7f5bSEtienne Carriere 	}
3830bdd7f5bSEtienne Carriere 
3840bdd7f5bSEtienne Carriere 	return TEE_SUCCESS;
3850bdd7f5bSEtienne Carriere }
3860bdd7f5bSEtienne Carriere 
3870bdd7f5bSEtienne Carriere static TEE_Result stm32_iwdg_probe(const void *fdt, int node,
3880bdd7f5bSEtienne Carriere 				   const void *compat_data __unused)
3890bdd7f5bSEtienne Carriere {
3900bdd7f5bSEtienne Carriere 	struct stm32_iwdg_device *iwdg = NULL;
3910bdd7f5bSEtienne Carriere 	TEE_Result res = TEE_SUCCESS;
3920bdd7f5bSEtienne Carriere 
3930bdd7f5bSEtienne Carriere 	iwdg = calloc(1, sizeof(*iwdg));
3940bdd7f5bSEtienne Carriere 	if (!iwdg)
3950bdd7f5bSEtienne Carriere 		return TEE_ERROR_OUT_OF_MEMORY;
3960bdd7f5bSEtienne Carriere 
3970bdd7f5bSEtienne Carriere 	res = stm32_iwdg_setup(iwdg, fdt, node);
3980bdd7f5bSEtienne Carriere 	if (res)
399a096e2d9SEtienne Carriere 		goto out;
4000bdd7f5bSEtienne Carriere 
401a096e2d9SEtienne Carriere 	iwdg->wdt_chip.ops = &stm32_iwdg_ops;
402a096e2d9SEtienne Carriere 
403a096e2d9SEtienne Carriere 	res = watchdog_register(&iwdg->wdt_chip);
404a096e2d9SEtienne Carriere 
405a096e2d9SEtienne Carriere out:
4060bdd7f5bSEtienne Carriere 	if (res)
4070bdd7f5bSEtienne Carriere 		free(iwdg);
408a096e2d9SEtienne Carriere 
4090bdd7f5bSEtienne Carriere 	return res;
4100bdd7f5bSEtienne Carriere }
4110bdd7f5bSEtienne Carriere 
4120bdd7f5bSEtienne Carriere static const struct dt_device_match stm32_iwdg_match_table[] = {
4130bdd7f5bSEtienne Carriere 	{ .compatible = "st,stm32mp1-iwdg" },
4140bdd7f5bSEtienne Carriere 	{ }
4150bdd7f5bSEtienne Carriere };
4160bdd7f5bSEtienne Carriere 
4170bdd7f5bSEtienne Carriere DEFINE_DT_DRIVER(stm32_iwdg_dt_driver) = {
4180bdd7f5bSEtienne Carriere 	.name = "stm32-iwdg",
4190bdd7f5bSEtienne Carriere 	.match_table = stm32_iwdg_match_table,
4200bdd7f5bSEtienne Carriere 	.probe = stm32_iwdg_probe,
4210bdd7f5bSEtienne Carriere };
422