10bdd7f5bSEtienne Carriere // SPDX-License-Identifier: BSD-3-Clause 20bdd7f5bSEtienne Carriere /* 30bdd7f5bSEtienne Carriere * Copyright (c) 2017-2022, 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> 90bdd7f5bSEtienne Carriere #include <drivers/stm32_iwdg.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> 220bdd7f5bSEtienne Carriere #include <libfdt.h> 230bdd7f5bSEtienne Carriere #include <mm/core_memprot.h> 240bdd7f5bSEtienne Carriere #include <sm/sm.h> 250bdd7f5bSEtienne Carriere #include <stm32_util.h> 260bdd7f5bSEtienne Carriere #include <string.h> 270bdd7f5bSEtienne Carriere #include <trace.h> 280bdd7f5bSEtienne Carriere 290bdd7f5bSEtienne Carriere /* IWDG Compatibility */ 300bdd7f5bSEtienne Carriere #define IWDG_TIMEOUT_US U(1000) 310bdd7f5bSEtienne Carriere #define IWDG_CNT_MASK GENMASK_32(11, 0) 320bdd7f5bSEtienne Carriere 330bdd7f5bSEtienne Carriere /* IWDG registers offsets */ 340bdd7f5bSEtienne Carriere #define IWDG_KR_OFFSET U(0x00) 350bdd7f5bSEtienne Carriere #define IWDG_PR_OFFSET U(0x04) 360bdd7f5bSEtienne Carriere #define IWDG_RLR_OFFSET U(0x08) 370bdd7f5bSEtienne Carriere #define IWDG_SR_OFFSET U(0x0C) 380bdd7f5bSEtienne Carriere #define IWDG_EWCR_OFFSET U(0x14) 390bdd7f5bSEtienne Carriere 400bdd7f5bSEtienne Carriere #define IWDG_KR_ACCESS_KEY U(0x5555) 410bdd7f5bSEtienne Carriere #define IWDG_KR_RELOAD_KEY U(0xAAAA) 420bdd7f5bSEtienne Carriere #define IWDG_KR_START_KEY U(0xCCCC) 430bdd7f5bSEtienne Carriere 440bdd7f5bSEtienne Carriere /* Use a fixed prescaler divider of 256 */ 450bdd7f5bSEtienne Carriere #define IWDG_PRESCALER_256 U(256) 460bdd7f5bSEtienne Carriere #define IWDG_PR_DIV_256 U(0x06) 470bdd7f5bSEtienne Carriere #define IWDG_PR_DIV_MASK GENMASK_32(3, 0) 480bdd7f5bSEtienne Carriere 490bdd7f5bSEtienne Carriere #define IWDG_SR_PVU BIT(0) 500bdd7f5bSEtienne Carriere #define IWDG_SR_RVU BIT(1) 510bdd7f5bSEtienne Carriere #define IWDG_SR_WVU BIT(2) 520bdd7f5bSEtienne Carriere #define IWDG_SR_EWU BIT(3) 530bdd7f5bSEtienne Carriere #define IWDG_SR_UPDATE_MASK (IWDG_SR_PVU | IWDG_SR_RVU | IWDG_SR_WVU | \ 540bdd7f5bSEtienne Carriere IWDG_SR_EWU) 550bdd7f5bSEtienne Carriere 560bdd7f5bSEtienne Carriere #define IWDG_EWCR_EWIE BIT(15) 570bdd7f5bSEtienne Carriere #define IWDG_EWCR_EWIC BIT(14) 580bdd7f5bSEtienne Carriere 590bdd7f5bSEtienne Carriere /* 600bdd7f5bSEtienne Carriere * Values for struct stm32_iwdg_device::flags 610bdd7f5bSEtienne Carriere * IWDG_FLAGS_HW_ENABLED Watchdog is enabled by BootROM 620bdd7f5bSEtienne Carriere * IWDG_FLAGS_DISABLE_ON_STOP Watchdog is freezed in SoC STOP mode 630bdd7f5bSEtienne Carriere * IWDG_FLAGS_DISABLE_ON_STANDBY Watchdog is freezed in SoC STANDBY mode 640bdd7f5bSEtienne Carriere * IWDG_FLAGS_NON_SECURE Instance is assigned to non-secure world 650bdd7f5bSEtienne Carriere * IWDG_FLAGS_ENABLED Watchdog has been enabled 660bdd7f5bSEtienne Carriere */ 670bdd7f5bSEtienne Carriere #define IWDG_FLAGS_HW_ENABLED BIT(0) 680bdd7f5bSEtienne Carriere #define IWDG_FLAGS_DISABLE_ON_STOP BIT(1) 690bdd7f5bSEtienne Carriere #define IWDG_FLAGS_DISABLE_ON_STANDBY BIT(2) 700bdd7f5bSEtienne Carriere #define IWDG_FLAGS_NON_SECURE BIT(3) 710bdd7f5bSEtienne Carriere #define IWDG_FLAGS_ENABLED BIT(4) 720bdd7f5bSEtienne Carriere 730bdd7f5bSEtienne Carriere /* 740bdd7f5bSEtienne Carriere * IWDG watch instance data 750bdd7f5bSEtienne Carriere * @base - IWDG interface IOMEM base address 760bdd7f5bSEtienne Carriere * @clock - Bus clock 770bdd7f5bSEtienne Carriere * @clk_lsi - IWDG source clock 780bdd7f5bSEtienne Carriere * @flags - Property flags for the IWDG instance 790bdd7f5bSEtienne Carriere * @timeout - Watchdog elaspure timeout 800bdd7f5bSEtienne Carriere * @wdt_chip - Wathcdog chip instance 810bdd7f5bSEtienne Carriere * @link - Link in registered watchdog instance list 820bdd7f5bSEtienne Carriere */ 830bdd7f5bSEtienne Carriere struct stm32_iwdg_device { 840bdd7f5bSEtienne Carriere struct io_pa_va base; 850bdd7f5bSEtienne Carriere struct clk *clock; 860bdd7f5bSEtienne Carriere struct clk *clk_lsi; 870bdd7f5bSEtienne Carriere uint32_t flags; 880bdd7f5bSEtienne Carriere unsigned long timeout; 890bdd7f5bSEtienne Carriere struct wdt_chip wdt_chip; 900bdd7f5bSEtienne Carriere SLIST_ENTRY(stm32_iwdg_device) link; 910bdd7f5bSEtienne Carriere }; 920bdd7f5bSEtienne Carriere 930bdd7f5bSEtienne Carriere static unsigned int iwdg_lock = SPINLOCK_UNLOCK; 940bdd7f5bSEtienne Carriere 950bdd7f5bSEtienne Carriere static SLIST_HEAD(iwdg_dev_list_head, stm32_iwdg_device) iwdg_dev_list = 960bdd7f5bSEtienne Carriere SLIST_HEAD_INITIALIZER(iwdg_dev_list_head); 970bdd7f5bSEtienne Carriere 980bdd7f5bSEtienne Carriere static vaddr_t get_base(struct stm32_iwdg_device *iwdg) 990bdd7f5bSEtienne Carriere { 1000bdd7f5bSEtienne Carriere return io_pa_or_va(&iwdg->base, 1); 1010bdd7f5bSEtienne Carriere } 1020bdd7f5bSEtienne Carriere 1030bdd7f5bSEtienne Carriere static bool is_assigned_to_nsec(struct stm32_iwdg_device *iwdg) 1040bdd7f5bSEtienne Carriere { 1050bdd7f5bSEtienne Carriere return iwdg->flags & IWDG_FLAGS_NON_SECURE; 1060bdd7f5bSEtienne Carriere } 1070bdd7f5bSEtienne Carriere 1080bdd7f5bSEtienne Carriere static bool is_enable(struct stm32_iwdg_device *iwdg) 1090bdd7f5bSEtienne Carriere { 1100bdd7f5bSEtienne Carriere return iwdg->flags & IWDG_FLAGS_ENABLED; 1110bdd7f5bSEtienne Carriere } 1120bdd7f5bSEtienne Carriere 1130bdd7f5bSEtienne Carriere /* Return counter value to related to input timeout in seconds, or 0 on error */ 1140bdd7f5bSEtienne Carriere static uint32_t iwdg_timeout_cnt(struct stm32_iwdg_device *iwdg, 1150bdd7f5bSEtienne Carriere unsigned long to_sec) 1160bdd7f5bSEtienne Carriere { 1170bdd7f5bSEtienne Carriere uint64_t reload = (uint64_t)to_sec * clk_get_rate(iwdg->clk_lsi); 1180bdd7f5bSEtienne Carriere uint64_t cnt = (reload / IWDG_PRESCALER_256) - 1; 1190bdd7f5bSEtienne Carriere 1200bdd7f5bSEtienne Carriere /* Be safe and expect any counter to be above 2 */ 1210bdd7f5bSEtienne Carriere if (cnt > IWDG_CNT_MASK || cnt < 3) 1220bdd7f5bSEtienne Carriere return 0; 1230bdd7f5bSEtienne Carriere 1240bdd7f5bSEtienne Carriere return cnt; 1250bdd7f5bSEtienne Carriere } 1260bdd7f5bSEtienne Carriere 1270bdd7f5bSEtienne Carriere /* Wait IWDG programming completes */ 1280bdd7f5bSEtienne Carriere static TEE_Result iwdg_wait_sync(struct stm32_iwdg_device *iwdg) 1290bdd7f5bSEtienne Carriere { 1300bdd7f5bSEtienne Carriere uint64_t timeout_ref = timeout_init_us(IWDG_TIMEOUT_US); 1310bdd7f5bSEtienne Carriere vaddr_t iwdg_base = get_base(iwdg); 1320bdd7f5bSEtienne Carriere 1330bdd7f5bSEtienne Carriere while (io_read32(iwdg_base + IWDG_SR_OFFSET) & IWDG_SR_UPDATE_MASK) 1340bdd7f5bSEtienne Carriere if (timeout_elapsed(timeout_ref)) 1350bdd7f5bSEtienne Carriere break; 1360bdd7f5bSEtienne Carriere 137077bbb8aSEtienne Carriere if (io_read32(iwdg_base + IWDG_SR_OFFSET) & IWDG_SR_UPDATE_MASK) 1380bdd7f5bSEtienne Carriere return TEE_ERROR_GENERIC; 1390bdd7f5bSEtienne Carriere 1400bdd7f5bSEtienne Carriere return TEE_SUCCESS; 1410bdd7f5bSEtienne Carriere } 1420bdd7f5bSEtienne Carriere 1430bdd7f5bSEtienne Carriere static TEE_Result configure_timeout(struct stm32_iwdg_device *iwdg) 1440bdd7f5bSEtienne Carriere { 1450bdd7f5bSEtienne Carriere TEE_Result res = TEE_ERROR_GENERIC; 1460bdd7f5bSEtienne Carriere vaddr_t iwdg_base = get_base(iwdg); 1470bdd7f5bSEtienne Carriere uint32_t rlr_value = 0; 1480bdd7f5bSEtienne Carriere 1490bdd7f5bSEtienne Carriere assert(is_enable(iwdg)); 1500bdd7f5bSEtienne Carriere 1510bdd7f5bSEtienne Carriere rlr_value = iwdg_timeout_cnt(iwdg, iwdg->timeout); 1520bdd7f5bSEtienne Carriere if (!rlr_value) 1530bdd7f5bSEtienne Carriere return TEE_ERROR_GENERIC; 1540bdd7f5bSEtienne Carriere 1550bdd7f5bSEtienne Carriere clk_enable(iwdg->clock); 1560bdd7f5bSEtienne Carriere 1570bdd7f5bSEtienne Carriere io_write32(iwdg_base + IWDG_KR_OFFSET, IWDG_KR_ACCESS_KEY); 1580bdd7f5bSEtienne Carriere io_write32(iwdg_base + IWDG_PR_OFFSET, IWDG_PR_DIV_256); 1590bdd7f5bSEtienne Carriere io_write32(iwdg_base + IWDG_RLR_OFFSET, rlr_value); 1600bdd7f5bSEtienne Carriere io_write32(iwdg_base + IWDG_KR_OFFSET, IWDG_KR_RELOAD_KEY); 1610bdd7f5bSEtienne Carriere 1620bdd7f5bSEtienne Carriere res = iwdg_wait_sync(iwdg); 1630bdd7f5bSEtienne Carriere 1640bdd7f5bSEtienne Carriere clk_disable(iwdg->clock); 1650bdd7f5bSEtienne Carriere 1660bdd7f5bSEtienne Carriere return res; 1670bdd7f5bSEtienne Carriere } 1680bdd7f5bSEtienne Carriere 1690bdd7f5bSEtienne Carriere static void iwdg_start(struct stm32_iwdg_device *iwdg) 1700bdd7f5bSEtienne Carriere { 1710bdd7f5bSEtienne Carriere clk_enable(iwdg->clock); 1720bdd7f5bSEtienne Carriere io_write32(get_base(iwdg) + IWDG_KR_OFFSET, IWDG_KR_START_KEY); 1730bdd7f5bSEtienne Carriere clk_disable(iwdg->clock); 1740bdd7f5bSEtienne Carriere 1750bdd7f5bSEtienne Carriere iwdg->flags |= IWDG_FLAGS_ENABLED; 1760bdd7f5bSEtienne Carriere } 1770bdd7f5bSEtienne Carriere 1780bdd7f5bSEtienne Carriere static void iwdg_refresh(struct stm32_iwdg_device *iwdg) 1790bdd7f5bSEtienne Carriere { 1800bdd7f5bSEtienne Carriere clk_enable(iwdg->clock); 1810bdd7f5bSEtienne Carriere io_write32(get_base(iwdg) + IWDG_KR_OFFSET, IWDG_KR_RELOAD_KEY); 1820bdd7f5bSEtienne Carriere clk_disable(iwdg->clock); 1830bdd7f5bSEtienne Carriere } 1840bdd7f5bSEtienne Carriere 1850bdd7f5bSEtienne Carriere /* Operators for watchdog OP-TEE interface */ 1860bdd7f5bSEtienne Carriere static struct stm32_iwdg_device *wdt_chip_to_iwdg(struct wdt_chip *chip) 1870bdd7f5bSEtienne Carriere { 1880bdd7f5bSEtienne Carriere return container_of(chip, struct stm32_iwdg_device, wdt_chip); 1890bdd7f5bSEtienne Carriere } 1900bdd7f5bSEtienne Carriere 191*fc9063ddSEtienne Carriere static TEE_Result iwdg_wdt_init(struct wdt_chip *chip, 192*fc9063ddSEtienne Carriere unsigned long *min_timeout, 193*fc9063ddSEtienne Carriere unsigned long *max_timeout) 194*fc9063ddSEtienne Carriere { 195*fc9063ddSEtienne Carriere struct stm32_iwdg_device *iwdg = wdt_chip_to_iwdg(chip); 196*fc9063ddSEtienne Carriere unsigned long rate = clk_get_rate(iwdg->clk_lsi); 197*fc9063ddSEtienne Carriere 198*fc9063ddSEtienne Carriere if (!rate) 199*fc9063ddSEtienne Carriere return TEE_ERROR_GENERIC; 200*fc9063ddSEtienne Carriere 201*fc9063ddSEtienne Carriere /* Be safe and expect any counter to be above 2 */ 202*fc9063ddSEtienne Carriere *min_timeout = 3 * IWDG_PRESCALER_256 / rate; 203*fc9063ddSEtienne Carriere *max_timeout = (IWDG_CNT_MASK + 1) * IWDG_PRESCALER_256 / rate; 204*fc9063ddSEtienne Carriere 205*fc9063ddSEtienne Carriere return TEE_SUCCESS; 206*fc9063ddSEtienne Carriere } 207*fc9063ddSEtienne Carriere 2080bdd7f5bSEtienne Carriere static void iwdg_wdt_start(struct wdt_chip *chip) 2090bdd7f5bSEtienne Carriere { 2100bdd7f5bSEtienne Carriere struct stm32_iwdg_device *iwdg = wdt_chip_to_iwdg(chip); 2110bdd7f5bSEtienne Carriere 2120bdd7f5bSEtienne Carriere iwdg_start(iwdg); 2130bdd7f5bSEtienne Carriere 2140bdd7f5bSEtienne Carriere if (configure_timeout(iwdg)) 2150bdd7f5bSEtienne Carriere panic(); 2160bdd7f5bSEtienne Carriere } 2170bdd7f5bSEtienne Carriere 2180bdd7f5bSEtienne Carriere static void iwdg_wdt_refresh(struct wdt_chip *chip) 2190bdd7f5bSEtienne Carriere { 2200bdd7f5bSEtienne Carriere struct stm32_iwdg_device *iwdg = wdt_chip_to_iwdg(chip); 2210bdd7f5bSEtienne Carriere 2220bdd7f5bSEtienne Carriere iwdg_refresh(iwdg); 2230bdd7f5bSEtienne Carriere } 2240bdd7f5bSEtienne Carriere 2250bdd7f5bSEtienne Carriere static TEE_Result iwdg_wdt_set_timeout(struct wdt_chip *chip, 2260bdd7f5bSEtienne Carriere unsigned long timeout) 2270bdd7f5bSEtienne Carriere { 2280bdd7f5bSEtienne Carriere struct stm32_iwdg_device *iwdg = wdt_chip_to_iwdg(chip); 2290bdd7f5bSEtienne Carriere 2300bdd7f5bSEtienne Carriere if (!iwdg_timeout_cnt(iwdg, timeout)) 2310bdd7f5bSEtienne Carriere return TEE_ERROR_BAD_PARAMETERS; 2320bdd7f5bSEtienne Carriere 2330bdd7f5bSEtienne Carriere iwdg->timeout = timeout; 2340bdd7f5bSEtienne Carriere 2350bdd7f5bSEtienne Carriere if (is_enable(iwdg)) { 2360bdd7f5bSEtienne Carriere TEE_Result res = TEE_ERROR_GENERIC; 2370bdd7f5bSEtienne Carriere 2380bdd7f5bSEtienne Carriere res = configure_timeout(iwdg); 2390bdd7f5bSEtienne Carriere if (res) 2400bdd7f5bSEtienne Carriere return res; 2410bdd7f5bSEtienne Carriere } 2420bdd7f5bSEtienne Carriere 2430bdd7f5bSEtienne Carriere return TEE_SUCCESS; 2440bdd7f5bSEtienne Carriere } 2450bdd7f5bSEtienne Carriere 2460bdd7f5bSEtienne Carriere static const struct wdt_ops stm32_iwdg_ops = { 247*fc9063ddSEtienne Carriere .init = iwdg_wdt_init, 2480bdd7f5bSEtienne Carriere .start = iwdg_wdt_start, 2490bdd7f5bSEtienne Carriere .ping = iwdg_wdt_refresh, 2500bdd7f5bSEtienne Carriere .set_timeout = iwdg_wdt_set_timeout, 2510bdd7f5bSEtienne Carriere }; 2520bdd7f5bSEtienne Carriere DECLARE_KEEP_PAGER(stm32_iwdg_ops); 2530bdd7f5bSEtienne Carriere 2540bdd7f5bSEtienne Carriere /* Refresh all registered watchdogs */ 2550bdd7f5bSEtienne Carriere void stm32_iwdg_refresh(void) 2560bdd7f5bSEtienne Carriere { 2570bdd7f5bSEtienne Carriere struct stm32_iwdg_device *iwdg = NULL; 2580bdd7f5bSEtienne Carriere uint32_t exceptions = cpu_spin_lock_xsave(&iwdg_lock); 2590bdd7f5bSEtienne Carriere 2600bdd7f5bSEtienne Carriere SLIST_FOREACH(iwdg, &iwdg_dev_list, link) 2610bdd7f5bSEtienne Carriere iwdg_refresh(iwdg); 2620bdd7f5bSEtienne Carriere 2630bdd7f5bSEtienne Carriere cpu_spin_unlock_xrestore(&iwdg_lock, exceptions); 2640bdd7f5bSEtienne Carriere } 2650bdd7f5bSEtienne Carriere 2660bdd7f5bSEtienne Carriere /* Driver initialization */ 2670bdd7f5bSEtienne Carriere static TEE_Result stm32_iwdg_parse_fdt(struct stm32_iwdg_device *iwdg, 2680bdd7f5bSEtienne Carriere const void *fdt, int node) 2690bdd7f5bSEtienne Carriere { 2700bdd7f5bSEtienne Carriere TEE_Result res = TEE_ERROR_GENERIC; 2710bdd7f5bSEtienne Carriere struct dt_node_info dt_info = { }; 2720bdd7f5bSEtienne Carriere const fdt32_t *cuint = NULL; 2730bdd7f5bSEtienne Carriere 274f354a5d8SGatien Chevallier fdt_fill_device_info(fdt, &dt_info, node); 2750bdd7f5bSEtienne Carriere 2760bdd7f5bSEtienne Carriere if (dt_info.reg == DT_INFO_INVALID_REG || 2770bdd7f5bSEtienne Carriere dt_info.reg_size == DT_INFO_INVALID_REG_SIZE) 2780bdd7f5bSEtienne Carriere panic(); 2790bdd7f5bSEtienne Carriere 2800bdd7f5bSEtienne Carriere res = clk_dt_get_by_name(fdt, node, "pclk", &iwdg->clock); 2810bdd7f5bSEtienne Carriere if (res) 2820bdd7f5bSEtienne Carriere return res; 2830bdd7f5bSEtienne Carriere 2840bdd7f5bSEtienne Carriere res = clk_dt_get_by_name(fdt, node, "lsi", &iwdg->clk_lsi); 2850bdd7f5bSEtienne Carriere if (res) 2860bdd7f5bSEtienne Carriere return res; 2870bdd7f5bSEtienne Carriere 2880bdd7f5bSEtienne Carriere if (dt_info.status == DT_STATUS_OK_NSEC) 2890bdd7f5bSEtienne Carriere iwdg->flags |= IWDG_FLAGS_NON_SECURE; 2900bdd7f5bSEtienne Carriere 2910bdd7f5bSEtienne Carriere /* Get IOMEM address */ 2920bdd7f5bSEtienne Carriere iwdg->base.pa = dt_info.reg; 2930bdd7f5bSEtienne Carriere 2940bdd7f5bSEtienne Carriere if (iwdg->flags & IWDG_FLAGS_NON_SECURE) 2950bdd7f5bSEtienne Carriere io_pa_or_va_nsec(&iwdg->base, dt_info.reg_size); 2960bdd7f5bSEtienne Carriere else 2970bdd7f5bSEtienne Carriere io_pa_or_va_secure(&iwdg->base, dt_info.reg_size); 2980bdd7f5bSEtienne Carriere 2990bdd7f5bSEtienne Carriere assert(iwdg->base.va); 3000bdd7f5bSEtienne Carriere 3010bdd7f5bSEtienne Carriere /* Get and check timeout value */ 3020bdd7f5bSEtienne Carriere cuint = fdt_getprop(fdt, node, "timeout-sec", NULL); 3030bdd7f5bSEtienne Carriere if (!cuint) 3040bdd7f5bSEtienne Carriere return TEE_ERROR_BAD_PARAMETERS; 3050bdd7f5bSEtienne Carriere 3060bdd7f5bSEtienne Carriere iwdg->timeout = (int)fdt32_to_cpu(*cuint); 3070bdd7f5bSEtienne Carriere if (!iwdg->timeout) 3080bdd7f5bSEtienne Carriere return TEE_ERROR_BAD_PARAMETERS; 3090bdd7f5bSEtienne Carriere 3100bdd7f5bSEtienne Carriere if (!iwdg_timeout_cnt(iwdg, iwdg->timeout)) { 3110bdd7f5bSEtienne Carriere EMSG("Timeout %lu not applicable", iwdg->timeout); 3120bdd7f5bSEtienne Carriere return TEE_ERROR_BAD_PARAMETERS; 3130bdd7f5bSEtienne Carriere } 3140bdd7f5bSEtienne Carriere 3150bdd7f5bSEtienne Carriere /* DT can specify low power cases */ 3160bdd7f5bSEtienne Carriere if (!fdt_getprop(fdt, node, "stm32,enable-on-stop", NULL)) 3170bdd7f5bSEtienne Carriere iwdg->flags |= IWDG_FLAGS_DISABLE_ON_STOP; 3180bdd7f5bSEtienne Carriere 3190bdd7f5bSEtienne Carriere if (!fdt_getprop(fdt, node, "stm32,enable-on-standby", NULL)) 3200bdd7f5bSEtienne Carriere iwdg->flags |= IWDG_FLAGS_DISABLE_ON_STANDBY; 3210bdd7f5bSEtienne Carriere 3220bdd7f5bSEtienne Carriere return TEE_SUCCESS; 3230bdd7f5bSEtienne Carriere } 3240bdd7f5bSEtienne Carriere 3250bdd7f5bSEtienne Carriere /* Platform should override this function to provide IWDG fuses configuration */ 3260bdd7f5bSEtienne Carriere TEE_Result __weak stm32_get_iwdg_otp_config(paddr_t pbase __unused, 3270bdd7f5bSEtienne Carriere struct stm32_iwdg_otp_data *otp_d) 3280bdd7f5bSEtienne Carriere { 3290bdd7f5bSEtienne Carriere otp_d->hw_enabled = false; 3300bdd7f5bSEtienne Carriere otp_d->disable_on_stop = false; 3310bdd7f5bSEtienne Carriere otp_d->disable_on_standby = false; 3320bdd7f5bSEtienne Carriere 3330bdd7f5bSEtienne Carriere return TEE_SUCCESS; 3340bdd7f5bSEtienne Carriere } 3350bdd7f5bSEtienne Carriere 3360bdd7f5bSEtienne Carriere static TEE_Result stm32_iwdg_setup(struct stm32_iwdg_device *iwdg, 3370bdd7f5bSEtienne Carriere const void *fdt, int node) 3380bdd7f5bSEtienne Carriere { 3390bdd7f5bSEtienne Carriere struct stm32_iwdg_otp_data otp_data = { }; 3400bdd7f5bSEtienne Carriere TEE_Result res = TEE_SUCCESS; 3410bdd7f5bSEtienne Carriere 3420bdd7f5bSEtienne Carriere res = stm32_iwdg_parse_fdt(iwdg, fdt, node); 3430bdd7f5bSEtienne Carriere if (res) 3440bdd7f5bSEtienne Carriere return res; 3450bdd7f5bSEtienne Carriere 3460bdd7f5bSEtienne Carriere res = stm32_get_iwdg_otp_config(iwdg->base.pa, &otp_data); 3470bdd7f5bSEtienne Carriere if (res) 3480bdd7f5bSEtienne Carriere return res; 3490bdd7f5bSEtienne Carriere 3500bdd7f5bSEtienne Carriere if (otp_data.hw_enabled) 3510bdd7f5bSEtienne Carriere iwdg->flags |= IWDG_FLAGS_HW_ENABLED; 3520bdd7f5bSEtienne Carriere if (otp_data.disable_on_stop) 3530bdd7f5bSEtienne Carriere iwdg->flags |= IWDG_FLAGS_DISABLE_ON_STOP; 3540bdd7f5bSEtienne Carriere if (otp_data.disable_on_standby) 3550bdd7f5bSEtienne Carriere iwdg->flags |= IWDG_FLAGS_DISABLE_ON_STANDBY; 3560bdd7f5bSEtienne Carriere 3570bdd7f5bSEtienne Carriere /* Enable watchdog source clock once for all */ 3580bdd7f5bSEtienne Carriere clk_enable(iwdg->clk_lsi); 3590bdd7f5bSEtienne Carriere 3600bdd7f5bSEtienne Carriere if (otp_data.hw_enabled) { 3610bdd7f5bSEtienne Carriere iwdg->flags |= IWDG_FLAGS_ENABLED; 3620bdd7f5bSEtienne Carriere 3630bdd7f5bSEtienne Carriere /* Configure timeout if watchdog is already enabled */ 3640bdd7f5bSEtienne Carriere res = configure_timeout(iwdg); 3650bdd7f5bSEtienne Carriere if (res) 3660bdd7f5bSEtienne Carriere return res; 3670bdd7f5bSEtienne Carriere 3680bdd7f5bSEtienne Carriere iwdg_refresh(iwdg); 3690bdd7f5bSEtienne Carriere } 3700bdd7f5bSEtienne Carriere 3710bdd7f5bSEtienne Carriere return TEE_SUCCESS; 3720bdd7f5bSEtienne Carriere } 3730bdd7f5bSEtienne Carriere 3740bdd7f5bSEtienne Carriere static TEE_Result stm32_iwdg_register(struct stm32_iwdg_device *iwdg) 3750bdd7f5bSEtienne Carriere { 3760bdd7f5bSEtienne Carriere TEE_Result res = TEE_ERROR_GENERIC; 3770bdd7f5bSEtienne Carriere 3780bdd7f5bSEtienne Carriere if (is_assigned_to_nsec(iwdg)) { 3790bdd7f5bSEtienne Carriere stm32mp_register_non_secure_periph_iomem(iwdg->base.pa); 3800bdd7f5bSEtienne Carriere } else { 3810bdd7f5bSEtienne Carriere stm32mp_register_secure_periph_iomem(iwdg->base.pa); 3820bdd7f5bSEtienne Carriere 3830bdd7f5bSEtienne Carriere /* Expose watchdog runtime service only to secure IWDG */ 3840bdd7f5bSEtienne Carriere iwdg->wdt_chip.ops = &stm32_iwdg_ops; 3850bdd7f5bSEtienne Carriere 3860bdd7f5bSEtienne Carriere res = watchdog_register(&iwdg->wdt_chip); 3870bdd7f5bSEtienne Carriere if (res) 3880bdd7f5bSEtienne Carriere return res; 3890bdd7f5bSEtienne Carriere } 3900bdd7f5bSEtienne Carriere 3910bdd7f5bSEtienne Carriere SLIST_INSERT_HEAD(&iwdg_dev_list, iwdg, link); 3920bdd7f5bSEtienne Carriere 3930bdd7f5bSEtienne Carriere return TEE_SUCCESS; 3940bdd7f5bSEtienne Carriere } 3950bdd7f5bSEtienne Carriere 3960bdd7f5bSEtienne Carriere static TEE_Result stm32_iwdg_probe(const void *fdt, int node, 3970bdd7f5bSEtienne Carriere const void *compat_data __unused) 3980bdd7f5bSEtienne Carriere { 3990bdd7f5bSEtienne Carriere struct stm32_iwdg_device *iwdg = NULL; 4000bdd7f5bSEtienne Carriere TEE_Result res = TEE_SUCCESS; 4010bdd7f5bSEtienne Carriere 4020bdd7f5bSEtienne Carriere iwdg = calloc(1, sizeof(*iwdg)); 4030bdd7f5bSEtienne Carriere if (!iwdg) 4040bdd7f5bSEtienne Carriere return TEE_ERROR_OUT_OF_MEMORY; 4050bdd7f5bSEtienne Carriere 4060bdd7f5bSEtienne Carriere res = stm32_iwdg_setup(iwdg, fdt, node); 4070bdd7f5bSEtienne Carriere if (res) 4080bdd7f5bSEtienne Carriere goto err; 4090bdd7f5bSEtienne Carriere 4100bdd7f5bSEtienne Carriere res = stm32_iwdg_register(iwdg); 4110bdd7f5bSEtienne Carriere if (res) 4120bdd7f5bSEtienne Carriere goto err; 4130bdd7f5bSEtienne Carriere 4140bdd7f5bSEtienne Carriere return TEE_SUCCESS; 4150bdd7f5bSEtienne Carriere 4160bdd7f5bSEtienne Carriere err: 4170bdd7f5bSEtienne Carriere free(iwdg); 4180bdd7f5bSEtienne Carriere return res; 4190bdd7f5bSEtienne Carriere } 4200bdd7f5bSEtienne Carriere 4210bdd7f5bSEtienne Carriere static const struct dt_device_match stm32_iwdg_match_table[] = { 4220bdd7f5bSEtienne Carriere { .compatible = "st,stm32mp1-iwdg" }, 4230bdd7f5bSEtienne Carriere { } 4240bdd7f5bSEtienne Carriere }; 4250bdd7f5bSEtienne Carriere 4260bdd7f5bSEtienne Carriere DEFINE_DT_DRIVER(stm32_iwdg_dt_driver) = { 4270bdd7f5bSEtienne Carriere .name = "stm32-iwdg", 4280bdd7f5bSEtienne Carriere .match_table = stm32_iwdg_match_table, 4290bdd7f5bSEtienne Carriere .probe = stm32_iwdg_probe, 4300bdd7f5bSEtienne Carriere }; 431