From ee327d1d93588609a36744464b336452a9444caf Mon Sep 17 00:00:00 2001 From: Mark Kettenis Date: Wed, 12 Jan 2022 19:55:15 +0100 Subject: [PATCH 1/3] watchdog: Add a driver for the Apple watchdog 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 Reviewed-by: Sven Peter Reviewed-by: Simon Glass Reviewed-by: Stefan Roese Tested-on: Apple M1 Macbook Tested-by: Simon Glass --- MAINTAINERS | 1 + drivers/watchdog/Kconfig | 9 +++ drivers/watchdog/Makefile | 1 + drivers/watchdog/apple_wdt.c | 115 +++++++++++++++++++++++++++++++++++ 4 files changed, 126 insertions(+) create mode 100644 drivers/watchdog/apple_wdt.c diff --git a/MAINTAINERS b/MAINTAINERS index f4bd3d79e2..6ae81c5659 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -116,6 +116,7 @@ F: arch/arm/mach-apple/ F: configs/apple_m1_defconfig F: drivers/iommu/apple_dart.c F: drivers/pinctrl/pinctrl-apple.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..c7307f41cb --- /dev/null +++ b/drivers/watchdog/apple_wdt.c @@ -0,0 +1,115 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (C) 2021 Mark Kettenis + */ + +#include +#include +#include +#include +#include + +#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; + + /* + * It can take up to 25ms until the SoC actually resets, so + * wait 50ms just to be sure. + */ + mdelay(50); + + 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, +}; From b0e6c73a790815c80092da10269d26125b05a435 Mon Sep 17 00:00:00 2001 From: Mark Kettenis Date: Wed, 12 Jan 2022 19:55:16 +0100 Subject: [PATCH 2/3] arm: dts: apple: Add watchdog timer node Add a node for the watchdog timer based on the proposed Linux device tree bindings. Remove the old reboot node which was a watchdog timert node in disguise using a preliminary device tree binding. Signed-off-by: Sven Peter Signed-off-by: Mark Kettenis Reviewed-by: Simon Glass Reviewed-by: Stefan Roese Tested-on: Apple M1 Macbook Tested-by: Simon Glass --- arch/arm/dts/t8103.dtsi | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/arch/arm/dts/t8103.dtsi b/arch/arm/dts/t8103.dtsi index 7d9cb272f9..03d8436fec 100644 --- a/arch/arm/dts/t8103.dtsi +++ b/arch/arm/dts/t8103.dtsi @@ -328,6 +328,14 @@ ; }; + wdt: watchdog@23d2b0000 { + compatible = "apple,t8103-wdt", "apple,wdt"; + reg = <0x2 0x3d2b0000 0x0 0x4000>; + clocks = <&clkref>; + interrupt-parent = <&aic>; + interrupts = ; + }; + pinctrl_smc: pinctrl@23e820000 { compatible = "apple,t8103-pinctrl", "apple,pinctrl"; reg = <0x2 0x3e820000 0x0 0x4000>; @@ -529,11 +537,6 @@ status = "disabled"; }; - reboot@23d2b0000 { - compatible = "apple,reboot-v0"; - reg = <0x2 0x3d2b0000 0x0 0x4000>; - }; - spi@23510c000 { compatible = "apple,t8103-spi", "apple,spi"; reg = <0x2 0x3510c000 0x0 0x4000>; From 9a8e3736da25a62449f84aef2b566e2b31ca60fd Mon Sep 17 00:00:00 2001 From: Mark Kettenis Date: Wed, 12 Jan 2022 19:55:17 +0100 Subject: [PATCH 3/3] arm: apple: Use watchdog timer for system reset Rely on the new watchdog timer driver and the sysreset uclass to reset the system. This gets rid of hard-coded addresses and should work on systems based on the new M1 Pro and M1 Max SoCs as well. Signed-off-by: Mark Kettenis Reviewed-by: Simon Glass Reviewed-by: Stefan Roese Tested-on: Apple M1 Macbook Tested-by: Simon Glass --- arch/arm/Kconfig | 3 +++ arch/arm/mach-apple/board.c | 24 ------------------------ 2 files changed, 3 insertions(+), 24 deletions(-) diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig index 7264d72bde..78c6b2e92d 100644 --- a/arch/arm/Kconfig +++ b/arch/arm/Kconfig @@ -942,6 +942,9 @@ config ARCH_APPLE select OF_CONTROL select PINCTRL select POSITION_INDEPENDENT + select SYSRESET + select SYSRESET_WATCHDOG + select SYSRESET_WATCHDOG_AUTO select USB imply CMD_DM imply CMD_GPT diff --git a/arch/arm/mach-apple/board.c b/arch/arm/mach-apple/board.c index 0bfbc473ec..b7e8d212f1 100644 --- a/arch/arm/mach-apple/board.c +++ b/arch/arm/mach-apple/board.c @@ -119,30 +119,6 @@ int dram_init_banksize(void) return fdtdec_setup_memory_banksize(); } -#define APPLE_WDT_BASE 0x23d2b0000ULL - -#define APPLE_WDT_SYS_CTL_ENABLE BIT(2) - -typedef struct apple_wdt { - u32 reserved0[3]; - u32 chip_ctl; - u32 sys_tmr; - u32 sys_cmp; - u32 reserved1; - u32 sys_ctl; -} apple_wdt_t; - -void reset_cpu(void) -{ - apple_wdt_t *wdt = (apple_wdt_t *)APPLE_WDT_BASE; - - writel(0, &wdt->sys_cmp); - writel(APPLE_WDT_SYS_CTL_ENABLE, &wdt->sys_ctl); - - while(1) - wfi(); -} - extern long fw_dtb_pointer; void *board_fdt_blob_setup(int *err)