[PATCH v2 1/2] dt-bindings: watchdog: Add Apple Watchdog
Subsystems:
arm/apple machine support , open firmware and flattened device tree bindings , the rest , watchdog device drivers
STALE1704d
REVIEWED: 8 (7M)
2 review trailers (1 from subsystem maintainers).
11 messages,
4 authors,
2021-12-07 · open the first message on its own page
Apple SoCs come with a simple embedded watchdog. This watchdog is also
required in order to reset the SoC.
Reviewed-by: Mark Kettenis <redacted>
Reviewed-by: Rob Herring <robh@kernel.org>
Signed-off-by: Sven Peter <redacted>
---
v1 --> v2:
- add Mark's and Rob's r-b tags
.../bindings/watchdog/apple,wdt.yaml | 52 +++++++++++++++++++
MAINTAINERS | 1 +
2 files changed, 53 insertions(+)
create mode 100644 Documentation/devicetree/bindings/watchdog/apple,wdt.yaml
diff --git a/Documentation/devicetree/bindings/watchdog/apple,wdt.yaml b/Documentation/devicetree/bindings/watchdog/apple,wdt.yaml
new file mode 100644
index 000000000000..e58c56a6fdf6
--- /dev/null
+++ b/Documentation/devicetree/bindings/watchdog/apple,wdt.yaml @@ -0,0 +1,52 @@
+ # SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+ %YAML 1.2
+ ---
+ $id : http://devicetree.org/schemas/watchdog/apple,wdt.yaml#
+ $schema : http://devicetree.org/meta-schemas/core.yaml#
+
+ title : Apple SoC Watchdog
+
+ allOf :
+ - $ref : "watchdog.yaml#"
+
+ maintainers :
+ - Sven Peter <sven@svenpeter.dev>
+
+ properties :
+ compatible :
+ items :
+ - enum :
+ - apple,t8103-wdt
+ - apple,t6000-wdt
+ - const : apple,wdt
+
+ reg :
+ maxItems : 1
+
+ clocks :
+ maxItems : 1
+
+ interrupts :
+ maxItems : 1
+
+ required :
+ - compatible
+ - reg
+ - clocks
+ - interrupts
+
+ unevaluatedProperties : false
+
+ examples :
+ - |
+ #include <dt-bindings/interrupt-controller/apple-aic.h>
+ #include <dt-bindings/interrupt-controller/irq.h>
+
+ wdt : watchdog@50000000 {
+ compatible = "apple,t8103-wdt", "apple,wdt";
+ reg = <0x50000000 0x4000>;
+ clocks = <&clk>;
+ interrupts = <AIC_IRQ 123 IRQ_TYPE_LEVEL_HIGH>;
+ } ;
+
+ ... diff --git a/MAINTAINERS b/MAINTAINERS
index 360e9aa0205d..859201bbd4e8 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS @@ -1750,6 +1750,7 @@ F: Documentation/devicetree/bindings/interrupt-controller/apple,aic.yaml
F: Documentation/devicetree/bindings/mailbox/apple,mailbox.yaml
F: Documentation/devicetree/bindings/pci/apple,pcie.yaml
F: Documentation/devicetree/bindings/pinctrl/apple,pinctrl.yaml
+ F: Documentation/devicetree/bindings/watchdog/apple,wdt.yaml
F: arch/arm64/boot/dts/apple/
F: drivers/i2c/busses/i2c-pasemi-core.c
F: drivers/i2c/busses/i2c-pasemi-platform.c --
2.25.1
_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
Add support for the watchdog timer found in Apple SoCs. This driver is
also required to reboot these machines.
Signed-off-by: Sven Peter <redacted>
---
v1 -> v2:
- set the default timeout to 30s and call watchdog_init_timeout
to allow the device tree to override it
- set WDOG_HW_RUNNING if the watchdog is enabled at boot
- check that the clock rate is not zero
- use unsigned long instead of u32 for clk_rate
- use devm_add_action_or_reset instead of manually calling
clk_disable_unprepare
- explain the magic number in apple_wdt_restart
MAINTAINERS | 1 +
drivers/watchdog/Kconfig | 12 ++
drivers/watchdog/Makefile | 1 +
drivers/watchdog/apple_wdt.c | 226 +++++++++++++++++++++++++++++++++++
4 files changed, 240 insertions(+)
create mode 100644 drivers/watchdog/apple_wdt.c
diff --git a/MAINTAINERS b/MAINTAINERS
index 859201bbd4e8..6190f0b40983 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS @@ -1757,6 +1757,7 @@ F: drivers/i2c/busses/i2c-pasemi-platform.c
F: drivers/irqchip/irq-apple-aic.c
F: drivers/mailbox/apple-mailbox.c
F: drivers/pinctrl/pinctrl-apple-gpio.c
+ F: drivers/watchdog/apple_wdt.c
F: include/dt-bindings/interrupt-controller/apple-aic.h
F: include/dt-bindings/pinctrl/apple.h
F: include/linux/apple-mailbox.h diff --git a/drivers/watchdog/Kconfig b/drivers/watchdog/Kconfig
index 9d222ba17ec6..170dec880c8f 100644
--- a/drivers/watchdog/Kconfig
+++ b/drivers/watchdog/Kconfig @@ -976,6 +976,18 @@ config MSC313E_WATCHDOG
To compile this driver as a module, choose M here: the
module will be called msc313e_wdt.
+ config APPLE_WATCHDOG
+ tristate "Apple SoC watchdog"
+ depends on ARCH_APPLE || COMPILE_TEST
+ select WATCHDOG_CORE
+ help
+ Say Y here to include support for the Watchdog found in Apple
+ SoCs such as the M1. Next to the common watchdog features this
+ driver is also required in order to reboot these SoCs.
+
+ To compile this driver as a module, choose M here: the
+ module will be called apple_wdt.
+
# X86 (i386 + ia64 + x86_64) Architecture
config ACQUIRE_WDT diff --git a/drivers/watchdog/Makefile b/drivers/watchdog/Makefile
index 2ee97064145b..270a518bd8f3 100644
--- a/drivers/watchdog/Makefile
+++ b/drivers/watchdog/Makefile @@ -93,6 +93,7 @@ obj-$(CONFIG_PM8916_WATCHDOG) += pm8916_wdt.o
obj-$(CONFIG_ARM_SMC_WATCHDOG) += arm_smc_wdt.o
obj-$(CONFIG_VISCONTI_WATCHDOG) += visconti_wdt.o
obj-$(CONFIG_MSC313E_WATCHDOG) += msc313e_wdt.o
+ obj-$(CONFIG_APPLE_WATCHDOG) += apple_wdt.o
# X86 (i386 + ia64 + x86_64) Architecture
obj-$(CONFIG_ACQUIRE_WDT) += acquirewdt.o diff --git a/drivers/watchdog/apple_wdt.c b/drivers/watchdog/apple_wdt.c
new file mode 100644
index 000000000000..76e5bedd50d1
--- /dev/null
+++ b/drivers/watchdog/apple_wdt.c @@ -0,0 +1,226 @@
+ // SPDX-License-Identifier: GPL-2.0-only OR MIT
+ /*
+ * Apple SoC Watchdog driver
+ *
+ * Copyright ( C ) The Asahi Linux Contributors
+ */
+
+ #include <linux/bits.h>
+ #include <linux/clk.h>
+ #include <linux/delay.h>
+ #include <linux/io.h>
+ #include <linux/kernel.h>
+ #include <linux/limits.h>
+ #include <linux/module.h>
+ #include <linux/of.h>
+ #include <linux/platform_device.h>
+ #include <linux/watchdog.h>
+
+ /*
+ * Apple Watchdog MMIO registers
+ *
+ * This HW block has three separate watchdogs . WD0 resets the machine
+ * to recovery mode and is not very useful for us . WD1 and WD2 trigger a normal
+ * machine reset . WD0 additionally supports a configurable interrupt .
+ * This information can be used to implement pretimeout support at a later time .
+ *
+ * APPLE_WDT_WDx_CUR_TIME is a simple counter incremented for each tick of the
+ * reference clock . It can also be overwritten to any value .
+ * Whenever APPLE_WDT_CTRL_RESET_EN is set in APPLE_WDT_WDx_CTRL and
+ * APPLE_WDTx_WD1_CUR_TIME >= APPLE_WDTx_WD1_BITE_TIME the entire machine is
+ * reset .
+ * Whenever APPLE_WDT_CTRL_IRQ_EN is set and APPLE_WDTx_WD1_CUR_TIME >=
+ * APPLE_WDTx_WD1_BARK_TIME an interrupt is triggered and
+ * APPLE_WDT_CTRL_IRQ_STATUS is set . The interrupt can be cleared by writing
+ * 1 to APPLE_WDT_CTRL_IRQ_STATUS .
+ */
+ #define APPLE_WDT_WD0_CUR_TIME 0x00
+ #define APPLE_WDT_WD0_BITE_TIME 0x04
+ #define APPLE_WDT_WD0_BARK_TIME 0x08
+ #define APPLE_WDT_WD0_CTRL 0x0c
+
+ #define APPLE_WDT_WD1_CUR_TIME 0x10
+ #define APPLE_WDT_WD1_BITE_TIME 0x14
+ #define APPLE_WDT_WD1_CTRL 0x1c
+
+ #define APPLE_WDT_WD2_CUR_TIME 0x20
+ #define APPLE_WDT_WD2_BITE_TIME 0x24
+ #define APPLE_WDT_WD2_CTRL 0x2c
+
+ #define APPLE_WDT_CTRL_IRQ_EN BIT(0)
+ #define APPLE_WDT_CTRL_IRQ_STATUS BIT(1)
+ #define APPLE_WDT_CTRL_RESET_EN BIT(2)
+
+ #define APPLE_WDT_TIMEOUT_DEFAULT 30
+
+ struct apple_wdt {
+ struct watchdog_device wdd ;
+ void __iomem * regs ;
+ unsigned long clk_rate ;
+ };
+
+ static struct apple_wdt * to_apple_wdt ( struct watchdog_device * wdd )
+ {
+ return container_of ( wdd , struct apple_wdt , wdd );
+ }
+
+ static int apple_wdt_start ( struct watchdog_device * wdd )
+ {
+ struct apple_wdt * wdt = to_apple_wdt ( wdd );
+
+ writel_relaxed ( 0 , wdt -> regs + APPLE_WDT_WD1_CUR_TIME );
+ writel_relaxed ( APPLE_WDT_CTRL_RESET_EN , wdt -> regs + APPLE_WDT_WD1_CTRL );
+
+ return 0 ;
+ }
+
+ static int apple_wdt_stop ( struct watchdog_device * wdd )
+ {
+ struct apple_wdt * wdt = to_apple_wdt ( wdd );
+
+ writel_relaxed ( 0 , wdt -> regs + APPLE_WDT_WD1_CTRL );
+
+ return 0 ;
+ }
+
+ static int apple_wdt_ping ( struct watchdog_device * wdd )
+ {
+ struct apple_wdt * wdt = to_apple_wdt ( wdd );
+
+ writel_relaxed ( 0 , wdt -> regs + APPLE_WDT_WD1_CUR_TIME );
+
+ return 0 ;
+ }
+
+ static int apple_wdt_set_timeout ( struct watchdog_device * wdd , unsigned int s )
+ {
+ struct apple_wdt * wdt = to_apple_wdt ( wdd );
+
+ writel_relaxed ( 0 , wdt -> regs + APPLE_WDT_WD1_CUR_TIME );
+ writel_relaxed ( wdt -> clk_rate * s , wdt -> regs + APPLE_WDT_WD1_BITE_TIME );
+
+ wdd -> timeout = s ;
+
+ return 0 ;
+ }
+
+ static unsigned int apple_wdt_get_timeleft ( struct watchdog_device * wdd )
+ {
+ struct apple_wdt * wdt = to_apple_wdt ( wdd );
+ u32 cur_time , reset_time ;
+
+ cur_time = readl_relaxed ( wdt -> regs + APPLE_WDT_WD1_CUR_TIME );
+ reset_time = readl_relaxed ( wdt -> regs + APPLE_WDT_WD1_BITE_TIME );
+
+ return ( reset_time - cur_time ) / wdt -> clk_rate ;
+ }
+
+ static int apple_wdt_restart ( struct watchdog_device * wdd , unsigned long mode ,
+ void * cmd )
+ {
+ struct apple_wdt * wdt = to_apple_wdt ( wdd );
+
+ writel_relaxed ( APPLE_WDT_CTRL_RESET_EN , wdt -> regs + APPLE_WDT_WD1_CTRL );
+ writel_relaxed ( 0 , wdt -> regs + APPLE_WDT_WD1_BITE_TIME );
+ writel_relaxed ( 0 , wdt -> regs + APPLE_WDT_WD1_CUR_TIME );
+
+ /*
+ * Flush writes and then wait for the SoC to reset . Even though the
+ * reset is queued almost immediately experiments have shown that it
+ * can take up to ~ 20-25 ms until the SoC is actually reset . Just wait
+ * 50 ms here to be safe .
+ */
+ ( void ) readl_relaxed ( wdt -> regs + APPLE_WDT_WD1_CUR_TIME );
+ mdelay ( 50 );
+
+ return 0 ;
+ }
+
+ static void apple_wdt_clk_disable_unprepare ( void * data )
+ {
+ clk_disable_unprepare ( data );
+ }
+
+ static struct watchdog_ops apple_wdt_ops = {
+ . owner = THIS_MODULE ,
+ . start = apple_wdt_start ,
+ . stop = apple_wdt_stop ,
+ . ping = apple_wdt_ping ,
+ . set_timeout = apple_wdt_set_timeout ,
+ . get_timeleft = apple_wdt_get_timeleft ,
+ . restart = apple_wdt_restart ,
+ };
+
+ static struct watchdog_info apple_wdt_info = {
+ . identity = "Apple SoC Watchdog" ,
+ . options = WDIOF_MAGICCLOSE | WDIOF_KEEPALIVEPING | WDIOF_SETTIMEOUT ,
+ };
+
+ static int apple_wdt_probe ( struct platform_device * pdev )
+ {
+ struct device * dev = & pdev -> dev ;
+ struct apple_wdt * wdt ;
+ struct clk * clk ;
+ u32 wdt_ctrl ;
+ int ret ;
+
+ wdt = devm_kzalloc ( dev , sizeof ( * wdt ), GFP_KERNEL );
+ if ( ! wdt )
+ return - ENOMEM ;
+
+ wdt -> regs = devm_platform_ioremap_resource ( pdev , 0 );
+ if ( IS_ERR ( wdt -> regs ))
+ return PTR_ERR ( wdt -> regs );
+
+ clk = devm_clk_get ( dev , NULL );
+ if ( IS_ERR ( clk ))
+ return PTR_ERR ( clk );
+
+ ret = clk_prepare_enable ( clk );
+ if ( ret )
+ return ret ;
+
+ ret = devm_add_action_or_reset ( dev , apple_wdt_clk_disable_unprepare ,
+ clk );
+ if ( ret )
+ return ret ;
+
+ wdt -> clk_rate = clk_get_rate ( clk );
+ if ( ! wdt -> clk_rate )
+ return - EINVAL ;
+
+ wdt -> wdd . ops = & apple_wdt_ops ;
+ wdt -> wdd . info = & apple_wdt_info ;
+ wdt -> wdd . max_timeout = U32_MAX / wdt -> clk_rate ;
+ wdt -> wdd . timeout = APPLE_WDT_TIMEOUT_DEFAULT ;
+
+ wdt_ctrl = readl_relaxed ( wdt -> regs + APPLE_WDT_WD1_CTRL );
+ if ( wdt_ctrl & APPLE_WDT_CTRL_RESET_EN )
+ set_bit ( WDOG_HW_RUNNING , & wdt -> wdd . status );
+
+ watchdog_init_timeout ( & wdt -> wdd , 0 , dev );
+ apple_wdt_set_timeout ( & wdt -> wdd , wdt -> wdd . timeout );
+ watchdog_stop_on_unregister ( & wdt -> wdd );
+ watchdog_set_restart_priority ( & wdt -> wdd , 128 );
+
+ return devm_watchdog_register_device ( dev , & wdt -> wdd );
+ }
+
+ static const struct of_device_id apple_wdt_of_match [] = {
+ { . compatible = "apple,wdt" },
+ {},
+ };
+ MODULE_DEVICE_TABLE ( of , apple_wdt_of_match );
+
+ static struct platform_driver apple_wdt_driver = {
+ . driver = {
+ . name = "apple-watchdog" ,
+ . of_match_table = apple_wdt_of_match ,
+ },
+ . probe = apple_wdt_probe ,
+ };
+ module_platform_driver ( apple_wdt_driver );
+
+ MODULE_DESCRIPTION ( "Apple SoC watchdog driver" );
+ MODULE_AUTHOR ( "Sven Peter <sven@svenpeter.dev>" );
+ MODULE_LICENSE ( "Dual MIT/GPL" ); --
2.25.1
_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
Hej,
On 2021-11-30 17:18:09 +0100, Sven Peter wrote: Add support for the watchdog timer found in Apple SoCs. This driver is
also required to reboot these machines.
Signed-off-by: Sven Peter <redacted>
---
v1 -> v2:
- set the default timeout to 30s and call watchdog_init_timeout
to allow the device tree to override it
- set WDOG_HW_RUNNING if the watchdog is enabled at boot
- check that the clock rate is not zero
- use unsigned long instead of u32 for clk_rate
- use devm_add_action_or_reset instead of manually calling
clk_disable_unprepare
- explain the magic number in apple_wdt_restart
MAINTAINERS | 1 +
drivers/watchdog/Kconfig | 12 ++
drivers/watchdog/Makefile | 1 +
drivers/watchdog/apple_wdt.c | 226 +++++++++++++++++++++++++++++++++++
4 files changed, 240 insertions(+)
create mode 100644 drivers/watchdog/apple_wdt.c
Tested on M1 and M1 Max. Feel free to add
Tested-by: Janne Grunau <j@jannau.net>
best
Janne
_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
On 2021年12月1日 1:18:09 JST, Sven Peter [off-list ref] wrote: quoted hunk Add support for the watchdog timer found in Apple SoCs. This driver is
also required to reboot these machines.
Signed-off-by: Sven Peter <redacted>
---
v1 -> v2:
- set the default timeout to 30s and call watchdog_init_timeout
to allow the device tree to override it
- set WDOG_HW_RUNNING if the watchdog is enabled at boot
- check that the clock rate is not zero
- use unsigned long instead of u32 for clk_rate
- use devm_add_action_or_reset instead of manually calling
clk_disable_unprepare
- explain the magic number in apple_wdt_restart
MAINTAINERS | 1 +
drivers/watchdog/Kconfig | 12 ++
drivers/watchdog/Makefile | 1 +
drivers/watchdog/apple_wdt.c | 226 +++++++++++++++++++++++++++++++++++
4 files changed, 240 insertions(+)
create mode 100644 drivers/watchdog/apple_wdt.c
diff --git a/MAINTAINERS b/MAINTAINERS
index 859201bbd4e8..6190f0b40983 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS @@ -1757,6 +1757,7 @@ F: drivers/i2c/busses/i2c-pasemi-platform.c F: drivers/irqchip/irq-apple-aic.c
F: drivers/mailbox/apple-mailbox.c
F: drivers/pinctrl/pinctrl-apple-gpio.c
+F: drivers/watchdog/apple_wdt.c
F: include/dt-bindings/interrupt-controller/apple-aic.h
F: include/dt-bindings/pinctrl/apple.h
F: include/linux/apple-mailbox.h diff --git a/drivers/watchdog/Kconfig b/drivers/watchdog/Kconfig
index 9d222ba17ec6..170dec880c8f 100644
--- a/drivers/watchdog/Kconfig
+++ b/drivers/watchdog/Kconfig @@ -976,6 +976,18 @@ config MSC313E_WATCHDOG To compile this driver as a module, choose M here: the
module will be called msc313e_wdt.
+config APPLE_WATCHDOG
+ tristate "Apple SoC watchdog"
+ depends on ARCH_APPLE || COMPILE_TEST
+ select WATCHDOG_CORE
+ help
+ Say Y here to include support for the Watchdog found in Apple
+ SoCs such as the M1. Next to the common watchdog features this
+ driver is also required in order to reboot these SoCs.
+
+ To compile this driver as a module, choose M here: the
+ module will be called apple_wdt.
+
# X86 (i386 + ia64 + x86_64) Architecture
config ACQUIRE_WDT diff --git a/drivers/watchdog/Makefile b/drivers/watchdog/Makefile
index 2ee97064145b..270a518bd8f3 100644
--- a/drivers/watchdog/Makefile
+++ b/drivers/watchdog/Makefile @@ -93,6 +93,7 @@ obj-$(CONFIG_PM8916_WATCHDOG) += pm8916_wdt.o obj-$(CONFIG_ARM_SMC_WATCHDOG) += arm_smc_wdt.o
obj-$(CONFIG_VISCONTI_WATCHDOG) += visconti_wdt.o
obj-$(CONFIG_MSC313E_WATCHDOG) += msc313e_wdt.o
+obj-$(CONFIG_APPLE_WATCHDOG) += apple_wdt.o
# X86 (i386 + ia64 + x86_64) Architecture
obj-$(CONFIG_ACQUIRE_WDT) += acquirewdt.o diff --git a/drivers/watchdog/apple_wdt.c b/drivers/watchdog/apple_wdt.c
new file mode 100644
index 000000000000..76e5bedd50d1
--- /dev/null
+++ b/drivers/watchdog/apple_wdt.c @@ -0,0 +1,226 @@
+ // SPDX-License-Identifier: GPL-2.0-only OR MIT
+ /*
+ * Apple SoC Watchdog driver
+ *
+ * Copyright ( C ) The Asahi Linux Contributors
+ */
+
+ #include <linux/bits.h>
+ #include <linux/clk.h>
+ #include <linux/delay.h>
+ #include <linux/io.h>
+ #include <linux/kernel.h>
+ #include <linux/limits.h>
+ #include <linux/module.h>
+ #include <linux/of.h>
+ #include <linux/platform_device.h>
+ #include <linux/watchdog.h>
+
+ /*
+ * Apple Watchdog MMIO registers
+ *
+ * This HW block has three separate watchdogs . WD0 resets the machine
+ * to recovery mode and is not very useful for us . WD1 and WD2 trigger a normal
+ * machine reset . WD0 additionally supports a configurable interrupt .
+ * This information can be used to implement pretimeout support at a later time .
+ *
+ * APPLE_WDT_WDx_CUR_TIME is a simple counter incremented for each tick of the
+ * reference clock . It can also be overwritten to any value .
+ * Whenever APPLE_WDT_CTRL_RESET_EN is set in APPLE_WDT_WDx_CTRL and
+ * APPLE_WDTx_WD1_CUR_TIME >= APPLE_WDTx_WD1_BITE_TIME the entire machine is
+ * reset .
+ * Whenever APPLE_WDT_CTRL_IRQ_EN is set and APPLE_WDTx_WD1_CUR_TIME >=
+ * APPLE_WDTx_WD1_BARK_TIME an interrupt is triggered and
+ * APPLE_WDT_CTRL_IRQ_STATUS is set . The interrupt can be cleared by writing
+ * 1 to APPLE_WDT_CTRL_IRQ_STATUS .
+ */
+ #define APPLE_WDT_WD0_CUR_TIME 0x00
+ #define APPLE_WDT_WD0_BITE_TIME 0x04
+ #define APPLE_WDT_WD0_BARK_TIME 0x08
+ #define APPLE_WDT_WD0_CTRL 0x0c
+
+ #define APPLE_WDT_WD1_CUR_TIME 0x10
+ #define APPLE_WDT_WD1_BITE_TIME 0x14
+ #define APPLE_WDT_WD1_CTRL 0x1c
+
+ #define APPLE_WDT_WD2_CUR_TIME 0x20
+ #define APPLE_WDT_WD2_BITE_TIME 0x24
+ #define APPLE_WDT_WD2_CTRL 0x2c
+
+ #define APPLE_WDT_CTRL_IRQ_EN BIT(0)
+ #define APPLE_WDT_CTRL_IRQ_STATUS BIT(1)
+ #define APPLE_WDT_CTRL_RESET_EN BIT(2)
+
+ #define APPLE_WDT_TIMEOUT_DEFAULT 30
+
+ struct apple_wdt {
+ struct watchdog_device wdd ;
+ void __iomem * regs ;
+ unsigned long clk_rate ;
+ };
+
+ static struct apple_wdt * to_apple_wdt ( struct watchdog_device * wdd )
+ {
+ return container_of ( wdd , struct apple_wdt , wdd );
+ }
+
+ static int apple_wdt_start ( struct watchdog_device * wdd )
+ {
+ struct apple_wdt * wdt = to_apple_wdt ( wdd );
+
+ writel_relaxed ( 0 , wdt -> regs + APPLE_WDT_WD1_CUR_TIME );
+ writel_relaxed ( APPLE_WDT_CTRL_RESET_EN , wdt -> regs + APPLE_WDT_WD1_CTRL );
+
+ return 0 ;
+ }
+
+ static int apple_wdt_stop ( struct watchdog_device * wdd )
+ {
+ struct apple_wdt * wdt = to_apple_wdt ( wdd );
+
+ writel_relaxed ( 0 , wdt -> regs + APPLE_WDT_WD1_CTRL );
+
+ return 0 ;
+ }
+
+ static int apple_wdt_ping ( struct watchdog_device * wdd )
+ {
+ struct apple_wdt * wdt = to_apple_wdt ( wdd );
+
+ writel_relaxed ( 0 , wdt -> regs + APPLE_WDT_WD1_CUR_TIME );
+
+ return 0 ;
+ }
+
+ static int apple_wdt_set_timeout ( struct watchdog_device * wdd , unsigned int s )
+ {
+ struct apple_wdt * wdt = to_apple_wdt ( wdd );
+
+ writel_relaxed ( 0 , wdt -> regs + APPLE_WDT_WD1_CUR_TIME );
+ writel_relaxed ( wdt -> clk_rate * s , wdt -> regs + APPLE_WDT_WD1_BITE_TIME );
+
+ wdd -> timeout = s ;
+
+ return 0 ;
+ }
+
+ static unsigned int apple_wdt_get_timeleft ( struct watchdog_device * wdd )
+ {
+ struct apple_wdt * wdt = to_apple_wdt ( wdd );
+ u32 cur_time , reset_time ;
+
+ cur_time = readl_relaxed ( wdt -> regs + APPLE_WDT_WD1_CUR_TIME );
+ reset_time = readl_relaxed ( wdt -> regs + APPLE_WDT_WD1_BITE_TIME );
+
+ return ( reset_time - cur_time ) / wdt -> clk_rate ;
+ }
+
+ static int apple_wdt_restart ( struct watchdog_device * wdd , unsigned long mode ,
+ void * cmd )
+ {
+ struct apple_wdt * wdt = to_apple_wdt ( wdd );
+
+ writel_relaxed ( APPLE_WDT_CTRL_RESET_EN , wdt -> regs + APPLE_WDT_WD1_CTRL );
+ writel_relaxed ( 0 , wdt -> regs + APPLE_WDT_WD1_BITE_TIME );
+ writel_relaxed ( 0 , wdt -> regs + APPLE_WDT_WD1_CUR_TIME );
+
+ /*
+ * Flush writes and then wait for the SoC to reset . Even though the
+ * reset is queued almost immediately experiments have shown that it
+ * can take up to ~ 20-25 ms until the SoC is actually reset . Just wait
+ * 50 ms here to be safe .
+ */
+ ( void ) readl_relaxed ( wdt -> regs + APPLE_WDT_WD1_CUR_TIME );
+ mdelay ( 50 );
+
+ return 0 ;
+ }
+
+ static void apple_wdt_clk_disable_unprepare ( void * data )
+ {
+ clk_disable_unprepare ( data );
+ }
+
+ static struct watchdog_ops apple_wdt_ops = {
+ . owner = THIS_MODULE ,
+ . start = apple_wdt_start ,
+ . stop = apple_wdt_stop ,
+ . ping = apple_wdt_ping ,
+ . set_timeout = apple_wdt_set_timeout ,
+ . get_timeleft = apple_wdt_get_timeleft ,
+ . restart = apple_wdt_restart ,
+ };
+
+ static struct watchdog_info apple_wdt_info = {
+ . identity = "Apple SoC Watchdog" ,
+ . options = WDIOF_MAGICCLOSE | WDIOF_KEEPALIVEPING | WDIOF_SETTIMEOUT ,
+ };
+
+ static int apple_wdt_probe ( struct platform_device * pdev )
+ {
+ struct device * dev = & pdev -> dev ;
+ struct apple_wdt * wdt ;
+ struct clk * clk ;
+ u32 wdt_ctrl ;
+ int ret ;
+
+ wdt = devm_kzalloc ( dev , sizeof ( * wdt ), GFP_KERNEL );
+ if ( ! wdt )
+ return - ENOMEM ;
+
+ wdt -> regs = devm_platform_ioremap_resource ( pdev , 0 );
+ if ( IS_ERR ( wdt -> regs ))
+ return PTR_ERR ( wdt -> regs );
+
+ clk = devm_clk_get ( dev , NULL );
+ if ( IS_ERR ( clk ))
+ return PTR_ERR ( clk );
+
+ ret = clk_prepare_enable ( clk );
+ if ( ret )
+ return ret ;
+
+ ret = devm_add_action_or_reset ( dev , apple_wdt_clk_disable_unprepare ,
+ clk );
+ if ( ret )
+ return ret ;
+
+ wdt -> clk_rate = clk_get_rate ( clk );
+ if ( ! wdt -> clk_rate )
+ return - EINVAL ;
+
+ wdt -> wdd . ops = & apple_wdt_ops ;
+ wdt -> wdd . info = & apple_wdt_info ;
+ wdt -> wdd . max_timeout = U32_MAX / wdt -> clk_rate ;
+ wdt -> wdd . timeout = APPLE_WDT_TIMEOUT_DEFAULT ;
+
+ wdt_ctrl = readl_relaxed ( wdt -> regs + APPLE_WDT_WD1_CTRL );
+ if ( wdt_ctrl & APPLE_WDT_CTRL_RESET_EN )
+ set_bit ( WDOG_HW_RUNNING , & wdt -> wdd . status );
+
+ watchdog_init_timeout ( & wdt -> wdd , 0 , dev );
+ apple_wdt_set_timeout ( & wdt -> wdd , wdt -> wdd . timeout );
+ watchdog_stop_on_unregister ( & wdt -> wdd );
+ watchdog_set_restart_priority ( & wdt -> wdd , 128 );
+
+ return devm_watchdog_register_device ( dev , & wdt -> wdd );
+ }
+
+ static const struct of_device_id apple_wdt_of_match [] = {
+ { . compatible = "apple,wdt" },
+ {},
+ };
+ MODULE_DEVICE_TABLE ( of , apple_wdt_of_match );
+
+ static struct platform_driver apple_wdt_driver = {
+ . driver = {
+ . name = "apple-watchdog" ,
+ . of_match_table = apple_wdt_of_match ,
+ },
+ . probe = apple_wdt_probe ,
+ };
+ module_platform_driver ( apple_wdt_driver );
+
+ MODULE_DESCRIPTION ( "Apple SoC watchdog driver" );
+ MODULE_AUTHOR ( "Sven Peter <sven@svenpeter.dev>" );
+ MODULE_LICENSE ( "Dual MIT/GPL" );
Reviewed-by: Hector Martin <redacted>
Thanks, looks good! Any chance you can do a quick v3 with the MAINTAINERS changes split off? As usual, I'd rather make life easier for people merging upstream :)
--
Hector Martin "marcan" (marcan@marcan.st)
Public key: https://mrcn.st/pub
_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
On 01/12/2021 01.18, Sven Peter wrote: Add support for the watchdog timer found in Apple SoCs. This driver is
also required to reboot these machines.
Signed-off-by: Sven Peter <redacted>
--- <snip>
Tested on j274 and j314s. Feel free to add:
Tested-by: Hector Martin <redacted>
--
Hector Martin (marcan@marcan.st)
Public Key: https://mrcn.st/pub
_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
On 12/1/21 10:44 PM, Hector Martin "marcan" wrote:
On 2021年12月1日 1:18:09 JST, Sven Peter [off-list ref] wrote: quoted Add support for the watchdog timer found in Apple SoCs. This driver is
also required to reboot these machines.
Signed-off-by: Sven Peter <redacted>
---
v1 -> v2:
- set the default timeout to 30s and call watchdog_init_timeout
to allow the device tree to override it
- set WDOG_HW_RUNNING if the watchdog is enabled at boot
- check that the clock rate is not zero
- use unsigned long instead of u32 for clk_rate
- use devm_add_action_or_reset instead of manually calling
clk_disable_unprepare
- explain the magic number in apple_wdt_restart
MAINTAINERS | 1 +
drivers/watchdog/Kconfig | 12 ++
drivers/watchdog/Makefile | 1 +
drivers/watchdog/apple_wdt.c | 226 +++++++++++++++++++++++++++++++++++
4 files changed, 240 insertions(+)
create mode 100644 drivers/watchdog/apple_wdt.c
diff --git a/MAINTAINERS b/MAINTAINERS
index 859201bbd4e8..6190f0b40983 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS @@ -1757,6 +1757,7 @@ F: drivers/i2c/busses/i2c-pasemi-platform.c F: drivers/irqchip/irq-apple-aic.c
F: drivers/mailbox/apple-mailbox.c
F: drivers/pinctrl/pinctrl-apple-gpio.c
+F: drivers/watchdog/apple_wdt.c
F: include/dt-bindings/interrupt-controller/apple-aic.h
F: include/dt-bindings/pinctrl/apple.h
F: include/linux/apple-mailbox.h diff --git a/drivers/watchdog/Kconfig b/drivers/watchdog/Kconfig
index 9d222ba17ec6..170dec880c8f 100644
--- a/drivers/watchdog/Kconfig
+++ b/drivers/watchdog/Kconfig @@ -976,6 +976,18 @@ config MSC313E_WATCHDOG To compile this driver as a module, choose M here: the
module will be called msc313e_wdt.
+config APPLE_WATCHDOG
+ tristate "Apple SoC watchdog"
+ depends on ARCH_APPLE || COMPILE_TEST
+ select WATCHDOG_CORE
+ help
+ Say Y here to include support for the Watchdog found in Apple
+ SoCs such as the M1. Next to the common watchdog features this
+ driver is also required in order to reboot these SoCs.
+
+ To compile this driver as a module, choose M here: the
+ module will be called apple_wdt.
+
# X86 (i386 + ia64 + x86_64) Architecture
config ACQUIRE_WDT diff --git a/drivers/watchdog/Makefile b/drivers/watchdog/Makefile
index 2ee97064145b..270a518bd8f3 100644
--- a/drivers/watchdog/Makefile
+++ b/drivers/watchdog/Makefile @@ -93,6 +93,7 @@ obj-$(CONFIG_PM8916_WATCHDOG) += pm8916_wdt.o obj-$(CONFIG_ARM_SMC_WATCHDOG) += arm_smc_wdt.o
obj-$(CONFIG_VISCONTI_WATCHDOG) += visconti_wdt.o
obj-$(CONFIG_MSC313E_WATCHDOG) += msc313e_wdt.o
+obj-$(CONFIG_APPLE_WATCHDOG) += apple_wdt.o
# X86 (i386 + ia64 + x86_64) Architecture
obj-$(CONFIG_ACQUIRE_WDT) += acquirewdt.o diff --git a/drivers/watchdog/apple_wdt.c b/drivers/watchdog/apple_wdt.c
new file mode 100644
index 000000000000..76e5bedd50d1
--- /dev/null
+++ b/drivers/watchdog/apple_wdt.c @@ -0,0 +1,226 @@
+ // SPDX-License-Identifier: GPL-2.0-only OR MIT
+ /*
+ * Apple SoC Watchdog driver
+ *
+ * Copyright ( C ) The Asahi Linux Contributors
+ */
+
+ #include <linux/bits.h>
+ #include <linux/clk.h>
+ #include <linux/delay.h>
+ #include <linux/io.h>
+ #include <linux/kernel.h>
+ #include <linux/limits.h>
+ #include <linux/module.h>
+ #include <linux/of.h>
+ #include <linux/platform_device.h>
+ #include <linux/watchdog.h>
+
+ /*
+ * Apple Watchdog MMIO registers
+ *
+ * This HW block has three separate watchdogs . WD0 resets the machine
+ * to recovery mode and is not very useful for us . WD1 and WD2 trigger a normal
+ * machine reset . WD0 additionally supports a configurable interrupt .
+ * This information can be used to implement pretimeout support at a later time .
+ *
+ * APPLE_WDT_WDx_CUR_TIME is a simple counter incremented for each tick of the
+ * reference clock . It can also be overwritten to any value .
+ * Whenever APPLE_WDT_CTRL_RESET_EN is set in APPLE_WDT_WDx_CTRL and
+ * APPLE_WDTx_WD1_CUR_TIME >= APPLE_WDTx_WD1_BITE_TIME the entire machine is
+ * reset .
+ * Whenever APPLE_WDT_CTRL_IRQ_EN is set and APPLE_WDTx_WD1_CUR_TIME >=
+ * APPLE_WDTx_WD1_BARK_TIME an interrupt is triggered and
+ * APPLE_WDT_CTRL_IRQ_STATUS is set . The interrupt can be cleared by writing
+ * 1 to APPLE_WDT_CTRL_IRQ_STATUS .
+ */
+ #define APPLE_WDT_WD0_CUR_TIME 0x00
+ #define APPLE_WDT_WD0_BITE_TIME 0x04
+ #define APPLE_WDT_WD0_BARK_TIME 0x08
+ #define APPLE_WDT_WD0_CTRL 0x0c
+
+ #define APPLE_WDT_WD1_CUR_TIME 0x10
+ #define APPLE_WDT_WD1_BITE_TIME 0x14
+ #define APPLE_WDT_WD1_CTRL 0x1c
+
+ #define APPLE_WDT_WD2_CUR_TIME 0x20
+ #define APPLE_WDT_WD2_BITE_TIME 0x24
+ #define APPLE_WDT_WD2_CTRL 0x2c
+
+ #define APPLE_WDT_CTRL_IRQ_EN BIT(0)
+ #define APPLE_WDT_CTRL_IRQ_STATUS BIT(1)
+ #define APPLE_WDT_CTRL_RESET_EN BIT(2)
+
+ #define APPLE_WDT_TIMEOUT_DEFAULT 30
+
+ struct apple_wdt {
+ struct watchdog_device wdd ;
+ void __iomem * regs ;
+ unsigned long clk_rate ;
+ };
+
+ static struct apple_wdt * to_apple_wdt ( struct watchdog_device * wdd )
+ {
+ return container_of ( wdd , struct apple_wdt , wdd );
+ }
+
+ static int apple_wdt_start ( struct watchdog_device * wdd )
+ {
+ struct apple_wdt * wdt = to_apple_wdt ( wdd );
+
+ writel_relaxed ( 0 , wdt -> regs + APPLE_WDT_WD1_CUR_TIME );
+ writel_relaxed ( APPLE_WDT_CTRL_RESET_EN , wdt -> regs + APPLE_WDT_WD1_CTRL );
+
+ return 0 ;
+ }
+
+ static int apple_wdt_stop ( struct watchdog_device * wdd )
+ {
+ struct apple_wdt * wdt = to_apple_wdt ( wdd );
+
+ writel_relaxed ( 0 , wdt -> regs + APPLE_WDT_WD1_CTRL );
+
+ return 0 ;
+ }
+
+ static int apple_wdt_ping ( struct watchdog_device * wdd )
+ {
+ struct apple_wdt * wdt = to_apple_wdt ( wdd );
+
+ writel_relaxed ( 0 , wdt -> regs + APPLE_WDT_WD1_CUR_TIME );
+
+ return 0 ;
+ }
+
+ static int apple_wdt_set_timeout ( struct watchdog_device * wdd , unsigned int s )
+ {
+ struct apple_wdt * wdt = to_apple_wdt ( wdd );
+
+ writel_relaxed ( 0 , wdt -> regs + APPLE_WDT_WD1_CUR_TIME );
+ writel_relaxed ( wdt -> clk_rate * s , wdt -> regs + APPLE_WDT_WD1_BITE_TIME );
+
+ wdd -> timeout = s ;
+
+ return 0 ;
+ }
+
+ static unsigned int apple_wdt_get_timeleft ( struct watchdog_device * wdd )
+ {
+ struct apple_wdt * wdt = to_apple_wdt ( wdd );
+ u32 cur_time , reset_time ;
+
+ cur_time = readl_relaxed ( wdt -> regs + APPLE_WDT_WD1_CUR_TIME );
+ reset_time = readl_relaxed ( wdt -> regs + APPLE_WDT_WD1_BITE_TIME );
+
+ return ( reset_time - cur_time ) / wdt -> clk_rate ;
+ }
+
+ static int apple_wdt_restart ( struct watchdog_device * wdd , unsigned long mode ,
+ void * cmd )
+ {
+ struct apple_wdt * wdt = to_apple_wdt ( wdd );
+
+ writel_relaxed ( APPLE_WDT_CTRL_RESET_EN , wdt -> regs + APPLE_WDT_WD1_CTRL );
+ writel_relaxed ( 0 , wdt -> regs + APPLE_WDT_WD1_BITE_TIME );
+ writel_relaxed ( 0 , wdt -> regs + APPLE_WDT_WD1_CUR_TIME );
+
+ /*
+ * Flush writes and then wait for the SoC to reset . Even though the
+ * reset is queued almost immediately experiments have shown that it
+ * can take up to ~ 20-25 ms until the SoC is actually reset . Just wait
+ * 50 ms here to be safe .
+ */
+ ( void ) readl_relaxed ( wdt -> regs + APPLE_WDT_WD1_CUR_TIME );
+ mdelay ( 50 );
+
+ return 0 ;
+ }
+
+ static void apple_wdt_clk_disable_unprepare ( void * data )
+ {
+ clk_disable_unprepare ( data );
+ }
+
+ static struct watchdog_ops apple_wdt_ops = {
+ . owner = THIS_MODULE ,
+ . start = apple_wdt_start ,
+ . stop = apple_wdt_stop ,
+ . ping = apple_wdt_ping ,
+ . set_timeout = apple_wdt_set_timeout ,
+ . get_timeleft = apple_wdt_get_timeleft ,
+ . restart = apple_wdt_restart ,
+ };
+
+ static struct watchdog_info apple_wdt_info = {
+ . identity = "Apple SoC Watchdog" ,
+ . options = WDIOF_MAGICCLOSE | WDIOF_KEEPALIVEPING | WDIOF_SETTIMEOUT ,
+ };
+
+ static int apple_wdt_probe ( struct platform_device * pdev )
+ {
+ struct device * dev = & pdev -> dev ;
+ struct apple_wdt * wdt ;
+ struct clk * clk ;
+ u32 wdt_ctrl ;
+ int ret ;
+
+ wdt = devm_kzalloc ( dev , sizeof ( * wdt ), GFP_KERNEL );
+ if ( ! wdt )
+ return - ENOMEM ;
+
+ wdt -> regs = devm_platform_ioremap_resource ( pdev , 0 );
+ if ( IS_ERR ( wdt -> regs ))
+ return PTR_ERR ( wdt -> regs );
+
+ clk = devm_clk_get ( dev , NULL );
+ if ( IS_ERR ( clk ))
+ return PTR_ERR ( clk );
+
+ ret = clk_prepare_enable ( clk );
+ if ( ret )
+ return ret ;
+
+ ret = devm_add_action_or_reset ( dev , apple_wdt_clk_disable_unprepare ,
+ clk );
+ if ( ret )
+ return ret ;
+
+ wdt -> clk_rate = clk_get_rate ( clk );
+ if ( ! wdt -> clk_rate )
+ return - EINVAL ;
+
+ wdt -> wdd . ops = & apple_wdt_ops ;
+ wdt -> wdd . info = & apple_wdt_info ;
+ wdt -> wdd . max_timeout = U32_MAX / wdt -> clk_rate ;
+ wdt -> wdd . timeout = APPLE_WDT_TIMEOUT_DEFAULT ;
+
+ wdt_ctrl = readl_relaxed ( wdt -> regs + APPLE_WDT_WD1_CTRL );
+ if ( wdt_ctrl & APPLE_WDT_CTRL_RESET_EN )
+ set_bit ( WDOG_HW_RUNNING , & wdt -> wdd . status );
+
+ watchdog_init_timeout ( & wdt -> wdd , 0 , dev );
+ apple_wdt_set_timeout ( & wdt -> wdd , wdt -> wdd . timeout );
+ watchdog_stop_on_unregister ( & wdt -> wdd );
+ watchdog_set_restart_priority ( & wdt -> wdd , 128 );
+
+ return devm_watchdog_register_device ( dev , & wdt -> wdd );
+ }
+
+ static const struct of_device_id apple_wdt_of_match [] = {
+ { . compatible = "apple,wdt" },
+ {},
+ };
+ MODULE_DEVICE_TABLE ( of , apple_wdt_of_match );
+
+ static struct platform_driver apple_wdt_driver = {
+ . driver = {
+ . name = "apple-watchdog" ,
+ . of_match_table = apple_wdt_of_match ,
+ },
+ . probe = apple_wdt_probe ,
+ };
+ module_platform_driver ( apple_wdt_driver );
+
+ MODULE_DESCRIPTION ( "Apple SoC watchdog driver" );
+ MODULE_AUTHOR ( "Sven Peter <sven@svenpeter.dev>" );
+ MODULE_LICENSE ( "Dual MIT/GPL" );
Reviewed-by: Hector Martin <redacted>
Thanks, looks good! Any chance you can do a quick v3 with the MAINTAINERS changes split off? As usual, I'd rather make life easier for people merging upstream :)
Usually I don't ask for such a split, and I don't recall that being an issue.
So, from my perspective, that is not necessary.
Guenter
_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
On Tue, Nov 30, 2021 at 05:18:08PM +0100, Sven Peter wrote: Apple SoCs come with a simple embedded watchdog. This watchdog is also
required in order to reset the SoC.
Reviewed-by: Mark Kettenis <redacted>
Reviewed-by: Rob Herring <robh@kernel.org>
Signed-off-by: Sven Peter <redacted>
Reviewed-by: Guenter Roeck <linux@roeck-us.net>
quoted hunk ---
v1 --> v2:
- add Mark's and Rob's r-b tags
.../bindings/watchdog/apple,wdt.yaml | 52 +++++++++++++++++++
MAINTAINERS | 1 +
2 files changed, 53 insertions(+)
create mode 100644 Documentation/devicetree/bindings/watchdog/apple,wdt.yaml
diff --git a/Documentation/devicetree/bindings/watchdog/apple,wdt.yaml b/Documentation/devicetree/bindings/watchdog/apple,wdt.yaml
new file mode 100644
index 000000000000..e58c56a6fdf6
--- /dev/null
+++ b/Documentation/devicetree/bindings/watchdog/apple,wdt.yaml @@ -0,0 +1,52 @@
+ # SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+ %YAML 1.2
+ ---
+ $id : http://devicetree.org/schemas/watchdog/apple,wdt.yaml#
+ $schema : http://devicetree.org/meta-schemas/core.yaml#
+
+ title : Apple SoC Watchdog
+
+ allOf :
+ - $ref : "watchdog.yaml#"
+
+ maintainers :
+ - Sven Peter <sven@svenpeter.dev>
+
+ properties :
+ compatible :
+ items :
+ - enum :
+ - apple,t8103-wdt
+ - apple,t6000-wdt
+ - const : apple,wdt
+
+ reg :
+ maxItems : 1
+
+ clocks :
+ maxItems : 1
+
+ interrupts :
+ maxItems : 1
+
+ required :
+ - compatible
+ - reg
+ - clocks
+ - interrupts
+
+ unevaluatedProperties : false
+
+ examples :
+ - |
+ #include <dt-bindings/interrupt-controller/apple-aic.h>
+ #include <dt-bindings/interrupt-controller/irq.h>
+
+ wdt : watchdog@50000000 {
+ compatible = "apple,t8103-wdt", "apple,wdt";
+ reg = <0x50000000 0x4000>;
+ clocks = <&clk>;
+ interrupts = <AIC_IRQ 123 IRQ_TYPE_LEVEL_HIGH>;
+ } ;
+
+ ... diff --git a/MAINTAINERS b/MAINTAINERS
index 360e9aa0205d..859201bbd4e8 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS @@ -1750,6 +1750,7 @@ F: Documentation/devicetree/bindings/interrupt-controller/apple,aic.yaml
F: Documentation/devicetree/bindings/mailbox/apple,mailbox.yaml
F: Documentation/devicetree/bindings/pci/apple,pcie.yaml
F: Documentation/devicetree/bindings/pinctrl/apple,pinctrl.yaml
+ F: Documentation/devicetree/bindings/watchdog/apple,wdt.yaml
F: arch/arm64/boot/dts/apple/
F: drivers/i2c/busses/i2c-pasemi-core.c
F: drivers/i2c/busses/i2c-pasemi-platform.c --
2.25.1
_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
On Tue, Nov 30, 2021 at 05:18:09PM +0100, Sven Peter wrote: Add support for the watchdog timer found in Apple SoCs. This driver is
also required to reboot these machines.
Signed-off-by: Sven Peter <redacted>
Reviewed-by: Guenter Roeck <linux@roeck-us.net>
quoted hunk ---
v1 -> v2:
- set the default timeout to 30s and call watchdog_init_timeout
to allow the device tree to override it
- set WDOG_HW_RUNNING if the watchdog is enabled at boot
- check that the clock rate is not zero
- use unsigned long instead of u32 for clk_rate
- use devm_add_action_or_reset instead of manually calling
clk_disable_unprepare
- explain the magic number in apple_wdt_restart
MAINTAINERS | 1 +
drivers/watchdog/Kconfig | 12 ++
drivers/watchdog/Makefile | 1 +
drivers/watchdog/apple_wdt.c | 226 +++++++++++++++++++++++++++++++++++
4 files changed, 240 insertions(+)
create mode 100644 drivers/watchdog/apple_wdt.c
diff --git a/MAINTAINERS b/MAINTAINERS
index 859201bbd4e8..6190f0b40983 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS @@ -1757,6 +1757,7 @@ F: drivers/i2c/busses/i2c-pasemi-platform.c
F: drivers/irqchip/irq-apple-aic.c
F: drivers/mailbox/apple-mailbox.c
F: drivers/pinctrl/pinctrl-apple-gpio.c
+ F: drivers/watchdog/apple_wdt.c
F: include/dt-bindings/interrupt-controller/apple-aic.h
F: include/dt-bindings/pinctrl/apple.h
F: include/linux/apple-mailbox.h diff --git a/drivers/watchdog/Kconfig b/drivers/watchdog/Kconfig
index 9d222ba17ec6..170dec880c8f 100644
--- a/drivers/watchdog/Kconfig
+++ b/drivers/watchdog/Kconfig @@ -976,6 +976,18 @@ config MSC313E_WATCHDOG
To compile this driver as a module, choose M here: the
module will be called msc313e_wdt.
+ config APPLE_WATCHDOG
+ tristate "Apple SoC watchdog"
+ depends on ARCH_APPLE || COMPILE_TEST
+ select WATCHDOG_CORE
+ help
+ Say Y here to include support for the Watchdog found in Apple
+ SoCs such as the M1. Next to the common watchdog features this
+ driver is also required in order to reboot these SoCs.
+
+ To compile this driver as a module, choose M here: the
+ module will be called apple_wdt.
+
# X86 (i386 + ia64 + x86_64) Architecture
config ACQUIRE_WDT diff --git a/drivers/watchdog/Makefile b/drivers/watchdog/Makefile
index 2ee97064145b..270a518bd8f3 100644
--- a/drivers/watchdog/Makefile
+++ b/drivers/watchdog/Makefile @@ -93,6 +93,7 @@ obj-$(CONFIG_PM8916_WATCHDOG) += pm8916_wdt.o
obj-$(CONFIG_ARM_SMC_WATCHDOG) += arm_smc_wdt.o
obj-$(CONFIG_VISCONTI_WATCHDOG) += visconti_wdt.o
obj-$(CONFIG_MSC313E_WATCHDOG) += msc313e_wdt.o
+ obj-$(CONFIG_APPLE_WATCHDOG) += apple_wdt.o
# X86 (i386 + ia64 + x86_64) Architecture
obj-$(CONFIG_ACQUIRE_WDT) += acquirewdt.o diff --git a/drivers/watchdog/apple_wdt.c b/drivers/watchdog/apple_wdt.c
new file mode 100644
index 000000000000..76e5bedd50d1
--- /dev/null
+++ b/drivers/watchdog/apple_wdt.c @@ -0,0 +1,226 @@
+ // SPDX-License-Identifier: GPL-2.0-only OR MIT
+ /*
+ * Apple SoC Watchdog driver
+ *
+ * Copyright ( C ) The Asahi Linux Contributors
+ */
+
+ #include <linux/bits.h>
+ #include <linux/clk.h>
+ #include <linux/delay.h>
+ #include <linux/io.h>
+ #include <linux/kernel.h>
+ #include <linux/limits.h>
+ #include <linux/module.h>
+ #include <linux/of.h>
+ #include <linux/platform_device.h>
+ #include <linux/watchdog.h>
+
+ /*
+ * Apple Watchdog MMIO registers
+ *
+ * This HW block has three separate watchdogs . WD0 resets the machine
+ * to recovery mode and is not very useful for us . WD1 and WD2 trigger a normal
+ * machine reset . WD0 additionally supports a configurable interrupt .
+ * This information can be used to implement pretimeout support at a later time .
+ *
+ * APPLE_WDT_WDx_CUR_TIME is a simple counter incremented for each tick of the
+ * reference clock . It can also be overwritten to any value .
+ * Whenever APPLE_WDT_CTRL_RESET_EN is set in APPLE_WDT_WDx_CTRL and
+ * APPLE_WDTx_WD1_CUR_TIME >= APPLE_WDTx_WD1_BITE_TIME the entire machine is
+ * reset .
+ * Whenever APPLE_WDT_CTRL_IRQ_EN is set and APPLE_WDTx_WD1_CUR_TIME >=
+ * APPLE_WDTx_WD1_BARK_TIME an interrupt is triggered and
+ * APPLE_WDT_CTRL_IRQ_STATUS is set . The interrupt can be cleared by writing
+ * 1 to APPLE_WDT_CTRL_IRQ_STATUS .
+ */
+ #define APPLE_WDT_WD0_CUR_TIME 0x00
+ #define APPLE_WDT_WD0_BITE_TIME 0x04
+ #define APPLE_WDT_WD0_BARK_TIME 0x08
+ #define APPLE_WDT_WD0_CTRL 0x0c
+
+ #define APPLE_WDT_WD1_CUR_TIME 0x10
+ #define APPLE_WDT_WD1_BITE_TIME 0x14
+ #define APPLE_WDT_WD1_CTRL 0x1c
+
+ #define APPLE_WDT_WD2_CUR_TIME 0x20
+ #define APPLE_WDT_WD2_BITE_TIME 0x24
+ #define APPLE_WDT_WD2_CTRL 0x2c
+
+ #define APPLE_WDT_CTRL_IRQ_EN BIT(0)
+ #define APPLE_WDT_CTRL_IRQ_STATUS BIT(1)
+ #define APPLE_WDT_CTRL_RESET_EN BIT(2)
+
+ #define APPLE_WDT_TIMEOUT_DEFAULT 30
+
+ struct apple_wdt {
+ struct watchdog_device wdd ;
+ void __iomem * regs ;
+ unsigned long clk_rate ;
+ };
+
+ static struct apple_wdt * to_apple_wdt ( struct watchdog_device * wdd )
+ {
+ return container_of ( wdd , struct apple_wdt , wdd );
+ }
+
+ static int apple_wdt_start ( struct watchdog_device * wdd )
+ {
+ struct apple_wdt * wdt = to_apple_wdt ( wdd );
+
+ writel_relaxed ( 0 , wdt -> regs + APPLE_WDT_WD1_CUR_TIME );
+ writel_relaxed ( APPLE_WDT_CTRL_RESET_EN , wdt -> regs + APPLE_WDT_WD1_CTRL );
+
+ return 0 ;
+ }
+
+ static int apple_wdt_stop ( struct watchdog_device * wdd )
+ {
+ struct apple_wdt * wdt = to_apple_wdt ( wdd );
+
+ writel_relaxed ( 0 , wdt -> regs + APPLE_WDT_WD1_CTRL );
+
+ return 0 ;
+ }
+
+ static int apple_wdt_ping ( struct watchdog_device * wdd )
+ {
+ struct apple_wdt * wdt = to_apple_wdt ( wdd );
+
+ writel_relaxed ( 0 , wdt -> regs + APPLE_WDT_WD1_CUR_TIME );
+
+ return 0 ;
+ }
+
+ static int apple_wdt_set_timeout ( struct watchdog_device * wdd , unsigned int s )
+ {
+ struct apple_wdt * wdt = to_apple_wdt ( wdd );
+
+ writel_relaxed ( 0 , wdt -> regs + APPLE_WDT_WD1_CUR_TIME );
+ writel_relaxed ( wdt -> clk_rate * s , wdt -> regs + APPLE_WDT_WD1_BITE_TIME );
+
+ wdd -> timeout = s ;
+
+ return 0 ;
+ }
+
+ static unsigned int apple_wdt_get_timeleft ( struct watchdog_device * wdd )
+ {
+ struct apple_wdt * wdt = to_apple_wdt ( wdd );
+ u32 cur_time , reset_time ;
+
+ cur_time = readl_relaxed ( wdt -> regs + APPLE_WDT_WD1_CUR_TIME );
+ reset_time = readl_relaxed ( wdt -> regs + APPLE_WDT_WD1_BITE_TIME );
+
+ return ( reset_time - cur_time ) / wdt -> clk_rate ;
+ }
+
+ static int apple_wdt_restart ( struct watchdog_device * wdd , unsigned long mode ,
+ void * cmd )
+ {
+ struct apple_wdt * wdt = to_apple_wdt ( wdd );
+
+ writel_relaxed ( APPLE_WDT_CTRL_RESET_EN , wdt -> regs + APPLE_WDT_WD1_CTRL );
+ writel_relaxed ( 0 , wdt -> regs + APPLE_WDT_WD1_BITE_TIME );
+ writel_relaxed ( 0 , wdt -> regs + APPLE_WDT_WD1_CUR_TIME );
+
+ /*
+ * Flush writes and then wait for the SoC to reset . Even though the
+ * reset is queued almost immediately experiments have shown that it
+ * can take up to ~ 20-25 ms until the SoC is actually reset . Just wait
+ * 50 ms here to be safe .
+ */
+ ( void ) readl_relaxed ( wdt -> regs + APPLE_WDT_WD1_CUR_TIME );
+ mdelay ( 50 );
+
+ return 0 ;
+ }
+
+ static void apple_wdt_clk_disable_unprepare ( void * data )
+ {
+ clk_disable_unprepare ( data );
+ }
+
+ static struct watchdog_ops apple_wdt_ops = {
+ . owner = THIS_MODULE ,
+ . start = apple_wdt_start ,
+ . stop = apple_wdt_stop ,
+ . ping = apple_wdt_ping ,
+ . set_timeout = apple_wdt_set_timeout ,
+ . get_timeleft = apple_wdt_get_timeleft ,
+ . restart = apple_wdt_restart ,
+ };
+
+ static struct watchdog_info apple_wdt_info = {
+ . identity = "Apple SoC Watchdog" ,
+ . options = WDIOF_MAGICCLOSE | WDIOF_KEEPALIVEPING | WDIOF_SETTIMEOUT ,
+ };
+
+ static int apple_wdt_probe ( struct platform_device * pdev )
+ {
+ struct device * dev = & pdev -> dev ;
+ struct apple_wdt * wdt ;
+ struct clk * clk ;
+ u32 wdt_ctrl ;
+ int ret ;
+
+ wdt = devm_kzalloc ( dev , sizeof ( * wdt ), GFP_KERNEL );
+ if ( ! wdt )
+ return - ENOMEM ;
+
+ wdt -> regs = devm_platform_ioremap_resource ( pdev , 0 );
+ if ( IS_ERR ( wdt -> regs ))
+ return PTR_ERR ( wdt -> regs );
+
+ clk = devm_clk_get ( dev , NULL );
+ if ( IS_ERR ( clk ))
+ return PTR_ERR ( clk );
+
+ ret = clk_prepare_enable ( clk );
+ if ( ret )
+ return ret ;
+
+ ret = devm_add_action_or_reset ( dev , apple_wdt_clk_disable_unprepare ,
+ clk );
+ if ( ret )
+ return ret ;
+
+ wdt -> clk_rate = clk_get_rate ( clk );
+ if ( ! wdt -> clk_rate )
+ return - EINVAL ;
+
+ wdt -> wdd . ops = & apple_wdt_ops ;
+ wdt -> wdd . info = & apple_wdt_info ;
+ wdt -> wdd . max_timeout = U32_MAX / wdt -> clk_rate ;
+ wdt -> wdd . timeout = APPLE_WDT_TIMEOUT_DEFAULT ;
+
+ wdt_ctrl = readl_relaxed ( wdt -> regs + APPLE_WDT_WD1_CTRL );
+ if ( wdt_ctrl & APPLE_WDT_CTRL_RESET_EN )
+ set_bit ( WDOG_HW_RUNNING , & wdt -> wdd . status );
+
+ watchdog_init_timeout ( & wdt -> wdd , 0 , dev );
+ apple_wdt_set_timeout ( & wdt -> wdd , wdt -> wdd . timeout );
+ watchdog_stop_on_unregister ( & wdt -> wdd );
+ watchdog_set_restart_priority ( & wdt -> wdd , 128 );
+
+ return devm_watchdog_register_device ( dev , & wdt -> wdd );
+ }
+
+ static const struct of_device_id apple_wdt_of_match [] = {
+ { . compatible = "apple,wdt" },
+ {},
+ };
+ MODULE_DEVICE_TABLE ( of , apple_wdt_of_match );
+
+ static struct platform_driver apple_wdt_driver = {
+ . driver = {
+ . name = "apple-watchdog" ,
+ . of_match_table = apple_wdt_of_match ,
+ },
+ . probe = apple_wdt_probe ,
+ };
+ module_platform_driver ( apple_wdt_driver );
+
+ MODULE_DESCRIPTION ( "Apple SoC watchdog driver" );
+ MODULE_AUTHOR ( "Sven Peter <sven@svenpeter.dev>" );
+ MODULE_LICENSE ( "Dual MIT/GPL" ); --
2.25.1
_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
On 02/12/2021 23.36, Guenter Roeck wrote: On 12/1/21 10:44 PM, Hector Martin "marcan" wrote: quoted
On 2021年12月1日 1:18:09 JST, Sven Peter [off-list ref] wrote: quoted Add support for the watchdog timer found in Apple SoCs. This driver is
also required to reboot these machines.
Signed-off-by: Sven Peter <redacted>
---
v1 -> v2:
- set the default timeout to 30s and call watchdog_init_timeout
to allow the device tree to override it
- set WDOG_HW_RUNNING if the watchdog is enabled at boot
- check that the clock rate is not zero
- use unsigned long instead of u32 for clk_rate
- use devm_add_action_or_reset instead of manually calling
clk_disable_unprepare
- explain the magic number in apple_wdt_restart
MAINTAINERS | 1 +
drivers/watchdog/Kconfig | 12 ++
drivers/watchdog/Makefile | 1 +
drivers/watchdog/apple_wdt.c | 226 +++++++++++++++++++++++++++++++++++
4 files changed, 240 insertions(+)
create mode 100644 drivers/watchdog/apple_wdt.c
diff --git a/MAINTAINERS b/MAINTAINERS
index 859201bbd4e8..6190f0b40983 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS @@ -1757,6 +1757,7 @@ F: drivers/i2c/busses/i2c-pasemi-platform.c F: drivers/irqchip/irq-apple-aic.c
F: drivers/mailbox/apple-mailbox.c
F: drivers/pinctrl/pinctrl-apple-gpio.c
+F: drivers/watchdog/apple_wdt.c
F: include/dt-bindings/interrupt-controller/apple-aic.h
F: include/dt-bindings/pinctrl/apple.h
F: include/linux/apple-mailbox.h diff --git a/drivers/watchdog/Kconfig b/drivers/watchdog/Kconfig
index 9d222ba17ec6..170dec880c8f 100644
--- a/drivers/watchdog/Kconfig
+++ b/drivers/watchdog/Kconfig @@ -976,6 +976,18 @@ config MSC313E_WATCHDOG To compile this driver as a module, choose M here: the
module will be called msc313e_wdt.
+config APPLE_WATCHDOG
+ tristate "Apple SoC watchdog"
+ depends on ARCH_APPLE || COMPILE_TEST
+ select WATCHDOG_CORE
+ help
+ Say Y here to include support for the Watchdog found in Apple
+ SoCs such as the M1. Next to the common watchdog features this
+ driver is also required in order to reboot these SoCs.
+
+ To compile this driver as a module, choose M here: the
+ module will be called apple_wdt.
+
# X86 (i386 + ia64 + x86_64) Architecture
config ACQUIRE_WDT diff --git a/drivers/watchdog/Makefile b/drivers/watchdog/Makefile
index 2ee97064145b..270a518bd8f3 100644
--- a/drivers/watchdog/Makefile
+++ b/drivers/watchdog/Makefile @@ -93,6 +93,7 @@ obj-$(CONFIG_PM8916_WATCHDOG) += pm8916_wdt.o obj-$(CONFIG_ARM_SMC_WATCHDOG) += arm_smc_wdt.o
obj-$(CONFIG_VISCONTI_WATCHDOG) += visconti_wdt.o
obj-$(CONFIG_MSC313E_WATCHDOG) += msc313e_wdt.o
+obj-$(CONFIG_APPLE_WATCHDOG) += apple_wdt.o
# X86 (i386 + ia64 + x86_64) Architecture
obj-$(CONFIG_ACQUIRE_WDT) += acquirewdt.o diff --git a/drivers/watchdog/apple_wdt.c b/drivers/watchdog/apple_wdt.c
new file mode 100644
index 000000000000..76e5bedd50d1
--- /dev/null
+++ b/drivers/watchdog/apple_wdt.c @@ -0,0 +1,226 @@
+ // SPDX-License-Identifier: GPL-2.0-only OR MIT
+ /*
+ * Apple SoC Watchdog driver
+ *
+ * Copyright ( C ) The Asahi Linux Contributors
+ */
+
+ #include <linux/bits.h>
+ #include <linux/clk.h>
+ #include <linux/delay.h>
+ #include <linux/io.h>
+ #include <linux/kernel.h>
+ #include <linux/limits.h>
+ #include <linux/module.h>
+ #include <linux/of.h>
+ #include <linux/platform_device.h>
+ #include <linux/watchdog.h>
+
+ /*
+ * Apple Watchdog MMIO registers
+ *
+ * This HW block has three separate watchdogs . WD0 resets the machine
+ * to recovery mode and is not very useful for us . WD1 and WD2 trigger a normal
+ * machine reset . WD0 additionally supports a configurable interrupt .
+ * This information can be used to implement pretimeout support at a later time .
+ *
+ * APPLE_WDT_WDx_CUR_TIME is a simple counter incremented for each tick of the
+ * reference clock . It can also be overwritten to any value .
+ * Whenever APPLE_WDT_CTRL_RESET_EN is set in APPLE_WDT_WDx_CTRL and
+ * APPLE_WDTx_WD1_CUR_TIME >= APPLE_WDTx_WD1_BITE_TIME the entire machine is
+ * reset .
+ * Whenever APPLE_WDT_CTRL_IRQ_EN is set and APPLE_WDTx_WD1_CUR_TIME >=
+ * APPLE_WDTx_WD1_BARK_TIME an interrupt is triggered and
+ * APPLE_WDT_CTRL_IRQ_STATUS is set . The interrupt can be cleared by writing
+ * 1 to APPLE_WDT_CTRL_IRQ_STATUS .
+ */
+ #define APPLE_WDT_WD0_CUR_TIME 0x00
+ #define APPLE_WDT_WD0_BITE_TIME 0x04
+ #define APPLE_WDT_WD0_BARK_TIME 0x08
+ #define APPLE_WDT_WD0_CTRL 0x0c
+
+ #define APPLE_WDT_WD1_CUR_TIME 0x10
+ #define APPLE_WDT_WD1_BITE_TIME 0x14
+ #define APPLE_WDT_WD1_CTRL 0x1c
+
+ #define APPLE_WDT_WD2_CUR_TIME 0x20
+ #define APPLE_WDT_WD2_BITE_TIME 0x24
+ #define APPLE_WDT_WD2_CTRL 0x2c
+
+ #define APPLE_WDT_CTRL_IRQ_EN BIT(0)
+ #define APPLE_WDT_CTRL_IRQ_STATUS BIT(1)
+ #define APPLE_WDT_CTRL_RESET_EN BIT(2)
+
+ #define APPLE_WDT_TIMEOUT_DEFAULT 30
+
+ struct apple_wdt {
+ struct watchdog_device wdd ;
+ void __iomem * regs ;
+ unsigned long clk_rate ;
+ };
+
+ static struct apple_wdt * to_apple_wdt ( struct watchdog_device * wdd )
+ {
+ return container_of ( wdd , struct apple_wdt , wdd );
+ }
+
+ static int apple_wdt_start ( struct watchdog_device * wdd )
+ {
+ struct apple_wdt * wdt = to_apple_wdt ( wdd );
+
+ writel_relaxed ( 0 , wdt -> regs + APPLE_WDT_WD1_CUR_TIME );
+ writel_relaxed ( APPLE_WDT_CTRL_RESET_EN , wdt -> regs + APPLE_WDT_WD1_CTRL );
+
+ return 0 ;
+ }
+
+ static int apple_wdt_stop ( struct watchdog_device * wdd )
+ {
+ struct apple_wdt * wdt = to_apple_wdt ( wdd );
+
+ writel_relaxed ( 0 , wdt -> regs + APPLE_WDT_WD1_CTRL );
+
+ return 0 ;
+ }
+
+ static int apple_wdt_ping ( struct watchdog_device * wdd )
+ {
+ struct apple_wdt * wdt = to_apple_wdt ( wdd );
+
+ writel_relaxed ( 0 , wdt -> regs + APPLE_WDT_WD1_CUR_TIME );
+
+ return 0 ;
+ }
+
+ static int apple_wdt_set_timeout ( struct watchdog_device * wdd , unsigned int s )
+ {
+ struct apple_wdt * wdt = to_apple_wdt ( wdd );
+
+ writel_relaxed ( 0 , wdt -> regs + APPLE_WDT_WD1_CUR_TIME );
+ writel_relaxed ( wdt -> clk_rate * s , wdt -> regs + APPLE_WDT_WD1_BITE_TIME );
+
+ wdd -> timeout = s ;
+
+ return 0 ;
+ }
+
+ static unsigned int apple_wdt_get_timeleft ( struct watchdog_device * wdd )
+ {
+ struct apple_wdt * wdt = to_apple_wdt ( wdd );
+ u32 cur_time , reset_time ;
+
+ cur_time = readl_relaxed ( wdt -> regs + APPLE_WDT_WD1_CUR_TIME );
+ reset_time = readl_relaxed ( wdt -> regs + APPLE_WDT_WD1_BITE_TIME );
+
+ return ( reset_time - cur_time ) / wdt -> clk_rate ;
+ }
+
+ static int apple_wdt_restart ( struct watchdog_device * wdd , unsigned long mode ,
+ void * cmd )
+ {
+ struct apple_wdt * wdt = to_apple_wdt ( wdd );
+
+ writel_relaxed ( APPLE_WDT_CTRL_RESET_EN , wdt -> regs + APPLE_WDT_WD1_CTRL );
+ writel_relaxed ( 0 , wdt -> regs + APPLE_WDT_WD1_BITE_TIME );
+ writel_relaxed ( 0 , wdt -> regs + APPLE_WDT_WD1_CUR_TIME );
+
+ /*
+ * Flush writes and then wait for the SoC to reset . Even though the
+ * reset is queued almost immediately experiments have shown that it
+ * can take up to ~ 20-25 ms until the SoC is actually reset . Just wait
+ * 50 ms here to be safe .
+ */
+ ( void ) readl_relaxed ( wdt -> regs + APPLE_WDT_WD1_CUR_TIME );
+ mdelay ( 50 );
+
+ return 0 ;
+ }
+
+ static void apple_wdt_clk_disable_unprepare ( void * data )
+ {
+ clk_disable_unprepare ( data );
+ }
+
+ static struct watchdog_ops apple_wdt_ops = {
+ . owner = THIS_MODULE ,
+ . start = apple_wdt_start ,
+ . stop = apple_wdt_stop ,
+ . ping = apple_wdt_ping ,
+ . set_timeout = apple_wdt_set_timeout ,
+ . get_timeleft = apple_wdt_get_timeleft ,
+ . restart = apple_wdt_restart ,
+ };
+
+ static struct watchdog_info apple_wdt_info = {
+ . identity = "Apple SoC Watchdog" ,
+ . options = WDIOF_MAGICCLOSE | WDIOF_KEEPALIVEPING | WDIOF_SETTIMEOUT ,
+ };
+
+ static int apple_wdt_probe ( struct platform_device * pdev )
+ {
+ struct device * dev = & pdev -> dev ;
+ struct apple_wdt * wdt ;
+ struct clk * clk ;
+ u32 wdt_ctrl ;
+ int ret ;
+
+ wdt = devm_kzalloc ( dev , sizeof ( * wdt ), GFP_KERNEL );
+ if ( ! wdt )
+ return - ENOMEM ;
+
+ wdt -> regs = devm_platform_ioremap_resource ( pdev , 0 );
+ if ( IS_ERR ( wdt -> regs ))
+ return PTR_ERR ( wdt -> regs );
+
+ clk = devm_clk_get ( dev , NULL );
+ if ( IS_ERR ( clk ))
+ return PTR_ERR ( clk );
+
+ ret = clk_prepare_enable ( clk );
+ if ( ret )
+ return ret ;
+
+ ret = devm_add_action_or_reset ( dev , apple_wdt_clk_disable_unprepare ,
+ clk );
+ if ( ret )
+ return ret ;
+
+ wdt -> clk_rate = clk_get_rate ( clk );
+ if ( ! wdt -> clk_rate )
+ return - EINVAL ;
+
+ wdt -> wdd . ops = & apple_wdt_ops ;
+ wdt -> wdd . info = & apple_wdt_info ;
+ wdt -> wdd . max_timeout = U32_MAX / wdt -> clk_rate ;
+ wdt -> wdd . timeout = APPLE_WDT_TIMEOUT_DEFAULT ;
+
+ wdt_ctrl = readl_relaxed ( wdt -> regs + APPLE_WDT_WD1_CTRL );
+ if ( wdt_ctrl & APPLE_WDT_CTRL_RESET_EN )
+ set_bit ( WDOG_HW_RUNNING , & wdt -> wdd . status );
+
+ watchdog_init_timeout ( & wdt -> wdd , 0 , dev );
+ apple_wdt_set_timeout ( & wdt -> wdd , wdt -> wdd . timeout );
+ watchdog_stop_on_unregister ( & wdt -> wdd );
+ watchdog_set_restart_priority ( & wdt -> wdd , 128 );
+
+ return devm_watchdog_register_device ( dev , & wdt -> wdd );
+ }
+
+ static const struct of_device_id apple_wdt_of_match [] = {
+ { . compatible = "apple,wdt" },
+ {},
+ };
+ MODULE_DEVICE_TABLE ( of , apple_wdt_of_match );
+
+ static struct platform_driver apple_wdt_driver = {
+ . driver = {
+ . name = "apple-watchdog" ,
+ . of_match_table = apple_wdt_of_match ,
+ },
+ . probe = apple_wdt_probe ,
+ };
+ module_platform_driver ( apple_wdt_driver );
+
+ MODULE_DESCRIPTION ( "Apple SoC watchdog driver" );
+ MODULE_AUTHOR ( "Sven Peter <sven@svenpeter.dev>" );
+ MODULE_LICENSE ( "Dual MIT/GPL" );
Reviewed-by: Hector Martin <redacted>
Thanks, looks good! Any chance you can do a quick v3 with the MAINTAINERS changes split off? As usual, I'd rather make life easier for people merging upstream :)
Usually I don't ask for such a split, and I don't recall that being an issue.
So, from my perspective, that is not necessary.
We're upstreaming a bunch of things in parallel that touch the same
MAINTAINERS section, and if each of those goes through subsystem trees,
they all end up creating a pile of conflicts when merged. This has
already happened a few times (I got a ping from linux-next about it the
other day).
To make life easier for upstream mergers, we prefer to split off
MAINTAINERS changes. Then instead of going through the subsystem tree, I
can push those up through the SoC tree, making sure to resolve any
conflicts/ordering issues. Then everyone upstream of us is happy :-)
--
Hector Martin (marcan@marcan.st)
Public Key: https://mrcn.st/pub
_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
On 12/3/21 5:50 AM, Hector Martin wrote: On 02/12/2021 23.36, Guenter Roeck wrote: quoted On 12/1/21 10:44 PM, Hector Martin "marcan" wrote: quoted
On 2021年12月1日 1:18:09 JST, Sven Peter [off-list ref] wrote: quoted Add support for the watchdog timer found in Apple SoCs. This driver is
also required to reboot these machines.
Signed-off-by: Sven Peter <redacted>
---
v1 -> v2:
- set the default timeout to 30s and call watchdog_init_timeout
to allow the device tree to override it
- set WDOG_HW_RUNNING if the watchdog is enabled at boot
- check that the clock rate is not zero
- use unsigned long instead of u32 for clk_rate
- use devm_add_action_or_reset instead of manually calling
clk_disable_unprepare
- explain the magic number in apple_wdt_restart
MAINTAINERS | 1 +
drivers/watchdog/Kconfig | 12 ++
drivers/watchdog/Makefile | 1 +
drivers/watchdog/apple_wdt.c | 226 +++++++++++++++++++++++++++++++++++
4 files changed, 240 insertions(+)
create mode 100644 drivers/watchdog/apple_wdt.c
diff --git a/MAINTAINERS b/MAINTAINERS
index 859201bbd4e8..6190f0b40983 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS @@ -1757,6 +1757,7 @@ F: drivers/i2c/busses/i2c-pasemi-platform.c F: drivers/irqchip/irq-apple-aic.c
F: drivers/mailbox/apple-mailbox.c
F: drivers/pinctrl/pinctrl-apple-gpio.c
+F: drivers/watchdog/apple_wdt.c
F: include/dt-bindings/interrupt-controller/apple-aic.h
F: include/dt-bindings/pinctrl/apple.h
F: include/linux/apple-mailbox.h diff --git a/drivers/watchdog/Kconfig b/drivers/watchdog/Kconfig
index 9d222ba17ec6..170dec880c8f 100644
--- a/drivers/watchdog/Kconfig
+++ b/drivers/watchdog/Kconfig @@ -976,6 +976,18 @@ config MSC313E_WATCHDOG To compile this driver as a module, choose M here: the
module will be called msc313e_wdt.
+config APPLE_WATCHDOG
+ tristate "Apple SoC watchdog"
+ depends on ARCH_APPLE || COMPILE_TEST
+ select WATCHDOG_CORE
+ help
+ Say Y here to include support for the Watchdog found in Apple
+ SoCs such as the M1. Next to the common watchdog features this
+ driver is also required in order to reboot these SoCs.
+
+ To compile this driver as a module, choose M here: the
+ module will be called apple_wdt.
+
# X86 (i386 + ia64 + x86_64) Architecture
config ACQUIRE_WDT diff --git a/drivers/watchdog/Makefile b/drivers/watchdog/Makefile
index 2ee97064145b..270a518bd8f3 100644
--- a/drivers/watchdog/Makefile
+++ b/drivers/watchdog/Makefile @@ -93,6 +93,7 @@ obj-$(CONFIG_PM8916_WATCHDOG) += pm8916_wdt.o obj-$(CONFIG_ARM_SMC_WATCHDOG) += arm_smc_wdt.o
obj-$(CONFIG_VISCONTI_WATCHDOG) += visconti_wdt.o
obj-$(CONFIG_MSC313E_WATCHDOG) += msc313e_wdt.o
+obj-$(CONFIG_APPLE_WATCHDOG) += apple_wdt.o
# X86 (i386 + ia64 + x86_64) Architecture
obj-$(CONFIG_ACQUIRE_WDT) += acquirewdt.o diff --git a/drivers/watchdog/apple_wdt.c b/drivers/watchdog/apple_wdt.c
new file mode 100644
index 000000000000..76e5bedd50d1
--- /dev/null
+++ b/drivers/watchdog/apple_wdt.c @@ -0,0 +1,226 @@
+ // SPDX-License-Identifier: GPL-2.0-only OR MIT
+ /*
+ * Apple SoC Watchdog driver
+ *
+ * Copyright ( C ) The Asahi Linux Contributors
+ */
+
+ #include <linux/bits.h>
+ #include <linux/clk.h>
+ #include <linux/delay.h>
+ #include <linux/io.h>
+ #include <linux/kernel.h>
+ #include <linux/limits.h>
+ #include <linux/module.h>
+ #include <linux/of.h>
+ #include <linux/platform_device.h>
+ #include <linux/watchdog.h>
+
+ /*
+ * Apple Watchdog MMIO registers
+ *
+ * This HW block has three separate watchdogs . WD0 resets the machine
+ * to recovery mode and is not very useful for us . WD1 and WD2 trigger a normal
+ * machine reset . WD0 additionally supports a configurable interrupt .
+ * This information can be used to implement pretimeout support at a later time .
+ *
+ * APPLE_WDT_WDx_CUR_TIME is a simple counter incremented for each tick of the
+ * reference clock . It can also be overwritten to any value .
+ * Whenever APPLE_WDT_CTRL_RESET_EN is set in APPLE_WDT_WDx_CTRL and
+ * APPLE_WDTx_WD1_CUR_TIME >= APPLE_WDTx_WD1_BITE_TIME the entire machine is
+ * reset .
+ * Whenever APPLE_WDT_CTRL_IRQ_EN is set and APPLE_WDTx_WD1_CUR_TIME >=
+ * APPLE_WDTx_WD1_BARK_TIME an interrupt is triggered and
+ * APPLE_WDT_CTRL_IRQ_STATUS is set . The interrupt can be cleared by writing
+ * 1 to APPLE_WDT_CTRL_IRQ_STATUS .
+ */
+ #define APPLE_WDT_WD0_CUR_TIME 0x00
+ #define APPLE_WDT_WD0_BITE_TIME 0x04
+ #define APPLE_WDT_WD0_BARK_TIME 0x08
+ #define APPLE_WDT_WD0_CTRL 0x0c
+
+ #define APPLE_WDT_WD1_CUR_TIME 0x10
+ #define APPLE_WDT_WD1_BITE_TIME 0x14
+ #define APPLE_WDT_WD1_CTRL 0x1c
+
+ #define APPLE_WDT_WD2_CUR_TIME 0x20
+ #define APPLE_WDT_WD2_BITE_TIME 0x24
+ #define APPLE_WDT_WD2_CTRL 0x2c
+
+ #define APPLE_WDT_CTRL_IRQ_EN BIT(0)
+ #define APPLE_WDT_CTRL_IRQ_STATUS BIT(1)
+ #define APPLE_WDT_CTRL_RESET_EN BIT(2)
+
+ #define APPLE_WDT_TIMEOUT_DEFAULT 30
+
+ struct apple_wdt {
+ struct watchdog_device wdd ;
+ void __iomem * regs ;
+ unsigned long clk_rate ;
+ };
+
+ static struct apple_wdt * to_apple_wdt ( struct watchdog_device * wdd )
+ {
+ return container_of ( wdd , struct apple_wdt , wdd );
+ }
+
+ static int apple_wdt_start ( struct watchdog_device * wdd )
+ {
+ struct apple_wdt * wdt = to_apple_wdt ( wdd );
+
+ writel_relaxed ( 0 , wdt -> regs + APPLE_WDT_WD1_CUR_TIME );
+ writel_relaxed ( APPLE_WDT_CTRL_RESET_EN , wdt -> regs + APPLE_WDT_WD1_CTRL );
+
+ return 0 ;
+ }
+
+ static int apple_wdt_stop ( struct watchdog_device * wdd )
+ {
+ struct apple_wdt * wdt = to_apple_wdt ( wdd );
+
+ writel_relaxed ( 0 , wdt -> regs + APPLE_WDT_WD1_CTRL );
+
+ return 0 ;
+ }
+
+ static int apple_wdt_ping ( struct watchdog_device * wdd )
+ {
+ struct apple_wdt * wdt = to_apple_wdt ( wdd );
+
+ writel_relaxed ( 0 , wdt -> regs + APPLE_WDT_WD1_CUR_TIME );
+
+ return 0 ;
+ }
+
+ static int apple_wdt_set_timeout ( struct watchdog_device * wdd , unsigned int s )
+ {
+ struct apple_wdt * wdt = to_apple_wdt ( wdd );
+
+ writel_relaxed ( 0 , wdt -> regs + APPLE_WDT_WD1_CUR_TIME );
+ writel_relaxed ( wdt -> clk_rate * s , wdt -> regs + APPLE_WDT_WD1_BITE_TIME );
+
+ wdd -> timeout = s ;
+
+ return 0 ;
+ }
+
+ static unsigned int apple_wdt_get_timeleft ( struct watchdog_device * wdd )
+ {
+ struct apple_wdt * wdt = to_apple_wdt ( wdd );
+ u32 cur_time , reset_time ;
+
+ cur_time = readl_relaxed ( wdt -> regs + APPLE_WDT_WD1_CUR_TIME );
+ reset_time = readl_relaxed ( wdt -> regs + APPLE_WDT_WD1_BITE_TIME );
+
+ return ( reset_time - cur_time ) / wdt -> clk_rate ;
+ }
+
+ static int apple_wdt_restart ( struct watchdog_device * wdd , unsigned long mode ,
+ void * cmd )
+ {
+ struct apple_wdt * wdt = to_apple_wdt ( wdd );
+
+ writel_relaxed ( APPLE_WDT_CTRL_RESET_EN , wdt -> regs + APPLE_WDT_WD1_CTRL );
+ writel_relaxed ( 0 , wdt -> regs + APPLE_WDT_WD1_BITE_TIME );
+ writel_relaxed ( 0 , wdt -> regs + APPLE_WDT_WD1_CUR_TIME );
+
+ /*
+ * Flush writes and then wait for the SoC to reset . Even though the
+ * reset is queued almost immediately experiments have shown that it
+ * can take up to ~ 20-25 ms until the SoC is actually reset . Just wait
+ * 50 ms here to be safe .
+ */
+ ( void ) readl_relaxed ( wdt -> regs + APPLE_WDT_WD1_CUR_TIME );
+ mdelay ( 50 );
+
+ return 0 ;
+ }
+
+ static void apple_wdt_clk_disable_unprepare ( void * data )
+ {
+ clk_disable_unprepare ( data );
+ }
+
+ static struct watchdog_ops apple_wdt_ops = {
+ . owner = THIS_MODULE ,
+ . start = apple_wdt_start ,
+ . stop = apple_wdt_stop ,
+ . ping = apple_wdt_ping ,
+ . set_timeout = apple_wdt_set_timeout ,
+ . get_timeleft = apple_wdt_get_timeleft ,
+ . restart = apple_wdt_restart ,
+ };
+
+ static struct watchdog_info apple_wdt_info = {
+ . identity = "Apple SoC Watchdog" ,
+ . options = WDIOF_MAGICCLOSE | WDIOF_KEEPALIVEPING | WDIOF_SETTIMEOUT ,
+ };
+
+ static int apple_wdt_probe ( struct platform_device * pdev )
+ {
+ struct device * dev = & pdev -> dev ;
+ struct apple_wdt * wdt ;
+ struct clk * clk ;
+ u32 wdt_ctrl ;
+ int ret ;
+
+ wdt = devm_kzalloc ( dev , sizeof ( * wdt ), GFP_KERNEL );
+ if ( ! wdt )
+ return - ENOMEM ;
+
+ wdt -> regs = devm_platform_ioremap_resource ( pdev , 0 );
+ if ( IS_ERR ( wdt -> regs ))
+ return PTR_ERR ( wdt -> regs );
+
+ clk = devm_clk_get ( dev , NULL );
+ if ( IS_ERR ( clk ))
+ return PTR_ERR ( clk );
+
+ ret = clk_prepare_enable ( clk );
+ if ( ret )
+ return ret ;
+
+ ret = devm_add_action_or_reset ( dev , apple_wdt_clk_disable_unprepare ,
+ clk );
+ if ( ret )
+ return ret ;
+
+ wdt -> clk_rate = clk_get_rate ( clk );
+ if ( ! wdt -> clk_rate )
+ return - EINVAL ;
+
+ wdt -> wdd . ops = & apple_wdt_ops ;
+ wdt -> wdd . info = & apple_wdt_info ;
+ wdt -> wdd . max_timeout = U32_MAX / wdt -> clk_rate ;
+ wdt -> wdd . timeout = APPLE_WDT_TIMEOUT_DEFAULT ;
+
+ wdt_ctrl = readl_relaxed ( wdt -> regs + APPLE_WDT_WD1_CTRL );
+ if ( wdt_ctrl & APPLE_WDT_CTRL_RESET_EN )
+ set_bit ( WDOG_HW_RUNNING , & wdt -> wdd . status );
+
+ watchdog_init_timeout ( & wdt -> wdd , 0 , dev );
+ apple_wdt_set_timeout ( & wdt -> wdd , wdt -> wdd . timeout );
+ watchdog_stop_on_unregister ( & wdt -> wdd );
+ watchdog_set_restart_priority ( & wdt -> wdd , 128 );
+
+ return devm_watchdog_register_device ( dev , & wdt -> wdd );
+ }
+
+ static const struct of_device_id apple_wdt_of_match [] = {
+ { . compatible = "apple,wdt" },
+ {},
+ };
+ MODULE_DEVICE_TABLE ( of , apple_wdt_of_match );
+
+ static struct platform_driver apple_wdt_driver = {
+ . driver = {
+ . name = "apple-watchdog" ,
+ . of_match_table = apple_wdt_of_match ,
+ },
+ . probe = apple_wdt_probe ,
+ };
+ module_platform_driver ( apple_wdt_driver );
+
+ MODULE_DESCRIPTION ( "Apple SoC watchdog driver" );
+ MODULE_AUTHOR ( "Sven Peter <sven@svenpeter.dev>" );
+ MODULE_LICENSE ( "Dual MIT/GPL" );
Reviewed-by: Hector Martin <redacted>
Thanks, looks good! Any chance you can do a quick v3 with the MAINTAINERS changes split off? As usual, I'd rather make life easier for people merging upstream :)
Usually I don't ask for such a split, and I don't recall that being an issue.
So, from my perspective, that is not necessary.
We're upstreaming a bunch of things in parallel that touch the same MAINTAINERS section, and if each of those goes through subsystem trees, they all end up creating a pile of conflicts when merged. This has already happened a few times (I got a ping from linux-next about it the other day).
To make life easier for upstream mergers, we prefer to split off MAINTAINERS changes. Then instead of going through the subsystem tree, I can push those up through the SoC tree, making sure to resolve any conflicts/ordering issues. Then everyone upstream of us is happy :-)
Ok, makes sense.
Thanks,
Guenter
_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
On 02/12/2021 23.43, Guenter Roeck wrote: On Tue, Nov 30, 2021 at 05:18:08PM +0100, Sven Peter wrote: quoted Apple SoCs come with a simple embedded watchdog. This watchdog is also
required in order to reset the SoC.
Reviewed-by: Mark Kettenis <redacted>
Reviewed-by: Rob Herring <robh@kernel.org>
Signed-off-by: Sven Peter <redacted>
Reviewed-by: Guenter Roeck <linux@roeck-us.net>
quoted ---
v1 --> v2:
- add Mark's and Rob's r-b tags
.../bindings/watchdog/apple,wdt.yaml | 52 +++++++++++++++++++
MAINTAINERS | 1 +
2 files changed, 53 insertions(+)
create mode 100644 Documentation/devicetree/bindings/watchdog/apple,wdt.yaml
diff --git a/Documentation/devicetree/bindings/watchdog/apple,wdt.yaml b/Documentation/devicetree/bindings/watchdog/apple,wdt.yaml
new file mode 100644
index 000000000000..e58c56a6fdf6
--- /dev/null
+++ b/Documentation/devicetree/bindings/watchdog/apple,wdt.yaml @@ -0,0 +1,52 @@
+ # SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+ %YAML 1.2
+ ---
+ $id : http://devicetree.org/schemas/watchdog/apple,wdt.yaml#
+ $schema : http://devicetree.org/meta-schemas/core.yaml#
+
+ title : Apple SoC Watchdog
+
+ allOf :
+ - $ref : "watchdog.yaml#"
+
+ maintainers :
+ - Sven Peter <sven@svenpeter.dev>
+
+ properties :
+ compatible :
+ items :
+ - enum :
+ - apple,t8103-wdt
+ - apple,t6000-wdt
+ - const : apple,wdt
+
+ reg :
+ maxItems : 1
+
+ clocks :
+ maxItems : 1
+
+ interrupts :
+ maxItems : 1
+
+ required :
+ - compatible
+ - reg
+ - clocks
+ - interrupts
+
+ unevaluatedProperties : false
+
+ examples :
+ - |
+ #include <dt-bindings/interrupt-controller/apple-aic.h>
+ #include <dt-bindings/interrupt-controller/irq.h>
+
+ wdt : watchdog@50000000 {
+ compatible = "apple,t8103-wdt", "apple,wdt";
+ reg = <0x50000000 0x4000>;
+ clocks = <&clk>;
+ interrupts = <AIC_IRQ 123 IRQ_TYPE_LEVEL_HIGH>;
+ } ;
+
+ ... diff --git a/MAINTAINERS b/MAINTAINERS
index 360e9aa0205d..859201bbd4e8 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS @@ -1750,6 +1750,7 @@ F: Documentation/devicetree/bindings/interrupt-controller/apple,aic.yaml
F: Documentation/devicetree/bindings/mailbox/apple,mailbox.yaml
F: Documentation/devicetree/bindings/pci/apple,pcie.yaml
F: Documentation/devicetree/bindings/pinctrl/apple,pinctrl.yaml
+ F: Documentation/devicetree/bindings/watchdog/apple,wdt.yaml
F: arch/arm64/boot/dts/apple/
F: drivers/i2c/busses/i2c-pasemi-core.c
F: drivers/i2c/busses/i2c-pasemi-platform.c --
2.25.1
I've gone ahead and applied this patch to the asahi-soc/dt tree, so we
can build off of that to add the DT nodes.
Sven, can you spin a v3 with the MAINTAINERS split and just the driver
itself, without this patch? I imagine Guenter will want to take the
driver itself through the linux-watchdog tree.
--
Hector Martin (marcan@marcan.st)
Public Key: https://mrcn.st/pub
_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel