вт, 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.

Reply via email to