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> 9*dff60fe8SClé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> 260bdd7f5bSEtienne Carriere #include <stm32_util.h> 270bdd7f5bSEtienne Carriere #include <string.h> 280bdd7f5bSEtienne Carriere #include <trace.h> 290bdd7f5bSEtienne Carriere 300bdd7f5bSEtienne Carriere /* IWDG Compatibility */ 313d5793d2SAntonio Borneo #define IWDG_TIMEOUT_US U(10000) 320bdd7f5bSEtienne Carriere #define IWDG_CNT_MASK GENMASK_32(11, 0) 333d5793d2SAntonio Borneo #define IWDG_ONF_MIN_VER U(0x31) 34e4b8d29aSEtienne Carriere #define IWDG_ICR_MIN_VER U(0x40) 350bdd7f5bSEtienne Carriere 360bdd7f5bSEtienne Carriere /* IWDG registers offsets */ 370bdd7f5bSEtienne Carriere #define IWDG_KR_OFFSET U(0x00) 380bdd7f5bSEtienne Carriere #define IWDG_PR_OFFSET U(0x04) 390bdd7f5bSEtienne Carriere #define IWDG_RLR_OFFSET U(0x08) 400bdd7f5bSEtienne Carriere #define IWDG_SR_OFFSET U(0x0C) 410bdd7f5bSEtienne Carriere #define IWDG_EWCR_OFFSET U(0x14) 42e4b8d29aSEtienne Carriere #define IWDG_ICR_OFFSET U(0x18) 433d5793d2SAntonio Borneo #define IWDG_VERR_OFFSET U(0x3F4) 440bdd7f5bSEtienne Carriere 453d5793d2SAntonio Borneo #define IWDG_KR_WPROT_KEY U(0x0000) 460bdd7f5bSEtienne Carriere #define IWDG_KR_ACCESS_KEY U(0x5555) 470bdd7f5bSEtienne Carriere #define IWDG_KR_RELOAD_KEY U(0xAAAA) 480bdd7f5bSEtienne Carriere #define IWDG_KR_START_KEY U(0xCCCC) 490bdd7f5bSEtienne Carriere 500bdd7f5bSEtienne Carriere /* Use a fixed prescaler divider of 256 */ 510bdd7f5bSEtienne Carriere #define IWDG_PRESCALER_256 U(256) 520bdd7f5bSEtienne Carriere #define IWDG_PR_DIV_256 U(0x06) 530bdd7f5bSEtienne Carriere #define IWDG_PR_DIV_MASK GENMASK_32(3, 0) 540bdd7f5bSEtienne Carriere 550bdd7f5bSEtienne Carriere #define IWDG_SR_PVU BIT(0) 560bdd7f5bSEtienne Carriere #define IWDG_SR_RVU BIT(1) 570bdd7f5bSEtienne Carriere #define IWDG_SR_WVU BIT(2) 580bdd7f5bSEtienne Carriere #define IWDG_SR_EWU BIT(3) 590bdd7f5bSEtienne Carriere #define IWDG_SR_UPDATE_MASK (IWDG_SR_PVU | IWDG_SR_RVU | IWDG_SR_WVU | \ 600bdd7f5bSEtienne Carriere IWDG_SR_EWU) 613d5793d2SAntonio Borneo #define IWDG_SR_ONF BIT(8) 62e4b8d29aSEtienne Carriere #define IWDG_SR_EWIF BIT(14) 63e4b8d29aSEtienne Carriere #define IWDG_SR_EWIF_V40 BIT(15) 640bdd7f5bSEtienne Carriere 650bdd7f5bSEtienne Carriere #define IWDG_EWCR_EWIE BIT(15) 660bdd7f5bSEtienne Carriere #define IWDG_EWCR_EWIC BIT(14) 670bdd7f5bSEtienne Carriere 68e4b8d29aSEtienne Carriere #define IWDG_ICR_EWIC BIT(15) 69e4b8d29aSEtienne Carriere 703d5793d2SAntonio Borneo #define IWDG_VERR_REV_MASK GENMASK_32(7, 0) 713d5793d2SAntonio Borneo 72e4b8d29aSEtienne Carriere /* Define default early timeout delay to 5 sec before timeout */ 73e4b8d29aSEtienne Carriere #define IWDG_ETIMEOUT_SEC U(5) 74e4b8d29aSEtienne Carriere 750bdd7f5bSEtienne Carriere /* 760bdd7f5bSEtienne Carriere * Values for struct stm32_iwdg_device::flags 770bdd7f5bSEtienne Carriere * IWDG_FLAGS_ENABLED Watchdog has been enabled 780bdd7f5bSEtienne Carriere */ 79c501c3e1SLionel Debieve #define IWDG_FLAGS_ENABLED BIT(0) 800bdd7f5bSEtienne Carriere 810bdd7f5bSEtienne Carriere /* 820bdd7f5bSEtienne Carriere * IWDG watch instance data 830bdd7f5bSEtienne Carriere * @base - IWDG interface IOMEM base address 84b2f17e87SEtienne Carriere * @clk_pclk - Bus clock 850bdd7f5bSEtienne Carriere * @clk_lsi - IWDG source clock 86e4b8d29aSEtienne Carriere * @itr_chip - Interrupt chip device 87e4b8d29aSEtienne Carriere * @itr_num - Interrupt number for the IWDG instance 88e4b8d29aSEtienne Carriere * @itr_handler - Interrupt handler 89*dff60fe8SClément Le Goffic * @reset - Reset controller device used to control the ability of the watchdog 90*dff60fe8SClément Le Goffic * to reset the system 910bdd7f5bSEtienne Carriere * @flags - Property flags for the IWDG instance 920bdd7f5bSEtienne Carriere * @timeout - Watchdog elaspure timeout 933d5793d2SAntonio Borneo * @hw_version - Watchdog HW version 94eb47832fSAntonio Borneo * @last_refresh - Time of last watchdog refresh 950bdd7f5bSEtienne Carriere * @wdt_chip - Wathcdog chip instance 960bdd7f5bSEtienne Carriere */ 970bdd7f5bSEtienne Carriere struct stm32_iwdg_device { 980bdd7f5bSEtienne Carriere struct io_pa_va base; 99b2f17e87SEtienne Carriere struct clk *clk_pclk; 1000bdd7f5bSEtienne Carriere struct clk *clk_lsi; 101e4b8d29aSEtienne Carriere struct itr_chip *itr_chip; 102e4b8d29aSEtienne Carriere size_t itr_num; 103e4b8d29aSEtienne Carriere struct itr_handler *itr_handler; 104*dff60fe8SClément Le Goffic struct rstctrl *reset; 1050bdd7f5bSEtienne Carriere uint32_t flags; 1060bdd7f5bSEtienne Carriere unsigned long timeout; 1073d5793d2SAntonio Borneo unsigned int hw_version; 108eb47832fSAntonio Borneo TEE_Time last_refresh; 1090bdd7f5bSEtienne Carriere struct wdt_chip wdt_chip; 1100bdd7f5bSEtienne Carriere }; 1110bdd7f5bSEtienne Carriere 112e4b8d29aSEtienne Carriere static uint32_t sr_ewif_mask(struct stm32_iwdg_device *iwdg) 113e4b8d29aSEtienne Carriere { 114e4b8d29aSEtienne Carriere if (iwdg->hw_version >= IWDG_ICR_MIN_VER) 115e4b8d29aSEtienne Carriere return IWDG_SR_EWIF_V40; 116e4b8d29aSEtienne Carriere else 117e4b8d29aSEtienne Carriere return IWDG_SR_EWIF; 118e4b8d29aSEtienne Carriere } 119e4b8d29aSEtienne Carriere 1200bdd7f5bSEtienne Carriere static vaddr_t get_base(struct stm32_iwdg_device *iwdg) 1210bdd7f5bSEtienne Carriere { 1220bdd7f5bSEtienne Carriere return io_pa_or_va(&iwdg->base, 1); 1230bdd7f5bSEtienne Carriere } 1240bdd7f5bSEtienne Carriere 1252f9b82faSEtienne Carriere static void iwdg_wdt_set_enabled(struct stm32_iwdg_device *iwdg) 1262f9b82faSEtienne Carriere { 1272f9b82faSEtienne Carriere iwdg->flags |= IWDG_FLAGS_ENABLED; 1282f9b82faSEtienne Carriere } 1292f9b82faSEtienne Carriere 1302f9b82faSEtienne Carriere static bool iwdg_wdt_is_enabled(struct stm32_iwdg_device *iwdg) 1310bdd7f5bSEtienne Carriere { 1320bdd7f5bSEtienne Carriere return iwdg->flags & IWDG_FLAGS_ENABLED; 1330bdd7f5bSEtienne Carriere } 1340bdd7f5bSEtienne Carriere 1350bdd7f5bSEtienne Carriere /* Return counter value to related to input timeout in seconds, or 0 on error */ 1360bdd7f5bSEtienne Carriere static uint32_t iwdg_timeout_cnt(struct stm32_iwdg_device *iwdg, 1370bdd7f5bSEtienne Carriere unsigned long to_sec) 1380bdd7f5bSEtienne Carriere { 1390bdd7f5bSEtienne Carriere uint64_t reload = (uint64_t)to_sec * clk_get_rate(iwdg->clk_lsi); 1400bdd7f5bSEtienne Carriere uint64_t cnt = (reload / IWDG_PRESCALER_256) - 1; 1410bdd7f5bSEtienne Carriere 1420bdd7f5bSEtienne Carriere /* Be safe and expect any counter to be above 2 */ 1430bdd7f5bSEtienne Carriere if (cnt > IWDG_CNT_MASK || cnt < 3) 1440bdd7f5bSEtienne Carriere return 0; 1450bdd7f5bSEtienne Carriere 1460bdd7f5bSEtienne Carriere return cnt; 1470bdd7f5bSEtienne Carriere } 1480bdd7f5bSEtienne Carriere 1490bdd7f5bSEtienne Carriere /* Wait IWDG programming completes */ 1500bdd7f5bSEtienne Carriere static TEE_Result iwdg_wait_sync(struct stm32_iwdg_device *iwdg) 1510bdd7f5bSEtienne Carriere { 1520bdd7f5bSEtienne Carriere uint64_t timeout_ref = timeout_init_us(IWDG_TIMEOUT_US); 1530bdd7f5bSEtienne Carriere vaddr_t iwdg_base = get_base(iwdg); 1540bdd7f5bSEtienne Carriere 1550bdd7f5bSEtienne Carriere while (io_read32(iwdg_base + IWDG_SR_OFFSET) & IWDG_SR_UPDATE_MASK) 1560bdd7f5bSEtienne Carriere if (timeout_elapsed(timeout_ref)) 1570bdd7f5bSEtienne Carriere break; 1580bdd7f5bSEtienne Carriere 159077bbb8aSEtienne Carriere if (io_read32(iwdg_base + IWDG_SR_OFFSET) & IWDG_SR_UPDATE_MASK) 1600bdd7f5bSEtienne Carriere return TEE_ERROR_GENERIC; 1610bdd7f5bSEtienne Carriere 1620bdd7f5bSEtienne Carriere return TEE_SUCCESS; 1630bdd7f5bSEtienne Carriere } 1640bdd7f5bSEtienne Carriere 165*dff60fe8SClément Le Goffic static void stm32_iwdg_it_ack(struct stm32_iwdg_device *iwdg) 166*dff60fe8SClément Le Goffic { 167*dff60fe8SClément Le Goffic vaddr_t iwdg_base = get_base(iwdg); 168*dff60fe8SClément Le Goffic 169*dff60fe8SClément Le Goffic if (iwdg->hw_version >= IWDG_ICR_MIN_VER) 170*dff60fe8SClément Le Goffic io_setbits32(iwdg_base + IWDG_ICR_OFFSET, IWDG_ICR_EWIC); 171*dff60fe8SClément Le Goffic else 172*dff60fe8SClément Le Goffic io_setbits32(iwdg_base + IWDG_EWCR_OFFSET, IWDG_EWCR_EWIC); 173*dff60fe8SClément Le Goffic } 174*dff60fe8SClément Le Goffic 175e4b8d29aSEtienne Carriere static enum itr_return stm32_iwdg_it_handler(struct itr_handler *h) 176e4b8d29aSEtienne Carriere { 177e4b8d29aSEtienne Carriere unsigned int __maybe_unused cpu = get_core_pos(); 178e4b8d29aSEtienne Carriere struct stm32_iwdg_device *iwdg = h->data; 179e4b8d29aSEtienne Carriere vaddr_t iwdg_base = get_base(iwdg); 180e4b8d29aSEtienne Carriere 181e4b8d29aSEtienne Carriere DMSG("CPU %u IT Watchdog %#"PRIxPA, cpu, iwdg->base.pa); 182e4b8d29aSEtienne Carriere 183e4b8d29aSEtienne Carriere /* Check for spurious interrupt */ 184e4b8d29aSEtienne Carriere if (!(io_read32(iwdg_base + IWDG_SR_OFFSET) & sr_ewif_mask(iwdg))) 185e4b8d29aSEtienne Carriere return ITRR_NONE; 186e4b8d29aSEtienne Carriere 187e4b8d29aSEtienne Carriere /* 188e4b8d29aSEtienne Carriere * Writing IWDG_EWCR_EWIT triggers a watchdog refresh. 189e4b8d29aSEtienne Carriere * To prevent the watchdog refresh, write-protect all the registers; 190e4b8d29aSEtienne Carriere * this makes read-only all IWDG_EWCR fields except IWDG_EWCR_EWIC. 191e4b8d29aSEtienne Carriere */ 192e4b8d29aSEtienne Carriere io_write32(iwdg_base + IWDG_KR_OFFSET, IWDG_KR_WPROT_KEY); 193e4b8d29aSEtienne Carriere 194e4b8d29aSEtienne Carriere /* Disable early interrupt */ 195*dff60fe8SClément Le Goffic stm32_iwdg_it_ack(iwdg); 196e4b8d29aSEtienne Carriere 197e4b8d29aSEtienne Carriere panic("Watchdog"); 198e4b8d29aSEtienne Carriere 199e4b8d29aSEtienne Carriere return ITRR_HANDLED; 200e4b8d29aSEtienne Carriere } 201e4b8d29aSEtienne Carriere DECLARE_KEEP_PAGER(stm32_iwdg_it_handler); 202e4b8d29aSEtienne Carriere 2030bdd7f5bSEtienne Carriere static TEE_Result configure_timeout(struct stm32_iwdg_device *iwdg) 2040bdd7f5bSEtienne Carriere { 2050bdd7f5bSEtienne Carriere TEE_Result res = TEE_ERROR_GENERIC; 2060bdd7f5bSEtienne Carriere vaddr_t iwdg_base = get_base(iwdg); 2070bdd7f5bSEtienne Carriere uint32_t rlr_value = 0; 208e4b8d29aSEtienne Carriere uint32_t ewie_value = 0; 209e4b8d29aSEtienne Carriere uint32_t early_timeout = 0; 2100bdd7f5bSEtienne Carriere 2112f9b82faSEtienne Carriere assert(iwdg_wdt_is_enabled(iwdg)); 2120bdd7f5bSEtienne Carriere 2130bdd7f5bSEtienne Carriere rlr_value = iwdg_timeout_cnt(iwdg, iwdg->timeout); 2140bdd7f5bSEtienne Carriere if (!rlr_value) 2150bdd7f5bSEtienne Carriere return TEE_ERROR_GENERIC; 2160bdd7f5bSEtienne Carriere 217e4b8d29aSEtienne Carriere if (iwdg->itr_handler) { 218e4b8d29aSEtienne Carriere if (iwdg->timeout >= 2 * IWDG_ETIMEOUT_SEC) 219e4b8d29aSEtienne Carriere early_timeout = IWDG_ETIMEOUT_SEC; 220e4b8d29aSEtienne Carriere else 221e4b8d29aSEtienne Carriere early_timeout = iwdg->timeout / 4; 222e4b8d29aSEtienne Carriere ewie_value = iwdg_timeout_cnt(iwdg, early_timeout); 223e4b8d29aSEtienne Carriere interrupt_enable(iwdg->itr_chip, iwdg->itr_num); 224e4b8d29aSEtienne Carriere } 225e4b8d29aSEtienne Carriere 2260bdd7f5bSEtienne Carriere io_write32(iwdg_base + IWDG_KR_OFFSET, IWDG_KR_ACCESS_KEY); 2270bdd7f5bSEtienne Carriere io_write32(iwdg_base + IWDG_PR_OFFSET, IWDG_PR_DIV_256); 2280bdd7f5bSEtienne Carriere io_write32(iwdg_base + IWDG_RLR_OFFSET, rlr_value); 229e4b8d29aSEtienne Carriere if (ewie_value && 230e4b8d29aSEtienne Carriere !(io_read32(iwdg_base + IWDG_EWCR_OFFSET) & IWDG_EWCR_EWIE)) 231e4b8d29aSEtienne Carriere io_write32(iwdg_base + IWDG_EWCR_OFFSET, 232e4b8d29aSEtienne Carriere ewie_value | IWDG_EWCR_EWIE); 2330bdd7f5bSEtienne Carriere 2340bdd7f5bSEtienne Carriere res = iwdg_wait_sync(iwdg); 2350bdd7f5bSEtienne Carriere 236e4b8d29aSEtienne Carriere io_write32(iwdg_base + IWDG_KR_OFFSET, IWDG_KR_RELOAD_KEY); 237e4b8d29aSEtienne Carriere 2380bdd7f5bSEtienne Carriere return res; 2390bdd7f5bSEtienne Carriere } 2400bdd7f5bSEtienne Carriere 2410bdd7f5bSEtienne Carriere static void iwdg_start(struct stm32_iwdg_device *iwdg) 2420bdd7f5bSEtienne Carriere { 243eb47832fSAntonio Borneo TEE_Result res = TEE_ERROR_GENERIC; 244eb47832fSAntonio Borneo 245eb47832fSAntonio Borneo res = tee_time_get_sys_time(&iwdg->last_refresh); 246eb47832fSAntonio Borneo if (res) 247eb47832fSAntonio Borneo panic(); 248eb47832fSAntonio Borneo 2490bdd7f5bSEtienne Carriere io_write32(get_base(iwdg) + IWDG_KR_OFFSET, IWDG_KR_START_KEY); 2500bdd7f5bSEtienne Carriere 2512f9b82faSEtienne Carriere iwdg_wdt_set_enabled(iwdg); 2520bdd7f5bSEtienne Carriere } 2530bdd7f5bSEtienne Carriere 2540bdd7f5bSEtienne Carriere static void iwdg_refresh(struct stm32_iwdg_device *iwdg) 2550bdd7f5bSEtienne Carriere { 256eb47832fSAntonio Borneo TEE_Result res = TEE_ERROR_GENERIC; 257eb47832fSAntonio Borneo 258eb47832fSAntonio Borneo res = tee_time_get_sys_time(&iwdg->last_refresh); 259eb47832fSAntonio Borneo if (res) 260eb47832fSAntonio Borneo panic(); 261eb47832fSAntonio Borneo 2620bdd7f5bSEtienne Carriere io_write32(get_base(iwdg) + IWDG_KR_OFFSET, IWDG_KR_RELOAD_KEY); 2630bdd7f5bSEtienne Carriere } 2640bdd7f5bSEtienne Carriere 2650bdd7f5bSEtienne Carriere /* Operators for watchdog OP-TEE interface */ 2660bdd7f5bSEtienne Carriere static struct stm32_iwdg_device *wdt_chip_to_iwdg(struct wdt_chip *chip) 2670bdd7f5bSEtienne Carriere { 2680bdd7f5bSEtienne Carriere return container_of(chip, struct stm32_iwdg_device, wdt_chip); 2690bdd7f5bSEtienne Carriere } 2700bdd7f5bSEtienne Carriere 271fc9063ddSEtienne Carriere static TEE_Result iwdg_wdt_init(struct wdt_chip *chip, 272fc9063ddSEtienne Carriere unsigned long *min_timeout, 273fc9063ddSEtienne Carriere unsigned long *max_timeout) 274fc9063ddSEtienne Carriere { 275fc9063ddSEtienne Carriere struct stm32_iwdg_device *iwdg = wdt_chip_to_iwdg(chip); 276fc9063ddSEtienne Carriere unsigned long rate = clk_get_rate(iwdg->clk_lsi); 277fc9063ddSEtienne Carriere 278fc9063ddSEtienne Carriere if (!rate) 279fc9063ddSEtienne Carriere return TEE_ERROR_GENERIC; 280fc9063ddSEtienne Carriere 281fc9063ddSEtienne Carriere /* Be safe and expect any counter to be above 2 */ 282fc9063ddSEtienne Carriere *min_timeout = 3 * IWDG_PRESCALER_256 / rate; 283fc9063ddSEtienne Carriere *max_timeout = (IWDG_CNT_MASK + 1) * IWDG_PRESCALER_256 / rate; 284fc9063ddSEtienne Carriere 285fc9063ddSEtienne Carriere return TEE_SUCCESS; 286fc9063ddSEtienne Carriere } 287fc9063ddSEtienne Carriere 2880bdd7f5bSEtienne Carriere static void iwdg_wdt_start(struct wdt_chip *chip) 2890bdd7f5bSEtienne Carriere { 2900bdd7f5bSEtienne Carriere struct stm32_iwdg_device *iwdg = wdt_chip_to_iwdg(chip); 2910bdd7f5bSEtienne Carriere 2920bdd7f5bSEtienne Carriere iwdg_start(iwdg); 293*dff60fe8SClément Le Goffic if (iwdg->reset && iwdg->itr_handler) 294*dff60fe8SClément Le Goffic stm32_iwdg_it_ack(iwdg); 2950bdd7f5bSEtienne Carriere 2960bdd7f5bSEtienne Carriere if (configure_timeout(iwdg)) 2970bdd7f5bSEtienne Carriere panic(); 298*dff60fe8SClément Le Goffic 299*dff60fe8SClément Le Goffic if (iwdg->reset) 300*dff60fe8SClément Le Goffic if (rstctrl_assert(iwdg->reset)) 301*dff60fe8SClément Le Goffic panic(); 302*dff60fe8SClément Le Goffic } 303*dff60fe8SClément Le Goffic 304*dff60fe8SClément Le Goffic static void iwdg_wdt_stop(struct wdt_chip *chip) 305*dff60fe8SClément Le Goffic { 306*dff60fe8SClément Le Goffic struct stm32_iwdg_device *iwdg = wdt_chip_to_iwdg(chip); 307*dff60fe8SClément Le Goffic 308*dff60fe8SClément Le Goffic if (iwdg->reset) { 309*dff60fe8SClément Le Goffic if (rstctrl_deassert(iwdg->reset)) 310*dff60fe8SClément Le Goffic panic(); 311*dff60fe8SClément Le Goffic if (iwdg->itr_handler) 312*dff60fe8SClément Le Goffic interrupt_disable(iwdg->itr_chip, iwdg->itr_num); 313*dff60fe8SClément Le Goffic } 3140bdd7f5bSEtienne Carriere } 3150bdd7f5bSEtienne Carriere 3160bdd7f5bSEtienne Carriere static void iwdg_wdt_refresh(struct wdt_chip *chip) 3170bdd7f5bSEtienne Carriere { 3180bdd7f5bSEtienne Carriere struct stm32_iwdg_device *iwdg = wdt_chip_to_iwdg(chip); 3190bdd7f5bSEtienne Carriere 3200bdd7f5bSEtienne Carriere iwdg_refresh(iwdg); 3210bdd7f5bSEtienne Carriere } 3220bdd7f5bSEtienne Carriere 3230bdd7f5bSEtienne Carriere static TEE_Result iwdg_wdt_set_timeout(struct wdt_chip *chip, 3240bdd7f5bSEtienne Carriere unsigned long timeout) 3250bdd7f5bSEtienne Carriere { 3260bdd7f5bSEtienne Carriere struct stm32_iwdg_device *iwdg = wdt_chip_to_iwdg(chip); 3270bdd7f5bSEtienne Carriere 3280bdd7f5bSEtienne Carriere if (!iwdg_timeout_cnt(iwdg, timeout)) 3290bdd7f5bSEtienne Carriere return TEE_ERROR_BAD_PARAMETERS; 3300bdd7f5bSEtienne Carriere 3310bdd7f5bSEtienne Carriere iwdg->timeout = timeout; 3320bdd7f5bSEtienne Carriere 3332f9b82faSEtienne Carriere if (iwdg_wdt_is_enabled(iwdg)) { 3340bdd7f5bSEtienne Carriere TEE_Result res = TEE_ERROR_GENERIC; 3350bdd7f5bSEtienne Carriere 3360bdd7f5bSEtienne Carriere res = configure_timeout(iwdg); 3370bdd7f5bSEtienne Carriere if (res) 3380bdd7f5bSEtienne Carriere return res; 3390bdd7f5bSEtienne Carriere } 3400bdd7f5bSEtienne Carriere 3410bdd7f5bSEtienne Carriere return TEE_SUCCESS; 3420bdd7f5bSEtienne Carriere } 3430bdd7f5bSEtienne Carriere 344eb47832fSAntonio Borneo static TEE_Result iwdg_wdt_get_timeleft(struct wdt_chip *chip, bool *is_started, 345eb47832fSAntonio Borneo unsigned long *timeleft) 346eb47832fSAntonio Borneo { 347eb47832fSAntonio Borneo struct stm32_iwdg_device *iwdg = wdt_chip_to_iwdg(chip); 348eb47832fSAntonio Borneo TEE_Result res = TEE_ERROR_GENERIC; 349eb47832fSAntonio Borneo TEE_Time time = { }; 350eb47832fSAntonio Borneo TEE_Time now = { }; 351eb47832fSAntonio Borneo 352eb47832fSAntonio Borneo *is_started = iwdg_wdt_is_enabled(iwdg); 353eb47832fSAntonio Borneo 354eb47832fSAntonio Borneo if (!*is_started) 355eb47832fSAntonio Borneo return TEE_SUCCESS; 356eb47832fSAntonio Borneo 357eb47832fSAntonio Borneo res = tee_time_get_sys_time(&now); 358eb47832fSAntonio Borneo if (res) 359eb47832fSAntonio Borneo panic(); 360eb47832fSAntonio Borneo 361eb47832fSAntonio Borneo time.seconds = iwdg->timeout; 362eb47832fSAntonio Borneo TEE_TIME_ADD(iwdg->last_refresh, time, time); 363eb47832fSAntonio Borneo if (TEE_TIME_LE(time, now)) { 364eb47832fSAntonio Borneo *timeleft = 0; 365eb47832fSAntonio Borneo } else { 366eb47832fSAntonio Borneo TEE_TIME_SUB(time, now, time); 367eb47832fSAntonio Borneo *timeleft = time.seconds; 368eb47832fSAntonio Borneo } 369eb47832fSAntonio Borneo 370eb47832fSAntonio Borneo return TEE_SUCCESS; 371eb47832fSAntonio Borneo } 372eb47832fSAntonio Borneo 3730bdd7f5bSEtienne Carriere static const struct wdt_ops stm32_iwdg_ops = { 374fc9063ddSEtienne Carriere .init = iwdg_wdt_init, 3750bdd7f5bSEtienne Carriere .start = iwdg_wdt_start, 376*dff60fe8SClément Le Goffic .stop = iwdg_wdt_stop, 3770bdd7f5bSEtienne Carriere .ping = iwdg_wdt_refresh, 3780bdd7f5bSEtienne Carriere .set_timeout = iwdg_wdt_set_timeout, 379eb47832fSAntonio Borneo .get_timeleft = iwdg_wdt_get_timeleft, 3800bdd7f5bSEtienne Carriere }; 3810bdd7f5bSEtienne Carriere DECLARE_KEEP_PAGER(stm32_iwdg_ops); 3820bdd7f5bSEtienne Carriere 3830bdd7f5bSEtienne Carriere /* Driver initialization */ 3840bdd7f5bSEtienne Carriere static TEE_Result stm32_iwdg_parse_fdt(struct stm32_iwdg_device *iwdg, 3850bdd7f5bSEtienne Carriere const void *fdt, int node) 3860bdd7f5bSEtienne Carriere { 3870bdd7f5bSEtienne Carriere TEE_Result res = TEE_ERROR_GENERIC; 3880bdd7f5bSEtienne Carriere struct dt_node_info dt_info = { }; 3890bdd7f5bSEtienne Carriere const fdt32_t *cuint = NULL; 3900bdd7f5bSEtienne Carriere 391f354a5d8SGatien Chevallier fdt_fill_device_info(fdt, &dt_info, node); 3920bdd7f5bSEtienne Carriere 3930bdd7f5bSEtienne Carriere if (dt_info.reg == DT_INFO_INVALID_REG || 3940bdd7f5bSEtienne Carriere dt_info.reg_size == DT_INFO_INVALID_REG_SIZE) 3950bdd7f5bSEtienne Carriere panic(); 3960bdd7f5bSEtienne Carriere 397b2f17e87SEtienne Carriere res = clk_dt_get_by_name(fdt, node, "pclk", &iwdg->clk_pclk); 3980bdd7f5bSEtienne Carriere if (res) 3990bdd7f5bSEtienne Carriere return res; 4000bdd7f5bSEtienne Carriere 4010bdd7f5bSEtienne Carriere res = clk_dt_get_by_name(fdt, node, "lsi", &iwdg->clk_lsi); 4020bdd7f5bSEtienne Carriere if (res) 4030bdd7f5bSEtienne Carriere return res; 4040bdd7f5bSEtienne Carriere 405e4b8d29aSEtienne Carriere res = interrupt_dt_get(fdt, node, &iwdg->itr_chip, &iwdg->itr_num); 406e4b8d29aSEtienne Carriere if (res && res != TEE_ERROR_ITEM_NOT_FOUND) 407e4b8d29aSEtienne Carriere return res; 408e4b8d29aSEtienne Carriere if (!res) { 409e4b8d29aSEtienne Carriere res = interrupt_create_handler(iwdg->itr_chip, iwdg->itr_num, 410e4b8d29aSEtienne Carriere stm32_iwdg_it_handler, iwdg, 0, 411e4b8d29aSEtienne Carriere &iwdg->itr_handler); 412e4b8d29aSEtienne Carriere if (res) 413e4b8d29aSEtienne Carriere return res; 414e4b8d29aSEtienne Carriere } 415e4b8d29aSEtienne Carriere 416*dff60fe8SClément Le Goffic res = rstctrl_dt_get_by_index(fdt, node, 0, &iwdg->reset); 417*dff60fe8SClément Le Goffic if (res && res != TEE_ERROR_ITEM_NOT_FOUND) 418*dff60fe8SClément Le Goffic goto err_itr; 419*dff60fe8SClément Le Goffic 4200bdd7f5bSEtienne Carriere /* Get IOMEM address */ 4210bdd7f5bSEtienne Carriere iwdg->base.pa = dt_info.reg; 4220bdd7f5bSEtienne Carriere io_pa_or_va_secure(&iwdg->base, dt_info.reg_size); 4230bdd7f5bSEtienne Carriere assert(iwdg->base.va); 4240bdd7f5bSEtienne Carriere 4250bdd7f5bSEtienne Carriere /* Get and check timeout value */ 4260bdd7f5bSEtienne Carriere cuint = fdt_getprop(fdt, node, "timeout-sec", NULL); 427e4b8d29aSEtienne Carriere if (!cuint) { 428e4b8d29aSEtienne Carriere res = TEE_ERROR_BAD_PARAMETERS; 429e4b8d29aSEtienne Carriere goto err_itr; 430e4b8d29aSEtienne Carriere } 4310bdd7f5bSEtienne Carriere 4320bdd7f5bSEtienne Carriere iwdg->timeout = (int)fdt32_to_cpu(*cuint); 433e4b8d29aSEtienne Carriere if (!iwdg->timeout) { 434e4b8d29aSEtienne Carriere res = TEE_ERROR_BAD_PARAMETERS; 435e4b8d29aSEtienne Carriere goto err_itr; 436e4b8d29aSEtienne Carriere } 4370bdd7f5bSEtienne Carriere 4380bdd7f5bSEtienne Carriere if (!iwdg_timeout_cnt(iwdg, iwdg->timeout)) { 4390bdd7f5bSEtienne Carriere EMSG("Timeout %lu not applicable", iwdg->timeout); 440e4b8d29aSEtienne Carriere res = TEE_ERROR_BAD_PARAMETERS; 441e4b8d29aSEtienne Carriere goto err_itr; 4420bdd7f5bSEtienne Carriere } 4430bdd7f5bSEtienne Carriere 4440bdd7f5bSEtienne Carriere return TEE_SUCCESS; 445e4b8d29aSEtienne Carriere 446e4b8d29aSEtienne Carriere err_itr: 447e4b8d29aSEtienne Carriere interrupt_remove_free_handler(iwdg->itr_handler); 448e4b8d29aSEtienne Carriere 449e4b8d29aSEtienne Carriere return res; 4500bdd7f5bSEtienne Carriere } 4510bdd7f5bSEtienne Carriere 4523d5793d2SAntonio Borneo static void iwdg_wdt_get_version_and_status(struct stm32_iwdg_device *iwdg) 4533d5793d2SAntonio Borneo { 4543d5793d2SAntonio Borneo vaddr_t iwdg_base = get_base(iwdg); 4553d5793d2SAntonio Borneo uint32_t rlr_value = 0; 4563d5793d2SAntonio Borneo 4573d5793d2SAntonio Borneo iwdg->hw_version = io_read32(iwdg_base + IWDG_VERR_OFFSET) & 4583d5793d2SAntonio Borneo IWDG_VERR_REV_MASK; 4593d5793d2SAntonio Borneo 4603d5793d2SAntonio Borneo /* Test if watchdog is already running */ 4613d5793d2SAntonio Borneo if (iwdg->hw_version >= IWDG_ONF_MIN_VER) { 4623d5793d2SAntonio Borneo if (io_read32(iwdg_base + IWDG_SR_OFFSET) & IWDG_SR_ONF) 4633d5793d2SAntonio Borneo iwdg_wdt_set_enabled(iwdg); 4643d5793d2SAntonio Borneo } else { 4653d5793d2SAntonio Borneo /* 4663d5793d2SAntonio Borneo * Workaround for old versions without IWDG_SR_ONF bit: 4673d5793d2SAntonio Borneo * - write in IWDG_RLR_OFFSET 4683d5793d2SAntonio Borneo * - wait for sync 4693d5793d2SAntonio Borneo * - if sync succeeds, then iwdg is running 4703d5793d2SAntonio Borneo */ 4713d5793d2SAntonio Borneo io_write32(iwdg_base + IWDG_KR_OFFSET, IWDG_KR_ACCESS_KEY); 4723d5793d2SAntonio Borneo 4733d5793d2SAntonio Borneo rlr_value = io_read32(iwdg_base + IWDG_RLR_OFFSET); 4743d5793d2SAntonio Borneo io_write32(iwdg_base + IWDG_RLR_OFFSET, rlr_value); 4753d5793d2SAntonio Borneo 4763d5793d2SAntonio Borneo if (!iwdg_wait_sync(iwdg)) 4773d5793d2SAntonio Borneo iwdg_wdt_set_enabled(iwdg); 4783d5793d2SAntonio Borneo 4793d5793d2SAntonio Borneo io_write32(iwdg_base + IWDG_KR_OFFSET, IWDG_KR_WPROT_KEY); 4803d5793d2SAntonio Borneo } 4813d5793d2SAntonio Borneo 4823d5793d2SAntonio Borneo DMSG("Watchdog is %sabled", iwdg_wdt_is_enabled(iwdg) ? "en" : "dis"); 4833d5793d2SAntonio Borneo } 4843d5793d2SAntonio Borneo 4850bdd7f5bSEtienne Carriere static TEE_Result stm32_iwdg_setup(struct stm32_iwdg_device *iwdg, 4860bdd7f5bSEtienne Carriere const void *fdt, int node) 4870bdd7f5bSEtienne Carriere { 4880bdd7f5bSEtienne Carriere TEE_Result res = TEE_SUCCESS; 4890bdd7f5bSEtienne Carriere 4900bdd7f5bSEtienne Carriere res = stm32_iwdg_parse_fdt(iwdg, fdt, node); 4910bdd7f5bSEtienne Carriere if (res) 4920bdd7f5bSEtienne Carriere return res; 4930bdd7f5bSEtienne Carriere 49436d2a417SEtienne Carriere /* Enable watchdog source and bus clocks once for all */ 4955c7ebea7SAntonio Borneo if (clk_enable(iwdg->clk_lsi)) 4965c7ebea7SAntonio Borneo panic(); 4975c7ebea7SAntonio Borneo 4985c7ebea7SAntonio Borneo if (clk_enable(iwdg->clk_pclk)) 4995c7ebea7SAntonio Borneo panic(); 5000bdd7f5bSEtienne Carriere 5013d5793d2SAntonio Borneo iwdg_wdt_get_version_and_status(iwdg); 5020bdd7f5bSEtienne Carriere 5033d5793d2SAntonio Borneo if (iwdg_wdt_is_enabled(iwdg)) { 5040bdd7f5bSEtienne Carriere /* Configure timeout if watchdog is already enabled */ 5050bdd7f5bSEtienne Carriere res = configure_timeout(iwdg); 5060bdd7f5bSEtienne Carriere if (res) 5075c7ebea7SAntonio Borneo panic(); 5080bdd7f5bSEtienne Carriere 5090bdd7f5bSEtienne Carriere iwdg_refresh(iwdg); 5100bdd7f5bSEtienne Carriere } 5110bdd7f5bSEtienne Carriere 5120bdd7f5bSEtienne Carriere return TEE_SUCCESS; 5130bdd7f5bSEtienne Carriere } 5140bdd7f5bSEtienne Carriere 515235baec9SEtienne Carriere static TEE_Result stm32_iwdg_pm(enum pm_op op, unsigned int pm_hint __unused, 516235baec9SEtienne Carriere const struct pm_callback_handle *pm_handle) 517235baec9SEtienne Carriere { 518235baec9SEtienne Carriere struct stm32_iwdg_device *iwdg = PM_CALLBACK_GET_HANDLE(pm_handle); 519235baec9SEtienne Carriere 520235baec9SEtienne Carriere if (op == PM_OP_RESUME) { 521235baec9SEtienne Carriere clk_enable(iwdg->clk_lsi); 522235baec9SEtienne Carriere clk_enable(iwdg->clk_pclk); 523235baec9SEtienne Carriere } else { 524235baec9SEtienne Carriere clk_disable(iwdg->clk_lsi); 525235baec9SEtienne Carriere clk_disable(iwdg->clk_pclk); 526235baec9SEtienne Carriere } 527235baec9SEtienne Carriere 528235baec9SEtienne Carriere return TEE_SUCCESS; 529235baec9SEtienne Carriere } 530235baec9SEtienne Carriere DECLARE_KEEP_PAGER(stm32_iwdg_pm); 531235baec9SEtienne Carriere 5320bdd7f5bSEtienne Carriere static TEE_Result stm32_iwdg_probe(const void *fdt, int node, 5330bdd7f5bSEtienne Carriere const void *compat_data __unused) 5340bdd7f5bSEtienne Carriere { 5350bdd7f5bSEtienne Carriere struct stm32_iwdg_device *iwdg = NULL; 5360bdd7f5bSEtienne Carriere TEE_Result res = TEE_SUCCESS; 5370bdd7f5bSEtienne Carriere 5380bdd7f5bSEtienne Carriere iwdg = calloc(1, sizeof(*iwdg)); 5390bdd7f5bSEtienne Carriere if (!iwdg) 5400bdd7f5bSEtienne Carriere return TEE_ERROR_OUT_OF_MEMORY; 5410bdd7f5bSEtienne Carriere 5420bdd7f5bSEtienne Carriere res = stm32_iwdg_setup(iwdg, fdt, node); 5430bdd7f5bSEtienne Carriere if (res) 544235baec9SEtienne Carriere goto out_free; 5450bdd7f5bSEtienne Carriere 546a096e2d9SEtienne Carriere iwdg->wdt_chip.ops = &stm32_iwdg_ops; 547a096e2d9SEtienne Carriere 548235baec9SEtienne Carriere register_pm_core_service_cb(stm32_iwdg_pm, iwdg, "stm32-iwdg"); 549a096e2d9SEtienne Carriere 550235baec9SEtienne Carriere res = watchdog_register(&iwdg->wdt_chip); 5510bdd7f5bSEtienne Carriere if (res) 552235baec9SEtienne Carriere goto out_pm; 553235baec9SEtienne Carriere 554235baec9SEtienne Carriere return TEE_SUCCESS; 555235baec9SEtienne Carriere 556235baec9SEtienne Carriere out_pm: 557235baec9SEtienne Carriere unregister_pm_core_service_cb(stm32_iwdg_pm, iwdg); 558235baec9SEtienne Carriere out_free: 5590bdd7f5bSEtienne Carriere free(iwdg); 560a096e2d9SEtienne Carriere 5610bdd7f5bSEtienne Carriere return res; 5620bdd7f5bSEtienne Carriere } 5630bdd7f5bSEtienne Carriere 5640bdd7f5bSEtienne Carriere static const struct dt_device_match stm32_iwdg_match_table[] = { 5650bdd7f5bSEtienne Carriere { .compatible = "st,stm32mp1-iwdg" }, 5660bdd7f5bSEtienne Carriere { } 5670bdd7f5bSEtienne Carriere }; 5680bdd7f5bSEtienne Carriere 5690bdd7f5bSEtienne Carriere DEFINE_DT_DRIVER(stm32_iwdg_dt_driver) = { 5700bdd7f5bSEtienne Carriere .name = "stm32-iwdg", 5710bdd7f5bSEtienne Carriere .match_table = stm32_iwdg_match_table, 5720bdd7f5bSEtienne Carriere .probe = stm32_iwdg_probe, 5730bdd7f5bSEtienne Carriere }; 574