diff --git a/MAINTAINERS b/MAINTAINERS index 36625795a4..2e5a248808 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -312,6 +312,7 @@ F: drivers/ram/stm32mp1/ F: drivers/misc/stm32_rcc.c F: drivers/reset/stm32-reset.c F: drivers/spi/stm32_qspi.c +F: drivers/watchdog/stm32mp_wdt.c ARM STM STV0991 M: Vikas Manocha diff --git a/arch/arm/mach-stm32mp/Kconfig b/arch/arm/mach-stm32mp/Kconfig index a8b09fb711..d9ad6b423b 100644 --- a/arch/arm/mach-stm32mp/Kconfig +++ b/arch/arm/mach-stm32mp/Kconfig @@ -17,6 +17,7 @@ config SPL select SPL_DM_RESET select SPL_SERIAL_SUPPORT select SPL_SYSCON + select SPL_WATCHDOG_SUPPORT imply BOOTSTAGE_STASH if SPL_BOOTSTAGE imply SPL_BOOTSTAGE if BOOTSTAGE imply SPL_DISPLAY_PRINT diff --git a/drivers/watchdog/Kconfig b/drivers/watchdog/Kconfig index 7a8619f05c..dbafb74a34 100644 --- a/drivers/watchdog/Kconfig +++ b/drivers/watchdog/Kconfig @@ -154,6 +154,14 @@ config WDT_SP805 Select this to enable SP805 watchdog timer, which can be found on some nxp layerscape chips. +config WDT_STM32MP + bool "IWDG watchdog driver for STM32 MP's family" + depends on WDT + imply WATCHDOG + help + Enable the STM32 watchdog (IWDG) driver. Enable support to + configure STM32's on-SoC watchdog. + config XILINX_TB_WATCHDOG bool "Xilinx Axi watchdog timer support" depends on WDT diff --git a/drivers/watchdog/Makefile b/drivers/watchdog/Makefile index 6f20e73810..e3f4fdb406 100644 --- a/drivers/watchdog/Makefile +++ b/drivers/watchdog/Makefile @@ -28,3 +28,4 @@ obj-$(CONFIG_WDT_MPC8xx) += mpc8xx_wdt.o obj-$(CONFIG_WDT_MT7621) += mt7621_wdt.o obj-$(CONFIG_WDT_MTK) += mtk_wdt.o obj-$(CONFIG_WDT_SP805) += sp805_wdt.o +obj-$(CONFIG_WDT_STM32MP) += stm32mp_wdt.o diff --git a/drivers/watchdog/stm32mp_wdt.c b/drivers/watchdog/stm32mp_wdt.c new file mode 100644 index 0000000000..8093d0a9f4 --- /dev/null +++ b/drivers/watchdog/stm32mp_wdt.c @@ -0,0 +1,135 @@ +// SPDX-License-Identifier: GPL-2.0+ OR BSD-3-Clause +/* + * Copyright (C) 2019, STMicroelectronics - All Rights Reserved + */ + +#include +#include +#include +#include +#include +#include +#include + +/* IWDG registers */ +#define IWDG_KR 0x00 /* Key register */ +#define IWDG_PR 0x04 /* Prescaler Register */ +#define IWDG_RLR 0x08 /* ReLoad Register */ +#define IWDG_SR 0x0C /* Status 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 */ + +/* IWDG_PR register bit values */ +#define PR_256 0x06 /* Prescaler set to 256 */ + +/* IWDG_RLR register values */ +#define RLR_MAX 0xFFF /* Max value supported by reload register */ + +/* IWDG_SR register bit values */ +#define SR_PVU BIT(0) /* Watchdog prescaler value update */ +#define SR_RVU BIT(1) /* Watchdog counter reload value update */ + +struct stm32mp_wdt_priv { + fdt_addr_t base; /* registers addr in physical memory */ + unsigned long wdt_clk_rate; /* Watchdog dedicated clock rate */ +}; + +static int stm32mp_wdt_reset(struct udevice *dev) +{ + struct stm32mp_wdt_priv *priv = dev_get_priv(dev); + + writel(KR_KEY_RELOAD, priv->base + IWDG_KR); + + return 0; +} + +static int stm32mp_wdt_start(struct udevice *dev, u64 timeout_ms, ulong flags) +{ + struct stm32mp_wdt_priv *priv = dev_get_priv(dev); + int reload; + u32 val; + int ret; + + /* Prescaler fixed to 256 */ + reload = timeout_ms * priv->wdt_clk_rate / 256; + if (reload > RLR_MAX + 1) + /* Force to max watchdog counter reload value */ + reload = RLR_MAX + 1; + else if (!reload) + /* Force to min watchdog counter reload value */ + reload = priv->wdt_clk_rate / 256; + + /* Set prescaler & reload registers */ + writel(KR_KEY_EWA, priv->base + IWDG_KR); + writel(PR_256, priv->base + IWDG_PR); + writel(reload - 1, priv->base + IWDG_RLR); + + /* Enable watchdog */ + writel(KR_KEY_ENABLE, priv->base + IWDG_KR); + + /* Wait for the registers to be updated */ + ret = readl_poll_timeout(priv->base + IWDG_SR, val, + val & (SR_PVU | SR_RVU), CONFIG_SYS_HZ); + + if (ret < 0) { + pr_err("Updating IWDG registers timeout"); + return -ETIMEDOUT; + } + + return 0; +} + +static int stm32mp_wdt_probe(struct udevice *dev) +{ + struct stm32mp_wdt_priv *priv = dev_get_priv(dev); + struct clk clk; + int ret; + + debug("IWDG init\n"); + + priv->base = devfdt_get_addr(dev); + if (priv->base == FDT_ADDR_T_NONE) + return -EINVAL; + + /* Enable clock */ + ret = clk_get_by_name(dev, "pclk", &clk); + if (ret) + return ret; + + ret = clk_enable(&clk); + if (ret) + return ret; + + /* Get LSI clock */ + ret = clk_get_by_name(dev, "lsi", &clk); + if (ret) + return ret; + + priv->wdt_clk_rate = clk_get_rate(&clk); + + debug("IWDG init done\n"); + + return 0; +} + +static const struct wdt_ops stm32mp_wdt_ops = { + .start = stm32mp_wdt_start, + .reset = stm32mp_wdt_reset, +}; + +static const struct udevice_id stm32mp_wdt_match[] = { + { .compatible = "st,stm32mp1-iwdg" }, + { /* sentinel */ } +}; + +U_BOOT_DRIVER(stm32mp_wdt) = { + .name = "stm32mp-wdt", + .id = UCLASS_WDT, + .of_match = stm32mp_wdt_match, + .priv_auto_alloc_size = sizeof(struct stm32mp_wdt_priv), + .probe = stm32mp_wdt_probe, + .ops = &stm32mp_wdt_ops, +};