xref: /optee_os/core/drivers/stm32_iwdg.c (revision 0bdd7f5ba8219e74776200ddde235e3c3ff8ba3a)
1*0bdd7f5bSEtienne Carriere // SPDX-License-Identifier: BSD-3-Clause
2*0bdd7f5bSEtienne Carriere /*
3*0bdd7f5bSEtienne Carriere  * Copyright (c) 2017-2022, STMicroelectronics - All Rights Reserved
4*0bdd7f5bSEtienne Carriere  */
5*0bdd7f5bSEtienne Carriere 
6*0bdd7f5bSEtienne Carriere #include <assert.h>
7*0bdd7f5bSEtienne Carriere #include <drivers/clk.h>
8*0bdd7f5bSEtienne Carriere #include <drivers/clk_dt.h>
9*0bdd7f5bSEtienne Carriere #include <drivers/stm32_iwdg.h>
10*0bdd7f5bSEtienne Carriere #include <drivers/wdt.h>
11*0bdd7f5bSEtienne Carriere #include <io.h>
12*0bdd7f5bSEtienne Carriere #include <keep.h>
13*0bdd7f5bSEtienne Carriere #include <kernel/boot.h>
14*0bdd7f5bSEtienne Carriere #include <kernel/delay.h>
15*0bdd7f5bSEtienne Carriere #include <kernel/dt.h>
16*0bdd7f5bSEtienne Carriere #include <kernel/interrupt.h>
17*0bdd7f5bSEtienne Carriere #include <kernel/misc.h>
18*0bdd7f5bSEtienne Carriere #include <kernel/panic.h>
19*0bdd7f5bSEtienne Carriere #include <kernel/pm.h>
20*0bdd7f5bSEtienne Carriere #include <kernel/spinlock.h>
21*0bdd7f5bSEtienne Carriere #include <libfdt.h>
22*0bdd7f5bSEtienne Carriere #include <mm/core_memprot.h>
23*0bdd7f5bSEtienne Carriere #include <sm/sm.h>
24*0bdd7f5bSEtienne Carriere #include <stm32_util.h>
25*0bdd7f5bSEtienne Carriere #include <string.h>
26*0bdd7f5bSEtienne Carriere #include <trace.h>
27*0bdd7f5bSEtienne Carriere 
28*0bdd7f5bSEtienne Carriere /* IWDG Compatibility */
29*0bdd7f5bSEtienne Carriere #define IWDG_TIMEOUT_US		U(1000)
30*0bdd7f5bSEtienne Carriere #define IWDG_CNT_MASK		GENMASK_32(11, 0)
31*0bdd7f5bSEtienne Carriere 
32*0bdd7f5bSEtienne Carriere /* IWDG registers offsets */
33*0bdd7f5bSEtienne Carriere #define IWDG_KR_OFFSET		U(0x00)
34*0bdd7f5bSEtienne Carriere #define IWDG_PR_OFFSET		U(0x04)
35*0bdd7f5bSEtienne Carriere #define IWDG_RLR_OFFSET		U(0x08)
36*0bdd7f5bSEtienne Carriere #define IWDG_SR_OFFSET		U(0x0C)
37*0bdd7f5bSEtienne Carriere #define IWDG_EWCR_OFFSET	U(0x14)
38*0bdd7f5bSEtienne Carriere 
39*0bdd7f5bSEtienne Carriere #define IWDG_KR_ACCESS_KEY	U(0x5555)
40*0bdd7f5bSEtienne Carriere #define IWDG_KR_RELOAD_KEY	U(0xAAAA)
41*0bdd7f5bSEtienne Carriere #define IWDG_KR_START_KEY	U(0xCCCC)
42*0bdd7f5bSEtienne Carriere 
43*0bdd7f5bSEtienne Carriere /* Use a fixed prescaler divider of 256 */
44*0bdd7f5bSEtienne Carriere #define IWDG_PRESCALER_256	U(256)
45*0bdd7f5bSEtienne Carriere #define IWDG_PR_DIV_256		U(0x06)
46*0bdd7f5bSEtienne Carriere #define IWDG_PR_DIV_MASK	GENMASK_32(3, 0)
47*0bdd7f5bSEtienne Carriere 
48*0bdd7f5bSEtienne Carriere #define IWDG_SR_PVU		BIT(0)
49*0bdd7f5bSEtienne Carriere #define IWDG_SR_RVU		BIT(1)
50*0bdd7f5bSEtienne Carriere #define IWDG_SR_WVU		BIT(2)
51*0bdd7f5bSEtienne Carriere #define IWDG_SR_EWU		BIT(3)
52*0bdd7f5bSEtienne Carriere #define IWDG_SR_UPDATE_MASK	(IWDG_SR_PVU | IWDG_SR_RVU | IWDG_SR_WVU | \
53*0bdd7f5bSEtienne Carriere 				 IWDG_SR_EWU)
54*0bdd7f5bSEtienne Carriere 
55*0bdd7f5bSEtienne Carriere #define IWDG_EWCR_EWIE		BIT(15)
56*0bdd7f5bSEtienne Carriere #define IWDG_EWCR_EWIC		BIT(14)
57*0bdd7f5bSEtienne Carriere 
58*0bdd7f5bSEtienne Carriere /*
59*0bdd7f5bSEtienne Carriere  * Values for struct stm32_iwdg_device::flags
60*0bdd7f5bSEtienne Carriere  * IWDG_FLAGS_HW_ENABLED                Watchdog is enabled by BootROM
61*0bdd7f5bSEtienne Carriere  * IWDG_FLAGS_DISABLE_ON_STOP           Watchdog is freezed in SoC STOP mode
62*0bdd7f5bSEtienne Carriere  * IWDG_FLAGS_DISABLE_ON_STANDBY        Watchdog is freezed in SoC STANDBY mode
63*0bdd7f5bSEtienne Carriere  * IWDG_FLAGS_NON_SECURE                Instance is assigned to non-secure world
64*0bdd7f5bSEtienne Carriere  * IWDG_FLAGS_ENABLED			Watchdog has been enabled
65*0bdd7f5bSEtienne Carriere  */
66*0bdd7f5bSEtienne Carriere #define IWDG_FLAGS_HW_ENABLED			BIT(0)
67*0bdd7f5bSEtienne Carriere #define IWDG_FLAGS_DISABLE_ON_STOP		BIT(1)
68*0bdd7f5bSEtienne Carriere #define IWDG_FLAGS_DISABLE_ON_STANDBY		BIT(2)
69*0bdd7f5bSEtienne Carriere #define IWDG_FLAGS_NON_SECURE			BIT(3)
70*0bdd7f5bSEtienne Carriere #define IWDG_FLAGS_ENABLED			BIT(4)
71*0bdd7f5bSEtienne Carriere 
72*0bdd7f5bSEtienne Carriere /*
73*0bdd7f5bSEtienne Carriere  * IWDG watch instance data
74*0bdd7f5bSEtienne Carriere  * @base - IWDG interface IOMEM base address
75*0bdd7f5bSEtienne Carriere  * @clock - Bus clock
76*0bdd7f5bSEtienne Carriere  * @clk_lsi - IWDG source clock
77*0bdd7f5bSEtienne Carriere  * @flags - Property flags for the IWDG instance
78*0bdd7f5bSEtienne Carriere  * @timeout - Watchdog elaspure timeout
79*0bdd7f5bSEtienne Carriere  * @wdt_chip - Wathcdog chip instance
80*0bdd7f5bSEtienne Carriere  * @link - Link in registered watchdog instance list
81*0bdd7f5bSEtienne Carriere  */
82*0bdd7f5bSEtienne Carriere struct stm32_iwdg_device {
83*0bdd7f5bSEtienne Carriere 	struct io_pa_va base;
84*0bdd7f5bSEtienne Carriere 	struct clk *clock;
85*0bdd7f5bSEtienne Carriere 	struct clk *clk_lsi;
86*0bdd7f5bSEtienne Carriere 	uint32_t flags;
87*0bdd7f5bSEtienne Carriere 	unsigned long timeout;
88*0bdd7f5bSEtienne Carriere 	struct wdt_chip wdt_chip;
89*0bdd7f5bSEtienne Carriere 	SLIST_ENTRY(stm32_iwdg_device) link;
90*0bdd7f5bSEtienne Carriere };
91*0bdd7f5bSEtienne Carriere 
92*0bdd7f5bSEtienne Carriere static unsigned int iwdg_lock = SPINLOCK_UNLOCK;
93*0bdd7f5bSEtienne Carriere 
94*0bdd7f5bSEtienne Carriere static SLIST_HEAD(iwdg_dev_list_head, stm32_iwdg_device) iwdg_dev_list =
95*0bdd7f5bSEtienne Carriere 	SLIST_HEAD_INITIALIZER(iwdg_dev_list_head);
96*0bdd7f5bSEtienne Carriere 
97*0bdd7f5bSEtienne Carriere static vaddr_t get_base(struct stm32_iwdg_device *iwdg)
98*0bdd7f5bSEtienne Carriere {
99*0bdd7f5bSEtienne Carriere 	return io_pa_or_va(&iwdg->base, 1);
100*0bdd7f5bSEtienne Carriere }
101*0bdd7f5bSEtienne Carriere 
102*0bdd7f5bSEtienne Carriere static bool is_assigned_to_nsec(struct stm32_iwdg_device *iwdg)
103*0bdd7f5bSEtienne Carriere {
104*0bdd7f5bSEtienne Carriere 	return iwdg->flags & IWDG_FLAGS_NON_SECURE;
105*0bdd7f5bSEtienne Carriere }
106*0bdd7f5bSEtienne Carriere 
107*0bdd7f5bSEtienne Carriere static bool is_enable(struct stm32_iwdg_device *iwdg)
108*0bdd7f5bSEtienne Carriere {
109*0bdd7f5bSEtienne Carriere 	return iwdg->flags & IWDG_FLAGS_ENABLED;
110*0bdd7f5bSEtienne Carriere }
111*0bdd7f5bSEtienne Carriere 
112*0bdd7f5bSEtienne Carriere /* Return counter value to related to input timeout in seconds, or 0 on error */
113*0bdd7f5bSEtienne Carriere static uint32_t iwdg_timeout_cnt(struct stm32_iwdg_device *iwdg,
114*0bdd7f5bSEtienne Carriere 				 unsigned long to_sec)
115*0bdd7f5bSEtienne Carriere {
116*0bdd7f5bSEtienne Carriere 	uint64_t reload = (uint64_t)to_sec * clk_get_rate(iwdg->clk_lsi);
117*0bdd7f5bSEtienne Carriere 	uint64_t cnt = (reload / IWDG_PRESCALER_256) - 1;
118*0bdd7f5bSEtienne Carriere 
119*0bdd7f5bSEtienne Carriere 	/* Be safe and expect any counter to be above 2 */
120*0bdd7f5bSEtienne Carriere 	if (cnt > IWDG_CNT_MASK || cnt < 3)
121*0bdd7f5bSEtienne Carriere 		return 0;
122*0bdd7f5bSEtienne Carriere 
123*0bdd7f5bSEtienne Carriere 	return cnt;
124*0bdd7f5bSEtienne Carriere }
125*0bdd7f5bSEtienne Carriere 
126*0bdd7f5bSEtienne Carriere /* Wait IWDG programming completes */
127*0bdd7f5bSEtienne Carriere static TEE_Result iwdg_wait_sync(struct stm32_iwdg_device *iwdg)
128*0bdd7f5bSEtienne Carriere {
129*0bdd7f5bSEtienne Carriere 	uint64_t timeout_ref = timeout_init_us(IWDG_TIMEOUT_US);
130*0bdd7f5bSEtienne Carriere 	vaddr_t iwdg_base = get_base(iwdg);
131*0bdd7f5bSEtienne Carriere 
132*0bdd7f5bSEtienne Carriere 	while (io_read32(iwdg_base + IWDG_SR_OFFSET) & IWDG_SR_UPDATE_MASK)
133*0bdd7f5bSEtienne Carriere 		if (timeout_elapsed(timeout_ref))
134*0bdd7f5bSEtienne Carriere 			break;
135*0bdd7f5bSEtienne Carriere 
136*0bdd7f5bSEtienne Carriere 	if (!(io_read32(iwdg_base + IWDG_SR_OFFSET) & IWDG_SR_UPDATE_MASK))
137*0bdd7f5bSEtienne Carriere 		return TEE_ERROR_GENERIC;
138*0bdd7f5bSEtienne Carriere 
139*0bdd7f5bSEtienne Carriere 	return TEE_SUCCESS;
140*0bdd7f5bSEtienne Carriere }
141*0bdd7f5bSEtienne Carriere 
142*0bdd7f5bSEtienne Carriere static TEE_Result configure_timeout(struct stm32_iwdg_device *iwdg)
143*0bdd7f5bSEtienne Carriere {
144*0bdd7f5bSEtienne Carriere 	TEE_Result res = TEE_ERROR_GENERIC;
145*0bdd7f5bSEtienne Carriere 	vaddr_t iwdg_base = get_base(iwdg);
146*0bdd7f5bSEtienne Carriere 	uint32_t rlr_value = 0;
147*0bdd7f5bSEtienne Carriere 
148*0bdd7f5bSEtienne Carriere 	assert(is_enable(iwdg));
149*0bdd7f5bSEtienne Carriere 
150*0bdd7f5bSEtienne Carriere 	rlr_value = iwdg_timeout_cnt(iwdg, iwdg->timeout);
151*0bdd7f5bSEtienne Carriere 	if (!rlr_value)
152*0bdd7f5bSEtienne Carriere 		return TEE_ERROR_GENERIC;
153*0bdd7f5bSEtienne Carriere 
154*0bdd7f5bSEtienne Carriere 	clk_enable(iwdg->clock);
155*0bdd7f5bSEtienne Carriere 
156*0bdd7f5bSEtienne Carriere 	io_write32(iwdg_base + IWDG_KR_OFFSET, IWDG_KR_ACCESS_KEY);
157*0bdd7f5bSEtienne Carriere 	io_write32(iwdg_base + IWDG_PR_OFFSET, IWDG_PR_DIV_256);
158*0bdd7f5bSEtienne Carriere 	io_write32(iwdg_base + IWDG_RLR_OFFSET, rlr_value);
159*0bdd7f5bSEtienne Carriere 	io_write32(iwdg_base + IWDG_KR_OFFSET, IWDG_KR_RELOAD_KEY);
160*0bdd7f5bSEtienne Carriere 
161*0bdd7f5bSEtienne Carriere 	res = iwdg_wait_sync(iwdg);
162*0bdd7f5bSEtienne Carriere 
163*0bdd7f5bSEtienne Carriere 	clk_disable(iwdg->clock);
164*0bdd7f5bSEtienne Carriere 
165*0bdd7f5bSEtienne Carriere 	return res;
166*0bdd7f5bSEtienne Carriere }
167*0bdd7f5bSEtienne Carriere 
168*0bdd7f5bSEtienne Carriere static void iwdg_start(struct stm32_iwdg_device *iwdg)
169*0bdd7f5bSEtienne Carriere {
170*0bdd7f5bSEtienne Carriere 	clk_enable(iwdg->clock);
171*0bdd7f5bSEtienne Carriere 	io_write32(get_base(iwdg) + IWDG_KR_OFFSET, IWDG_KR_START_KEY);
172*0bdd7f5bSEtienne Carriere 	clk_disable(iwdg->clock);
173*0bdd7f5bSEtienne Carriere 
174*0bdd7f5bSEtienne Carriere 	iwdg->flags |= IWDG_FLAGS_ENABLED;
175*0bdd7f5bSEtienne Carriere }
176*0bdd7f5bSEtienne Carriere 
177*0bdd7f5bSEtienne Carriere static void iwdg_refresh(struct stm32_iwdg_device *iwdg)
178*0bdd7f5bSEtienne Carriere {
179*0bdd7f5bSEtienne Carriere 	clk_enable(iwdg->clock);
180*0bdd7f5bSEtienne Carriere 	io_write32(get_base(iwdg) + IWDG_KR_OFFSET, IWDG_KR_RELOAD_KEY);
181*0bdd7f5bSEtienne Carriere 	clk_disable(iwdg->clock);
182*0bdd7f5bSEtienne Carriere }
183*0bdd7f5bSEtienne Carriere 
184*0bdd7f5bSEtienne Carriere /* Operators for watchdog OP-TEE interface */
185*0bdd7f5bSEtienne Carriere static struct stm32_iwdg_device *wdt_chip_to_iwdg(struct wdt_chip *chip)
186*0bdd7f5bSEtienne Carriere {
187*0bdd7f5bSEtienne Carriere 	return container_of(chip, struct stm32_iwdg_device, wdt_chip);
188*0bdd7f5bSEtienne Carriere }
189*0bdd7f5bSEtienne Carriere 
190*0bdd7f5bSEtienne Carriere static void iwdg_wdt_start(struct wdt_chip *chip)
191*0bdd7f5bSEtienne Carriere {
192*0bdd7f5bSEtienne Carriere 	struct stm32_iwdg_device *iwdg = wdt_chip_to_iwdg(chip);
193*0bdd7f5bSEtienne Carriere 
194*0bdd7f5bSEtienne Carriere 	iwdg_start(iwdg);
195*0bdd7f5bSEtienne Carriere 
196*0bdd7f5bSEtienne Carriere 	if (configure_timeout(iwdg))
197*0bdd7f5bSEtienne Carriere 		panic();
198*0bdd7f5bSEtienne Carriere }
199*0bdd7f5bSEtienne Carriere 
200*0bdd7f5bSEtienne Carriere static void iwdg_wdt_refresh(struct wdt_chip *chip)
201*0bdd7f5bSEtienne Carriere {
202*0bdd7f5bSEtienne Carriere 	struct stm32_iwdg_device *iwdg = wdt_chip_to_iwdg(chip);
203*0bdd7f5bSEtienne Carriere 
204*0bdd7f5bSEtienne Carriere 	iwdg_refresh(iwdg);
205*0bdd7f5bSEtienne Carriere }
206*0bdd7f5bSEtienne Carriere 
207*0bdd7f5bSEtienne Carriere static TEE_Result iwdg_wdt_set_timeout(struct wdt_chip *chip,
208*0bdd7f5bSEtienne Carriere 				       unsigned long timeout)
209*0bdd7f5bSEtienne Carriere {
210*0bdd7f5bSEtienne Carriere 	struct stm32_iwdg_device *iwdg = wdt_chip_to_iwdg(chip);
211*0bdd7f5bSEtienne Carriere 
212*0bdd7f5bSEtienne Carriere 	if (!iwdg_timeout_cnt(iwdg, timeout))
213*0bdd7f5bSEtienne Carriere 		return TEE_ERROR_BAD_PARAMETERS;
214*0bdd7f5bSEtienne Carriere 
215*0bdd7f5bSEtienne Carriere 	iwdg->timeout = timeout;
216*0bdd7f5bSEtienne Carriere 
217*0bdd7f5bSEtienne Carriere 	if (is_enable(iwdg)) {
218*0bdd7f5bSEtienne Carriere 		TEE_Result res = TEE_ERROR_GENERIC;
219*0bdd7f5bSEtienne Carriere 
220*0bdd7f5bSEtienne Carriere 		res = configure_timeout(iwdg);
221*0bdd7f5bSEtienne Carriere 		if (res)
222*0bdd7f5bSEtienne Carriere 			return res;
223*0bdd7f5bSEtienne Carriere 	}
224*0bdd7f5bSEtienne Carriere 
225*0bdd7f5bSEtienne Carriere 	return TEE_SUCCESS;
226*0bdd7f5bSEtienne Carriere }
227*0bdd7f5bSEtienne Carriere 
228*0bdd7f5bSEtienne Carriere static const struct wdt_ops stm32_iwdg_ops = {
229*0bdd7f5bSEtienne Carriere 	.start = iwdg_wdt_start,
230*0bdd7f5bSEtienne Carriere 	.ping = iwdg_wdt_refresh,
231*0bdd7f5bSEtienne Carriere 	.set_timeout = iwdg_wdt_set_timeout,
232*0bdd7f5bSEtienne Carriere };
233*0bdd7f5bSEtienne Carriere DECLARE_KEEP_PAGER(stm32_iwdg_ops);
234*0bdd7f5bSEtienne Carriere 
235*0bdd7f5bSEtienne Carriere /* Refresh all registered watchdogs */
236*0bdd7f5bSEtienne Carriere void stm32_iwdg_refresh(void)
237*0bdd7f5bSEtienne Carriere {
238*0bdd7f5bSEtienne Carriere 	struct stm32_iwdg_device *iwdg = NULL;
239*0bdd7f5bSEtienne Carriere 	uint32_t exceptions = cpu_spin_lock_xsave(&iwdg_lock);
240*0bdd7f5bSEtienne Carriere 
241*0bdd7f5bSEtienne Carriere 	SLIST_FOREACH(iwdg, &iwdg_dev_list, link)
242*0bdd7f5bSEtienne Carriere 		iwdg_refresh(iwdg);
243*0bdd7f5bSEtienne Carriere 
244*0bdd7f5bSEtienne Carriere 	cpu_spin_unlock_xrestore(&iwdg_lock, exceptions);
245*0bdd7f5bSEtienne Carriere }
246*0bdd7f5bSEtienne Carriere 
247*0bdd7f5bSEtienne Carriere /* Driver initialization */
248*0bdd7f5bSEtienne Carriere static TEE_Result stm32_iwdg_parse_fdt(struct stm32_iwdg_device *iwdg,
249*0bdd7f5bSEtienne Carriere 				       const void *fdt, int node)
250*0bdd7f5bSEtienne Carriere {
251*0bdd7f5bSEtienne Carriere 	TEE_Result res = TEE_ERROR_GENERIC;
252*0bdd7f5bSEtienne Carriere 	struct dt_node_info dt_info = { };
253*0bdd7f5bSEtienne Carriere 	const fdt32_t *cuint = NULL;
254*0bdd7f5bSEtienne Carriere 
255*0bdd7f5bSEtienne Carriere 	_fdt_fill_device_info(fdt, &dt_info, node);
256*0bdd7f5bSEtienne Carriere 
257*0bdd7f5bSEtienne Carriere 	if (dt_info.reg == DT_INFO_INVALID_REG ||
258*0bdd7f5bSEtienne Carriere 	    dt_info.reg_size == DT_INFO_INVALID_REG_SIZE)
259*0bdd7f5bSEtienne Carriere 		panic();
260*0bdd7f5bSEtienne Carriere 
261*0bdd7f5bSEtienne Carriere 	res = clk_dt_get_by_name(fdt, node, "pclk", &iwdg->clock);
262*0bdd7f5bSEtienne Carriere 	if (res)
263*0bdd7f5bSEtienne Carriere 		return res;
264*0bdd7f5bSEtienne Carriere 
265*0bdd7f5bSEtienne Carriere 	res = clk_dt_get_by_name(fdt, node, "lsi", &iwdg->clk_lsi);
266*0bdd7f5bSEtienne Carriere 	if (res)
267*0bdd7f5bSEtienne Carriere 		return res;
268*0bdd7f5bSEtienne Carriere 
269*0bdd7f5bSEtienne Carriere 	if (dt_info.status == DT_STATUS_OK_NSEC)
270*0bdd7f5bSEtienne Carriere 		iwdg->flags |= IWDG_FLAGS_NON_SECURE;
271*0bdd7f5bSEtienne Carriere 
272*0bdd7f5bSEtienne Carriere 	/* Get IOMEM address */
273*0bdd7f5bSEtienne Carriere 	iwdg->base.pa = dt_info.reg;
274*0bdd7f5bSEtienne Carriere 
275*0bdd7f5bSEtienne Carriere 	if (iwdg->flags & IWDG_FLAGS_NON_SECURE)
276*0bdd7f5bSEtienne Carriere 		io_pa_or_va_nsec(&iwdg->base, dt_info.reg_size);
277*0bdd7f5bSEtienne Carriere 	else
278*0bdd7f5bSEtienne Carriere 		io_pa_or_va_secure(&iwdg->base, dt_info.reg_size);
279*0bdd7f5bSEtienne Carriere 
280*0bdd7f5bSEtienne Carriere 	assert(iwdg->base.va);
281*0bdd7f5bSEtienne Carriere 
282*0bdd7f5bSEtienne Carriere 	/* Get and check timeout value */
283*0bdd7f5bSEtienne Carriere 	cuint = fdt_getprop(fdt, node, "timeout-sec", NULL);
284*0bdd7f5bSEtienne Carriere 	if (!cuint)
285*0bdd7f5bSEtienne Carriere 		return TEE_ERROR_BAD_PARAMETERS;
286*0bdd7f5bSEtienne Carriere 
287*0bdd7f5bSEtienne Carriere 	iwdg->timeout = (int)fdt32_to_cpu(*cuint);
288*0bdd7f5bSEtienne Carriere 	if (!iwdg->timeout)
289*0bdd7f5bSEtienne Carriere 		return TEE_ERROR_BAD_PARAMETERS;
290*0bdd7f5bSEtienne Carriere 
291*0bdd7f5bSEtienne Carriere 	if (!iwdg_timeout_cnt(iwdg, iwdg->timeout)) {
292*0bdd7f5bSEtienne Carriere 		EMSG("Timeout %lu not applicable", iwdg->timeout);
293*0bdd7f5bSEtienne Carriere 		return TEE_ERROR_BAD_PARAMETERS;
294*0bdd7f5bSEtienne Carriere 	}
295*0bdd7f5bSEtienne Carriere 
296*0bdd7f5bSEtienne Carriere 	/* DT can specify low power cases */
297*0bdd7f5bSEtienne Carriere 	if (!fdt_getprop(fdt, node, "stm32,enable-on-stop", NULL))
298*0bdd7f5bSEtienne Carriere 		iwdg->flags |= IWDG_FLAGS_DISABLE_ON_STOP;
299*0bdd7f5bSEtienne Carriere 
300*0bdd7f5bSEtienne Carriere 	if (!fdt_getprop(fdt, node, "stm32,enable-on-standby", NULL))
301*0bdd7f5bSEtienne Carriere 		iwdg->flags |= IWDG_FLAGS_DISABLE_ON_STANDBY;
302*0bdd7f5bSEtienne Carriere 
303*0bdd7f5bSEtienne Carriere 	return TEE_SUCCESS;
304*0bdd7f5bSEtienne Carriere }
305*0bdd7f5bSEtienne Carriere 
306*0bdd7f5bSEtienne Carriere /* Platform should override this function to provide IWDG fuses configuration */
307*0bdd7f5bSEtienne Carriere TEE_Result __weak stm32_get_iwdg_otp_config(paddr_t pbase __unused,
308*0bdd7f5bSEtienne Carriere 					    struct stm32_iwdg_otp_data *otp_d)
309*0bdd7f5bSEtienne Carriere {
310*0bdd7f5bSEtienne Carriere 	otp_d->hw_enabled = false;
311*0bdd7f5bSEtienne Carriere 	otp_d->disable_on_stop = false;
312*0bdd7f5bSEtienne Carriere 	otp_d->disable_on_standby = false;
313*0bdd7f5bSEtienne Carriere 
314*0bdd7f5bSEtienne Carriere 	return TEE_SUCCESS;
315*0bdd7f5bSEtienne Carriere }
316*0bdd7f5bSEtienne Carriere 
317*0bdd7f5bSEtienne Carriere static TEE_Result stm32_iwdg_setup(struct stm32_iwdg_device *iwdg,
318*0bdd7f5bSEtienne Carriere 				   const void *fdt, int node)
319*0bdd7f5bSEtienne Carriere {
320*0bdd7f5bSEtienne Carriere 	struct stm32_iwdg_otp_data otp_data = { };
321*0bdd7f5bSEtienne Carriere 	TEE_Result res = TEE_SUCCESS;
322*0bdd7f5bSEtienne Carriere 
323*0bdd7f5bSEtienne Carriere 	res = stm32_iwdg_parse_fdt(iwdg, fdt, node);
324*0bdd7f5bSEtienne Carriere 	if (res)
325*0bdd7f5bSEtienne Carriere 		return res;
326*0bdd7f5bSEtienne Carriere 
327*0bdd7f5bSEtienne Carriere 	res = stm32_get_iwdg_otp_config(iwdg->base.pa, &otp_data);
328*0bdd7f5bSEtienne Carriere 	if (res)
329*0bdd7f5bSEtienne Carriere 		return res;
330*0bdd7f5bSEtienne Carriere 
331*0bdd7f5bSEtienne Carriere 	if (otp_data.hw_enabled)
332*0bdd7f5bSEtienne Carriere 		iwdg->flags |= IWDG_FLAGS_HW_ENABLED;
333*0bdd7f5bSEtienne Carriere 	if (otp_data.disable_on_stop)
334*0bdd7f5bSEtienne Carriere 		iwdg->flags |= IWDG_FLAGS_DISABLE_ON_STOP;
335*0bdd7f5bSEtienne Carriere 	if (otp_data.disable_on_standby)
336*0bdd7f5bSEtienne Carriere 		iwdg->flags |= IWDG_FLAGS_DISABLE_ON_STANDBY;
337*0bdd7f5bSEtienne Carriere 
338*0bdd7f5bSEtienne Carriere 	/* Enable watchdog source clock once for all */
339*0bdd7f5bSEtienne Carriere 	clk_enable(iwdg->clk_lsi);
340*0bdd7f5bSEtienne Carriere 
341*0bdd7f5bSEtienne Carriere 	if (otp_data.hw_enabled) {
342*0bdd7f5bSEtienne Carriere 		iwdg->flags |= IWDG_FLAGS_ENABLED;
343*0bdd7f5bSEtienne Carriere 
344*0bdd7f5bSEtienne Carriere 		/* Configure timeout if watchdog is already enabled */
345*0bdd7f5bSEtienne Carriere 		res = configure_timeout(iwdg);
346*0bdd7f5bSEtienne Carriere 		if (res)
347*0bdd7f5bSEtienne Carriere 			return res;
348*0bdd7f5bSEtienne Carriere 
349*0bdd7f5bSEtienne Carriere 		iwdg_refresh(iwdg);
350*0bdd7f5bSEtienne Carriere 	}
351*0bdd7f5bSEtienne Carriere 
352*0bdd7f5bSEtienne Carriere 	return TEE_SUCCESS;
353*0bdd7f5bSEtienne Carriere }
354*0bdd7f5bSEtienne Carriere 
355*0bdd7f5bSEtienne Carriere static TEE_Result stm32_iwdg_register(struct stm32_iwdg_device *iwdg)
356*0bdd7f5bSEtienne Carriere {
357*0bdd7f5bSEtienne Carriere 	TEE_Result res = TEE_ERROR_GENERIC;
358*0bdd7f5bSEtienne Carriere 
359*0bdd7f5bSEtienne Carriere 	if (is_assigned_to_nsec(iwdg)) {
360*0bdd7f5bSEtienne Carriere 		stm32mp_register_non_secure_periph_iomem(iwdg->base.pa);
361*0bdd7f5bSEtienne Carriere 	} else {
362*0bdd7f5bSEtienne Carriere 		stm32mp_register_secure_periph_iomem(iwdg->base.pa);
363*0bdd7f5bSEtienne Carriere 
364*0bdd7f5bSEtienne Carriere 		/* Expose watchdog runtime service only to secure IWDG */
365*0bdd7f5bSEtienne Carriere 		iwdg->wdt_chip.ops = &stm32_iwdg_ops;
366*0bdd7f5bSEtienne Carriere 
367*0bdd7f5bSEtienne Carriere 		res = watchdog_register(&iwdg->wdt_chip);
368*0bdd7f5bSEtienne Carriere 		if (res)
369*0bdd7f5bSEtienne Carriere 			return res;
370*0bdd7f5bSEtienne Carriere 	}
371*0bdd7f5bSEtienne Carriere 
372*0bdd7f5bSEtienne Carriere 	SLIST_INSERT_HEAD(&iwdg_dev_list, iwdg, link);
373*0bdd7f5bSEtienne Carriere 
374*0bdd7f5bSEtienne Carriere 	return TEE_SUCCESS;
375*0bdd7f5bSEtienne Carriere }
376*0bdd7f5bSEtienne Carriere 
377*0bdd7f5bSEtienne Carriere static TEE_Result stm32_iwdg_probe(const void *fdt, int node,
378*0bdd7f5bSEtienne Carriere 				   const void *compat_data __unused)
379*0bdd7f5bSEtienne Carriere {
380*0bdd7f5bSEtienne Carriere 	struct stm32_iwdg_device *iwdg = NULL;
381*0bdd7f5bSEtienne Carriere 	TEE_Result res = TEE_SUCCESS;
382*0bdd7f5bSEtienne Carriere 
383*0bdd7f5bSEtienne Carriere 	iwdg = calloc(1, sizeof(*iwdg));
384*0bdd7f5bSEtienne Carriere 	if (!iwdg)
385*0bdd7f5bSEtienne Carriere 		return TEE_ERROR_OUT_OF_MEMORY;
386*0bdd7f5bSEtienne Carriere 
387*0bdd7f5bSEtienne Carriere 	res = stm32_iwdg_setup(iwdg, fdt, node);
388*0bdd7f5bSEtienne Carriere 	if (res)
389*0bdd7f5bSEtienne Carriere 		goto err;
390*0bdd7f5bSEtienne Carriere 
391*0bdd7f5bSEtienne Carriere 	res = stm32_iwdg_register(iwdg);
392*0bdd7f5bSEtienne Carriere 	if (res)
393*0bdd7f5bSEtienne Carriere 		goto err;
394*0bdd7f5bSEtienne Carriere 
395*0bdd7f5bSEtienne Carriere 	return TEE_SUCCESS;
396*0bdd7f5bSEtienne Carriere 
397*0bdd7f5bSEtienne Carriere err:
398*0bdd7f5bSEtienne Carriere 	free(iwdg);
399*0bdd7f5bSEtienne Carriere 	return res;
400*0bdd7f5bSEtienne Carriere }
401*0bdd7f5bSEtienne Carriere 
402*0bdd7f5bSEtienne Carriere static const struct dt_device_match stm32_iwdg_match_table[] = {
403*0bdd7f5bSEtienne Carriere 	{ .compatible = "st,stm32mp1-iwdg" },
404*0bdd7f5bSEtienne Carriere 	{ }
405*0bdd7f5bSEtienne Carriere };
406*0bdd7f5bSEtienne Carriere 
407*0bdd7f5bSEtienne Carriere DEFINE_DT_DRIVER(stm32_iwdg_dt_driver) = {
408*0bdd7f5bSEtienne Carriere 	.name = "stm32-iwdg",
409*0bdd7f5bSEtienne Carriere 	.match_table = stm32_iwdg_match_table,
410*0bdd7f5bSEtienne Carriere 	.probe = stm32_iwdg_probe,
411*0bdd7f5bSEtienne Carriere };
412