вт, 26 серп. 2025 р. о 12:39 Lukasz Majewski <lu...@nabladev.com> пише: > > This commit provides support for Tegra's 30 watchdog functionality. > The WATCHDOG index 0 in conjunction with TIMER 5 has been used. as the > same setup is used in Linux kernel driver. > > Signed-off-by: Lukasz Majewski <lu...@nabladev.com> > --- > drivers/watchdog/Kconfig | 7 ++ > drivers/watchdog/Makefile | 1 + > drivers/watchdog/tegra_wdt.c | 121 +++++++++++++++++++++++++++++++++++ > 3 files changed, 129 insertions(+) > create mode 100644 drivers/watchdog/tegra_wdt.c > > diff --git a/drivers/watchdog/Kconfig b/drivers/watchdog/Kconfig > index 9e149a75e81..a10baed7232 100644 > --- a/drivers/watchdog/Kconfig > +++ b/drivers/watchdog/Kconfig > @@ -454,6 +454,13 @@ config WDT_TANGIER > Intel Tangier SoC. If you're using a board with Intel Tangier > SoC, say Y here. > > +config WDT_TEGRA > + bool "Tegra watchdog" > + depends on WDT && ARCH_TEGRA > + help > + Select this to enable support for the watchdog timer > + embedded in NVIDIA Tegra SoCs. > + > config WDT_ARM_SMC > bool "ARM SMC watchdog timer support" > depends on WDT && ARM_SMCCC > diff --git a/drivers/watchdog/Makefile b/drivers/watchdog/Makefile > index d52d17e1c90..02e2674f8af 100644 > --- a/drivers/watchdog/Makefile > +++ b/drivers/watchdog/Makefile > @@ -53,6 +53,7 @@ obj-$(CONFIG_WDT_STARFIVE) += starfive_wdt.o > obj-$(CONFIG_WDT_STM32MP) += stm32mp_wdt.o > obj-$(CONFIG_WDT_SUNXI) += sunxi_wdt.o > obj-$(CONFIG_WDT_TANGIER) += tangier_wdt.o > +obj-$(CONFIG_WDT_TEGRA) += tegra_wdt.o > obj-$(CONFIG_WDT_XILINX) += xilinx_wwdt.o > obj-$(CONFIG_WDT_ADI) += adi_wdt.o > obj-$(CONFIG_WDT_QCOM) += qcom-wdt.o > diff --git a/drivers/watchdog/tegra_wdt.c b/drivers/watchdog/tegra_wdt.c > new file mode 100644 > index 00000000000..812ec312fee > --- /dev/null > +++ b/drivers/watchdog/tegra_wdt.c > @@ -0,0 +1,121 @@ > +// SPDX-License-Identifier: GPL-2.0 > +/* > + * NVIDIA Tegra Watchdog driver > + * > + * Copyright (C) 2025 NABLA Software Engineering > + * Lukasz Majewski, NABLA Software Engineering, lu...@nabladev.com > + */ > + > +#include <dm.h> > +#include <wdt.h> > +#include <hang.h> > +#include <asm/io.h> > +#include <watchdog.h> > + > +/* Timer registers */ > +#define TIMER_PTV (0x0)
Here and further, use 0x0 without (), hex numbers in lower case > +#define TIMER_EN BIT(31) > +#define TIMER_PERIODIC BIT(30) > + > +/* WDT registers */ > +#define WDT_CFG (0x0) > +#define WDT_CFG_PERIOD_SHIFT 4 > +#define WDT_CFG_PERIOD_MASK GENMASK(7, 0) > +#define WDT_CFG_INT_EN BIT(12) > +#define WDT_CFG_PMC2CAR_RST_EN BIT(15) > +#define WDT_CMD (0x8) > +#define WDT_CMD_START_COUNTER BIT(0) > +#define WDT_CMD_DISABLE_COUNTER BIT(1) > +#define WDT_UNLOCK (0xC) > +#define WDT_UNLOCK_PATTERN (0xc45a) > + > +/* Use watchdog ID 0 */ > +#define WDT0_BASE 0x100 > + > +/* Use Timer 5 as WDT counter */ > +#define WDT_TIM5_BASE 0x60 > +#define WDT_TIM5_ID 5 > + > +struct tegra_wdt_priv { > + void __iomem *wdt_base; > + void __iomem *tim_base; > +}; > + > +static int tegra_wdt_reset(struct udevice *dev) > +{ > + struct tegra_wdt_priv *priv = dev_get_priv(dev); > + > + writel(WDT_CMD_START_COUNTER, priv->wdt_base + WDT_CMD); > + > + return 0; > +} > + > +static int tegra_wdt_start(struct udevice *dev, u64 timeout, ulong flags) > +{ > + struct tegra_wdt_priv *priv = dev_get_priv(dev); > + u32 timeout_sec = timeout / 1000; > + > + /* > + * Timer for WDT has a fixed 1MHz clock, so for 1 second period one > + * shall write 1000000ul. > + * > + * On Tegra the watchdog reset actually occurs on the 4th expiration > + * of this counter, so we set the period to 1/4. > + */ > + writel(TIMER_EN | TIMER_PERIODIC | (1000000ul / 4), > + priv->tim_base + TIMER_PTV); > + > + /* Support for timeout from 1 to 255 seconds */ > + if (timeout_sec < 1 || timeout_sec > 255) > + return -EINVAL; Check should be before any register writing is done. > + > + writel(WDT_CFG_PMC2CAR_RST_EN | (timeout_sec << WDT_CFG_PERIOD_SHIFT) > | > + WDT_TIM5_ID, priv->wdt_base + WDT_CFG); > + > + writel(WDT_CMD_START_COUNTER, priv->wdt_base + WDT_CMD); > + > + return 0; > +} > + > +static int tegra_wdt_stop(struct udevice *dev) > +{ > + struct tegra_wdt_priv *priv = dev_get_priv(dev); > + > + writel(WDT_UNLOCK_PATTERN, priv->wdt_base + WDT_UNLOCK); > + writel(WDT_CMD_DISABLE_COUNTER, priv->wdt_base + WDT_CMD); > + writel(0, priv->tim_base + TIMER_PTV); > + > + return 0; > +} > + > +static int tegra_wdt_probe(struct udevice *dev) > +{ > + struct tegra_wdt_priv *priv = dev_get_priv(dev); > + void __iomem *base; > + > + if (!device_is_compatible(dev, "nvidia,tegra30-timer")) > + return -ENODEV; Remove this restriction, it is not needed. > + > + base = dev_read_addr_ptr(dev); > + if (!base) > + return -ENOENT; > + > + priv->wdt_base = base + WDT0_BASE; > + priv->tim_base = base + WDT_TIM5_BASE; > + > + return 0; > +} > + > +static const struct wdt_ops tegra_wdt_ops = { > + .start = tegra_wdt_start, > + .stop = tegra_wdt_stop, > + .reset = tegra_wdt_reset, > +}; > + > +U_BOOT_DRIVER(tegra_wdt) = { > + .name = "tegra_wdt", > + .id = UCLASS_WDT, > + .priv_auto = sizeof(struct tegra_wdt_priv), > + .probe = tegra_wdt_probe, > + .ops = &tegra_wdt_ops, > +}; > -- > 2.39.5 > Everything else seems to be fine. Apply those cosmetic changes and I will pick your patches.