From 091303c788c2360d6ec0bf1b315d160b3b556a07 Mon Sep 17 00:00:00 2001 From: Antonio Borneo Date: Fri, 23 May 2025 11:46:55 +0200 Subject: [PATCH 1/4] watchdog: wdt-uclass.c: add wdt_set_force_autostart() helper The watchdog could have been already started by a previous boot stage (e.g. bootrom or secure OS). U-Boot has to start and kick the watchdog even when CONFIG_WATCHDOG_AUTOSTART is not enabled or when the DT property u-boot,noautostart is present. Add the helper wdt_set_force_autostart() that can be called by the driver's probe() when it detects that the watchdog has already been started and is running. Co-developed-by: Patrice Chotard Signed-off-by: Patrice Chotard Signed-off-by: Antonio Borneo Reviewed-by: Patrice Chotard --- drivers/watchdog/wdt-uclass.c | 9 +++++++++ include/wdt.h | 9 +++++++++ 2 files changed, 18 insertions(+) diff --git a/drivers/watchdog/wdt-uclass.c b/drivers/watchdog/wdt-uclass.c index 10be334e9ed..b32590069d9 100644 --- a/drivers/watchdog/wdt-uclass.c +++ b/drivers/watchdog/wdt-uclass.c @@ -46,6 +46,15 @@ struct wdt_priv { struct cyclic_info cyclic; }; +int wdt_set_force_autostart(struct udevice *dev) +{ + struct wdt_priv *priv = dev_get_uclass_priv(dev); + + priv->autostart = true; + + return 0; +} + static void wdt_cyclic(struct cyclic_info *c) { struct wdt_priv *priv = container_of(c, struct wdt_priv, cyclic); diff --git a/include/wdt.h b/include/wdt.h index 5026f5a6db4..1ef656585c4 100644 --- a/include/wdt.h +++ b/include/wdt.h @@ -18,6 +18,15 @@ struct udevice; * which typically include placing the system in a safe, known state. */ +/* + * Force watchdog start during init. Called by driver's probe when the watchdog + * is detected as already started. + * + * @dev: WDT Device + * @return: 0 if OK, -ve on error + */ +int wdt_set_force_autostart(struct udevice *dev); + /* * Start the timer * From 6319fabe32e76d72ceb60176b63cf8a46e78d4df Mon Sep 17 00:00:00 2001 From: Antonio Borneo Date: Fri, 23 May 2025 11:46:56 +0200 Subject: [PATCH 2/4] watchdog: arm_smc_wdt: get wdt status through SMCWD_GET_TIMELEFT The optional SMCWD_GET_TIMELEFT command can be used to detect if the watchdog has already been started. See the implementation in OP-TEE secure OS [1]. At probe time, check if the watchdog is already started and then call wdt_set_force_autostart(). This will keep U-Boot pinging the watchdog even when the property 'u-boot,noautostart' is present. Link: https://github.com/OP-TEE/optee_os/commit/a7f2d4bd8632 [1] Signed-off-by: Antonio Borneo Reviewed-by: Patrice Chotard --- drivers/watchdog/arm_smc_wdt.c | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/drivers/watchdog/arm_smc_wdt.c b/drivers/watchdog/arm_smc_wdt.c index 0ea44445700..f6854aa9ac9 100644 --- a/drivers/watchdog/arm_smc_wdt.c +++ b/drivers/watchdog/arm_smc_wdt.c @@ -46,6 +46,8 @@ static int smcwd_call(struct udevice *dev, enum smcwd_call call, return -ENODEV; if (res->a0 == PSCI_RET_INVALID_PARAMS) return -EINVAL; + if (res->a0 == PSCI_RET_DISABLED) + return -ENODATA; if (res->a0 != PSCI_RET_SUCCESS) return -EIO; @@ -99,6 +101,21 @@ static int smcwd_probe(struct udevice *dev) priv->min_timeout = res.a1; priv->max_timeout = res.a2; + /* If already started, then force u-boot to use it */ + err = smcwd_call(dev, SMCWD_GET_TIMELEFT, 0, NULL); + switch (err) { + case 0: + dev_dbg(dev, "Already started\n"); + wdt_set_force_autostart(dev); + break; + case -ENODATA: + dev_dbg(dev, "Not already started\n"); + break; + default: + /* Optional SMCWD_GET_TIMELEFT not implemented */ + break; + } + return 0; } From 4bef031d6991def2afc297caf0da5c7d4a680663 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Le=20Goffic?= Date: Fri, 23 May 2025 11:46:57 +0200 Subject: [PATCH 3/4] watchdog: stm32mp: check the watchdog status MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add a mean to check the IWDG status based on the peripheral version. This is done by either checking the status bit ONF either by updating the reload register with the same value and check if the reload succeed. Signed-off-by: Clément Le Goffic Signed-off-by: Antonio Borneo Reviewed-by: Patrice Chotard --- drivers/watchdog/stm32mp_wdt.c | 33 +++++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/drivers/watchdog/stm32mp_wdt.c b/drivers/watchdog/stm32mp_wdt.c index 97ab8cfe7ab..0712524b4a8 100644 --- a/drivers/watchdog/stm32mp_wdt.c +++ b/drivers/watchdog/stm32mp_wdt.c @@ -21,11 +21,13 @@ #define IWDG_PR 0x04 /* Prescaler Register */ #define IWDG_RLR 0x08 /* ReLoad Register */ #define IWDG_SR 0x0C /* Status Register */ +#define IWDG_VERR 0x3F4 /* Version Register */ /* IWDG_KR register bit mask */ #define KR_KEY_RELOAD 0xAAAA /* Reload counter enable */ #define KR_KEY_ENABLE 0xCCCC /* Peripheral enable */ #define KR_KEY_EWA 0x5555 /* Write access enable */ +#define KR_KEY_DWA 0x0000 /* Write access disable*/ /* IWDG_PR register bit values */ #define PR_256 0x06 /* Prescaler set to 256 */ @@ -36,10 +38,17 @@ /* IWDG_SR register bit values */ #define SR_PVU BIT(0) /* Watchdog prescaler value update */ #define SR_RVU BIT(1) /* Watchdog counter reload value update */ +#define SR_ONF BIT(8) /* Watchdog enable status bit */ + +/* IWDG Compatibility */ +#define ONF_MIN_VER 0x31 + +#define TIMEOUT_US 10000 struct stm32mp_wdt_priv { fdt_addr_t base; /* registers addr in physical memory */ unsigned long wdt_clk_rate; /* Watchdog dedicated clock rate */ + unsigned int hw_version; /* Peripheral version */ }; static int stm32mp_wdt_reset(struct udevice *dev) @@ -90,6 +99,7 @@ static int stm32mp_wdt_start(struct udevice *dev, u64 timeout_ms, ulong flags) static int stm32mp_wdt_probe(struct udevice *dev) { struct stm32mp_wdt_priv *priv = dev_get_priv(dev); + u32 rlr, sr; struct clk clk; int ret; @@ -115,6 +125,29 @@ static int stm32mp_wdt_probe(struct udevice *dev) priv->wdt_clk_rate = clk_get_rate(&clk); + priv->hw_version = readl(priv->base + IWDG_VERR); + + if (priv->hw_version >= ONF_MIN_VER) { + if (readl(priv->base + IWDG_SR) & SR_ONF) + wdt_set_force_autostart(dev); + } else { + /* + * Workaround for old versions without IWDG_SR_ONF bit: + * - write in IWDG_RLR_OFFSET + * - wait for sync + * - if sync succeeds, then iwdg is running + */ + writel(KR_KEY_EWA, priv->base + IWDG_KR); + rlr = readl(priv->base + IWDG_RLR); + writel(rlr, priv->base + IWDG_RLR); + ret = readl_poll_timeout(priv->base + IWDG_SR, sr, sr & SR_RVU, + TIMEOUT_US); + if (!ret) + wdt_set_force_autostart(dev); + + writel(KR_KEY_DWA, priv->base + IWDG_KR); + } + dev_dbg(dev, "IWDG init done\n"); return 0; From 7e1e22c70d51d1594ddd3b54e943e511efd65a35 Mon Sep 17 00:00:00 2001 From: Patrice Chotard Date: Fri, 23 May 2025 11:46:58 +0200 Subject: [PATCH 4/4] watchdog: don't autostart watchdog on STM32MP architecture On STM32MP reference boards, the watchdog is started by a previous boot stage (e.g. bootrom or secure OS), so the config flag WATCHDOG_AUTOSTART is not required. It's preferable to rely on the DT properties "u-boot,autostart" or "u-boot,noautostart", if needed. For backward compatibility on defconfigs that are based on SPL, thus cannot rely on a previous boot stage for starting the watchdog, enable WATCHDOG_AUTOSTART in their respective defconfig. The change in stm32mp15_dhsom.config is propagated to: - configs/stm32mp15_dhcom_basic.config - configs/stm32mp15_dhcor_basic.config and then to: - stm32mp15_dhcom_basic_defconfig - stm32mp15_dhcom_drc02_basic_defconfig - stm32mp15_dhcom_pdk2_basic_defconfig - stm32mp15_dhcom_picoitx_basic_defconfig - stm32mp15_dhcor_avenger96_basic_defconfig - stm32mp15_dhcor_basic_defconfig - stm32mp15_dhcor_drc_compact_basic_defconfig - stm32mp15_dhcor_testbench_basic_defconfig Signed-off-by: Patrice Chotard Signed-off-by: Antonio Borneo Reviewed-by: Patrice Chotard --- configs/stm32mp15-icore-stm32mp1-ctouch2_defconfig | 1 + configs/stm32mp15-icore-stm32mp1-edimm2.2_defconfig | 1 + configs/stm32mp15-microgea-stm32mp1-microdev2-of7_defconfig | 1 + configs/stm32mp15-microgea-stm32mp1-microdev2_defconfig | 1 + configs/stm32mp15_basic_defconfig | 1 + configs/stm32mp15_dhsom.config | 1 + drivers/watchdog/Kconfig | 1 + 7 files changed, 7 insertions(+) diff --git a/configs/stm32mp15-icore-stm32mp1-ctouch2_defconfig b/configs/stm32mp15-icore-stm32mp1-ctouch2_defconfig index 2e86abac801..2c71d36dec0 100644 --- a/configs/stm32mp15-icore-stm32mp1-ctouch2_defconfig +++ b/configs/stm32mp15-icore-stm32mp1-ctouch2_defconfig @@ -89,6 +89,7 @@ CONFIG_DM_RTC=y CONFIG_RTC_STM32=y CONFIG_SERIAL_RX_BUFFER=y CONFIG_SYSRESET_SYSCON=y +CONFIG_WATCHDOG_AUTOSTART=y CONFIG_WDT=y CONFIG_WDT_STM32MP=y # CONFIG_BINMAN_FDT is not set diff --git a/configs/stm32mp15-icore-stm32mp1-edimm2.2_defconfig b/configs/stm32mp15-icore-stm32mp1-edimm2.2_defconfig index b800b4c4073..69e9ea4b0c7 100644 --- a/configs/stm32mp15-icore-stm32mp1-edimm2.2_defconfig +++ b/configs/stm32mp15-icore-stm32mp1-edimm2.2_defconfig @@ -89,6 +89,7 @@ CONFIG_DM_RTC=y CONFIG_RTC_STM32=y CONFIG_SERIAL_RX_BUFFER=y CONFIG_SYSRESET_SYSCON=y +CONFIG_WATCHDOG_AUTOSTART=y CONFIG_WDT=y CONFIG_WDT_STM32MP=y # CONFIG_BINMAN_FDT is not set diff --git a/configs/stm32mp15-microgea-stm32mp1-microdev2-of7_defconfig b/configs/stm32mp15-microgea-stm32mp1-microdev2-of7_defconfig index 870e17e451a..ea584f5f794 100644 --- a/configs/stm32mp15-microgea-stm32mp1-microdev2-of7_defconfig +++ b/configs/stm32mp15-microgea-stm32mp1-microdev2-of7_defconfig @@ -89,6 +89,7 @@ CONFIG_DM_RTC=y CONFIG_RTC_STM32=y CONFIG_SERIAL_RX_BUFFER=y CONFIG_SYSRESET_SYSCON=y +CONFIG_WATCHDOG_AUTOSTART=y CONFIG_WDT=y CONFIG_WDT_STM32MP=y # CONFIG_BINMAN_FDT is not set diff --git a/configs/stm32mp15-microgea-stm32mp1-microdev2_defconfig b/configs/stm32mp15-microgea-stm32mp1-microdev2_defconfig index 88ee89aa13a..5b12980f0a4 100644 --- a/configs/stm32mp15-microgea-stm32mp1-microdev2_defconfig +++ b/configs/stm32mp15-microgea-stm32mp1-microdev2_defconfig @@ -89,6 +89,7 @@ CONFIG_DM_RTC=y CONFIG_RTC_STM32=y CONFIG_SERIAL_RX_BUFFER=y CONFIG_SYSRESET_SYSCON=y +CONFIG_WATCHDOG_AUTOSTART=y CONFIG_WDT=y CONFIG_WDT_STM32MP=y # CONFIG_BINMAN_FDT is not set diff --git a/configs/stm32mp15_basic_defconfig b/configs/stm32mp15_basic_defconfig index 4c8ad87e72b..79593cce9fe 100644 --- a/configs/stm32mp15_basic_defconfig +++ b/configs/stm32mp15_basic_defconfig @@ -191,6 +191,7 @@ CONFIG_SPLASH_SCREEN_ALIGN=y CONFIG_BMP_16BPP=y CONFIG_BMP_24BPP=y CONFIG_BMP_32BPP=y +CONFIG_WATCHDOG_AUTOSTART=y CONFIG_WDT=y CONFIG_WDT_STM32MP=y # CONFIG_BINMAN_FDT is not set diff --git a/configs/stm32mp15_dhsom.config b/configs/stm32mp15_dhsom.config index c84116482f6..565b49584e3 100644 --- a/configs/stm32mp15_dhsom.config +++ b/configs/stm32mp15_dhsom.config @@ -76,3 +76,4 @@ CONFIG_PREBOOT="run dh_preboot" CONFIG_SYS_SPI_U_BOOT_OFFS=0x80000 CONFIG_TARGET_DH_STM32MP1_PDK2=y CONFIG_USE_SERVERIP=y +CONFIG_WATCHDOG_AUTOSTART=y diff --git a/drivers/watchdog/Kconfig b/drivers/watchdog/Kconfig index e9ea874d0e3..56290b32bd9 100644 --- a/drivers/watchdog/Kconfig +++ b/drivers/watchdog/Kconfig @@ -15,6 +15,7 @@ config WATCHDOG_AUTOSTART bool "Automatically start watchdog timer" depends on WDT default n if ARCH_SUNXI + default n if ARCH_STM32MP default y help Automatically start watchdog timer and start servicing it during