xref: /optee_os/core/drivers/stm32_iwdg.c (revision f354a5d8f98eb3bc8f3360eccd7405144266b5b4)
10bdd7f5bSEtienne Carriere // SPDX-License-Identifier: BSD-3-Clause
20bdd7f5bSEtienne Carriere /*
30bdd7f5bSEtienne Carriere  * Copyright (c) 2017-2022, 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/stm32_iwdg.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>
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>
210bdd7f5bSEtienne Carriere #include <libfdt.h>
220bdd7f5bSEtienne Carriere #include <mm/core_memprot.h>
230bdd7f5bSEtienne Carriere #include <sm/sm.h>
240bdd7f5bSEtienne Carriere #include <stm32_util.h>
250bdd7f5bSEtienne Carriere #include <string.h>
260bdd7f5bSEtienne Carriere #include <trace.h>
270bdd7f5bSEtienne Carriere 
280bdd7f5bSEtienne Carriere /* IWDG Compatibility */
290bdd7f5bSEtienne Carriere #define IWDG_TIMEOUT_US		U(1000)
300bdd7f5bSEtienne Carriere #define IWDG_CNT_MASK		GENMASK_32(11, 0)
310bdd7f5bSEtienne Carriere 
320bdd7f5bSEtienne Carriere /* IWDG registers offsets */
330bdd7f5bSEtienne Carriere #define IWDG_KR_OFFSET		U(0x00)
340bdd7f5bSEtienne Carriere #define IWDG_PR_OFFSET		U(0x04)
350bdd7f5bSEtienne Carriere #define IWDG_RLR_OFFSET		U(0x08)
360bdd7f5bSEtienne Carriere #define IWDG_SR_OFFSET		U(0x0C)
370bdd7f5bSEtienne Carriere #define IWDG_EWCR_OFFSET	U(0x14)
380bdd7f5bSEtienne Carriere 
390bdd7f5bSEtienne Carriere #define IWDG_KR_ACCESS_KEY	U(0x5555)
400bdd7f5bSEtienne Carriere #define IWDG_KR_RELOAD_KEY	U(0xAAAA)
410bdd7f5bSEtienne Carriere #define IWDG_KR_START_KEY	U(0xCCCC)
420bdd7f5bSEtienne Carriere 
430bdd7f5bSEtienne Carriere /* Use a fixed prescaler divider of 256 */
440bdd7f5bSEtienne Carriere #define IWDG_PRESCALER_256	U(256)
450bdd7f5bSEtienne Carriere #define IWDG_PR_DIV_256		U(0x06)
460bdd7f5bSEtienne Carriere #define IWDG_PR_DIV_MASK	GENMASK_32(3, 0)
470bdd7f5bSEtienne Carriere 
480bdd7f5bSEtienne Carriere #define IWDG_SR_PVU		BIT(0)
490bdd7f5bSEtienne Carriere #define IWDG_SR_RVU		BIT(1)
500bdd7f5bSEtienne Carriere #define IWDG_SR_WVU		BIT(2)
510bdd7f5bSEtienne Carriere #define IWDG_SR_EWU		BIT(3)
520bdd7f5bSEtienne Carriere #define IWDG_SR_UPDATE_MASK	(IWDG_SR_PVU | IWDG_SR_RVU | IWDG_SR_WVU | \
530bdd7f5bSEtienne Carriere 				 IWDG_SR_EWU)
540bdd7f5bSEtienne Carriere 
550bdd7f5bSEtienne Carriere #define IWDG_EWCR_EWIE		BIT(15)
560bdd7f5bSEtienne Carriere #define IWDG_EWCR_EWIC		BIT(14)
570bdd7f5bSEtienne Carriere 
580bdd7f5bSEtienne Carriere /*
590bdd7f5bSEtienne Carriere  * Values for struct stm32_iwdg_device::flags
600bdd7f5bSEtienne Carriere  * IWDG_FLAGS_HW_ENABLED                Watchdog is enabled by BootROM
610bdd7f5bSEtienne Carriere  * IWDG_FLAGS_DISABLE_ON_STOP           Watchdog is freezed in SoC STOP mode
620bdd7f5bSEtienne Carriere  * IWDG_FLAGS_DISABLE_ON_STANDBY        Watchdog is freezed in SoC STANDBY mode
630bdd7f5bSEtienne Carriere  * IWDG_FLAGS_NON_SECURE                Instance is assigned to non-secure world
640bdd7f5bSEtienne Carriere  * IWDG_FLAGS_ENABLED			Watchdog has been enabled
650bdd7f5bSEtienne Carriere  */
660bdd7f5bSEtienne Carriere #define IWDG_FLAGS_HW_ENABLED			BIT(0)
670bdd7f5bSEtienne Carriere #define IWDG_FLAGS_DISABLE_ON_STOP		BIT(1)
680bdd7f5bSEtienne Carriere #define IWDG_FLAGS_DISABLE_ON_STANDBY		BIT(2)
690bdd7f5bSEtienne Carriere #define IWDG_FLAGS_NON_SECURE			BIT(3)
700bdd7f5bSEtienne Carriere #define IWDG_FLAGS_ENABLED			BIT(4)
710bdd7f5bSEtienne Carriere 
720bdd7f5bSEtienne Carriere /*
730bdd7f5bSEtienne Carriere  * IWDG watch instance data
740bdd7f5bSEtienne Carriere  * @base - IWDG interface IOMEM base address
750bdd7f5bSEtienne Carriere  * @clock - Bus clock
760bdd7f5bSEtienne Carriere  * @clk_lsi - IWDG source clock
770bdd7f5bSEtienne Carriere  * @flags - Property flags for the IWDG instance
780bdd7f5bSEtienne Carriere  * @timeout - Watchdog elaspure timeout
790bdd7f5bSEtienne Carriere  * @wdt_chip - Wathcdog chip instance
800bdd7f5bSEtienne Carriere  * @link - Link in registered watchdog instance list
810bdd7f5bSEtienne Carriere  */
820bdd7f5bSEtienne Carriere struct stm32_iwdg_device {
830bdd7f5bSEtienne Carriere 	struct io_pa_va base;
840bdd7f5bSEtienne Carriere 	struct clk *clock;
850bdd7f5bSEtienne Carriere 	struct clk *clk_lsi;
860bdd7f5bSEtienne Carriere 	uint32_t flags;
870bdd7f5bSEtienne Carriere 	unsigned long timeout;
880bdd7f5bSEtienne Carriere 	struct wdt_chip wdt_chip;
890bdd7f5bSEtienne Carriere 	SLIST_ENTRY(stm32_iwdg_device) link;
900bdd7f5bSEtienne Carriere };
910bdd7f5bSEtienne Carriere 
920bdd7f5bSEtienne Carriere static unsigned int iwdg_lock = SPINLOCK_UNLOCK;
930bdd7f5bSEtienne Carriere 
940bdd7f5bSEtienne Carriere static SLIST_HEAD(iwdg_dev_list_head, stm32_iwdg_device) iwdg_dev_list =
950bdd7f5bSEtienne Carriere 	SLIST_HEAD_INITIALIZER(iwdg_dev_list_head);
960bdd7f5bSEtienne Carriere 
970bdd7f5bSEtienne Carriere static vaddr_t get_base(struct stm32_iwdg_device *iwdg)
980bdd7f5bSEtienne Carriere {
990bdd7f5bSEtienne Carriere 	return io_pa_or_va(&iwdg->base, 1);
1000bdd7f5bSEtienne Carriere }
1010bdd7f5bSEtienne Carriere 
1020bdd7f5bSEtienne Carriere static bool is_assigned_to_nsec(struct stm32_iwdg_device *iwdg)
1030bdd7f5bSEtienne Carriere {
1040bdd7f5bSEtienne Carriere 	return iwdg->flags & IWDG_FLAGS_NON_SECURE;
1050bdd7f5bSEtienne Carriere }
1060bdd7f5bSEtienne Carriere 
1070bdd7f5bSEtienne Carriere static bool is_enable(struct stm32_iwdg_device *iwdg)
1080bdd7f5bSEtienne Carriere {
1090bdd7f5bSEtienne Carriere 	return iwdg->flags & IWDG_FLAGS_ENABLED;
1100bdd7f5bSEtienne Carriere }
1110bdd7f5bSEtienne Carriere 
1120bdd7f5bSEtienne Carriere /* Return counter value to related to input timeout in seconds, or 0 on error */
1130bdd7f5bSEtienne Carriere static uint32_t iwdg_timeout_cnt(struct stm32_iwdg_device *iwdg,
1140bdd7f5bSEtienne Carriere 				 unsigned long to_sec)
1150bdd7f5bSEtienne Carriere {
1160bdd7f5bSEtienne Carriere 	uint64_t reload = (uint64_t)to_sec * clk_get_rate(iwdg->clk_lsi);
1170bdd7f5bSEtienne Carriere 	uint64_t cnt = (reload / IWDG_PRESCALER_256) - 1;
1180bdd7f5bSEtienne Carriere 
1190bdd7f5bSEtienne Carriere 	/* Be safe and expect any counter to be above 2 */
1200bdd7f5bSEtienne Carriere 	if (cnt > IWDG_CNT_MASK || cnt < 3)
1210bdd7f5bSEtienne Carriere 		return 0;
1220bdd7f5bSEtienne Carriere 
1230bdd7f5bSEtienne Carriere 	return cnt;
1240bdd7f5bSEtienne Carriere }
1250bdd7f5bSEtienne Carriere 
1260bdd7f5bSEtienne Carriere /* Wait IWDG programming completes */
1270bdd7f5bSEtienne Carriere static TEE_Result iwdg_wait_sync(struct stm32_iwdg_device *iwdg)
1280bdd7f5bSEtienne Carriere {
1290bdd7f5bSEtienne Carriere 	uint64_t timeout_ref = timeout_init_us(IWDG_TIMEOUT_US);
1300bdd7f5bSEtienne Carriere 	vaddr_t iwdg_base = get_base(iwdg);
1310bdd7f5bSEtienne Carriere 
1320bdd7f5bSEtienne Carriere 	while (io_read32(iwdg_base + IWDG_SR_OFFSET) & IWDG_SR_UPDATE_MASK)
1330bdd7f5bSEtienne Carriere 		if (timeout_elapsed(timeout_ref))
1340bdd7f5bSEtienne Carriere 			break;
1350bdd7f5bSEtienne Carriere 
1360bdd7f5bSEtienne Carriere 	if (!(io_read32(iwdg_base + IWDG_SR_OFFSET) & IWDG_SR_UPDATE_MASK))
1370bdd7f5bSEtienne Carriere 		return TEE_ERROR_GENERIC;
1380bdd7f5bSEtienne Carriere 
1390bdd7f5bSEtienne Carriere 	return TEE_SUCCESS;
1400bdd7f5bSEtienne Carriere }
1410bdd7f5bSEtienne Carriere 
1420bdd7f5bSEtienne Carriere static TEE_Result configure_timeout(struct stm32_iwdg_device *iwdg)
1430bdd7f5bSEtienne Carriere {
1440bdd7f5bSEtienne Carriere 	TEE_Result res = TEE_ERROR_GENERIC;
1450bdd7f5bSEtienne Carriere 	vaddr_t iwdg_base = get_base(iwdg);
1460bdd7f5bSEtienne Carriere 	uint32_t rlr_value = 0;
1470bdd7f5bSEtienne Carriere 
1480bdd7f5bSEtienne Carriere 	assert(is_enable(iwdg));
1490bdd7f5bSEtienne Carriere 
1500bdd7f5bSEtienne Carriere 	rlr_value = iwdg_timeout_cnt(iwdg, iwdg->timeout);
1510bdd7f5bSEtienne Carriere 	if (!rlr_value)
1520bdd7f5bSEtienne Carriere 		return TEE_ERROR_GENERIC;
1530bdd7f5bSEtienne Carriere 
1540bdd7f5bSEtienne Carriere 	clk_enable(iwdg->clock);
1550bdd7f5bSEtienne Carriere 
1560bdd7f5bSEtienne Carriere 	io_write32(iwdg_base + IWDG_KR_OFFSET, IWDG_KR_ACCESS_KEY);
1570bdd7f5bSEtienne Carriere 	io_write32(iwdg_base + IWDG_PR_OFFSET, IWDG_PR_DIV_256);
1580bdd7f5bSEtienne Carriere 	io_write32(iwdg_base + IWDG_RLR_OFFSET, rlr_value);
1590bdd7f5bSEtienne Carriere 	io_write32(iwdg_base + IWDG_KR_OFFSET, IWDG_KR_RELOAD_KEY);
1600bdd7f5bSEtienne Carriere 
1610bdd7f5bSEtienne Carriere 	res = iwdg_wait_sync(iwdg);
1620bdd7f5bSEtienne Carriere 
1630bdd7f5bSEtienne Carriere 	clk_disable(iwdg->clock);
1640bdd7f5bSEtienne Carriere 
1650bdd7f5bSEtienne Carriere 	return res;
1660bdd7f5bSEtienne Carriere }
1670bdd7f5bSEtienne Carriere 
1680bdd7f5bSEtienne Carriere static void iwdg_start(struct stm32_iwdg_device *iwdg)
1690bdd7f5bSEtienne Carriere {
1700bdd7f5bSEtienne Carriere 	clk_enable(iwdg->clock);
1710bdd7f5bSEtienne Carriere 	io_write32(get_base(iwdg) + IWDG_KR_OFFSET, IWDG_KR_START_KEY);
1720bdd7f5bSEtienne Carriere 	clk_disable(iwdg->clock);
1730bdd7f5bSEtienne Carriere 
1740bdd7f5bSEtienne Carriere 	iwdg->flags |= IWDG_FLAGS_ENABLED;
1750bdd7f5bSEtienne Carriere }
1760bdd7f5bSEtienne Carriere 
1770bdd7f5bSEtienne Carriere static void iwdg_refresh(struct stm32_iwdg_device *iwdg)
1780bdd7f5bSEtienne Carriere {
1790bdd7f5bSEtienne Carriere 	clk_enable(iwdg->clock);
1800bdd7f5bSEtienne Carriere 	io_write32(get_base(iwdg) + IWDG_KR_OFFSET, IWDG_KR_RELOAD_KEY);
1810bdd7f5bSEtienne Carriere 	clk_disable(iwdg->clock);
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 
1900bdd7f5bSEtienne Carriere static void iwdg_wdt_start(struct wdt_chip *chip)
1910bdd7f5bSEtienne Carriere {
1920bdd7f5bSEtienne Carriere 	struct stm32_iwdg_device *iwdg = wdt_chip_to_iwdg(chip);
1930bdd7f5bSEtienne Carriere 
1940bdd7f5bSEtienne Carriere 	iwdg_start(iwdg);
1950bdd7f5bSEtienne Carriere 
1960bdd7f5bSEtienne Carriere 	if (configure_timeout(iwdg))
1970bdd7f5bSEtienne Carriere 		panic();
1980bdd7f5bSEtienne Carriere }
1990bdd7f5bSEtienne Carriere 
2000bdd7f5bSEtienne Carriere static void iwdg_wdt_refresh(struct wdt_chip *chip)
2010bdd7f5bSEtienne Carriere {
2020bdd7f5bSEtienne Carriere 	struct stm32_iwdg_device *iwdg = wdt_chip_to_iwdg(chip);
2030bdd7f5bSEtienne Carriere 
2040bdd7f5bSEtienne Carriere 	iwdg_refresh(iwdg);
2050bdd7f5bSEtienne Carriere }
2060bdd7f5bSEtienne Carriere 
2070bdd7f5bSEtienne Carriere static TEE_Result iwdg_wdt_set_timeout(struct wdt_chip *chip,
2080bdd7f5bSEtienne Carriere 				       unsigned long timeout)
2090bdd7f5bSEtienne Carriere {
2100bdd7f5bSEtienne Carriere 	struct stm32_iwdg_device *iwdg = wdt_chip_to_iwdg(chip);
2110bdd7f5bSEtienne Carriere 
2120bdd7f5bSEtienne Carriere 	if (!iwdg_timeout_cnt(iwdg, timeout))
2130bdd7f5bSEtienne Carriere 		return TEE_ERROR_BAD_PARAMETERS;
2140bdd7f5bSEtienne Carriere 
2150bdd7f5bSEtienne Carriere 	iwdg->timeout = timeout;
2160bdd7f5bSEtienne Carriere 
2170bdd7f5bSEtienne Carriere 	if (is_enable(iwdg)) {
2180bdd7f5bSEtienne Carriere 		TEE_Result res = TEE_ERROR_GENERIC;
2190bdd7f5bSEtienne Carriere 
2200bdd7f5bSEtienne Carriere 		res = configure_timeout(iwdg);
2210bdd7f5bSEtienne Carriere 		if (res)
2220bdd7f5bSEtienne Carriere 			return res;
2230bdd7f5bSEtienne Carriere 	}
2240bdd7f5bSEtienne Carriere 
2250bdd7f5bSEtienne Carriere 	return TEE_SUCCESS;
2260bdd7f5bSEtienne Carriere }
2270bdd7f5bSEtienne Carriere 
2280bdd7f5bSEtienne Carriere static const struct wdt_ops stm32_iwdg_ops = {
2290bdd7f5bSEtienne Carriere 	.start = iwdg_wdt_start,
2300bdd7f5bSEtienne Carriere 	.ping = iwdg_wdt_refresh,
2310bdd7f5bSEtienne Carriere 	.set_timeout = iwdg_wdt_set_timeout,
2320bdd7f5bSEtienne Carriere };
2330bdd7f5bSEtienne Carriere DECLARE_KEEP_PAGER(stm32_iwdg_ops);
2340bdd7f5bSEtienne Carriere 
2350bdd7f5bSEtienne Carriere /* Refresh all registered watchdogs */
2360bdd7f5bSEtienne Carriere void stm32_iwdg_refresh(void)
2370bdd7f5bSEtienne Carriere {
2380bdd7f5bSEtienne Carriere 	struct stm32_iwdg_device *iwdg = NULL;
2390bdd7f5bSEtienne Carriere 	uint32_t exceptions = cpu_spin_lock_xsave(&iwdg_lock);
2400bdd7f5bSEtienne Carriere 
2410bdd7f5bSEtienne Carriere 	SLIST_FOREACH(iwdg, &iwdg_dev_list, link)
2420bdd7f5bSEtienne Carriere 		iwdg_refresh(iwdg);
2430bdd7f5bSEtienne Carriere 
2440bdd7f5bSEtienne Carriere 	cpu_spin_unlock_xrestore(&iwdg_lock, exceptions);
2450bdd7f5bSEtienne Carriere }
2460bdd7f5bSEtienne Carriere 
2470bdd7f5bSEtienne Carriere /* Driver initialization */
2480bdd7f5bSEtienne Carriere static TEE_Result stm32_iwdg_parse_fdt(struct stm32_iwdg_device *iwdg,
2490bdd7f5bSEtienne Carriere 				       const void *fdt, int node)
2500bdd7f5bSEtienne Carriere {
2510bdd7f5bSEtienne Carriere 	TEE_Result res = TEE_ERROR_GENERIC;
2520bdd7f5bSEtienne Carriere 	struct dt_node_info dt_info = { };
2530bdd7f5bSEtienne Carriere 	const fdt32_t *cuint = NULL;
2540bdd7f5bSEtienne Carriere 
255*f354a5d8SGatien Chevallier 	fdt_fill_device_info(fdt, &dt_info, node);
2560bdd7f5bSEtienne Carriere 
2570bdd7f5bSEtienne Carriere 	if (dt_info.reg == DT_INFO_INVALID_REG ||
2580bdd7f5bSEtienne Carriere 	    dt_info.reg_size == DT_INFO_INVALID_REG_SIZE)
2590bdd7f5bSEtienne Carriere 		panic();
2600bdd7f5bSEtienne Carriere 
2610bdd7f5bSEtienne Carriere 	res = clk_dt_get_by_name(fdt, node, "pclk", &iwdg->clock);
2620bdd7f5bSEtienne Carriere 	if (res)
2630bdd7f5bSEtienne Carriere 		return res;
2640bdd7f5bSEtienne Carriere 
2650bdd7f5bSEtienne Carriere 	res = clk_dt_get_by_name(fdt, node, "lsi", &iwdg->clk_lsi);
2660bdd7f5bSEtienne Carriere 	if (res)
2670bdd7f5bSEtienne Carriere 		return res;
2680bdd7f5bSEtienne Carriere 
2690bdd7f5bSEtienne Carriere 	if (dt_info.status == DT_STATUS_OK_NSEC)
2700bdd7f5bSEtienne Carriere 		iwdg->flags |= IWDG_FLAGS_NON_SECURE;
2710bdd7f5bSEtienne Carriere 
2720bdd7f5bSEtienne Carriere 	/* Get IOMEM address */
2730bdd7f5bSEtienne Carriere 	iwdg->base.pa = dt_info.reg;
2740bdd7f5bSEtienne Carriere 
2750bdd7f5bSEtienne Carriere 	if (iwdg->flags & IWDG_FLAGS_NON_SECURE)
2760bdd7f5bSEtienne Carriere 		io_pa_or_va_nsec(&iwdg->base, dt_info.reg_size);
2770bdd7f5bSEtienne Carriere 	else
2780bdd7f5bSEtienne Carriere 		io_pa_or_va_secure(&iwdg->base, dt_info.reg_size);
2790bdd7f5bSEtienne Carriere 
2800bdd7f5bSEtienne Carriere 	assert(iwdg->base.va);
2810bdd7f5bSEtienne Carriere 
2820bdd7f5bSEtienne Carriere 	/* Get and check timeout value */
2830bdd7f5bSEtienne Carriere 	cuint = fdt_getprop(fdt, node, "timeout-sec", NULL);
2840bdd7f5bSEtienne Carriere 	if (!cuint)
2850bdd7f5bSEtienne Carriere 		return TEE_ERROR_BAD_PARAMETERS;
2860bdd7f5bSEtienne Carriere 
2870bdd7f5bSEtienne Carriere 	iwdg->timeout = (int)fdt32_to_cpu(*cuint);
2880bdd7f5bSEtienne Carriere 	if (!iwdg->timeout)
2890bdd7f5bSEtienne Carriere 		return TEE_ERROR_BAD_PARAMETERS;
2900bdd7f5bSEtienne Carriere 
2910bdd7f5bSEtienne Carriere 	if (!iwdg_timeout_cnt(iwdg, iwdg->timeout)) {
2920bdd7f5bSEtienne Carriere 		EMSG("Timeout %lu not applicable", iwdg->timeout);
2930bdd7f5bSEtienne Carriere 		return TEE_ERROR_BAD_PARAMETERS;
2940bdd7f5bSEtienne Carriere 	}
2950bdd7f5bSEtienne Carriere 
2960bdd7f5bSEtienne Carriere 	/* DT can specify low power cases */
2970bdd7f5bSEtienne Carriere 	if (!fdt_getprop(fdt, node, "stm32,enable-on-stop", NULL))
2980bdd7f5bSEtienne Carriere 		iwdg->flags |= IWDG_FLAGS_DISABLE_ON_STOP;
2990bdd7f5bSEtienne Carriere 
3000bdd7f5bSEtienne Carriere 	if (!fdt_getprop(fdt, node, "stm32,enable-on-standby", NULL))
3010bdd7f5bSEtienne Carriere 		iwdg->flags |= IWDG_FLAGS_DISABLE_ON_STANDBY;
3020bdd7f5bSEtienne Carriere 
3030bdd7f5bSEtienne Carriere 	return TEE_SUCCESS;
3040bdd7f5bSEtienne Carriere }
3050bdd7f5bSEtienne Carriere 
3060bdd7f5bSEtienne Carriere /* Platform should override this function to provide IWDG fuses configuration */
3070bdd7f5bSEtienne Carriere TEE_Result __weak stm32_get_iwdg_otp_config(paddr_t pbase __unused,
3080bdd7f5bSEtienne Carriere 					    struct stm32_iwdg_otp_data *otp_d)
3090bdd7f5bSEtienne Carriere {
3100bdd7f5bSEtienne Carriere 	otp_d->hw_enabled = false;
3110bdd7f5bSEtienne Carriere 	otp_d->disable_on_stop = false;
3120bdd7f5bSEtienne Carriere 	otp_d->disable_on_standby = false;
3130bdd7f5bSEtienne Carriere 
3140bdd7f5bSEtienne Carriere 	return TEE_SUCCESS;
3150bdd7f5bSEtienne Carriere }
3160bdd7f5bSEtienne Carriere 
3170bdd7f5bSEtienne Carriere static TEE_Result stm32_iwdg_setup(struct stm32_iwdg_device *iwdg,
3180bdd7f5bSEtienne Carriere 				   const void *fdt, int node)
3190bdd7f5bSEtienne Carriere {
3200bdd7f5bSEtienne Carriere 	struct stm32_iwdg_otp_data otp_data = { };
3210bdd7f5bSEtienne Carriere 	TEE_Result res = TEE_SUCCESS;
3220bdd7f5bSEtienne Carriere 
3230bdd7f5bSEtienne Carriere 	res = stm32_iwdg_parse_fdt(iwdg, fdt, node);
3240bdd7f5bSEtienne Carriere 	if (res)
3250bdd7f5bSEtienne Carriere 		return res;
3260bdd7f5bSEtienne Carriere 
3270bdd7f5bSEtienne Carriere 	res = stm32_get_iwdg_otp_config(iwdg->base.pa, &otp_data);
3280bdd7f5bSEtienne Carriere 	if (res)
3290bdd7f5bSEtienne Carriere 		return res;
3300bdd7f5bSEtienne Carriere 
3310bdd7f5bSEtienne Carriere 	if (otp_data.hw_enabled)
3320bdd7f5bSEtienne Carriere 		iwdg->flags |= IWDG_FLAGS_HW_ENABLED;
3330bdd7f5bSEtienne Carriere 	if (otp_data.disable_on_stop)
3340bdd7f5bSEtienne Carriere 		iwdg->flags |= IWDG_FLAGS_DISABLE_ON_STOP;
3350bdd7f5bSEtienne Carriere 	if (otp_data.disable_on_standby)
3360bdd7f5bSEtienne Carriere 		iwdg->flags |= IWDG_FLAGS_DISABLE_ON_STANDBY;
3370bdd7f5bSEtienne Carriere 
3380bdd7f5bSEtienne Carriere 	/* Enable watchdog source clock once for all */
3390bdd7f5bSEtienne Carriere 	clk_enable(iwdg->clk_lsi);
3400bdd7f5bSEtienne Carriere 
3410bdd7f5bSEtienne Carriere 	if (otp_data.hw_enabled) {
3420bdd7f5bSEtienne Carriere 		iwdg->flags |= IWDG_FLAGS_ENABLED;
3430bdd7f5bSEtienne Carriere 
3440bdd7f5bSEtienne Carriere 		/* Configure timeout if watchdog is already enabled */
3450bdd7f5bSEtienne Carriere 		res = configure_timeout(iwdg);
3460bdd7f5bSEtienne Carriere 		if (res)
3470bdd7f5bSEtienne Carriere 			return res;
3480bdd7f5bSEtienne Carriere 
3490bdd7f5bSEtienne Carriere 		iwdg_refresh(iwdg);
3500bdd7f5bSEtienne Carriere 	}
3510bdd7f5bSEtienne Carriere 
3520bdd7f5bSEtienne Carriere 	return TEE_SUCCESS;
3530bdd7f5bSEtienne Carriere }
3540bdd7f5bSEtienne Carriere 
3550bdd7f5bSEtienne Carriere static TEE_Result stm32_iwdg_register(struct stm32_iwdg_device *iwdg)
3560bdd7f5bSEtienne Carriere {
3570bdd7f5bSEtienne Carriere 	TEE_Result res = TEE_ERROR_GENERIC;
3580bdd7f5bSEtienne Carriere 
3590bdd7f5bSEtienne Carriere 	if (is_assigned_to_nsec(iwdg)) {
3600bdd7f5bSEtienne Carriere 		stm32mp_register_non_secure_periph_iomem(iwdg->base.pa);
3610bdd7f5bSEtienne Carriere 	} else {
3620bdd7f5bSEtienne Carriere 		stm32mp_register_secure_periph_iomem(iwdg->base.pa);
3630bdd7f5bSEtienne Carriere 
3640bdd7f5bSEtienne Carriere 		/* Expose watchdog runtime service only to secure IWDG */
3650bdd7f5bSEtienne Carriere 		iwdg->wdt_chip.ops = &stm32_iwdg_ops;
3660bdd7f5bSEtienne Carriere 
3670bdd7f5bSEtienne Carriere 		res = watchdog_register(&iwdg->wdt_chip);
3680bdd7f5bSEtienne Carriere 		if (res)
3690bdd7f5bSEtienne Carriere 			return res;
3700bdd7f5bSEtienne Carriere 	}
3710bdd7f5bSEtienne Carriere 
3720bdd7f5bSEtienne Carriere 	SLIST_INSERT_HEAD(&iwdg_dev_list, iwdg, link);
3730bdd7f5bSEtienne Carriere 
3740bdd7f5bSEtienne Carriere 	return TEE_SUCCESS;
3750bdd7f5bSEtienne Carriere }
3760bdd7f5bSEtienne Carriere 
3770bdd7f5bSEtienne Carriere static TEE_Result stm32_iwdg_probe(const void *fdt, int node,
3780bdd7f5bSEtienne Carriere 				   const void *compat_data __unused)
3790bdd7f5bSEtienne Carriere {
3800bdd7f5bSEtienne Carriere 	struct stm32_iwdg_device *iwdg = NULL;
3810bdd7f5bSEtienne Carriere 	TEE_Result res = TEE_SUCCESS;
3820bdd7f5bSEtienne Carriere 
3830bdd7f5bSEtienne Carriere 	iwdg = calloc(1, sizeof(*iwdg));
3840bdd7f5bSEtienne Carriere 	if (!iwdg)
3850bdd7f5bSEtienne Carriere 		return TEE_ERROR_OUT_OF_MEMORY;
3860bdd7f5bSEtienne Carriere 
3870bdd7f5bSEtienne Carriere 	res = stm32_iwdg_setup(iwdg, fdt, node);
3880bdd7f5bSEtienne Carriere 	if (res)
3890bdd7f5bSEtienne Carriere 		goto err;
3900bdd7f5bSEtienne Carriere 
3910bdd7f5bSEtienne Carriere 	res = stm32_iwdg_register(iwdg);
3920bdd7f5bSEtienne Carriere 	if (res)
3930bdd7f5bSEtienne Carriere 		goto err;
3940bdd7f5bSEtienne Carriere 
3950bdd7f5bSEtienne Carriere 	return TEE_SUCCESS;
3960bdd7f5bSEtienne Carriere 
3970bdd7f5bSEtienne Carriere err:
3980bdd7f5bSEtienne Carriere 	free(iwdg);
3990bdd7f5bSEtienne Carriere 	return res;
4000bdd7f5bSEtienne Carriere }
4010bdd7f5bSEtienne Carriere 
4020bdd7f5bSEtienne Carriere static const struct dt_device_match stm32_iwdg_match_table[] = {
4030bdd7f5bSEtienne Carriere 	{ .compatible = "st,stm32mp1-iwdg" },
4040bdd7f5bSEtienne Carriere 	{ }
4050bdd7f5bSEtienne Carriere };
4060bdd7f5bSEtienne Carriere 
4070bdd7f5bSEtienne Carriere DEFINE_DT_DRIVER(stm32_iwdg_dt_driver) = {
4080bdd7f5bSEtienne Carriere 	.name = "stm32-iwdg",
4090bdd7f5bSEtienne Carriere 	.match_table = stm32_iwdg_match_table,
4100bdd7f5bSEtienne Carriere 	.probe = stm32_iwdg_probe,
4110bdd7f5bSEtienne Carriere };
412