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>
26f6ee86ecSClé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 */
324a62f44cSClément Le Goffic #define IWDG_TIMEOUT_US U(40000)
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
514a62f44cSClément Le Goffic /* Use a fixed prescaler divider of 1024 */
524a62f44cSClément Le Goffic #define IWDG_PRESCALER_1024 U(1024)
534a62f44cSClément Le Goffic #define IWDG_PR_DIV_1024 U(0x8)
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
94f6ee86ecSClément Le Goffic * @saved_nb_int - Saved number of interrupts before panic
95f6ee86ecSClé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
99f6ee86ecSClé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;
111f6ee86ecSClément Le Goffic unsigned long early_timeout;
112f6ee86ecSClément Le Goffic unsigned long saved_nb_int;
113f6ee86ecSClé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;
117f6ee86ecSClément Le Goffic unsigned long max_hw_timeout;
1180bdd7f5bSEtienne Carriere };
1190bdd7f5bSEtienne Carriere
sr_ewif_mask(struct stm32_iwdg_device * iwdg)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
get_base(struct stm32_iwdg_device * iwdg)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
iwdg_wdt_set_enabled(struct stm32_iwdg_device * iwdg)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
iwdg_wdt_is_enabled(struct stm32_iwdg_device * iwdg)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 */
iwdg_timeout_cnt(struct stm32_iwdg_device * iwdg,unsigned long to_sec)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);
1484a62f44cSClément Le Goffic uint64_t cnt = (reload / IWDG_PRESCALER_1024) - 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 */
iwdg_wait_sync(struct stm32_iwdg_device * iwdg)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
stm32_iwdg_it_ack(struct stm32_iwdg_device * iwdg)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
stm32_iwdg_it_handler(struct itr_handler * h)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
205f6ee86ecSClément Le Goffic if (iwdg->nb_int > 0) {
206b49d10f7SPatrick Delaunay /* Decrease interrupt counter when watchdog is not stopped*/
207b49d10f7SPatrick Delaunay if (iwdg->nb_int < ULONG_MAX)
208f6ee86ecSClément Le Goffic iwdg->nb_int--;
209f6ee86ecSClément Le Goffic io_write32(get_base(iwdg) + IWDG_KR_OFFSET, IWDG_KR_RELOAD_KEY);
210f6ee86ecSClément Le Goffic } else {
211e4b8d29aSEtienne Carriere panic("Watchdog");
212f6ee86ecSClément Le Goffic }
213e4b8d29aSEtienne Carriere
214e4b8d29aSEtienne Carriere return ITRR_HANDLED;
215e4b8d29aSEtienne Carriere }
216e4b8d29aSEtienne Carriere DECLARE_KEEP_PAGER(stm32_iwdg_it_handler);
217e4b8d29aSEtienne Carriere
configure_timeout(struct stm32_iwdg_device * iwdg)2180bdd7f5bSEtienne Carriere static TEE_Result configure_timeout(struct stm32_iwdg_device *iwdg)
2190bdd7f5bSEtienne Carriere {
2200bdd7f5bSEtienne Carriere TEE_Result res = TEE_ERROR_GENERIC;
2210bdd7f5bSEtienne Carriere vaddr_t iwdg_base = get_base(iwdg);
2220bdd7f5bSEtienne Carriere uint32_t rlr_value = 0;
223e4b8d29aSEtienne Carriere uint32_t ewie_value = 0;
2240bdd7f5bSEtienne Carriere
2252f9b82faSEtienne Carriere assert(iwdg_wdt_is_enabled(iwdg));
2260bdd7f5bSEtienne Carriere
2270bdd7f5bSEtienne Carriere rlr_value = iwdg_timeout_cnt(iwdg, iwdg->timeout);
2280bdd7f5bSEtienne Carriere if (!rlr_value)
2290bdd7f5bSEtienne Carriere return TEE_ERROR_GENERIC;
2300bdd7f5bSEtienne Carriere
231e4b8d29aSEtienne Carriere if (iwdg->itr_handler) {
232f6ee86ecSClément Le Goffic ewie_value = iwdg_timeout_cnt(iwdg, iwdg->early_timeout);
233e4b8d29aSEtienne Carriere interrupt_enable(iwdg->itr_chip, iwdg->itr_num);
234e4b8d29aSEtienne Carriere }
235e4b8d29aSEtienne Carriere
2360bdd7f5bSEtienne Carriere io_write32(iwdg_base + IWDG_KR_OFFSET, IWDG_KR_ACCESS_KEY);
2374a62f44cSClément Le Goffic io_write32(iwdg_base + IWDG_PR_OFFSET, IWDG_PR_DIV_1024);
2380bdd7f5bSEtienne Carriere io_write32(iwdg_base + IWDG_RLR_OFFSET, rlr_value);
239e4b8d29aSEtienne Carriere if (ewie_value &&
240e4b8d29aSEtienne Carriere !(io_read32(iwdg_base + IWDG_EWCR_OFFSET) & IWDG_EWCR_EWIE))
241e4b8d29aSEtienne Carriere io_write32(iwdg_base + IWDG_EWCR_OFFSET,
242e4b8d29aSEtienne Carriere ewie_value | IWDG_EWCR_EWIE);
2430bdd7f5bSEtienne Carriere
2440bdd7f5bSEtienne Carriere res = iwdg_wait_sync(iwdg);
2450bdd7f5bSEtienne Carriere
246e4b8d29aSEtienne Carriere io_write32(iwdg_base + IWDG_KR_OFFSET, IWDG_KR_RELOAD_KEY);
247e4b8d29aSEtienne Carriere
2480bdd7f5bSEtienne Carriere return res;
2490bdd7f5bSEtienne Carriere }
2500bdd7f5bSEtienne Carriere
iwdg_start(struct stm32_iwdg_device * iwdg)2510bdd7f5bSEtienne Carriere static void iwdg_start(struct stm32_iwdg_device *iwdg)
2520bdd7f5bSEtienne Carriere {
253eb47832fSAntonio Borneo TEE_Result res = TEE_ERROR_GENERIC;
254eb47832fSAntonio Borneo
255eb47832fSAntonio Borneo res = tee_time_get_sys_time(&iwdg->last_refresh);
256eb47832fSAntonio Borneo if (res)
257eb47832fSAntonio Borneo panic();
258eb47832fSAntonio Borneo
2590bdd7f5bSEtienne Carriere io_write32(get_base(iwdg) + IWDG_KR_OFFSET, IWDG_KR_START_KEY);
2600bdd7f5bSEtienne Carriere
2612f9b82faSEtienne Carriere iwdg_wdt_set_enabled(iwdg);
2620bdd7f5bSEtienne Carriere }
2630bdd7f5bSEtienne Carriere
iwdg_refresh(struct stm32_iwdg_device * iwdg)2640bdd7f5bSEtienne Carriere static void iwdg_refresh(struct stm32_iwdg_device *iwdg)
2650bdd7f5bSEtienne Carriere {
266eb47832fSAntonio Borneo TEE_Result res = TEE_ERROR_GENERIC;
267eb47832fSAntonio Borneo
268eb47832fSAntonio Borneo res = tee_time_get_sys_time(&iwdg->last_refresh);
269eb47832fSAntonio Borneo if (res)
270eb47832fSAntonio Borneo panic();
271eb47832fSAntonio Borneo
2720bdd7f5bSEtienne Carriere io_write32(get_base(iwdg) + IWDG_KR_OFFSET, IWDG_KR_RELOAD_KEY);
2730bdd7f5bSEtienne Carriere }
2740bdd7f5bSEtienne Carriere
2750bdd7f5bSEtienne Carriere /* Operators for watchdog OP-TEE interface */
wdt_chip_to_iwdg(struct wdt_chip * chip)2760bdd7f5bSEtienne Carriere static struct stm32_iwdg_device *wdt_chip_to_iwdg(struct wdt_chip *chip)
2770bdd7f5bSEtienne Carriere {
2780bdd7f5bSEtienne Carriere return container_of(chip, struct stm32_iwdg_device, wdt_chip);
2790bdd7f5bSEtienne Carriere }
2800bdd7f5bSEtienne Carriere
iwdg_wdt_init(struct wdt_chip * chip,unsigned long * min_timeout,unsigned long * max_timeout)281fc9063ddSEtienne Carriere static TEE_Result iwdg_wdt_init(struct wdt_chip *chip,
282fc9063ddSEtienne Carriere unsigned long *min_timeout,
283fc9063ddSEtienne Carriere unsigned long *max_timeout)
284fc9063ddSEtienne Carriere {
285fc9063ddSEtienne Carriere struct stm32_iwdg_device *iwdg = wdt_chip_to_iwdg(chip);
286fc9063ddSEtienne Carriere unsigned long rate = clk_get_rate(iwdg->clk_lsi);
287fc9063ddSEtienne Carriere
288fc9063ddSEtienne Carriere if (!rate)
289fc9063ddSEtienne Carriere return TEE_ERROR_GENERIC;
290fc9063ddSEtienne Carriere
291fc9063ddSEtienne Carriere /* Be safe and expect any counter to be above 2 */
2924a62f44cSClément Le Goffic *min_timeout = 3 * IWDG_PRESCALER_1024 / rate;
2934a62f44cSClément Le Goffic *max_timeout = (IWDG_CNT_MASK + 1) * IWDG_PRESCALER_1024 / rate;
294fc9063ddSEtienne Carriere
295fc9063ddSEtienne Carriere return TEE_SUCCESS;
296fc9063ddSEtienne Carriere }
297fc9063ddSEtienne Carriere
iwdg_wdt_start(struct wdt_chip * chip)2980bdd7f5bSEtienne Carriere static void iwdg_wdt_start(struct wdt_chip *chip)
2990bdd7f5bSEtienne Carriere {
3000bdd7f5bSEtienne Carriere struct stm32_iwdg_device *iwdg = wdt_chip_to_iwdg(chip);
3010bdd7f5bSEtienne Carriere
3020bdd7f5bSEtienne Carriere iwdg_start(iwdg);
303dff60fe8SClément Le Goffic if (iwdg->reset && iwdg->itr_handler)
304dff60fe8SClément Le Goffic stm32_iwdg_it_ack(iwdg);
3050bdd7f5bSEtienne Carriere
3060bdd7f5bSEtienne Carriere if (configure_timeout(iwdg))
3070bdd7f5bSEtienne Carriere panic();
308dff60fe8SClément Le Goffic
309dff60fe8SClément Le Goffic if (iwdg->reset)
310dff60fe8SClément Le Goffic if (rstctrl_assert(iwdg->reset))
311dff60fe8SClément Le Goffic panic();
312dff60fe8SClément Le Goffic }
313dff60fe8SClément Le Goffic
iwdg_wdt_stop(struct wdt_chip * chip)314dff60fe8SClément Le Goffic static void iwdg_wdt_stop(struct wdt_chip *chip)
315dff60fe8SClément Le Goffic {
316dff60fe8SClément Le Goffic struct stm32_iwdg_device *iwdg = wdt_chip_to_iwdg(chip);
317dff60fe8SClément Le Goffic
318dff60fe8SClément Le Goffic if (iwdg->reset) {
319dff60fe8SClément Le Goffic if (rstctrl_deassert(iwdg->reset))
320dff60fe8SClément Le Goffic panic();
321dff60fe8SClément Le Goffic if (iwdg->itr_handler)
322dff60fe8SClément Le Goffic interrupt_disable(iwdg->itr_chip, iwdg->itr_num);
323dff60fe8SClément Le Goffic }
324b49d10f7SPatrick Delaunay
325b49d10f7SPatrick Delaunay /* Reload on early interrupt and no more panic */
326b49d10f7SPatrick Delaunay iwdg->saved_nb_int = ULONG_MAX;
327b49d10f7SPatrick Delaunay iwdg->nb_int = ULONG_MAX;
3280bdd7f5bSEtienne Carriere }
3290bdd7f5bSEtienne Carriere
iwdg_wdt_refresh(struct wdt_chip * chip)3300bdd7f5bSEtienne Carriere static void iwdg_wdt_refresh(struct wdt_chip *chip)
3310bdd7f5bSEtienne Carriere {
3320bdd7f5bSEtienne Carriere struct stm32_iwdg_device *iwdg = wdt_chip_to_iwdg(chip);
3330bdd7f5bSEtienne Carriere
334f6ee86ecSClément Le Goffic iwdg->nb_int = iwdg->saved_nb_int;
3350bdd7f5bSEtienne Carriere iwdg_refresh(iwdg);
3360bdd7f5bSEtienne Carriere }
3370bdd7f5bSEtienne Carriere
stm32_iwdg_handle_timeouts(struct stm32_iwdg_device * iwdg,unsigned long timeout_sec)338f6ee86ecSClément Le Goffic static void stm32_iwdg_handle_timeouts(struct stm32_iwdg_device *iwdg,
339f6ee86ecSClément Le Goffic unsigned long timeout_sec)
340f6ee86ecSClément Le Goffic {
341f6ee86ecSClément Le Goffic unsigned long interval = 0;
342f6ee86ecSClément Le Goffic unsigned long rate = 0;
343f6ee86ecSClément Le Goffic unsigned long n = 0;
344f6ee86ecSClément Le Goffic long w = 0;
345f6ee86ecSClément Le Goffic
346f6ee86ecSClément Le Goffic rate = clk_get_rate(iwdg->clk_lsi);
3474a62f44cSClément Le Goffic iwdg->max_hw_timeout = (IWDG_CNT_MASK + 1) * IWDG_PRESCALER_1024 / rate;
348f6ee86ecSClément Le Goffic
349f6ee86ecSClément Le Goffic if (timeout_sec > iwdg->max_hw_timeout) {
350f6ee86ecSClément Le Goffic IMSG("Timeout exceeds hardware capability, approximate it");
351f6ee86ecSClément Le Goffic interval = iwdg->max_hw_timeout - IWDG_ETIMEOUT_SEC;
352f6ee86ecSClément Le Goffic n = (timeout_sec - IWDG_ETIMEOUT_SEC) / interval;
353f6ee86ecSClément Le Goffic w = ((timeout_sec - IWDG_ETIMEOUT_SEC) / (n + 1)) +
354f6ee86ecSClément Le Goffic IWDG_ETIMEOUT_SEC;
355f6ee86ecSClément Le Goffic iwdg->timeout = w;
356f6ee86ecSClément Le Goffic iwdg->early_timeout = IWDG_ETIMEOUT_SEC;
357f6ee86ecSClément Le Goffic } else {
358f6ee86ecSClément Le Goffic iwdg->timeout = timeout_sec;
359f6ee86ecSClément Le Goffic if (iwdg->timeout >= 2 * IWDG_ETIMEOUT_SEC)
360f6ee86ecSClément Le Goffic iwdg->early_timeout = IWDG_ETIMEOUT_SEC;
361f6ee86ecSClément Le Goffic else
362f6ee86ecSClément Le Goffic iwdg->early_timeout = iwdg->timeout / 4;
363f6ee86ecSClément Le Goffic }
364f6ee86ecSClément Le Goffic
365f6ee86ecSClément Le Goffic if (!iwdg->early_timeout)
366f6ee86ecSClément Le Goffic iwdg->early_timeout = 1;
367f6ee86ecSClément Le Goffic
368f6ee86ecSClément Le Goffic iwdg->saved_nb_int = n;
369f6ee86ecSClément Le Goffic iwdg->nb_int = n;
370f6ee86ecSClément Le Goffic }
371f6ee86ecSClément Le Goffic
iwdg_wdt_set_timeout(struct wdt_chip * chip,unsigned long timeout)3720bdd7f5bSEtienne Carriere static TEE_Result iwdg_wdt_set_timeout(struct wdt_chip *chip,
3730bdd7f5bSEtienne Carriere unsigned long timeout)
3740bdd7f5bSEtienne Carriere {
3750bdd7f5bSEtienne Carriere struct stm32_iwdg_device *iwdg = wdt_chip_to_iwdg(chip);
3760bdd7f5bSEtienne Carriere
3772f9b82faSEtienne Carriere if (iwdg_wdt_is_enabled(iwdg)) {
3780bdd7f5bSEtienne Carriere TEE_Result res = TEE_ERROR_GENERIC;
3790bdd7f5bSEtienne Carriere
380f6ee86ecSClément Le Goffic stm32_iwdg_handle_timeouts(iwdg, timeout);
381f6ee86ecSClément Le Goffic
3820bdd7f5bSEtienne Carriere res = configure_timeout(iwdg);
3830bdd7f5bSEtienne Carriere if (res)
3840bdd7f5bSEtienne Carriere return res;
3850bdd7f5bSEtienne Carriere }
3860bdd7f5bSEtienne Carriere
3870bdd7f5bSEtienne Carriere return TEE_SUCCESS;
3880bdd7f5bSEtienne Carriere }
3890bdd7f5bSEtienne Carriere
iwdg_wdt_get_timeleft(struct wdt_chip * chip,bool * is_started,unsigned long * timeleft)390eb47832fSAntonio Borneo static TEE_Result iwdg_wdt_get_timeleft(struct wdt_chip *chip, bool *is_started,
391eb47832fSAntonio Borneo unsigned long *timeleft)
392eb47832fSAntonio Borneo {
393eb47832fSAntonio Borneo struct stm32_iwdg_device *iwdg = wdt_chip_to_iwdg(chip);
394eb47832fSAntonio Borneo TEE_Result res = TEE_ERROR_GENERIC;
395eb47832fSAntonio Borneo TEE_Time time = { };
396eb47832fSAntonio Borneo TEE_Time now = { };
397eb47832fSAntonio Borneo
398eb47832fSAntonio Borneo *is_started = iwdg_wdt_is_enabled(iwdg);
399eb47832fSAntonio Borneo
400eb47832fSAntonio Borneo if (!*is_started)
401eb47832fSAntonio Borneo return TEE_SUCCESS;
402eb47832fSAntonio Borneo
403eb47832fSAntonio Borneo res = tee_time_get_sys_time(&now);
404eb47832fSAntonio Borneo if (res)
405eb47832fSAntonio Borneo panic();
406eb47832fSAntonio Borneo
407f6ee86ecSClément Le Goffic time.seconds =
408f6ee86ecSClément Le Goffic (iwdg->timeout - iwdg->early_timeout) * iwdg->saved_nb_int
409f6ee86ecSClément Le Goffic + iwdg->early_timeout;
410eb47832fSAntonio Borneo TEE_TIME_ADD(iwdg->last_refresh, time, time);
411eb47832fSAntonio Borneo if (TEE_TIME_LE(time, now)) {
412eb47832fSAntonio Borneo *timeleft = 0;
413eb47832fSAntonio Borneo } else {
414eb47832fSAntonio Borneo TEE_TIME_SUB(time, now, time);
415eb47832fSAntonio Borneo *timeleft = time.seconds;
416eb47832fSAntonio Borneo }
417eb47832fSAntonio Borneo
418eb47832fSAntonio Borneo return TEE_SUCCESS;
419eb47832fSAntonio Borneo }
420eb47832fSAntonio Borneo
4210bdd7f5bSEtienne Carriere static const struct wdt_ops stm32_iwdg_ops = {
422fc9063ddSEtienne Carriere .init = iwdg_wdt_init,
4230bdd7f5bSEtienne Carriere .start = iwdg_wdt_start,
424dff60fe8SClément Le Goffic .stop = iwdg_wdt_stop,
4250bdd7f5bSEtienne Carriere .ping = iwdg_wdt_refresh,
4260bdd7f5bSEtienne Carriere .set_timeout = iwdg_wdt_set_timeout,
427eb47832fSAntonio Borneo .get_timeleft = iwdg_wdt_get_timeleft,
4280bdd7f5bSEtienne Carriere };
4290bdd7f5bSEtienne Carriere DECLARE_KEEP_PAGER(stm32_iwdg_ops);
4300bdd7f5bSEtienne Carriere
4310bdd7f5bSEtienne Carriere /* Driver initialization */
stm32_iwdg_parse_fdt(struct stm32_iwdg_device * iwdg,const void * fdt,int node)4320bdd7f5bSEtienne Carriere static TEE_Result stm32_iwdg_parse_fdt(struct stm32_iwdg_device *iwdg,
4330bdd7f5bSEtienne Carriere const void *fdt, int node)
4340bdd7f5bSEtienne Carriere {
4350bdd7f5bSEtienne Carriere TEE_Result res = TEE_ERROR_GENERIC;
4360bdd7f5bSEtienne Carriere struct dt_node_info dt_info = { };
4370bdd7f5bSEtienne Carriere const fdt32_t *cuint = NULL;
4380bdd7f5bSEtienne Carriere
439f354a5d8SGatien Chevallier fdt_fill_device_info(fdt, &dt_info, node);
4400bdd7f5bSEtienne Carriere
4410bdd7f5bSEtienne Carriere if (dt_info.reg == DT_INFO_INVALID_REG ||
4420bdd7f5bSEtienne Carriere dt_info.reg_size == DT_INFO_INVALID_REG_SIZE)
4430bdd7f5bSEtienne Carriere panic();
4440bdd7f5bSEtienne Carriere
445b2f17e87SEtienne Carriere res = clk_dt_get_by_name(fdt, node, "pclk", &iwdg->clk_pclk);
4460bdd7f5bSEtienne Carriere if (res)
4470bdd7f5bSEtienne Carriere return res;
4480bdd7f5bSEtienne Carriere
4490bdd7f5bSEtienne Carriere res = clk_dt_get_by_name(fdt, node, "lsi", &iwdg->clk_lsi);
4500bdd7f5bSEtienne Carriere if (res)
4510bdd7f5bSEtienne Carriere return res;
4520bdd7f5bSEtienne Carriere
453e4b8d29aSEtienne Carriere res = interrupt_dt_get(fdt, node, &iwdg->itr_chip, &iwdg->itr_num);
454e4b8d29aSEtienne Carriere if (res && res != TEE_ERROR_ITEM_NOT_FOUND)
455e4b8d29aSEtienne Carriere return res;
456e4b8d29aSEtienne Carriere if (!res) {
457e4b8d29aSEtienne Carriere res = interrupt_create_handler(iwdg->itr_chip, iwdg->itr_num,
458e4b8d29aSEtienne Carriere stm32_iwdg_it_handler, iwdg, 0,
459e4b8d29aSEtienne Carriere &iwdg->itr_handler);
460e4b8d29aSEtienne Carriere if (res)
461e4b8d29aSEtienne Carriere return res;
462e4b8d29aSEtienne Carriere }
463e4b8d29aSEtienne Carriere
464dff60fe8SClément Le Goffic res = rstctrl_dt_get_by_index(fdt, node, 0, &iwdg->reset);
465dff60fe8SClément Le Goffic if (res && res != TEE_ERROR_ITEM_NOT_FOUND)
466dff60fe8SClément Le Goffic goto err_itr;
467dff60fe8SClément Le Goffic
4680bdd7f5bSEtienne Carriere /* Get IOMEM address */
4690bdd7f5bSEtienne Carriere iwdg->base.pa = dt_info.reg;
4700bdd7f5bSEtienne Carriere io_pa_or_va_secure(&iwdg->base, dt_info.reg_size);
4710bdd7f5bSEtienne Carriere assert(iwdg->base.va);
4720bdd7f5bSEtienne Carriere
4730bdd7f5bSEtienne Carriere /* Get and check timeout value */
4740bdd7f5bSEtienne Carriere cuint = fdt_getprop(fdt, node, "timeout-sec", NULL);
475e4b8d29aSEtienne Carriere if (!cuint) {
476e4b8d29aSEtienne Carriere res = TEE_ERROR_BAD_PARAMETERS;
477e4b8d29aSEtienne Carriere goto err_itr;
478e4b8d29aSEtienne Carriere }
4790bdd7f5bSEtienne Carriere
4800bdd7f5bSEtienne Carriere iwdg->timeout = (int)fdt32_to_cpu(*cuint);
481e4b8d29aSEtienne Carriere if (!iwdg->timeout) {
482e4b8d29aSEtienne Carriere res = TEE_ERROR_BAD_PARAMETERS;
483e4b8d29aSEtienne Carriere goto err_itr;
484e4b8d29aSEtienne Carriere }
4850bdd7f5bSEtienne Carriere
4860bdd7f5bSEtienne Carriere return TEE_SUCCESS;
487e4b8d29aSEtienne Carriere
488e4b8d29aSEtienne Carriere err_itr:
489e4b8d29aSEtienne Carriere interrupt_remove_free_handler(iwdg->itr_handler);
490e4b8d29aSEtienne Carriere
491e4b8d29aSEtienne Carriere return res;
4920bdd7f5bSEtienne Carriere }
4930bdd7f5bSEtienne Carriere
iwdg_wdt_get_version_and_status(struct stm32_iwdg_device * iwdg)4943d5793d2SAntonio Borneo static void iwdg_wdt_get_version_and_status(struct stm32_iwdg_device *iwdg)
4953d5793d2SAntonio Borneo {
4963d5793d2SAntonio Borneo vaddr_t iwdg_base = get_base(iwdg);
4973d5793d2SAntonio Borneo uint32_t rlr_value = 0;
4983d5793d2SAntonio Borneo
4993d5793d2SAntonio Borneo iwdg->hw_version = io_read32(iwdg_base + IWDG_VERR_OFFSET) &
5003d5793d2SAntonio Borneo IWDG_VERR_REV_MASK;
5013d5793d2SAntonio Borneo
5023d5793d2SAntonio Borneo /* Test if watchdog is already running */
5033d5793d2SAntonio Borneo if (iwdg->hw_version >= IWDG_ONF_MIN_VER) {
5043d5793d2SAntonio Borneo if (io_read32(iwdg_base + IWDG_SR_OFFSET) & IWDG_SR_ONF)
5053d5793d2SAntonio Borneo iwdg_wdt_set_enabled(iwdg);
5063d5793d2SAntonio Borneo } else {
5073d5793d2SAntonio Borneo /*
5083d5793d2SAntonio Borneo * Workaround for old versions without IWDG_SR_ONF bit:
5093d5793d2SAntonio Borneo * - write in IWDG_RLR_OFFSET
5103d5793d2SAntonio Borneo * - wait for sync
5113d5793d2SAntonio Borneo * - if sync succeeds, then iwdg is running
5123d5793d2SAntonio Borneo */
5133d5793d2SAntonio Borneo io_write32(iwdg_base + IWDG_KR_OFFSET, IWDG_KR_ACCESS_KEY);
5143d5793d2SAntonio Borneo
5153d5793d2SAntonio Borneo rlr_value = io_read32(iwdg_base + IWDG_RLR_OFFSET);
5163d5793d2SAntonio Borneo io_write32(iwdg_base + IWDG_RLR_OFFSET, rlr_value);
5173d5793d2SAntonio Borneo
5183d5793d2SAntonio Borneo if (!iwdg_wait_sync(iwdg))
5193d5793d2SAntonio Borneo iwdg_wdt_set_enabled(iwdg);
5203d5793d2SAntonio Borneo
5213d5793d2SAntonio Borneo io_write32(iwdg_base + IWDG_KR_OFFSET, IWDG_KR_WPROT_KEY);
5223d5793d2SAntonio Borneo }
5233d5793d2SAntonio Borneo
5243d5793d2SAntonio Borneo DMSG("Watchdog is %sabled", iwdg_wdt_is_enabled(iwdg) ? "en" : "dis");
5253d5793d2SAntonio Borneo }
5263d5793d2SAntonio Borneo
stm32_iwdg_pm(enum pm_op op,unsigned int pm_hint __unused,const struct pm_callback_handle * pm_handle)527235baec9SEtienne Carriere static TEE_Result stm32_iwdg_pm(enum pm_op op, unsigned int pm_hint __unused,
528235baec9SEtienne Carriere const struct pm_callback_handle *pm_handle)
529235baec9SEtienne Carriere {
530235baec9SEtienne Carriere struct stm32_iwdg_device *iwdg = PM_CALLBACK_GET_HANDLE(pm_handle);
531235baec9SEtienne Carriere
532235baec9SEtienne Carriere if (op == PM_OP_RESUME) {
533235baec9SEtienne Carriere clk_enable(iwdg->clk_lsi);
534235baec9SEtienne Carriere clk_enable(iwdg->clk_pclk);
535235baec9SEtienne Carriere } else {
536235baec9SEtienne Carriere clk_disable(iwdg->clk_lsi);
537235baec9SEtienne Carriere clk_disable(iwdg->clk_pclk);
538235baec9SEtienne Carriere }
539235baec9SEtienne Carriere
540235baec9SEtienne Carriere return TEE_SUCCESS;
541235baec9SEtienne Carriere }
542235baec9SEtienne Carriere DECLARE_KEEP_PAGER(stm32_iwdg_pm);
543235baec9SEtienne Carriere
stm32_iwdg_probe(const void * fdt,int node,const void * compat_data __unused)5440bdd7f5bSEtienne Carriere static TEE_Result stm32_iwdg_probe(const void *fdt, int node,
5450bdd7f5bSEtienne Carriere const void *compat_data __unused)
5460bdd7f5bSEtienne Carriere {
5470bdd7f5bSEtienne Carriere struct stm32_iwdg_device *iwdg = NULL;
5480bdd7f5bSEtienne Carriere TEE_Result res = TEE_SUCCESS;
5490bdd7f5bSEtienne Carriere
5500bdd7f5bSEtienne Carriere iwdg = calloc(1, sizeof(*iwdg));
5510bdd7f5bSEtienne Carriere if (!iwdg)
5520bdd7f5bSEtienne Carriere return TEE_ERROR_OUT_OF_MEMORY;
5530bdd7f5bSEtienne Carriere
554*bf491f27SClément Le Goffic res = stm32_iwdg_parse_fdt(iwdg, fdt, node);
5550bdd7f5bSEtienne Carriere if (res)
556235baec9SEtienne Carriere goto out_free;
5570bdd7f5bSEtienne Carriere
558*bf491f27SClément Le Goffic /* Enable watchdog source and bus clocks once for all */
559*bf491f27SClément Le Goffic if (clk_enable(iwdg->clk_lsi))
560*bf491f27SClément Le Goffic panic();
561*bf491f27SClément Le Goffic
562*bf491f27SClément Le Goffic if (clk_enable(iwdg->clk_pclk))
563*bf491f27SClément Le Goffic panic();
564*bf491f27SClément Le Goffic
565*bf491f27SClément Le Goffic iwdg_wdt_get_version_and_status(iwdg);
566*bf491f27SClément Le Goffic
567*bf491f27SClément Le Goffic res = iwdg_wdt_set_timeout(&iwdg->wdt_chip, iwdg->timeout);
568*bf491f27SClément Le Goffic if (res)
569*bf491f27SClément Le Goffic panic();
570*bf491f27SClément Le Goffic
571*bf491f27SClément Le Goffic if (iwdg_wdt_is_enabled(iwdg))
572*bf491f27SClément Le Goffic iwdg_wdt_refresh(&iwdg->wdt_chip);
573*bf491f27SClément Le Goffic
574a096e2d9SEtienne Carriere iwdg->wdt_chip.ops = &stm32_iwdg_ops;
575a096e2d9SEtienne Carriere
576235baec9SEtienne Carriere register_pm_core_service_cb(stm32_iwdg_pm, iwdg, "stm32-iwdg");
577a096e2d9SEtienne Carriere
578235baec9SEtienne Carriere res = watchdog_register(&iwdg->wdt_chip);
5790bdd7f5bSEtienne Carriere if (res)
580235baec9SEtienne Carriere goto out_pm;
581235baec9SEtienne Carriere
582235baec9SEtienne Carriere return TEE_SUCCESS;
583235baec9SEtienne Carriere
584235baec9SEtienne Carriere out_pm:
585235baec9SEtienne Carriere unregister_pm_core_service_cb(stm32_iwdg_pm, iwdg);
586235baec9SEtienne Carriere out_free:
5870bdd7f5bSEtienne Carriere free(iwdg);
588a096e2d9SEtienne Carriere
5890bdd7f5bSEtienne Carriere return res;
5900bdd7f5bSEtienne Carriere }
5910bdd7f5bSEtienne Carriere
5920bdd7f5bSEtienne Carriere static const struct dt_device_match stm32_iwdg_match_table[] = {
5930bdd7f5bSEtienne Carriere { .compatible = "st,stm32mp1-iwdg" },
5940bdd7f5bSEtienne Carriere { }
5950bdd7f5bSEtienne Carriere };
5960bdd7f5bSEtienne Carriere
5970bdd7f5bSEtienne Carriere DEFINE_DT_DRIVER(stm32_iwdg_dt_driver) = {
5980bdd7f5bSEtienne Carriere .name = "stm32-iwdg",
5990bdd7f5bSEtienne Carriere .match_table = stm32_iwdg_match_table,
6000bdd7f5bSEtienne Carriere .probe = stm32_iwdg_probe,
6010bdd7f5bSEtienne Carriere };
602