[PATCH 1/3] watchdog: Add a driver for the Apple watchdog
Mark Kettenis
mark.kettenis at xs4all.nl
Wed Jan 12 19:49:32 CET 2022
> Date: Tue, 11 Jan 2022 09:57:12 +0100
> From: Stefan Roese <sr at denx.de>
>
> On 11/14/21 12:19, Mark Kettenis wrote:
> > This driver supports the watchdog timer found on Apple's M1 SoC.
> > On systems that use these SoC, the watchdog timer is the primary
> > way to reboot the system.
> >
> > Signed-off-by: Mark Kettenis <kettenis at openbsd.org>
> > ---
> > MAINTAINERS | 1 +
> > drivers/watchdog/Kconfig | 9 +++
> > drivers/watchdog/Makefile | 1 +
> > drivers/watchdog/apple_wdt.c | 111 +++++++++++++++++++++++++++++++++++
> > 4 files changed, 122 insertions(+)
> > create mode 100644 drivers/watchdog/apple_wdt.c
> >
> > diff --git a/MAINTAINERS b/MAINTAINERS
> > index 00ff572d4d..fe56ffed60 100644
> > --- a/MAINTAINERS
> > +++ b/MAINTAINERS
> > @@ -115,6 +115,7 @@ F: arch/arm/include/asm/arch-m1/
> > F: arch/arm/mach-apple/
> > F: configs/apple_m1_defconfig
> > F: drivers/iommu/apple_dart.c
> > +F: drivers/watchdog/apple_wdt.c
> > F: include/configs/apple.h
> >
> > ARM
> > diff --git a/drivers/watchdog/Kconfig b/drivers/watchdog/Kconfig
> > index 1177f17fd8..cabac29053 100644
> > --- a/drivers/watchdog/Kconfig
> > +++ b/drivers/watchdog/Kconfig
> > @@ -81,6 +81,15 @@ config WDT
> > What exactly happens when the timer expires is up to a particular
> > device/driver.
> >
> > +config WDT_APPLE
> > + bool "Apple watchdog timer support"
> > + depends on WDT
> > + default y if ARCH_APPLE
> > + help
> > + Enable support for the watchdog timer on Apple SoCs.
> > + The watchdog will perform a full SoC reset resulting in a
> > + reboot of the entire system.
> > +
> > config WDT_ARMADA_37XX
> > bool "Marvell Armada 37xx watchdog timer support"
> > depends on WDT && ARMADA_3700
> > diff --git a/drivers/watchdog/Makefile b/drivers/watchdog/Makefile
> > index fa7ce583ce..6d2b3822c0 100644
> > --- a/drivers/watchdog/Makefile
> > +++ b/drivers/watchdog/Makefile
> > @@ -17,6 +17,7 @@ obj-$(CONFIG_DESIGNWARE_WATCHDOG) += designware_wdt.o
> > obj-$(CONFIG_ULP_WATCHDOG) += ulp_wdog.o
> > obj-$(CONFIG_$(SPL_TPL_)WDT) += wdt-uclass.o
> > obj-$(CONFIG_WDT_SANDBOX) += sandbox_wdt.o
> > +obj-$(CONFIG_WDT_APPLE) += apple_wdt.o
> > obj-$(CONFIG_WDT_ARMADA_37XX) += armada-37xx-wdt.o
> > obj-$(CONFIG_WDT_ASPEED) += ast_wdt.o
> > obj-$(CONFIG_WDT_AST2600) += ast2600_wdt.o
> > diff --git a/drivers/watchdog/apple_wdt.c b/drivers/watchdog/apple_wdt.c
> > new file mode 100644
> > index 0000000000..08eefcdaf9
> > --- /dev/null
> > +++ b/drivers/watchdog/apple_wdt.c
> > @@ -0,0 +1,111 @@
> > +// SPDX-License-Identifier: GPL-2.0+
> > +/*
> > + * Copyright (C) 2021 Mark Kettenis <kettenis at openbsd.org>
> > + */
> > +
> > +#include <clk.h>
> > +#include <dm.h>
> > +#include <wdt.h>
> > +#include <asm/io.h>
> > +#include <linux/delay.h>
> > +
> > +#define APPLE_WDT_CUR_TIME 0x10
> > +#define APPLE_WDT_BARK_TIME 0x14
> > +#define APPLE_WDT_CTRL 0x1c
> > +#define APPLE_WDT_CTRL_RESET_EN BIT(2)
> > +
> > +struct apple_wdt_priv {
> > + void *base;
> > + ulong clk_rate;
> > +};
> > +
> > +static int apple_wdt_reset(struct udevice *dev)
> > +{
> > + struct apple_wdt_priv *priv = dev_get_priv(dev);
> > +
> > + writel(0, priv->base + APPLE_WDT_CUR_TIME);
> > +
> > + return 0;
> > +}
> > +
> > +static int apple_wdt_start(struct udevice *dev, u64 timeout_ms, ulong flags)
> > +{
> > + struct apple_wdt_priv *priv = dev_get_priv(dev);
> > + u64 timeout;
> > +
> > + timeout = (timeout_ms * priv->clk_rate) / 1000;
> > + if (timeout > U32_MAX)
> > + return -EINVAL;
> > +
> > + writel(0, priv->base + APPLE_WDT_CUR_TIME);
> > + writel(timeout, priv->base + APPLE_WDT_BARK_TIME);
> > + writel(APPLE_WDT_CTRL_RESET_EN, priv->base + APPLE_WDT_CTRL);
> > +
> > + return 0;
> > +}
> > +
> > +static int apple_wdt_stop(struct udevice *dev)
> > +{
> > + struct apple_wdt_priv *priv = dev_get_priv(dev);
> > +
> > + writel(0, priv->base + APPLE_WDT_CTRL);
> > +
> > + return 0;
> > +}
> > +
> > +static int apple_wdt_expire_now(struct udevice *dev, ulong flags)
> > +{
> > + int ret;
> > +
> > + ret = apple_wdt_start(dev, 0, flags);
> > + if (ret)
> > + return ret;
> > +
> > + mdelay(150);
>
> Could you perhaps add a short comment here, why you chose 150ms?
Actually, the current version of the Linux driver uses 50ms, based on
an observation that it may take up to 25ms to actually reset the SoC.
So I've gone with that and added a comment.
>
> Other than this:
>
> Reviewed-by: Stefan Roese <sr at denx.de>
>
> Thanks,
> Stefan
>
> > +
> > + return 0;
> > +}
> > +
> > +static const struct wdt_ops apple_wdt_ops = {
> > + .reset = apple_wdt_reset,
> > + .start = apple_wdt_start,
> > + .stop = apple_wdt_stop,
> > + .expire_now = apple_wdt_expire_now,
> > +};
> > +
> > +static const struct udevice_id apple_wdt_ids[] = {
> > + { .compatible = "apple,wdt" },
> > + { /* sentinel */ }
> > +};
> > +
> > +static int apple_wdt_probe(struct udevice *dev)
> > +{
> > + struct apple_wdt_priv *priv = dev_get_priv(dev);
> > + struct clk clk;
> > + int ret;
> > +
> > + priv->base = dev_read_addr_ptr(dev);
> > + if (!priv->base)
> > + return -EINVAL;
> > +
> > + ret = clk_get_by_index(dev, 0, &clk);
> > + if (ret)
> > + return ret;
> > +
> > + ret = clk_enable(&clk);
> > + if (ret)
> > + return ret;
> > +
> > + priv->clk_rate = clk_get_rate(&clk);
> > +
> > + return 0;
> > +}
> > +
> > +U_BOOT_DRIVER(apple_wdt) = {
> > + .name = "apple_wdt",
> > + .id = UCLASS_WDT,
> > + .of_match = apple_wdt_ids,
> > + .priv_auto = sizeof(struct apple_wdt_priv),
> > + .probe = apple_wdt_probe,
> > + .ops = &apple_wdt_ops,
> > +};
> >
>
> Viele Grüße,
> Stefan Roese
>
> --
> DENX Software Engineering GmbH, Managing Director: Wolfgang Denk
> HRB 165235 Munich, Office: Kirchenstr.5, D-82194 Groebenzell, Germany
> Phone: (+49)-8142-66989-51 Fax: (+49)-8142-66989-80 Email: sr at denx.de
>
More information about the U-Boot
mailing list