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