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 */ 32*4a62f44cSClé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 51*4a62f44cSClément Le Goffic /* Use a fixed prescaler divider of 1024 */ 52*4a62f44cSClément Le Goffic #define IWDG_PRESCALER_1024 U(1024) 53*4a62f44cSClé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 120e4b8d29aSEtienne Carriere static uint32_t sr_ewif_mask(struct stm32_iwdg_device *iwdg) 121e4b8d29aSEtienne Carriere { 122e4b8d29aSEtienne Carriere if (iwdg->hw_version >= IWDG_ICR_MIN_VER) 123e4b8d29aSEtienne Carriere return IWDG_SR_EWIF_V40; 124e4b8d29aSEtienne Carriere else 125e4b8d29aSEtienne Carriere return IWDG_SR_EWIF; 126e4b8d29aSEtienne Carriere } 127e4b8d29aSEtienne Carriere 1280bdd7f5bSEtienne Carriere static vaddr_t get_base(struct stm32_iwdg_device *iwdg) 1290bdd7f5bSEtienne Carriere { 1300bdd7f5bSEtienne Carriere return io_pa_or_va(&iwdg->base, 1); 1310bdd7f5bSEtienne Carriere } 1320bdd7f5bSEtienne Carriere 1332f9b82faSEtienne Carriere static void iwdg_wdt_set_enabled(struct stm32_iwdg_device *iwdg) 1342f9b82faSEtienne Carriere { 1352f9b82faSEtienne Carriere iwdg->flags |= IWDG_FLAGS_ENABLED; 1362f9b82faSEtienne Carriere } 1372f9b82faSEtienne Carriere 1382f9b82faSEtienne Carriere static bool iwdg_wdt_is_enabled(struct stm32_iwdg_device *iwdg) 1390bdd7f5bSEtienne Carriere { 1400bdd7f5bSEtienne Carriere return iwdg->flags & IWDG_FLAGS_ENABLED; 1410bdd7f5bSEtienne Carriere } 1420bdd7f5bSEtienne Carriere 1430bdd7f5bSEtienne Carriere /* Return counter value to related to input timeout in seconds, or 0 on error */ 1440bdd7f5bSEtienne Carriere static uint32_t iwdg_timeout_cnt(struct stm32_iwdg_device *iwdg, 1450bdd7f5bSEtienne Carriere unsigned long to_sec) 1460bdd7f5bSEtienne Carriere { 1470bdd7f5bSEtienne Carriere uint64_t reload = (uint64_t)to_sec * clk_get_rate(iwdg->clk_lsi); 148*4a62f44cSClé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 */ 1580bdd7f5bSEtienne Carriere static TEE_Result iwdg_wait_sync(struct stm32_iwdg_device *iwdg) 1590bdd7f5bSEtienne Carriere { 1600bdd7f5bSEtienne Carriere uint64_t timeout_ref = timeout_init_us(IWDG_TIMEOUT_US); 1610bdd7f5bSEtienne Carriere vaddr_t iwdg_base = get_base(iwdg); 1620bdd7f5bSEtienne Carriere 1630bdd7f5bSEtienne Carriere while (io_read32(iwdg_base + IWDG_SR_OFFSET) & IWDG_SR_UPDATE_MASK) 1640bdd7f5bSEtienne Carriere if (timeout_elapsed(timeout_ref)) 1650bdd7f5bSEtienne Carriere break; 1660bdd7f5bSEtienne Carriere 167077bbb8aSEtienne Carriere if (io_read32(iwdg_base + IWDG_SR_OFFSET) & IWDG_SR_UPDATE_MASK) 1680bdd7f5bSEtienne Carriere return TEE_ERROR_GENERIC; 1690bdd7f5bSEtienne Carriere 1700bdd7f5bSEtienne Carriere return TEE_SUCCESS; 1710bdd7f5bSEtienne Carriere } 1720bdd7f5bSEtienne Carriere 173dff60fe8SClément Le Goffic static void stm32_iwdg_it_ack(struct stm32_iwdg_device *iwdg) 174dff60fe8SClément Le Goffic { 175dff60fe8SClément Le Goffic vaddr_t iwdg_base = get_base(iwdg); 176dff60fe8SClément Le Goffic 177dff60fe8SClément Le Goffic if (iwdg->hw_version >= IWDG_ICR_MIN_VER) 178dff60fe8SClément Le Goffic io_setbits32(iwdg_base + IWDG_ICR_OFFSET, IWDG_ICR_EWIC); 179dff60fe8SClément Le Goffic else 180dff60fe8SClément Le Goffic io_setbits32(iwdg_base + IWDG_EWCR_OFFSET, IWDG_EWCR_EWIC); 181dff60fe8SClément Le Goffic } 182dff60fe8SClément Le Goffic 183e4b8d29aSEtienne Carriere static enum itr_return stm32_iwdg_it_handler(struct itr_handler *h) 184e4b8d29aSEtienne Carriere { 185e4b8d29aSEtienne Carriere unsigned int __maybe_unused cpu = get_core_pos(); 186e4b8d29aSEtienne Carriere struct stm32_iwdg_device *iwdg = h->data; 187e4b8d29aSEtienne Carriere vaddr_t iwdg_base = get_base(iwdg); 188e4b8d29aSEtienne Carriere 189e4b8d29aSEtienne Carriere DMSG("CPU %u IT Watchdog %#"PRIxPA, cpu, iwdg->base.pa); 190e4b8d29aSEtienne Carriere 191e4b8d29aSEtienne Carriere /* Check for spurious interrupt */ 192e4b8d29aSEtienne Carriere if (!(io_read32(iwdg_base + IWDG_SR_OFFSET) & sr_ewif_mask(iwdg))) 193e4b8d29aSEtienne Carriere return ITRR_NONE; 194e4b8d29aSEtienne Carriere 195e4b8d29aSEtienne Carriere /* 196e4b8d29aSEtienne Carriere * Writing IWDG_EWCR_EWIT triggers a watchdog refresh. 197e4b8d29aSEtienne Carriere * To prevent the watchdog refresh, write-protect all the registers; 198e4b8d29aSEtienne Carriere * this makes read-only all IWDG_EWCR fields except IWDG_EWCR_EWIC. 199e4b8d29aSEtienne Carriere */ 200e4b8d29aSEtienne Carriere io_write32(iwdg_base + IWDG_KR_OFFSET, IWDG_KR_WPROT_KEY); 201e4b8d29aSEtienne Carriere 202e4b8d29aSEtienne Carriere /* Disable early interrupt */ 203dff60fe8SClément Le Goffic stm32_iwdg_it_ack(iwdg); 204e4b8d29aSEtienne Carriere 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 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); 237*4a62f44cSClé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 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 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 */ 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 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 */ 292*4a62f44cSClément Le Goffic *min_timeout = 3 * IWDG_PRESCALER_1024 / rate; 293*4a62f44cSClément Le Goffic *max_timeout = (IWDG_CNT_MASK + 1) * IWDG_PRESCALER_1024 / rate; 294fc9063ddSEtienne Carriere 295fc9063ddSEtienne Carriere return TEE_SUCCESS; 296fc9063ddSEtienne Carriere } 297fc9063ddSEtienne Carriere 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 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 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 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); 347*4a62f44cSClé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 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 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 */ 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 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 5270bdd7f5bSEtienne Carriere static TEE_Result stm32_iwdg_setup(struct stm32_iwdg_device *iwdg, 5280bdd7f5bSEtienne Carriere const void *fdt, int node) 5290bdd7f5bSEtienne Carriere { 5300bdd7f5bSEtienne Carriere TEE_Result res = TEE_SUCCESS; 5310bdd7f5bSEtienne Carriere 5320bdd7f5bSEtienne Carriere res = stm32_iwdg_parse_fdt(iwdg, fdt, node); 5330bdd7f5bSEtienne Carriere if (res) 5340bdd7f5bSEtienne Carriere return res; 5350bdd7f5bSEtienne Carriere 53636d2a417SEtienne Carriere /* Enable watchdog source and bus clocks once for all */ 5375c7ebea7SAntonio Borneo if (clk_enable(iwdg->clk_lsi)) 5385c7ebea7SAntonio Borneo panic(); 5395c7ebea7SAntonio Borneo 5405c7ebea7SAntonio Borneo if (clk_enable(iwdg->clk_pclk)) 5415c7ebea7SAntonio Borneo panic(); 5420bdd7f5bSEtienne Carriere 5433d5793d2SAntonio Borneo iwdg_wdt_get_version_and_status(iwdg); 5440bdd7f5bSEtienne Carriere 545f6ee86ecSClément Le Goffic res = iwdg_wdt_set_timeout(&iwdg->wdt_chip, iwdg->timeout); 5460bdd7f5bSEtienne Carriere if (res) 5475c7ebea7SAntonio Borneo panic(); 5480bdd7f5bSEtienne Carriere 549f6ee86ecSClément Le Goffic if (iwdg_wdt_is_enabled(iwdg)) 550f6ee86ecSClément Le Goffic iwdg_wdt_refresh(&iwdg->wdt_chip); 5510bdd7f5bSEtienne Carriere 5520bdd7f5bSEtienne Carriere return TEE_SUCCESS; 5530bdd7f5bSEtienne Carriere } 5540bdd7f5bSEtienne Carriere 555235baec9SEtienne Carriere static TEE_Result stm32_iwdg_pm(enum pm_op op, unsigned int pm_hint __unused, 556235baec9SEtienne Carriere const struct pm_callback_handle *pm_handle) 557235baec9SEtienne Carriere { 558235baec9SEtienne Carriere struct stm32_iwdg_device *iwdg = PM_CALLBACK_GET_HANDLE(pm_handle); 559235baec9SEtienne Carriere 560235baec9SEtienne Carriere if (op == PM_OP_RESUME) { 561235baec9SEtienne Carriere clk_enable(iwdg->clk_lsi); 562235baec9SEtienne Carriere clk_enable(iwdg->clk_pclk); 563235baec9SEtienne Carriere } else { 564235baec9SEtienne Carriere clk_disable(iwdg->clk_lsi); 565235baec9SEtienne Carriere clk_disable(iwdg->clk_pclk); 566235baec9SEtienne Carriere } 567235baec9SEtienne Carriere 568235baec9SEtienne Carriere return TEE_SUCCESS; 569235baec9SEtienne Carriere } 570235baec9SEtienne Carriere DECLARE_KEEP_PAGER(stm32_iwdg_pm); 571235baec9SEtienne Carriere 5720bdd7f5bSEtienne Carriere static TEE_Result stm32_iwdg_probe(const void *fdt, int node, 5730bdd7f5bSEtienne Carriere const void *compat_data __unused) 5740bdd7f5bSEtienne Carriere { 5750bdd7f5bSEtienne Carriere struct stm32_iwdg_device *iwdg = NULL; 5760bdd7f5bSEtienne Carriere TEE_Result res = TEE_SUCCESS; 5770bdd7f5bSEtienne Carriere 5780bdd7f5bSEtienne Carriere iwdg = calloc(1, sizeof(*iwdg)); 5790bdd7f5bSEtienne Carriere if (!iwdg) 5800bdd7f5bSEtienne Carriere return TEE_ERROR_OUT_OF_MEMORY; 5810bdd7f5bSEtienne Carriere 5820bdd7f5bSEtienne Carriere res = stm32_iwdg_setup(iwdg, fdt, node); 5830bdd7f5bSEtienne Carriere if (res) 584235baec9SEtienne Carriere goto out_free; 5850bdd7f5bSEtienne Carriere 586a096e2d9SEtienne Carriere iwdg->wdt_chip.ops = &stm32_iwdg_ops; 587a096e2d9SEtienne Carriere 588235baec9SEtienne Carriere register_pm_core_service_cb(stm32_iwdg_pm, iwdg, "stm32-iwdg"); 589a096e2d9SEtienne Carriere 590235baec9SEtienne Carriere res = watchdog_register(&iwdg->wdt_chip); 5910bdd7f5bSEtienne Carriere if (res) 592235baec9SEtienne Carriere goto out_pm; 593235baec9SEtienne Carriere 594235baec9SEtienne Carriere return TEE_SUCCESS; 595235baec9SEtienne Carriere 596235baec9SEtienne Carriere out_pm: 597235baec9SEtienne Carriere unregister_pm_core_service_cb(stm32_iwdg_pm, iwdg); 598235baec9SEtienne Carriere out_free: 5990bdd7f5bSEtienne Carriere free(iwdg); 600a096e2d9SEtienne Carriere 6010bdd7f5bSEtienne Carriere return res; 6020bdd7f5bSEtienne Carriere } 6030bdd7f5bSEtienne Carriere 6040bdd7f5bSEtienne Carriere static const struct dt_device_match stm32_iwdg_match_table[] = { 6050bdd7f5bSEtienne Carriere { .compatible = "st,stm32mp1-iwdg" }, 6060bdd7f5bSEtienne Carriere { } 6070bdd7f5bSEtienne Carriere }; 6080bdd7f5bSEtienne Carriere 6090bdd7f5bSEtienne Carriere DEFINE_DT_DRIVER(stm32_iwdg_dt_driver) = { 6100bdd7f5bSEtienne Carriere .name = "stm32-iwdg", 6110bdd7f5bSEtienne Carriere .match_table = stm32_iwdg_match_table, 6120bdd7f5bSEtienne Carriere .probe = stm32_iwdg_probe, 6130bdd7f5bSEtienne Carriere }; 614