From 151d4c1b2fbf7d9604a5f4de1bcb80a53a9ead1c Mon Sep 17 00:00:00 2001 From: Ethan Nelson-Moore Date: Mon, 27 Apr 2026 18:48:02 -0700 Subject: [PATCH 01/58] watchdog: remove driver for integrated WDT of ZFx86 486-based SoC The machzwd driver supports the integrated watchdog of the ZF Micro ZFx86 SoC, which contains a 486-compatible core [1]. Since 486 support was removed in commit 8b793a92d862 ("x86/cpu: Remove M486/M486SX/ELAN support"), the driver is no longer useful, Remove it. [1] https://www.zfmicro.com/zfx86.html Signed-off-by: Ethan Nelson-Moore Reviewed-by: Guenter Roeck Link: https://lore.kernel.org/r/20260428014806.35400-1-enelsonmoore@gmail.com Signed-off-by: Guenter Roeck --- .../watchdog/watchdog-parameters.rst | 10 - drivers/watchdog/Kconfig | 13 - drivers/watchdog/Makefile | 1 - drivers/watchdog/machzwd.c | 452 ------------------ 4 files changed, 476 deletions(-) delete mode 100644 drivers/watchdog/machzwd.c diff --git a/Documentation/watchdog/watchdog-parameters.rst b/Documentation/watchdog/watchdog-parameters.rst index 773241ed9986..173adee71901 100644 --- a/Documentation/watchdog/watchdog-parameters.rst +++ b/Documentation/watchdog/watchdog-parameters.rst @@ -284,16 +284,6 @@ ixp4xx_wdt: ------------------------------------------------- -machzwd: - nowayout: - Watchdog cannot be stopped once started - (default=kernel config parameter) - action: - after watchdog resets, generate: - 0 = RESET(*) 1 = SMI 2 = NMI 3 = SCI - -------------------------------------------------- - max63xx_wdt: heartbeat: Watchdog heartbeat period in seconds from 1 to 60, default 60 diff --git a/drivers/watchdog/Kconfig b/drivers/watchdog/Kconfig index dc78729ba2a5..97e320721cff 100644 --- a/drivers/watchdog/Kconfig +++ b/drivers/watchdog/Kconfig @@ -1712,19 +1712,6 @@ config W83977F_WDT To compile this driver as a module, choose M here: the module will be called w83977f_wdt. -config MACHZ_WDT - tristate "ZF MachZ Watchdog" - depends on (X86 || COMPILE_TEST) && HAS_IOPORT - help - If you are using a ZF Micro MachZ processor, say Y here, otherwise - N. This is the driver for the watchdog timer built-in on that - processor using ZF-Logic interface. This watchdog simply watches - your kernel to make sure it doesn't freeze, and if it does, it - reboots your computer after a certain amount of time. - - To compile this driver as a module, choose M here: the - module will be called machzwd. - config SBC_EPX_C3_WATCHDOG tristate "Winsystems SBC EPX-C3 watchdog" depends on (X86 || COMPILE_TEST) && HAS_IOPORT diff --git a/drivers/watchdog/Makefile b/drivers/watchdog/Makefile index d2fb16b9f9ce..2961ca141c61 100644 --- a/drivers/watchdog/Makefile +++ b/drivers/watchdog/Makefile @@ -146,7 +146,6 @@ obj-$(CONFIG_VIA_WDT) += via_wdt.o obj-$(CONFIG_W83627HF_WDT) += w83627hf_wdt.o obj-$(CONFIG_W83877F_WDT) += w83877f_wdt.o obj-$(CONFIG_W83977F_WDT) += w83977f_wdt.o -obj-$(CONFIG_MACHZ_WDT) += machzwd.o obj-$(CONFIG_SBC_EPX_C3_WATCHDOG) += sbc_epx_c3.o obj-$(CONFIG_INTEL_MID_WATCHDOG) += intel-mid_wdt.o obj-$(CONFIG_INTEL_OC_WATCHDOG) += intel_oc_wdt.o diff --git a/drivers/watchdog/machzwd.c b/drivers/watchdog/machzwd.c deleted file mode 100644 index 0ae8e5bc10ae..000000000000 --- a/drivers/watchdog/machzwd.c +++ /dev/null @@ -1,452 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -/* - * MachZ ZF-Logic Watchdog Timer driver for Linux - * - * The author does NOT admit liability nor provide warranty for - * any of this software. This material is provided "AS-IS" in - * the hope that it may be useful for others. - * - * Author: Fernando Fuganti - * - * Based on sbc60xxwdt.c by Jakob Oestergaard - * - * We have two timers (wd#1, wd#2) driven by a 32 KHz clock with the - * following periods: - * wd#1 - 2 seconds; - * wd#2 - 7.2 ms; - * After the expiration of wd#1, it can generate a NMI, SCI, SMI, or - * a system RESET and it starts wd#2 that unconditionally will RESET - * the system when the counter reaches zero. - * - * 14-Dec-2001 Matt Domsch - * Added nowayout module option to override CONFIG_WATCHDOG_NOWAYOUT - */ - -#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - - -/* ports */ -#define ZF_IOBASE 0x218 -#define INDEX 0x218 -#define DATA_B 0x219 -#define DATA_W 0x21A -#define DATA_D 0x21A - -/* indexes */ /* size */ -#define ZFL_VERSION 0x02 /* 16 */ -#define CONTROL 0x10 /* 16 */ -#define STATUS 0x12 /* 8 */ -#define COUNTER_1 0x0C /* 16 */ -#define COUNTER_2 0x0E /* 8 */ -#define PULSE_LEN 0x0F /* 8 */ - -/* controls */ -#define ENABLE_WD1 0x0001 -#define ENABLE_WD2 0x0002 -#define RESET_WD1 0x0010 -#define RESET_WD2 0x0020 -#define GEN_SCI 0x0100 -#define GEN_NMI 0x0200 -#define GEN_SMI 0x0400 -#define GEN_RESET 0x0800 - - -/* utilities */ - -#define WD1 0 -#define WD2 1 - -#define zf_writew(port, data) { outb(port, INDEX); outw(data, DATA_W); } -#define zf_writeb(port, data) { outb(port, INDEX); outb(data, DATA_B); } -#define zf_get_ZFL_version() zf_readw(ZFL_VERSION) - - -static unsigned short zf_readw(unsigned char port) -{ - outb(port, INDEX); - return inw(DATA_W); -} - - -MODULE_AUTHOR("Fernando Fuganti "); -MODULE_DESCRIPTION("MachZ ZF-Logic Watchdog driver"); -MODULE_LICENSE("GPL"); - -static bool nowayout = WATCHDOG_NOWAYOUT; -module_param(nowayout, bool, 0); -MODULE_PARM_DESC(nowayout, - "Watchdog cannot be stopped once started (default=" - __MODULE_STRING(WATCHDOG_NOWAYOUT) ")"); - -#define PFX "machzwd" - -static const struct watchdog_info zf_info = { - .options = WDIOF_KEEPALIVEPING | WDIOF_MAGICCLOSE, - .firmware_version = 1, - .identity = "ZF-Logic watchdog", -}; - - -/* - * action refers to action taken when watchdog resets - * 0 = GEN_RESET - * 1 = GEN_SMI - * 2 = GEN_NMI - * 3 = GEN_SCI - * defaults to GEN_RESET (0) - */ -static int action; -module_param(action, int, 0); -MODULE_PARM_DESC(action, "after watchdog resets, generate: " - "0 = RESET(*) 1 = SMI 2 = NMI 3 = SCI"); - -static void zf_ping(struct timer_list *unused); - -static int zf_action = GEN_RESET; -static unsigned long zf_is_open; -static char zf_expect_close; -static DEFINE_SPINLOCK(zf_port_lock); -static DEFINE_TIMER(zf_timer, zf_ping); -static unsigned long next_heartbeat; - - -/* timeout for user land heart beat (10 seconds) */ -#define ZF_USER_TIMEO (HZ*10) - -/* timeout for hardware watchdog (~500ms) */ -#define ZF_HW_TIMEO (HZ/2) - -/* number of ticks on WD#1 (driven by a 32KHz clock, 2s) */ -#define ZF_CTIMEOUT 0xffff - -#ifndef ZF_DEBUG -#define dprintk(format, args...) -#else -#define dprintk(format, args...) \ - pr_debug(":%s:%d: " format, __func__, __LINE__ , ## args) -#endif - - -static inline void zf_set_status(unsigned char new) -{ - zf_writeb(STATUS, new); -} - - -/* CONTROL register functions */ - -static inline unsigned short zf_get_control(void) -{ - return zf_readw(CONTROL); -} - -static inline void zf_set_control(unsigned short new) -{ - zf_writew(CONTROL, new); -} - - -/* WD#? counter functions */ -/* - * Just set counter value - */ - -static inline void zf_set_timer(unsigned short new, unsigned char n) -{ - switch (n) { - case WD1: - zf_writew(COUNTER_1, new); - fallthrough; - case WD2: - zf_writeb(COUNTER_2, new > 0xff ? 0xff : new); - fallthrough; - default: - return; - } -} - -/* - * stop hardware timer - */ -static void zf_timer_off(void) -{ - unsigned int ctrl_reg = 0; - unsigned long flags; - - /* stop internal ping */ - timer_delete_sync(&zf_timer); - - spin_lock_irqsave(&zf_port_lock, flags); - /* stop watchdog timer */ - ctrl_reg = zf_get_control(); - ctrl_reg |= (ENABLE_WD1|ENABLE_WD2); /* disable wd1 and wd2 */ - ctrl_reg &= ~(ENABLE_WD1|ENABLE_WD2); - zf_set_control(ctrl_reg); - spin_unlock_irqrestore(&zf_port_lock, flags); - - pr_info("Watchdog timer is now disabled\n"); -} - - -/* - * start hardware timer - */ -static void zf_timer_on(void) -{ - unsigned int ctrl_reg = 0; - unsigned long flags; - - spin_lock_irqsave(&zf_port_lock, flags); - - zf_writeb(PULSE_LEN, 0xff); - - zf_set_timer(ZF_CTIMEOUT, WD1); - - /* user land ping */ - next_heartbeat = jiffies + ZF_USER_TIMEO; - - /* start the timer for internal ping */ - mod_timer(&zf_timer, jiffies + ZF_HW_TIMEO); - - /* start watchdog timer */ - ctrl_reg = zf_get_control(); - ctrl_reg |= (ENABLE_WD1|zf_action); - zf_set_control(ctrl_reg); - spin_unlock_irqrestore(&zf_port_lock, flags); - - pr_info("Watchdog timer is now enabled\n"); -} - - -static void zf_ping(struct timer_list *unused) -{ - unsigned int ctrl_reg = 0; - unsigned long flags; - - zf_writeb(COUNTER_2, 0xff); - - if (time_before(jiffies, next_heartbeat)) { - dprintk("time_before: %ld\n", next_heartbeat - jiffies); - /* - * reset event is activated by transition from 0 to 1 on - * RESET_WD1 bit and we assume that it is already zero... - */ - - spin_lock_irqsave(&zf_port_lock, flags); - ctrl_reg = zf_get_control(); - ctrl_reg |= RESET_WD1; - zf_set_control(ctrl_reg); - - /* ...and nothing changes until here */ - ctrl_reg &= ~(RESET_WD1); - zf_set_control(ctrl_reg); - spin_unlock_irqrestore(&zf_port_lock, flags); - - mod_timer(&zf_timer, jiffies + ZF_HW_TIMEO); - } else - pr_crit("I will reset your machine\n"); -} - -static ssize_t zf_write(struct file *file, const char __user *buf, size_t count, - loff_t *ppos) -{ - /* See if we got the magic character */ - if (count) { - /* - * no need to check for close confirmation - * no way to disable watchdog ;) - */ - if (!nowayout) { - size_t ofs; - /* - * note: just in case someone wrote the magic character - * five months ago... - */ - zf_expect_close = 0; - - /* now scan */ - for (ofs = 0; ofs != count; ofs++) { - char c; - if (get_user(c, buf + ofs)) - return -EFAULT; - if (c == 'V') { - zf_expect_close = 42; - dprintk("zf_expect_close = 42\n"); - } - } - } - - /* - * Well, anyhow someone wrote to us, - * we should return that favour - */ - next_heartbeat = jiffies + ZF_USER_TIMEO; - dprintk("user ping at %ld\n", jiffies); - } - return count; -} - -static long zf_ioctl(struct file *file, unsigned int cmd, unsigned long arg) -{ - void __user *argp = (void __user *)arg; - int __user *p = argp; - switch (cmd) { - case WDIOC_GETSUPPORT: - if (copy_to_user(argp, &zf_info, sizeof(zf_info))) - return -EFAULT; - break; - case WDIOC_GETSTATUS: - case WDIOC_GETBOOTSTATUS: - return put_user(0, p); - case WDIOC_KEEPALIVE: - zf_ping(NULL); - break; - default: - return -ENOTTY; - } - return 0; -} - -static int zf_open(struct inode *inode, struct file *file) -{ - if (test_and_set_bit(0, &zf_is_open)) - return -EBUSY; - if (nowayout) - __module_get(THIS_MODULE); - zf_timer_on(); - return stream_open(inode, file); -} - -static int zf_close(struct inode *inode, struct file *file) -{ - if (zf_expect_close == 42) - zf_timer_off(); - else { - timer_delete(&zf_timer); - pr_err("device file closed unexpectedly. Will not stop the WDT!\n"); - } - clear_bit(0, &zf_is_open); - zf_expect_close = 0; - return 0; -} - -/* - * Notifier for system down - */ - -static int zf_notify_sys(struct notifier_block *this, unsigned long code, - void *unused) -{ - if (code == SYS_DOWN || code == SYS_HALT) - zf_timer_off(); - return NOTIFY_DONE; -} - -static const struct file_operations zf_fops = { - .owner = THIS_MODULE, - .write = zf_write, - .unlocked_ioctl = zf_ioctl, - .compat_ioctl = compat_ptr_ioctl, - .open = zf_open, - .release = zf_close, -}; - -static struct miscdevice zf_miscdev = { - .minor = WATCHDOG_MINOR, - .name = "watchdog", - .fops = &zf_fops, -}; - - -/* - * The device needs to learn about soft shutdowns in order to - * turn the timebomb registers off. - */ -static struct notifier_block zf_notifier = { - .notifier_call = zf_notify_sys, -}; - -static void __init zf_show_action(int act) -{ - static const char * const str[] = { "RESET", "SMI", "NMI", "SCI" }; - - pr_info("Watchdog using action = %s\n", str[act]); -} - -static int __init zf_init(void) -{ - int ret; - - pr_info("MachZ ZF-Logic Watchdog driver initializing\n"); - - ret = zf_get_ZFL_version(); - if (!ret || ret == 0xffff) { - pr_warn("no ZF-Logic found\n"); - return -ENODEV; - } - - if (action <= 3 && action >= 0) - zf_action = zf_action >> action; - else - action = 0; - - zf_show_action(action); - - if (!request_region(ZF_IOBASE, 3, "MachZ ZFL WDT")) { - pr_err("cannot reserve I/O ports at %d\n", ZF_IOBASE); - ret = -EBUSY; - goto no_region; - } - - ret = register_reboot_notifier(&zf_notifier); - if (ret) { - pr_err("can't register reboot notifier (err=%d)\n", ret); - goto no_reboot; - } - - ret = misc_register(&zf_miscdev); - if (ret) { - pr_err("can't misc_register on minor=%d\n", WATCHDOG_MINOR); - goto no_misc; - } - - zf_set_status(0); - zf_set_control(0); - - return 0; - -no_misc: - unregister_reboot_notifier(&zf_notifier); -no_reboot: - release_region(ZF_IOBASE, 3); -no_region: - return ret; -} - - -static void __exit zf_exit(void) -{ - zf_timer_off(); - - misc_deregister(&zf_miscdev); - unregister_reboot_notifier(&zf_notifier); - release_region(ZF_IOBASE, 3); -} - -module_init(zf_init); -module_exit(zf_exit); From 3b3a80dacd6e730196b45cae142970aa46c87c50 Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Wed, 22 Apr 2026 10:27:37 +0200 Subject: [PATCH 02/58] watchdog: qcom: Unify user-visible "Qualcomm" name Various names for Qualcomm as a company are used in user-visible config options: QCOM, Qualcomm and Qualcomm Technologies. Switch to unified "Qualcomm" so it will be easier for users to identify the options when for example running menuconfig. Signed-off-by: Krzysztof Kozlowski Reviewed-by: Guenter Roeck Link: https://lore.kernel.org/r/20260422082736.81981-2-krzysztof.kozlowski@oss.qualcomm.com Signed-off-by: Guenter Roeck --- drivers/watchdog/Kconfig | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/watchdog/Kconfig b/drivers/watchdog/Kconfig index 97e320721cff..e19872c0ae61 100644 --- a/drivers/watchdog/Kconfig +++ b/drivers/watchdog/Kconfig @@ -882,7 +882,7 @@ config TEGRA_WATCHDOG module will be called tegra_wdt. config QCOM_WDT - tristate "QCOM watchdog" + tristate "Qualcomm watchdog" depends on HAS_IOMEM depends on ARCH_QCOM || COMPILE_TEST select WATCHDOG_CORE @@ -1093,7 +1093,7 @@ config SPRD_WATCHDOG by Spreadtrum system. config PM8916_WATCHDOG - tristate "QCOM PM8916 pmic watchdog" + tristate "Qualcomm PM8916 pmic watchdog" depends on OF && MFD_SPMI_PMIC select WATCHDOG_CORE help From ea333d1991e449b13a3b28812c4493d376492012 Mon Sep 17 00:00:00 2001 From: Kathiravan Thirumoorthy Date: Wed, 8 Apr 2026 14:54:37 +0530 Subject: [PATCH 03/58] dt-bindings: watchdog: qcom-wdt: Document sram property Document the "sram" property for the watchdog device on Qualcomm IPQ platforms. Use this property to extract the restart reason from IMEM, which is updated by XBL. Populate the watchdog's bootstatus sysFS entry with this information, when the system reboots due to a watchdog timeout. Describe this property for the IPQ5424 watchdog device and extend support to other targets subsequently. Reviewed-by: Rob Herring (Arm) Signed-off-by: Kathiravan Thirumoorthy Link: https://lore.kernel.org/r/20260408-wdt_reset_reason-v10-1-caf66786329f@oss.qualcomm.com Signed-off-by: Guenter Roeck --- Documentation/devicetree/bindings/watchdog/qcom-wdt.yaml | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/Documentation/devicetree/bindings/watchdog/qcom-wdt.yaml b/Documentation/devicetree/bindings/watchdog/qcom-wdt.yaml index 9f861045b71e..3ead00da3cd6 100644 --- a/Documentation/devicetree/bindings/watchdog/qcom-wdt.yaml +++ b/Documentation/devicetree/bindings/watchdog/qcom-wdt.yaml @@ -84,6 +84,12 @@ properties: minItems: 1 maxItems: 5 + sram: + maxItems: 1 + description: + A reference to an region residing in IMEM(on-chip SRAM), which contains + the system restart reason value populated by the bootloader. + required: - compatible - reg From 158dd300b07b0d39e9fb791a4ad1bc68c8588530 Mon Sep 17 00:00:00 2001 From: Kathiravan Thirumoorthy Date: Wed, 8 Apr 2026 14:54:38 +0530 Subject: [PATCH 04/58] watchdog: qcom: add support to get the bootstatus from IMEM When the system boots up after a watchdog reset, the EXPIRED_STATUS bit in the WDT_STS register is cleared. To identify if the system was restarted due to WDT expiry, XBL update the information in the IMEM region. Update the driver to read the restart reason from IMEM and populate the bootstatus accordingly. With the CONFIG_WATCHDOG_SYSFS enabled, user can extract the information as below: cat /sys/devices/platform/soc@0/f410000.watchdog/watchdog/watchdog0/bootstatus 32 For backward compatibility, keep the EXPIRED_STATUS bit check. Add a new function qcom_wdt_get_bootstatus() to read the restart reason from IMEM. Reviewed-by: Konrad Dybcio Signed-off-by: Kathiravan Thirumoorthy Link: https://lore.kernel.org/r/20260408-wdt_reset_reason-v10-2-caf66786329f@oss.qualcomm.com Signed-off-by: Guenter Roeck --- drivers/watchdog/qcom-wdt.c | 42 +++++++++++++++++++++++++++++++++++-- 1 file changed, 40 insertions(+), 2 deletions(-) diff --git a/drivers/watchdog/qcom-wdt.c b/drivers/watchdog/qcom-wdt.c index dfaac5995c84..49bd04841f0c 100644 --- a/drivers/watchdog/qcom-wdt.c +++ b/drivers/watchdog/qcom-wdt.c @@ -9,6 +9,7 @@ #include #include #include +#include #include #include @@ -42,6 +43,7 @@ struct qcom_wdt_match_data { const u32 *offset; bool pretimeout; u32 max_tick_count; + u32 wdt_reason_val; }; struct qcom_wdt { @@ -185,6 +187,7 @@ static const struct qcom_wdt_match_data match_data_ipq5424 = { .offset = reg_offset_data_kpss, .pretimeout = true, .max_tick_count = 0xFFFFFU, + .wdt_reason_val = 5, }; static const struct qcom_wdt_match_data match_data_kpss = { @@ -193,6 +196,40 @@ static const struct qcom_wdt_match_data match_data_kpss = { .max_tick_count = 0xFFFFFU, }; +static int qcom_wdt_get_bootstatus(struct device *dev, struct qcom_wdt *wdt, + u32 val) +{ + struct device_node *imem; + struct resource res; + void __iomem *addr; + int ret; + + imem = of_parse_phandle(dev->of_node, "sram", 0); + if (!imem) { + /* Read the EXPIRED_STATUS bit as a fallback */ + if (readl(wdt_addr(wdt, WDT_STS)) & 1) + wdt->wdd.bootstatus = WDIOF_CARDRESET; + + return 0; + } + + ret = of_address_to_resource(imem, 0, &res); + of_node_put(imem); + if (ret) + return ret; + + addr = ioremap(res.start, resource_size(&res)); + if (!addr) + return -ENOMEM; + + if (readl(addr) == val) + wdt->wdd.bootstatus = WDIOF_CARDRESET; + + iounmap(addr); + + return 0; +} + static int qcom_wdt_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; @@ -273,8 +310,9 @@ static int qcom_wdt_probe(struct platform_device *pdev) wdt->wdd.parent = dev; wdt->layout = data->offset; - if (readl(wdt_addr(wdt, WDT_STS)) & 1) - wdt->wdd.bootstatus = WDIOF_CARDRESET; + ret = qcom_wdt_get_bootstatus(dev, wdt, data->wdt_reason_val); + if (ret) + dev_err(dev, "failed to get the bootstatus, %d\n", ret); /* * If 'timeout-sec' unspecified in devicetree, assume a 30 second From 066f6001fb58258269480547a920fc2812f37760 Mon Sep 17 00:00:00 2001 From: Mukesh Ojha Date: Wed, 1 Apr 2026 18:14:42 +0530 Subject: [PATCH 05/58] dt-bindings: watchdog: qcom-wdt: Document Hawi watchdog compatible Add devicetree binding for watchdog present on Qualcomm Hawi SoC. Signed-off-by: Mukesh Ojha Reviewed-by: Krzysztof Kozlowski Link: https://lore.kernel.org/r/20260401124442.591803-1-mukesh.ojha@oss.qualcomm.com Signed-off-by: Guenter Roeck --- Documentation/devicetree/bindings/watchdog/qcom-wdt.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/Documentation/devicetree/bindings/watchdog/qcom-wdt.yaml b/Documentation/devicetree/bindings/watchdog/qcom-wdt.yaml index 3ead00da3cd6..1d5d01c2547a 100644 --- a/Documentation/devicetree/bindings/watchdog/qcom-wdt.yaml +++ b/Documentation/devicetree/bindings/watchdog/qcom-wdt.yaml @@ -18,6 +18,7 @@ properties: - items: - enum: - qcom,apss-wdt-glymur + - qcom,apss-wdt-hawi - qcom,kpss-wdt-ipq4019 - qcom,apss-wdt-ipq5018 - qcom,apss-wdt-ipq5332 From 4f675f036cd5e9cfbe6df5f24f8338158af96a4b Mon Sep 17 00:00:00 2001 From: Yingjie Gao Date: Thu, 2 Apr 2026 15:16:17 +0800 Subject: [PATCH 06/58] watchdog: sp5100_tco: Use EFCH MMIO for newer Hygon FCH Commit 009637de1f65 ("watchdog: sp5100_tco: support Hygon FCH/SCH (Server Controller Hub)") added Hygon vendor matching to the efch layout selection, but newer Hygon 0x790b SMBus devices still need the efch_mmio path. The efch_mmio path enables EFCH_PM_DECODEEN_WDT_TMREN before probing the watchdog MMIO block. If firmware leaves that bit clear and the driver picks the legacy efch path instead, probe falls back to the alternate window and fails with "Watchdog hardware is disabled". Select efch_mmio for Hygon 0x790b devices with revision 0x51 or later, matching the equivalent AMD behavior and allowing the watchdog to initialize on those systems. Fixes: 009637de1f65 ("watchdog: sp5100_tco: support Hygon FCH/SCH (Server Controller Hub)") Signed-off-by: Yingjie Gao Reviewed-by: Guenter Roeck Link: https://lore.kernel.org/r/20260402071617.634563-2-gaoyingjie@uniontech.com Signed-off-by: Guenter Roeck --- drivers/watchdog/sp5100_tco.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/watchdog/sp5100_tco.c b/drivers/watchdog/sp5100_tco.c index 2bd3dc25cb03..7e99c3b1f367 100644 --- a/drivers/watchdog/sp5100_tco.c +++ b/drivers/watchdog/sp5100_tco.c @@ -92,7 +92,8 @@ static enum tco_reg_layout tco_reg_layout(struct pci_dev *dev) dev->device == PCI_DEVICE_ID_ATI_SBX00_SMBUS && dev->revision < 0x40) { return sp5100; - } else if (dev->vendor == PCI_VENDOR_ID_AMD && + } else if ((dev->vendor == PCI_VENDOR_ID_AMD || + dev->vendor == PCI_VENDOR_ID_HYGON) && sp5100_tco_pci->device == PCI_DEVICE_ID_AMD_KERNCZ_SMBUS && sp5100_tco_pci->revision >= AMD_ZEN_SMBUS_PCI_REV) { return efch_mmio; From 07d690db9fc741649f98506e492d5c10d1060b80 Mon Sep 17 00:00:00 2001 From: "Herve Codina (Schneider Electric)" Date: Tue, 24 Mar 2026 12:48:45 +0100 Subject: [PATCH 07/58] watchdog: rzn1: Use dev_err_probe() In the probe() function the following pattern is present several times: if (err) { dev_err(dev, ...); return err; } Replace them by dev_err_probe() calls. Signed-off-by: Herve Codina (Schneider Electric) Reviewed-by: Wolfram Sang Tested-by: Wolfram Sang Reviewed-by: Guenter Roeck Link: https://lore.kernel.org/r/20260324114849.242755-3-herve.codina@bootlin.com Signed-off-by: Guenter Roeck --- drivers/watchdog/rzn1_wdt.c | 18 ++++++------------ 1 file changed, 6 insertions(+), 12 deletions(-) diff --git a/drivers/watchdog/rzn1_wdt.c b/drivers/watchdog/rzn1_wdt.c index 96fd04fbc2a2..0b57540692f9 100644 --- a/drivers/watchdog/rzn1_wdt.c +++ b/drivers/watchdog/rzn1_wdt.c @@ -122,22 +122,16 @@ static int rzn1_wdt_probe(struct platform_device *pdev) ret = devm_request_irq(dev, irq, rzn1_wdt_irq, 0, np->name, wdt); - if (ret) { - dev_err(dev, "failed to request irq %d\n", irq); - return ret; - } + if (ret) + return dev_err_probe(dev, ret, "failed to request irq %d\n", irq); clk = devm_clk_get_enabled(dev, NULL); - if (IS_ERR(clk)) { - dev_err(dev, "failed to get the clock\n"); - return PTR_ERR(clk); - } + if (IS_ERR(clk)) + return dev_err_probe(dev, PTR_ERR(clk), "failed to get the clock\n"); clk_rate = clk_get_rate(clk); - if (!clk_rate) { - dev_err(dev, "failed to get the clock rate\n"); - return -EINVAL; - } + if (!clk_rate) + return dev_err_probe(dev, -EINVAL, "failed to get the clock rate\n"); wdt->clk_rate_khz = clk_rate / 1000; wdt->wdtdev.info = &rzn1_wdt_info; From cde09b031da871bc88ab693cf5ac503574a06639 Mon Sep 17 00:00:00 2001 From: "Herve Codina (Schneider Electric)" Date: Tue, 24 Mar 2026 12:48:44 +0100 Subject: [PATCH 08/58] watchdog: rzn1: Fix reverse xmas tree declaration Variables declared in probe() don't follow the reverse xmas tree convention. Fix the declaration in order to follow the convention. Signed-off-by: Herve Codina (Schneider Electric) Reviewed-by: Wolfram Sang Tested-by: Wolfram Sang Reviewed-by: Guenter Roeck Link: https://lore.kernel.org/r/20260324114849.242755-2-herve.codina@bootlin.com Signed-off-by: Guenter Roeck --- drivers/watchdog/rzn1_wdt.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/watchdog/rzn1_wdt.c b/drivers/watchdog/rzn1_wdt.c index 0b57540692f9..48d5afef62a5 100644 --- a/drivers/watchdog/rzn1_wdt.c +++ b/drivers/watchdog/rzn1_wdt.c @@ -101,10 +101,10 @@ static const struct watchdog_ops rzn1_wdt_ops = { static int rzn1_wdt_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; - struct rzn1_watchdog *wdt; struct device_node *np = dev->of_node; - struct clk *clk; + struct rzn1_watchdog *wdt; unsigned long clk_rate; + struct clk *clk; int ret; int irq; From 2963bb3d5acf9f8478ed5a8b07ff8b8a211cc25d Mon Sep 17 00:00:00 2001 From: Jose Javier Rodriguez Barbarin Date: Mon, 23 Mar 2026 12:24:13 +0100 Subject: [PATCH 09/58] watchdog: menz069_wdt: drop unneeded MODULE_ALIAS Since commit 1f4ea4838b13 ("mcb: Add missing modpost build support") the MODULE_ALIAS() is redundant as the module alias is now automatically generated from the MODULE_DEVICE_TABLE(). Remove the explicit alias. No functional change intended. Reviewed-by: Jorge Sanjuan Garcia Signed-off-by: Jose Javier Rodriguez Barbarin Reviewed-by: Guenter Roeck Link: https://lore.kernel.org/r/20260323112413.21923-2-dev-josejavier.rodriguez@duagon.com Signed-off-by: Guenter Roeck --- drivers/watchdog/menz69_wdt.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/watchdog/menz69_wdt.c b/drivers/watchdog/menz69_wdt.c index 6e5e4e5c0b56..3fe23451135d 100644 --- a/drivers/watchdog/menz69_wdt.c +++ b/drivers/watchdog/menz69_wdt.c @@ -163,5 +163,4 @@ module_mcb_driver(men_z069_driver); MODULE_AUTHOR("Johannes Thumshirn "); MODULE_DESCRIPTION("Watchdog driver for the MEN z069 IP-Core"); MODULE_LICENSE("GPL v2"); -MODULE_ALIAS("mcb:16z069"); MODULE_IMPORT_NS("MCB"); From d651a264536b30b423bd6478fc1a72eafb3e19e6 Mon Sep 17 00:00:00 2001 From: Eduard Bostina Date: Mon, 23 Mar 2026 19:59:44 +0200 Subject: [PATCH 10/58] dt-bindings: watchdog: Convert TS-4800 to DT schema Convert the Technologic Systems TS-4800 watchdog timer bindings to DT schema. Signed-off-by: Eduard Bostina Reviewed-by: Krzysztof Kozlowski Reviewed-by: Guenter Roeck Link: https://lore.kernel.org/r/20260323175948.302441-2-egbostina@gmail.com Signed-off-by: Guenter Roeck --- .../watchdog/technologic,ts4800-wdt.yaml | 40 +++++++++++++++++++ .../bindings/watchdog/ts4800-wdt.txt | 25 ------------ 2 files changed, 40 insertions(+), 25 deletions(-) create mode 100644 Documentation/devicetree/bindings/watchdog/technologic,ts4800-wdt.yaml delete mode 100644 Documentation/devicetree/bindings/watchdog/ts4800-wdt.txt diff --git a/Documentation/devicetree/bindings/watchdog/technologic,ts4800-wdt.yaml b/Documentation/devicetree/bindings/watchdog/technologic,ts4800-wdt.yaml new file mode 100644 index 000000000000..5c2541ac60cf --- /dev/null +++ b/Documentation/devicetree/bindings/watchdog/technologic,ts4800-wdt.yaml @@ -0,0 +1,40 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/watchdog/technologic,ts4800-wdt.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Technologic Systems TS-4800 Watchdog + +maintainers: + - Eduard Bostina + +properties: + compatible: + const: technologic,ts4800-wdt + + syscon: + $ref: /schemas/types.yaml#/definitions/phandle-array + items: + - items: + - description: Phandle to the FPGA's syscon + - description: Offset to the watchdog register + description: Phandle / integers array that points to the syscon node which + describes the FPGA's syscon registers. + +required: + - compatible + - syscon + +allOf: + - $ref: watchdog.yaml# + +unevaluatedProperties: false + +examples: + - | + watchdog { + compatible = "technologic,ts4800-wdt"; + syscon = <&syscon 0xe>; + timeout-sec = <10>; + }; diff --git a/Documentation/devicetree/bindings/watchdog/ts4800-wdt.txt b/Documentation/devicetree/bindings/watchdog/ts4800-wdt.txt deleted file mode 100644 index 8f6caad4258d..000000000000 --- a/Documentation/devicetree/bindings/watchdog/ts4800-wdt.txt +++ /dev/null @@ -1,25 +0,0 @@ -Technologic Systems Watchdog - -Required properties: -- compatible: must be "technologic,ts4800-wdt" -- syscon: phandle / integer array that points to the syscon node which - describes the FPGA's syscon registers. - - phandle to FPGA's syscon - - offset to the watchdog register - -Optional property: -- timeout-sec: contains the watchdog timeout in seconds. - -Example: - -syscon: syscon@b0010000 { - compatible = "syscon", "simple-mfd"; - reg = <0xb0010000 0x3d>; - reg-io-width = <2>; - - wdt@e { - compatible = "technologic,ts4800-wdt"; - syscon = <&syscon 0xe>; - timeout-sec = <10>; - }; -} From d8207d1e9a36b1a8a461581b2d4073ea28335603 Mon Sep 17 00:00:00 2001 From: Steve Wahl Date: Wed, 18 Mar 2026 10:50:05 -0500 Subject: [PATCH 11/58] watchdog/hpwdt: Refine hpwdt message for UV platform The watchdog hardware the hpwdt driver uses was added to the UV platform for UV_5, but the logging mentioned by this driver was not added to the BIOS. When the watchdog fires, the printed message had the administrators and developers looking for non-existent log files, and confused about whether a watchdog actually tripped. Change the message that prints on UV platforms so it doesn't send the user looking for non-existent logs. To aid in any future debugging, include all 8 bits of the NMISTAT register in the output, not just the two bits being used to determine this was "mynmi". And provide names to the bits in NMISTAT so the code is easier to understand. Signed-off-by: Steve Wahl Reviewed-by: Guenter Roeck Link: https://lore.kernel.org/r/20260318155005.90271-1-steve.wahl@hpe.com Signed-off-by: Guenter Roeck --- drivers/watchdog/hpwdt.c | 22 +++++++++++++++------- 1 file changed, 15 insertions(+), 7 deletions(-) diff --git a/drivers/watchdog/hpwdt.c b/drivers/watchdog/hpwdt.c index 2a848c35c14d..8af1fad2de0b 100644 --- a/drivers/watchdog/hpwdt.c +++ b/drivers/watchdog/hpwdt.c @@ -23,6 +23,7 @@ #include #ifdef CONFIG_HPWDT_NMI_DECODING #include +#include #endif #include @@ -159,24 +160,31 @@ static int hpwdt_set_pretimeout(struct watchdog_device *wdd, unsigned int req) return 0; } -static int hpwdt_my_nmi(void) -{ - return ioread8(hpwdt_nmistat) & 0x6; -} +#define NMISTAT_EASR BIT(0) +#define NMISTAT_EWDOG BIT(1) +#define NMISTAT_RTRAP BIT(2) +#define NMISTAT_DBELL BIT(3) +#define NMISTAT_EMSWDG BIT(4) +#define NMISTAT_GPI BIT(5) +#define NMISTAT_NMIOUT BIT(7) /* * NMI Handler */ static int hpwdt_pretimeout(unsigned int ulReason, struct pt_regs *regs) { - unsigned int mynmi = hpwdt_my_nmi(); - static char panic_msg[] = + u8 nmistat = ioread8(hpwdt_nmistat); + bool mynmi = (nmistat & (NMISTAT_EWDOG | NMISTAT_RTRAP)) != 0; + static char panic_msg_default[] = "00: An NMI occurred. Depending on your system the reason " "for the NMI is logged in any one of the following resources:\n" "1. Integrated Management Log (IML)\n" "2. OA Syslog\n" "3. OA Forward Progress Log\n" "4. iLO Event Log"; + static char panic_msg_uv[] = + "00: A watchdog NMI occurred."; + char *panic_msg = is_uv_system() ? panic_msg_uv : panic_msg_default; if (ulReason == NMI_UNKNOWN && !mynmi) return NMI_DONE; @@ -190,7 +198,7 @@ static int hpwdt_pretimeout(unsigned int ulReason, struct pt_regs *regs) hpwdt_ping_ticks(SECS_TO_TICKS(val)); } - hex_byte_pack(panic_msg, mynmi); + hex_byte_pack(panic_msg, nmistat); nmi_panic(regs, panic_msg); return NMI_HANDLED; From 5abb70c8f7582950e490823f4a94a3597ada9d83 Mon Sep 17 00:00:00 2001 From: Flavio Suligoi Date: Mon, 23 Mar 2026 13:52:04 +0100 Subject: [PATCH 12/58] watchdog: gpio_wdt: add ACPI support The gpio_wdt device driver uses the device property APIs, so it is firmware agnostic. For this reason we can now add the ACPI support in Kconfig. In this way it can be used seamlessly in ACPI and DT systems. For example, a typical GPIO watchdog device configuration, in an ACPI SSDT table, could be: Device (WDOG) { Name (_HID, "WDOG0001") Name (_CID, "PRP0001") Name (_UID, One) Name (_CRS, ResourceTemplate () { GpioIo (Exclusive, PullNone, 0, 0, IoRestrictionOutputOnly, "\\_SB.GPI0", 0, ResourceConsumer, ,) { 3 } }) Method (_STA, 0, NotSerialized) { Return (0x0F) } Name (_DSD, Package (2) { ToUUID ("daffd814-6eba-4d8c-8a91-bc9bbf4aa301"), Package (5) { Package () { "compatible", Package() { "linux,wdt-gpio" } }, Package () { "hw_algo", "toggle" }, Package () { "gpios", Package () { ^WDOG, 0, 0, 0 } }, Package () { "hw_margin_ms", 2000 }, Package () { "always-running", 1 }, }, }) } Signed-off-by: Flavio Suligoi Reviewed-by: Guenter Roeck Link: https://lore.kernel.org/r/20260323125204.164785-2-f.suligoi@asem.it Signed-off-by: Guenter Roeck --- drivers/watchdog/Kconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/watchdog/Kconfig b/drivers/watchdog/Kconfig index e19872c0ae61..8de43b614bb7 100644 --- a/drivers/watchdog/Kconfig +++ b/drivers/watchdog/Kconfig @@ -250,7 +250,7 @@ config DA9062_WATCHDOG config GPIO_WATCHDOG tristate "Watchdog device controlled through GPIO-line" - depends on OF_GPIO + depends on (ACPI && GPIOLIB) || OF_GPIO select WATCHDOG_CORE help If you say yes here you get support for watchdog device From 63077abe9ca8356b6c15b3dfda6d2761337badaf Mon Sep 17 00:00:00 2001 From: Shubham Chakraborty Date: Wed, 11 Mar 2026 17:46:01 +0530 Subject: [PATCH 13/58] docs: watchdog: explain watchdog API options Replace FIXME comment with proper documentation for WDIOS_DISABLECARD, WDIOS_ENABLECARD, and WDIOS_TEMPPANIC options. Signed-off-by: Shubham Chakraborty Reviewed-by: Guenter Roeck Link: https://lore.kernel.org/r/20260311121602.139967-1-chakrabortyshubham66@gmail.com Signed-off-by: Guenter Roeck --- Documentation/watchdog/watchdog-api.rst | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/Documentation/watchdog/watchdog-api.rst b/Documentation/watchdog/watchdog-api.rst index 78e228c272cf..984f7362d5fb 100644 --- a/Documentation/watchdog/watchdog-api.rst +++ b/Documentation/watchdog/watchdog-api.rst @@ -268,4 +268,13 @@ The following options are available: WDIOS_TEMPPANIC Kernel panic on temperature trip ================= ================================ -[FIXME -- better explanations] +The WDIOS_DISABLECARD option allows you to stop the watchdog timer via +software. Note that this will only work if the "nowayout" module parameter +(or CONFIG_WATCHDOG_NOWAYOUT) is not set. + +The WDIOS_ENABLECARD option turns on the watchdog timer. + +The WDIOS_TEMPPANIC option is used by some drivers (like the pcwd driver) +to trigger a system halt (typically a kernel panic or power off) when the +temperature trip point is reached. This ensures that the system is halted +immediately in case of an overheat condition. From ca316e145c35d59bf1c0b48ff4e38091e19b80a8 Mon Sep 17 00:00:00 2001 From: Hrishabh Rajput Date: Wed, 11 Mar 2026 11:16:31 +0530 Subject: [PATCH 14/58] watchdog: Add driver for Gunyah Watchdog On Qualcomm SoCs running under the Gunyah hypervisor, access to watchdog through MMIO is not available on all platforms. Depending on the hypervisor configuration, the watchdog is either fully emulated or exposed via ARM's SMC Calling Conventions (SMCCC) through the Vendor Specific Hypervisor Service Calls space. Add driver to support the SMC-based watchdog provided by the Gunyah Hypervisor. Device registration is done in the QCOM SCM driver after checks to restrict the watchdog initialization to Qualcomm devices running under Gunyah. Gunyah watchdog is not a hardware but an SMC-based vendor-specific hypervisor interface provided by the Gunyah hypervisor. The design involving QCOM SCM driver for registering the platform device has been devised to avoid adding non-hardware nodes to devicetree. Tested-by: Shivendra Pratap Tested-by: Neil Armstrong Tested-by: Mukesh Ojha Reviewed-by: Guenter Roeck Reviewed-by: Dmitry Baryshkov Signed-off-by: Hrishabh Rajput Signed-off-by: Pavankumar Kondeti Link: https://lore.kernel.org/r/20260311-gunyah_watchdog-v8-2-4c1c0689de22@oss.qualcomm.com Signed-off-by: Guenter Roeck --- MAINTAINERS | 1 + drivers/watchdog/Kconfig | 13 ++ drivers/watchdog/Makefile | 1 + drivers/watchdog/gunyah_wdt.c | 261 ++++++++++++++++++++++++++++++++++ 4 files changed, 276 insertions(+) create mode 100644 drivers/watchdog/gunyah_wdt.c diff --git a/MAINTAINERS b/MAINTAINERS index 2fb1c75afd16..f35e1769fa72 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -3337,6 +3337,7 @@ F: arch/arm64/boot/dts/qcom/ F: drivers/bus/qcom* F: drivers/firmware/qcom/ F: drivers/soc/qcom/ +F: drivers/watchdog/gunyah_wdt.c F: include/dt-bindings/arm/qcom,ids.h F: include/dt-bindings/firmware/qcom,scm.h F: include/dt-bindings/soc/qcom* diff --git a/drivers/watchdog/Kconfig b/drivers/watchdog/Kconfig index 8de43b614bb7..3397c82ded9c 100644 --- a/drivers/watchdog/Kconfig +++ b/drivers/watchdog/Kconfig @@ -2341,4 +2341,17 @@ config KEEMBAY_WATCHDOG To compile this driver as a module, choose M here: the module will be called keembay_wdt. +config GUNYAH_WATCHDOG + tristate "Qualcomm Gunyah Watchdog" + depends on ARCH_QCOM || COMPILE_TEST + depends on HAVE_ARM_SMCCC + select WATCHDOG_CORE + help + Say Y here to include support for watchdog timer provided by the + Gunyah hypervisor. The driver uses ARM SMC Calling Convention (SMCCC) + to interact with Gunyah Watchdog. + + To compile this driver as a module, choose M here: the + module will be called gunyah_wdt. + endif # WATCHDOG diff --git a/drivers/watchdog/Makefile b/drivers/watchdog/Makefile index 2961ca141c61..ff1dc6fd5867 100644 --- a/drivers/watchdog/Makefile +++ b/drivers/watchdog/Makefile @@ -103,6 +103,7 @@ obj-$(CONFIG_MSC313E_WATCHDOG) += msc313e_wdt.o obj-$(CONFIG_APPLE_WATCHDOG) += apple_wdt.o obj-$(CONFIG_SUNPLUS_WATCHDOG) += sunplus_wdt.o obj-$(CONFIG_MARVELL_GTI_WDT) += marvell_gti_wdt.o +obj-$(CONFIG_GUNYAH_WATCHDOG) += gunyah_wdt.o # X86 (i386 + ia64 + x86_64) Architecture obj-$(CONFIG_ACQUIRE_WDT) += acquirewdt.o diff --git a/drivers/watchdog/gunyah_wdt.c b/drivers/watchdog/gunyah_wdt.c new file mode 100644 index 000000000000..49dfef459e84 --- /dev/null +++ b/drivers/watchdog/gunyah_wdt.c @@ -0,0 +1,261 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#define GUNYAH_WDT_SMCCC_CALL_VAL(func_id) \ + ARM_SMCCC_CALL_VAL(ARM_SMCCC_FAST_CALL, ARM_SMCCC_SMC_32,\ + ARM_SMCCC_OWNER_VENDOR_HYP, func_id) + +/* SMCCC function IDs for watchdog operations */ +#define GUNYAH_WDT_CONTROL GUNYAH_WDT_SMCCC_CALL_VAL(0x0005) +#define GUNYAH_WDT_STATUS GUNYAH_WDT_SMCCC_CALL_VAL(0x0006) +#define GUNYAH_WDT_PING GUNYAH_WDT_SMCCC_CALL_VAL(0x0007) +#define GUNYAH_WDT_SET_TIME GUNYAH_WDT_SMCCC_CALL_VAL(0x0008) + +/* + * Control values for GUNYAH_WDT_CONTROL. + * Bit 0 is used to enable or disable the watchdog. If this bit is set, + * then the watchdog is enabled and vice versa. + * Bit 1 should always be set to 1 as this bit is reserved in Gunyah and + * it's expected to be 1. + */ +#define WDT_CTRL_ENABLE (BIT(1) | BIT(0)) +#define WDT_CTRL_DISABLE BIT(1) + +enum gunyah_error { + GUNYAH_ERROR_OK = 0, + GUNYAH_ERROR_UNIMPLEMENTED = -1, + GUNYAH_ERROR_ARG_INVAL = 1, +}; + +/** + * gunyah_error_remap() - Remap Gunyah hypervisor errors into a Linux error code + * @gunyah_error: Gunyah hypercall return value + */ +static inline int gunyah_error_remap(enum gunyah_error gunyah_error) +{ + switch (gunyah_error) { + case GUNYAH_ERROR_OK: + return 0; + case GUNYAH_ERROR_UNIMPLEMENTED: + return -EOPNOTSUPP; + default: + return -EINVAL; + } +} + +static int gunyah_wdt_call(unsigned long func_id, unsigned long arg1, + unsigned long arg2) +{ + struct arm_smccc_res res; + + arm_smccc_1_1_smc(func_id, arg1, arg2, &res); + return gunyah_error_remap(res.a0); +} + +static int gunyah_wdt_start(struct watchdog_device *wdd) +{ + unsigned int timeout_ms; + struct device *dev = wdd->parent; + int ret; + + ret = gunyah_wdt_call(GUNYAH_WDT_CONTROL, WDT_CTRL_DISABLE, 0); + if (ret && watchdog_active(wdd)) { + dev_err(dev, "%s: Failed to stop gunyah wdt %d\n", __func__, ret); + return ret; + } + + timeout_ms = wdd->timeout * 1000; + ret = gunyah_wdt_call(GUNYAH_WDT_SET_TIME, timeout_ms, timeout_ms); + if (ret) { + dev_err(dev, "%s: Failed to set timeout for gunyah wdt %d\n", + __func__, ret); + return ret; + } + + ret = gunyah_wdt_call(GUNYAH_WDT_CONTROL, WDT_CTRL_ENABLE, 0); + if (ret) + dev_err(dev, "%s: Failed to start gunyah wdt %d\n", __func__, ret); + + return ret; +} + +static int gunyah_wdt_stop(struct watchdog_device *wdd) +{ + return gunyah_wdt_call(GUNYAH_WDT_CONTROL, WDT_CTRL_DISABLE, 0); +} + +static int gunyah_wdt_ping(struct watchdog_device *wdd) +{ + return gunyah_wdt_call(GUNYAH_WDT_PING, 0, 0); +} + +static int gunyah_wdt_set_timeout(struct watchdog_device *wdd, + unsigned int timeout_sec) +{ + wdd->timeout = timeout_sec; + + if (watchdog_active(wdd)) + return gunyah_wdt_start(wdd); + + return 0; +} + +static int gunyah_wdt_get_time_since_last_ping(void) +{ + struct arm_smccc_res res; + + arm_smccc_1_1_smc(GUNYAH_WDT_STATUS, 0, 0, &res); + if (res.a0) + return gunyah_error_remap(res.a0); + + return res.a2 / 1000; +} + +static unsigned int gunyah_wdt_get_timeleft(struct watchdog_device *wdd) +{ + int seconds_since_last_ping; + + seconds_since_last_ping = gunyah_wdt_get_time_since_last_ping(); + if (seconds_since_last_ping < 0 || + seconds_since_last_ping > wdd->timeout) + return 0; + + return wdd->timeout - seconds_since_last_ping; +} + +static int gunyah_wdt_restart(struct watchdog_device *wdd, + unsigned long action, void *data) +{ + /* Set timeout to 1ms and send a ping */ + gunyah_wdt_call(GUNYAH_WDT_CONTROL, WDT_CTRL_DISABLE, 0); + gunyah_wdt_call(GUNYAH_WDT_SET_TIME, 1, 1); + gunyah_wdt_call(GUNYAH_WDT_CONTROL, WDT_CTRL_ENABLE, 0); + gunyah_wdt_call(GUNYAH_WDT_PING, 0, 0); + + /* Wait to make sure reset occurs */ + mdelay(100); + + return 0; +} + +static const struct watchdog_info gunyah_wdt_info = { + .identity = "Gunyah Watchdog", + .options = WDIOF_SETTIMEOUT + | WDIOF_KEEPALIVEPING + | WDIOF_MAGICCLOSE, +}; + +static const struct watchdog_ops gunyah_wdt_ops = { + .owner = THIS_MODULE, + .start = gunyah_wdt_start, + .stop = gunyah_wdt_stop, + .ping = gunyah_wdt_ping, + .set_timeout = gunyah_wdt_set_timeout, + .get_timeleft = gunyah_wdt_get_timeleft, + .restart = gunyah_wdt_restart +}; + +static int gunyah_wdt_probe(struct platform_device *pdev) +{ + struct watchdog_device *wdd; + struct device *dev = &pdev->dev; + int ret; + + ret = gunyah_wdt_call(GUNYAH_WDT_STATUS, 0, 0); + if (ret == -EOPNOTSUPP) + return -ENODEV; + + if (ret) + return dev_err_probe(dev, ret, "status check failed\n"); + + wdd = devm_kzalloc(dev, sizeof(*wdd), GFP_KERNEL); + if (!wdd) + return -ENOMEM; + + wdd->info = &gunyah_wdt_info; + wdd->ops = &gunyah_wdt_ops; + wdd->parent = dev; + + /* + * Although Gunyah expects 16-bit unsigned int values as timeout values + * in milliseconds, values above 0x8000 are reserved. This limits the + * max timeout value to 32 seconds. + */ + wdd->max_timeout = 32; /* seconds */ + wdd->min_timeout = 1; /* seconds */ + wdd->timeout = wdd->max_timeout; + + gunyah_wdt_stop(wdd); + platform_set_drvdata(pdev, wdd); + watchdog_set_restart_priority(wdd, 0); + + return devm_watchdog_register_device(dev, wdd); +} + +static void gunyah_wdt_remove(struct platform_device *pdev) +{ + struct watchdog_device *wdd = platform_get_drvdata(pdev); + + gunyah_wdt_stop(wdd); +} + +static int gunyah_wdt_suspend(struct device *dev) +{ + struct watchdog_device *wdd = dev_get_drvdata(dev); + + if (watchdog_active(wdd)) + gunyah_wdt_stop(wdd); + + return 0; +} + +static int gunyah_wdt_resume(struct device *dev) +{ + struct watchdog_device *wdd = dev_get_drvdata(dev); + + if (watchdog_active(wdd)) + gunyah_wdt_start(wdd); + + return 0; +} + +static DEFINE_SIMPLE_DEV_PM_OPS(gunyah_wdt_pm_ops, gunyah_wdt_suspend, gunyah_wdt_resume); + +/* + * Gunyah watchdog is a vendor-specific hypervisor interface provided by the + * Gunyah hypervisor. Using QCOM SCM driver to detect Gunyah watchdog SMCCC + * hypervisor service and register platform device when the service is available + * allows this driver to operate independently of the devicetree and avoids + * adding the non-hardware nodes to the devicetree. + */ +static const struct platform_device_id gunyah_wdt_id[] = { + { .name = "gunyah-wdt" }, + {} +}; +MODULE_DEVICE_TABLE(platform, gunyah_wdt_id); + +static struct platform_driver gunyah_wdt_driver = { + .driver = { + .name = "gunyah-wdt", + .pm = pm_sleep_ptr(&gunyah_wdt_pm_ops), + }, + .id_table = gunyah_wdt_id, + .probe = gunyah_wdt_probe, + .remove = gunyah_wdt_remove, +}; + +module_platform_driver(gunyah_wdt_driver); + +MODULE_DESCRIPTION("Gunyah Watchdog Driver"); +MODULE_LICENSE("GPL"); From e8bc610b14a99d24b0916635102cc52ba41def9b Mon Sep 17 00:00:00 2001 From: Balakrishnan Sambath Date: Mon, 2 Mar 2026 17:03:09 +0530 Subject: [PATCH 15/58] watchdog: sama5d4_wdt: Fix WDDIS detection on SAM9X60 and SAMA7G5 The driver hardcoded AT91_WDT_WDDIS (bit 15) in wdt_enabled and the probe initial state readout. SAM9X60 and SAMA7G5 use bit 12 (AT91_SAM9X60_WDDIS), causing incorrect WDDIS detection. Introduce a per-device wddis_mask field to select the correct WDDIS bit based on the compatible string. Fixes: 266da53c35fc ("watchdog: sama5d4: readout initial state") Co-developed-by: Andrei Simion Signed-off-by: Andrei Simion Signed-off-by: Balakrishnan Sambath Reviewed-by: Alexandre Belloni Link: https://lore.kernel.org/r/20260302113310.133989-2-balakrishnan.s@microchip.com Signed-off-by: Guenter Roeck --- drivers/watchdog/sama5d4_wdt.c | 48 +++++++++++++++------------------- 1 file changed, 21 insertions(+), 27 deletions(-) diff --git a/drivers/watchdog/sama5d4_wdt.c b/drivers/watchdog/sama5d4_wdt.c index 13e72918338a..704b786cc2ec 100644 --- a/drivers/watchdog/sama5d4_wdt.c +++ b/drivers/watchdog/sama5d4_wdt.c @@ -30,6 +30,7 @@ struct sama5d4_wdt { void __iomem *reg_base; u32 mr; u32 ir; + u32 wddis_mask; unsigned long last_ping; bool need_irq; bool sam9x60_support; @@ -48,7 +49,10 @@ MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default=" __MODULE_STRING(WATCHDOG_NOWAYOUT) ")"); -#define wdt_enabled (!(wdt->mr & AT91_WDT_WDDIS)) +static inline bool wdt_enabled(struct sama5d4_wdt *wdt) +{ + return !(wdt->mr & wdt->wddis_mask); +} #define wdt_read(wdt, field) \ readl_relaxed((wdt)->reg_base + (field)) @@ -81,12 +85,9 @@ static int sama5d4_wdt_start(struct watchdog_device *wdd) { struct sama5d4_wdt *wdt = watchdog_get_drvdata(wdd); - if (wdt->sam9x60_support) { + if (wdt->sam9x60_support) writel_relaxed(wdt->ir, wdt->reg_base + AT91_SAM9X60_IER); - wdt->mr &= ~AT91_SAM9X60_WDDIS; - } else { - wdt->mr &= ~AT91_WDT_WDDIS; - } + wdt->mr &= ~wdt->wddis_mask; wdt_write(wdt, AT91_WDT_MR, wdt->mr); return 0; @@ -96,12 +97,9 @@ static int sama5d4_wdt_stop(struct watchdog_device *wdd) { struct sama5d4_wdt *wdt = watchdog_get_drvdata(wdd); - if (wdt->sam9x60_support) { + if (wdt->sam9x60_support) writel_relaxed(wdt->ir, wdt->reg_base + AT91_SAM9X60_IDR); - wdt->mr |= AT91_SAM9X60_WDDIS; - } else { - wdt->mr |= AT91_WDT_WDDIS; - } + wdt->mr |= wdt->wddis_mask; wdt_write(wdt, AT91_WDT_MR, wdt->mr); return 0; @@ -117,7 +115,7 @@ static int sama5d4_wdt_ping(struct watchdog_device *wdd) } static int sama5d4_wdt_set_timeout(struct watchdog_device *wdd, - unsigned int timeout) + unsigned int timeout) { struct sama5d4_wdt *wdt = watchdog_get_drvdata(wdd); u32 value = WDT_SEC2TICKS(timeout); @@ -140,8 +138,8 @@ static int sama5d4_wdt_set_timeout(struct watchdog_device *wdd, * If the watchdog is enabled, then the timeout can be updated. Else, * wait that the user enables it. */ - if (wdt_enabled) - wdt_write(wdt, AT91_WDT_MR, wdt->mr & ~AT91_WDT_WDDIS); + if (wdt_enabled(wdt)) + wdt_write(wdt, AT91_WDT_MR, wdt->mr & ~wdt->wddis_mask); wdd->timeout = timeout; @@ -184,10 +182,7 @@ static int of_sama5d4_wdt_init(struct device_node *np, struct sama5d4_wdt *wdt) { const char *tmp; - if (wdt->sam9x60_support) - wdt->mr = AT91_SAM9X60_WDDIS; - else - wdt->mr = AT91_WDT_WDDIS; + wdt->mr = wdt->wddis_mask; if (!of_property_read_string(np, "atmel,watchdog-type", &tmp) && !strcmp(tmp, "software")) @@ -213,15 +208,11 @@ static int sama5d4_wdt_init(struct sama5d4_wdt *wdt) * If the watchdog is already running, we can safely update it. * Else, we have to disable it properly. */ - if (!wdt_enabled) { + if (!wdt_enabled(wdt)) { reg = wdt_read(wdt, AT91_WDT_MR); - if (wdt->sam9x60_support && (!(reg & AT91_SAM9X60_WDDIS))) + if (!(reg & wdt->wddis_mask)) wdt_write_nosleep(wdt, AT91_WDT_MR, - reg | AT91_SAM9X60_WDDIS); - else if (!wdt->sam9x60_support && - (!(reg & AT91_WDT_WDDIS))) - wdt_write_nosleep(wdt, AT91_WDT_MR, - reg | AT91_WDT_WDDIS); + reg | wdt->wddis_mask); } if (wdt->sam9x60_support) { @@ -273,6 +264,9 @@ static int sama5d4_wdt_probe(struct platform_device *pdev) of_device_is_compatible(dev->of_node, "microchip,sama7g5-wdt")) wdt->sam9x60_support = true; + wdt->wddis_mask = wdt->sam9x60_support ? AT91_SAM9X60_WDDIS + : AT91_WDT_WDDIS; + watchdog_set_drvdata(wdd, wdt); regs = devm_platform_ioremap_resource(pdev, 0); @@ -306,8 +300,8 @@ static int sama5d4_wdt_probe(struct platform_device *pdev) watchdog_init_timeout(wdd, wdt_timeout, dev); reg = wdt_read(wdt, AT91_WDT_MR); - if (!(reg & AT91_WDT_WDDIS)) { - wdt->mr &= ~AT91_WDT_WDDIS; + if (!(reg & wdt->wddis_mask)) { + wdt->mr &= ~wdt->wddis_mask; set_bit(WDOG_HW_RUNNING, &wdd->status); } From 882fc25d868fbd0d71751df4df49c4b55e3fa230 Mon Sep 17 00:00:00 2001 From: Fabio Estevam Date: Mon, 9 Mar 2026 21:20:40 -0300 Subject: [PATCH 16/58] dt-bindings: watchdog: rockchip: Add RV1103B compatible The RV1103B watchdog is compatible with the existing DesignWare Watchdog binding. Add the rockchip,rv1103b-wdt compatible string. Signed-off-by: Fabio Estevam Reviewed-by: Heiko Stuebner Acked-by: Krzysztof Kozlowski Reviewed-by: Guenter Roeck Link: https://lore.kernel.org/r/20260310002040.417424-1-festevam@gmail.com Signed-off-by: Guenter Roeck --- Documentation/devicetree/bindings/watchdog/snps,dw-wdt.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/Documentation/devicetree/bindings/watchdog/snps,dw-wdt.yaml b/Documentation/devicetree/bindings/watchdog/snps,dw-wdt.yaml index 609e98cdaaff..141c72909b15 100644 --- a/Documentation/devicetree/bindings/watchdog/snps,dw-wdt.yaml +++ b/Documentation/devicetree/bindings/watchdog/snps,dw-wdt.yaml @@ -33,6 +33,7 @@ properties: - rockchip,rk3568-wdt - rockchip,rk3576-wdt - rockchip,rk3588-wdt + - rockchip,rv1103b-wdt - rockchip,rv1108-wdt - rockchip,rv1126-wdt - const: snps,dw-wdt From f1ea97d64ba5c554b4e58d8b94a1440883ce9a70 Mon Sep 17 00:00:00 2001 From: Michael Walle Date: Mon, 2 Mar 2026 13:24:51 +0100 Subject: [PATCH 17/58] dt-bindings: watchdog: Drop SMARC-sAM67 support I was just informed that this product is discontinued (without being ever released to the market). Pull the plug and let's not waste any more maintainers time and revert commit 354f31e9d2a3 ("dt-bindings: watchdog: Add SMARC-sAM67 support"). Acked-by: Conor Dooley Signed-off-by: Michael Walle Reviewed-by: Guenter Roeck Link: https://lore.kernel.org/r/20260302122540.1377444-7-mwalle@kernel.org Signed-off-by: Guenter Roeck --- .../devicetree/bindings/watchdog/kontron,sl28cpld-wdt.yaml | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/Documentation/devicetree/bindings/watchdog/kontron,sl28cpld-wdt.yaml b/Documentation/devicetree/bindings/watchdog/kontron,sl28cpld-wdt.yaml index 0821ba0e84a3..872a8471ef65 100644 --- a/Documentation/devicetree/bindings/watchdog/kontron,sl28cpld-wdt.yaml +++ b/Documentation/devicetree/bindings/watchdog/kontron,sl28cpld-wdt.yaml @@ -18,12 +18,7 @@ allOf: properties: compatible: - oneOf: - - items: - - enum: - - kontron,sa67mcu-wdt - - const: kontron,sl28cpld-wdt - - const: kontron,sl28cpld-wdt + const: kontron,sl28cpld-wdt reg: maxItems: 1 From b5a8fc6b88f3d06ba20c0e056517fbeb9d251484 Mon Sep 17 00:00:00 2001 From: Fabrizio Castro Date: Tue, 3 Feb 2026 12:42:45 +0000 Subject: [PATCH 18/58] dt-bindings: watchdog: renesas,r9a09g057-wdt: Rework example When the bindings for the Renesas RZ/V2H(P) SoC were factored out IP WDT0 was selected for the example, however the HW user manual states that only IP WDT1 can be used by Linux. This commit is part of a series that removes WDT{0,2,3} support from the kernel, therefore the example from the bindings has lost its meaning. Update the example accordingly. Signed-off-by: Fabrizio Castro Acked-by: Conor Dooley Reviewed-by: Geert Uytterhoeven Link: https://lore.kernel.org/r/20260203124247.7320-2-fabrizio.castro.jz@renesas.com Signed-off-by: Guenter Roeck --- .../bindings/watchdog/renesas,r9a09g057-wdt.yaml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Documentation/devicetree/bindings/watchdog/renesas,r9a09g057-wdt.yaml b/Documentation/devicetree/bindings/watchdog/renesas,r9a09g057-wdt.yaml index 099200c4f136..975c5aa4d747 100644 --- a/Documentation/devicetree/bindings/watchdog/renesas,r9a09g057-wdt.yaml +++ b/Documentation/devicetree/bindings/watchdog/renesas,r9a09g057-wdt.yaml @@ -89,11 +89,11 @@ examples: - | #include - watchdog@11c00400 { + watchdog@14400000 { compatible = "renesas,r9a09g057-wdt"; - reg = <0x11c00400 0x400>; - clocks = <&cpg CPG_MOD 0x4b>, <&cpg CPG_MOD 0x4c>; + reg = <0x14400000 0x400>; + clocks = <&cpg CPG_MOD 0x4d>, <&cpg CPG_MOD 0x4e>; clock-names = "pclk", "oscclk"; - resets = <&cpg 0x75>; + resets = <&cpg 0x76>; power-domains = <&cpg>; }; From 587d5a3778e9fd25e327435b58b609487a692115 Mon Sep 17 00:00:00 2001 From: Biju Das Date: Fri, 6 Feb 2026 11:22:25 +0000 Subject: [PATCH 19/58] dt-bindings: watchdog: renesas,wdt: Document RZ/G3L support Document the support for the watchdog IP available on RZ/G3L SoC. The watchdog IP available on RZ/G3L SoC is identical to the one found on RZ/G2L SoC. Signed-off-by: Biju Das Acked-by: Rob Herring (Arm) Link: https://lore.kernel.org/r/20260206112227.233229-1-biju.das.jz@bp.renesas.com Signed-off-by: Guenter Roeck --- .../devicetree/bindings/watchdog/renesas,rzg2l-wdt.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/Documentation/devicetree/bindings/watchdog/renesas,rzg2l-wdt.yaml b/Documentation/devicetree/bindings/watchdog/renesas,rzg2l-wdt.yaml index a4d06c9c8b86..40b4fb26d9c5 100644 --- a/Documentation/devicetree/bindings/watchdog/renesas,rzg2l-wdt.yaml +++ b/Documentation/devicetree/bindings/watchdog/renesas,rzg2l-wdt.yaml @@ -18,6 +18,7 @@ properties: - renesas,r9a07g044-wdt # RZ/G2{L,LC} - renesas,r9a07g054-wdt # RZ/V2L - renesas,r9a08g045-wdt # RZ/G3S + - renesas,r9a08g046-wdt # RZ/G3L - const: renesas,rzg2l-wdt - items: From bdd918c4e4516bd2eadac712b2d2e6d29445bd2e Mon Sep 17 00:00:00 2001 From: Andrew Davis Date: Mon, 2 Mar 2026 12:08:53 -0600 Subject: [PATCH 20/58] watchdog: bcm2835_wdt: Switch to new sys-off handler API Kernel now supports chained power-off handlers. Use devm_register_sys_off_handler() that registers a power-off handler. Legacy pm_power_off() will be removed once all drivers and archs are converted to the new sys-off API. Signed-off-by: Andrew Davis Reviewed-by: Guenter Roeck Reviewed-by: Florian Fainelli Link: https://lore.kernel.org/r/20260302180853.224112-1-afd@ti.com Signed-off-by: Guenter Roeck --- drivers/watchdog/bcm2835_wdt.c | 28 +++++++++------------------- 1 file changed, 9 insertions(+), 19 deletions(-) diff --git a/drivers/watchdog/bcm2835_wdt.c b/drivers/watchdog/bcm2835_wdt.c index 9fcfee63905b..6fd8b1b8e386 100644 --- a/drivers/watchdog/bcm2835_wdt.c +++ b/drivers/watchdog/bcm2835_wdt.c @@ -19,6 +19,7 @@ #include #include #include +#include #define PM_RSTC 0x1c #define PM_RSTS 0x20 @@ -49,8 +50,6 @@ struct bcm2835_wdt { spinlock_t lock; }; -static struct bcm2835_wdt *bcm2835_power_off_wdt; - static unsigned int heartbeat; static bool nowayout = WATCHDOG_NOWAYOUT; @@ -150,9 +149,9 @@ static struct watchdog_device bcm2835_wdt_wdd = { * indicate to bootcode.bin not to reboot, then most of the chip will be * powered off. */ -static void bcm2835_power_off(void) +static int bcm2835_power_off(struct sys_off_data *data) { - struct bcm2835_wdt *wdt = bcm2835_power_off_wdt; + struct bcm2835_wdt *wdt = data->cb_data; u32 val; /* @@ -166,6 +165,8 @@ static void bcm2835_power_off(void) /* Continue with normal reset mechanism */ __bcm2835_restart(wdt); + + return NOTIFY_DONE; } static int bcm2835_wdt_probe(struct platform_device *pdev) @@ -206,28 +207,17 @@ static int bcm2835_wdt_probe(struct platform_device *pdev) if (err) return err; - if (of_device_is_system_power_controller(pdev->dev.parent->of_node)) { - if (!pm_power_off) { - pm_power_off = bcm2835_power_off; - bcm2835_power_off_wdt = wdt; - } else { - dev_info(dev, "Poweroff handler already present!\n"); - } - } + if (of_device_is_system_power_controller(pdev->dev.parent->of_node)) + devm_register_sys_off_handler(dev, SYS_OFF_MODE_POWER_OFF, + SYS_OFF_PRIO_DEFAULT, + bcm2835_power_off, wdt); dev_info(dev, "Broadcom BCM2835 watchdog timer"); return 0; } -static void bcm2835_wdt_remove(struct platform_device *pdev) -{ - if (pm_power_off == bcm2835_power_off) - pm_power_off = NULL; -} - static struct platform_driver bcm2835_wdt_driver = { .probe = bcm2835_wdt_probe, - .remove = bcm2835_wdt_remove, .driver = { .name = "bcm2835-wdt", }, From 96b3cfc3b8ad0524d12fed1e08bc5df3ff345f64 Mon Sep 17 00:00:00 2001 From: Felix Gu Date: Mon, 23 Feb 2026 19:59:20 +0800 Subject: [PATCH 21/58] watchdog: sprd_wdt: Remove redundant sprd_wdt_disable() on register failure The driver uses devm_add_action_or_reset() to register sprd_wdt_disable() as a managed cleanup action. When devm_watchdog_register_device() fails, the devm core will invoke the cleanup action automatically. The explicit sprd_wdt_disable() call in the error path is therefore redundant and results in adouble cleanup. Fixes: 78d9bfad2e89 ("watchdog: sprd_wdt: Convert to use device managed functions and other improvements") Signed-off-by: Felix Gu Reviewed-by: Guenter Roeck Reviewed-by: Baolin Wang Link: https://lore.kernel.org/r/20260223-sprd_wdt-v1-1-2e71f9a76ecb@gmail.com Signed-off-by: Guenter Roeck --- drivers/watchdog/sprd_wdt.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/drivers/watchdog/sprd_wdt.c b/drivers/watchdog/sprd_wdt.c index 4e689b6ff141..aacf04616fef 100644 --- a/drivers/watchdog/sprd_wdt.c +++ b/drivers/watchdog/sprd_wdt.c @@ -320,10 +320,9 @@ static int sprd_wdt_probe(struct platform_device *pdev) watchdog_init_timeout(&wdt->wdd, 0, dev); ret = devm_watchdog_register_device(dev, &wdt->wdd); - if (ret) { - sprd_wdt_disable(wdt); + if (ret) return ret; - } + platform_set_drvdata(pdev, wdt); return 0; From 79bbc356d537969332ddd0bf3b990438224e013b Mon Sep 17 00:00:00 2001 From: Brian Masney Date: Sun, 22 Feb 2026 18:24:16 -0500 Subject: [PATCH 22/58] watchdog: pic32-wdt: allow driver to be compiled on all architectures with COMPILE_TEST This driver currently only supports builds against a PIC32 target, or with COMPILE_TEST on MIPS. Now that commit 5aa5879eeebb ("watchdog: pic32-wdt: update include to use pic32.h from platform_data") is merged, it's possible to compile this driver on other architectures. To avoid future breakage of this driver in the future, let's update the Kconfig so that it can be built with COMPILE_TEST enabled on all architectures. Signed-off-by: Brian Masney Reviewed-by: Guenter Roeck Link: https://lore.kernel.org/r/20260222-watchdog-pic32-v1-1-a2538aa528d1@redhat.com Signed-off-by: Guenter Roeck --- drivers/watchdog/Kconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/watchdog/Kconfig b/drivers/watchdog/Kconfig index 3397c82ded9c..ae8c90fa194a 100644 --- a/drivers/watchdog/Kconfig +++ b/drivers/watchdog/Kconfig @@ -1998,7 +1998,7 @@ config MT7621_WDT config PIC32_WDT tristate "Microchip PIC32 hardware watchdog" select WATCHDOG_CORE - depends on MACH_PIC32 || (MIPS && COMPILE_TEST) + depends on MACH_PIC32 || COMPILE_TEST help Watchdog driver for the built in watchdog hardware in a PIC32. From 96bbd6217953d371617bf2131f0805ed7885a5d9 Mon Sep 17 00:00:00 2001 From: Brian Masney Date: Sun, 22 Feb 2026 18:24:17 -0500 Subject: [PATCH 23/58] watchdog: pic32-dmt: allow driver to be compiled on all architectures with COMPILE_TEST This driver currently only supports builds against a PIC32 target, or with COMPILE_TEST on MIPS. Now that commit 0f8a61ca78d6 ("watchdog: pic32-dmt: update include to use pic32.h from platform_data") is merged, it's possible to compile this driver on other architectures. To avoid future breakage of this driver in the future, let's update the Kconfig so that it can be built with COMPILE_TEST enabled on all architectures. Signed-off-by: Brian Masney Reviewed-by: Guenter Roeck Link: https://lore.kernel.org/r/20260222-watchdog-pic32-v1-2-a2538aa528d1@redhat.com Signed-off-by: Guenter Roeck --- drivers/watchdog/Kconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/watchdog/Kconfig b/drivers/watchdog/Kconfig index ae8c90fa194a..d58a0bd0bdeb 100644 --- a/drivers/watchdog/Kconfig +++ b/drivers/watchdog/Kconfig @@ -2011,7 +2011,7 @@ config PIC32_WDT config PIC32_DMT tristate "Microchip PIC32 Deadman Timer" select WATCHDOG_CORE - depends on MACH_PIC32 || (MIPS && COMPILE_TEST) + depends on MACH_PIC32 || COMPILE_TEST help Watchdog driver for PIC32 instruction fetch counting timer. This specific timer is typically be used in mission critical and safety From 4e9590d8d52ac3193d723f55bd0fbc6a18e10549 Mon Sep 17 00:00:00 2001 From: Judith Mendez Date: Fri, 6 Feb 2026 17:42:54 -0600 Subject: [PATCH 24/58] dt-bindings: watchdog: ti,rti-wdt: Add ti,am62l-rti-wdt compatible Add a new compatible ti,am62l-rti-wdt for AM62L SoC [0]. [0] https://www.ti.com/product/AM62L Signed-off-by: Judith Mendez Acked-by: Krzysztof Kozlowski Reviewed-by: Guenter Roeck Link: https://lore.kernel.org/r/20260206234255.380530-2-jm@ti.com Signed-off-by: Guenter Roeck --- Documentation/devicetree/bindings/watchdog/ti,rti-wdt.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/Documentation/devicetree/bindings/watchdog/ti,rti-wdt.yaml b/Documentation/devicetree/bindings/watchdog/ti,rti-wdt.yaml index 62ddc284a524..2966e5bfb6c0 100644 --- a/Documentation/devicetree/bindings/watchdog/ti,rti-wdt.yaml +++ b/Documentation/devicetree/bindings/watchdog/ti,rti-wdt.yaml @@ -23,6 +23,7 @@ allOf: properties: compatible: enum: + - ti,am62l-rti-wdt - ti,j7-rti-wdt reg: From aed1abf31ea3f13b8ac8aa0f6c1078a9bc7b9564 Mon Sep 17 00:00:00 2001 From: Judith Mendez Date: Fri, 6 Feb 2026 17:42:55 -0600 Subject: [PATCH 25/58] watchdog: rti_wdt: Add reaction control This configures the reaction between NMI and reset for WWD. On K3 SoCs other than AM62L SoC [0], watchdog reset output is routed to the ESM module which can subsequently route the signal to safety master or SoC reset. On AM62L, the watchdog reset output is routed to the SoC HW reset block. So, add a new compatible for AM62L to add SoC data and configure reaction to reset instead of NMI. [0] https://www.ti.com/product/AM62L Signed-off-by: Judith Mendez Reviewed-by: Guenter Roeck Link: https://lore.kernel.org/r/20260206234255.380530-3-jm@ti.com Signed-off-by: Guenter Roeck --- drivers/watchdog/rti_wdt.c | 39 ++++++++++++++++++++++++++++++++++---- 1 file changed, 35 insertions(+), 4 deletions(-) diff --git a/drivers/watchdog/rti_wdt.c b/drivers/watchdog/rti_wdt.c index be7d7db47591..c3c7715140ea 100644 --- a/drivers/watchdog/rti_wdt.c +++ b/drivers/watchdog/rti_wdt.c @@ -35,7 +35,8 @@ #define RTIWWDRXCTRL 0xa4 #define RTIWWDSIZECTRL 0xa8 -#define RTIWWDRX_NMI 0xa +#define RTIWWDRXN_RST 0x5 +#define RTIWWDRXN_NMI 0xa #define RTIWWDSIZE_50P 0x50 #define RTIWWDSIZE_25P 0x500 @@ -63,22 +64,29 @@ static int heartbeat; +struct rti_wdt_data { + bool nmi; +}; + /* * struct to hold data for each WDT device * @base - base io address of WD device * @freq - source clock frequency of WDT * @wdd - hold watchdog device as is in WDT core + * @nmi - Set if this WDT instance supports generating NMI */ struct rti_wdt_device { void __iomem *base; unsigned long freq; struct watchdog_device wdd; + bool nmi; }; static int rti_wdt_start(struct watchdog_device *wdd) { u32 timer_margin; struct rti_wdt_device *wdt = watchdog_get_drvdata(wdd); + u8 reaction; int ret; ret = pm_runtime_resume_and_get(wdd->parent); @@ -101,8 +109,13 @@ static int rti_wdt_start(struct watchdog_device *wdd) */ wdd->min_hw_heartbeat_ms = 520 * wdd->timeout + MAX_HW_ERROR; - /* Generate NMI when wdt expires */ - writel_relaxed(RTIWWDRX_NMI, wdt->base + RTIWWDRXCTRL); + /* When WDT expires, generate NMI or reset if NMI not supported */ + if (wdt->nmi) + reaction = RTIWWDRXN_NMI; + else + reaction = RTIWWDRXN_RST; + + writel_relaxed(reaction, wdt->base + RTIWWDRXCTRL); /* Open window size 50%; this is the largest window size available */ writel_relaxed(RTIWWDSIZE_50P, wdt->base + RTIWWDSIZECTRL); @@ -210,6 +223,7 @@ static int rti_wdt_probe(struct platform_device *pdev) { int ret = 0; struct device *dev = &pdev->dev; + const struct rti_wdt_data *data; struct watchdog_device *wdd; struct rti_wdt_device *wdt; struct clk *clk; @@ -254,6 +268,14 @@ static int rti_wdt_probe(struct platform_device *pdev) wdd->timeout = DEFAULT_HEARTBEAT; wdd->parent = dev; + data = device_get_match_data(dev); + if (!data) { + ret = -ENODEV; + goto err_iomap; + } + + wdt->nmi = data->nmi; + watchdog_set_drvdata(wdd, wdt); watchdog_set_nowayout(wdd, 1); watchdog_set_restart_priority(wdd, 128); @@ -361,8 +383,17 @@ static void rti_wdt_remove(struct platform_device *pdev) pm_runtime_disable(&pdev->dev); } +static const struct rti_wdt_data rti_wdt_j7_data = { + .nmi = true, +}; + +static const struct rti_wdt_data rti_wdt_am62l_data = { + .nmi = false, +}; + static const struct of_device_id rti_wdt_of_match[] = { - { .compatible = "ti,j7-rti-wdt", }, + { .compatible = "ti,j7-rti-wdt", .data = &rti_wdt_j7_data }, + { .compatible = "ti,am62l-rti-wdt", .data = &rti_wdt_am62l_data }, {}, }; MODULE_DEVICE_TABLE(of, rti_wdt_of_match); From 1f5b29add64e7c1500530079bbbe359cad28cb63 Mon Sep 17 00:00:00 2001 From: Ranjani Vaidyanathan Date: Fri, 6 Feb 2026 08:23:32 +0800 Subject: [PATCH 26/58] watchdog: imx7ulp_wdt: Keep WDOG running until A55 enters WFI on i.MX94 On i.MX94, watchdog sources clock from bus clock that will be always on during the lifecycle of Linux. There is a Low Power Clock Gating(LPCG) between the bus clock and watchdog, but the LPCG is not exported for software to control, it is hardware automatically controlled. When Cortex-A55 executes WFI during suspend flow, the LPCG will automatically gate off the clock to stop watchdog and resume clock when Cortex-A55 is woke up. So watchdog could always be alive to protect Linux, except Cortex-A platform WFI is executed in Linux suspend flow. Introduce a new hardware feature flag to indicate CPU low-power-mode auto clock gating support, and use it to avoid stopping the watchdog during suspend when LPCG can safely keep it running. Add i.MX94-specific watchdog hardware data and DT compatible entry to enable this behavior. Signed-off-by: Ranjani Vaidyanathan [peng.fan@nxp.com: rewrite commit log for clarity] Signed-off-by: Peng Fan Reviewed-by: Guenter Roeck Reviewed-by: Frank Li Link: https://lore.kernel.org/r/20260206-imx94-wdog-v2-1-4dd725faec1f@nxp.com Signed-off-by: Guenter Roeck --- drivers/watchdog/imx7ulp_wdt.c | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/drivers/watchdog/imx7ulp_wdt.c b/drivers/watchdog/imx7ulp_wdt.c index 03479110453c..855dc9d5083a 100644 --- a/drivers/watchdog/imx7ulp_wdt.c +++ b/drivers/watchdog/imx7ulp_wdt.c @@ -56,6 +56,7 @@ MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default=" struct imx_wdt_hw_feature { bool prescaler_enable; bool post_rcs_wait; + bool cpu_lpm_auto_cg; u32 wdog_clock_rate; }; @@ -360,7 +361,7 @@ static int __maybe_unused imx7ulp_wdt_suspend_noirq(struct device *dev) { struct imx7ulp_wdt_device *imx7ulp_wdt = dev_get_drvdata(dev); - if (watchdog_active(&imx7ulp_wdt->wdd)) + if (watchdog_active(&imx7ulp_wdt->wdd) && !imx7ulp_wdt->hw->cpu_lpm_auto_cg) imx7ulp_wdt_stop(&imx7ulp_wdt->wdd); clk_disable_unprepare(imx7ulp_wdt->clk); @@ -408,10 +409,17 @@ static const struct imx_wdt_hw_feature imx93_wdt_hw = { .wdog_clock_rate = 125, }; +static const struct imx_wdt_hw_feature imx94_wdt_hw = { + .prescaler_enable = true, + .wdog_clock_rate = 125, + .cpu_lpm_auto_cg = true, +}; + static const struct of_device_id imx7ulp_wdt_dt_ids[] = { { .compatible = "fsl,imx7ulp-wdt", .data = &imx7ulp_wdt_hw, }, { .compatible = "fsl,imx8ulp-wdt", .data = &imx8ulp_wdt_hw, }, { .compatible = "fsl,imx93-wdt", .data = &imx93_wdt_hw, }, + { .compatible = "fsl,imx94-wdt", .data = &imx94_wdt_hw, }, { /* sentinel */ } }; MODULE_DEVICE_TABLE(of, imx7ulp_wdt_dt_ids); From 10820327b9386c77aaa8c5c3c3690a7443baf9b9 Mon Sep 17 00:00:00 2001 From: Balakrishnan Sambath Date: Mon, 2 Mar 2026 17:03:10 +0530 Subject: [PATCH 27/58] watchdog: at91sam9_wdt.h: Document WDDIS bit position per SoC family AT91_WDT_WDDIS (bit 15) applies to SAMA5/AT91SAM9261 and AT91_SAM9X60_WDDIS (bit 12) to SAM9X60/SAMA7G5/SAM9X75. Update comments to reflect this and add SAMA7G5 and SAM9X75 datasheet references to the file header. Signed-off-by: Balakrishnan Sambath Reviewed-by: Alexandre Belloni Link: https://lore.kernel.org/r/20260302113310.133989-3-balakrishnan.s@microchip.com Signed-off-by: Guenter Roeck --- drivers/watchdog/at91sam9_wdt.h | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/drivers/watchdog/at91sam9_wdt.h b/drivers/watchdog/at91sam9_wdt.h index 298d545df1a1..2020694f8f6f 100644 --- a/drivers/watchdog/at91sam9_wdt.h +++ b/drivers/watchdog/at91sam9_wdt.h @@ -9,6 +9,8 @@ * Watchdog Timer (WDT) - System peripherals regsters. * Based on AT91SAM9261 datasheet revision D. * Based on SAM9X60 datasheet. + * Based on SAMA7G5 datasheet. + * Based on SAM9X75 datasheet. * */ @@ -27,10 +29,10 @@ #define AT91_SAM9X60_PERIODRST BIT(4) /* Period Reset */ #define AT91_SAM9X60_RPTHRST BIT(5) /* Minimum Restart Period */ #define AT91_WDT_WDFIEN BIT(12) /* Fault Interrupt Enable */ -#define AT91_SAM9X60_WDDIS BIT(12) /* Watchdog Disable */ +#define AT91_SAM9X60_WDDIS BIT(12) /* Watchdog Disable (SAM9X60, SAMA7G5, SAM9X75) */ #define AT91_WDT_WDRSTEN BIT(13) /* Reset Processor */ #define AT91_WDT_WDRPROC BIT(14) /* Timer Restart */ -#define AT91_WDT_WDDIS BIT(15) /* Watchdog Disable */ +#define AT91_WDT_WDDIS BIT(15) /* Watchdog Disable (SAMA5, AT91SAM9261) */ #define AT91_WDT_WDD (0xfffUL << 16) /* Delta Value */ #define AT91_WDT_SET_WDD(x) (((x) << 16) & AT91_WDT_WDD) #define AT91_WDT_WDDBGHLT BIT(28) /* Debug Halt */ From fec053e452a5e859cf6e8aa2c9d44d78fed60034 Mon Sep 17 00:00:00 2001 From: CL Wang Date: Thu, 15 Jan 2026 16:14:42 +0800 Subject: [PATCH 28/58] dt-bindings: watchdog: Add support for Andes ATCWDT200 Add the devicetree binding documentation for the Andes ATCWDT200 watchdog timer. ATCWDT200 is the IP name, which is embedded in AndesCore-based platforms or SoCs such as AE350 and Qilai. Signed-off-by: CL Wang Reviewed-by: Rob Herring (Arm) Link: https://lore.kernel.org/r/20260115081444.2452357-2-cl634@andestech.com Signed-off-by: Guenter Roeck --- .../watchdog/andestech,ae350-wdt.yaml | 53 +++++++++++++++++++ 1 file changed, 53 insertions(+) create mode 100644 Documentation/devicetree/bindings/watchdog/andestech,ae350-wdt.yaml diff --git a/Documentation/devicetree/bindings/watchdog/andestech,ae350-wdt.yaml b/Documentation/devicetree/bindings/watchdog/andestech,ae350-wdt.yaml new file mode 100644 index 000000000000..f1107c552788 --- /dev/null +++ b/Documentation/devicetree/bindings/watchdog/andestech,ae350-wdt.yaml @@ -0,0 +1,53 @@ +# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/watchdog/andestech,ae350-wdt.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Andes ATCWDT200 Watchdog Timer + +maintainers: + - CL Wang + +allOf: + - $ref: watchdog.yaml# + +properties: + compatible: + oneOf: + - items: + - enum: + - andestech,qilai-wdt + - const: andestech,ae350-wdt + - const: andestech,ae350-wdt + + reg: + maxItems: 1 + + clocks: + maxItems: 1 + + andestech,clock-source: + $ref: /schemas/types.yaml#/definitions/uint32 + enum: [0, 1] + description: | + Select the clock source for the watchdog timer. + 0 - External clock + 1 - P clock + +required: + - compatible + - reg + - clocks + - andestech,clock-source + +unevaluatedProperties: false + +examples: + - | + watchdog@f0500000 { + compatible = "andestech,ae350-wdt"; + reg = <0xf0500000 0x20>; + clocks = <&clk_wdt>; + andestech,clock-source = <0>; + }; From e4e0848ad046da9304e0656a71f74ddfbb316e98 Mon Sep 17 00:00:00 2001 From: CL Wang Date: Thu, 15 Jan 2026 16:14:43 +0800 Subject: [PATCH 29/58] watchdog: atcwdt200: Add driver for Andes ATCWDT200 Add support for the Andes ATCWDT200 watchdog timer. The driver implements programmable reset and interrupt timers, and includes automatic detection of the supported IntTime bit-width. Integrated with the Linux watchdog framework, it supports basic operations including start, stop, ping, timeout configuration, and system reset via the restart handler. Signed-off-by: CL Wang Link: https://lore.kernel.org/r/20260115081444.2452357-3-cl634@andestech.com Signed-off-by: Guenter Roeck --- drivers/watchdog/Kconfig | 9 + drivers/watchdog/Makefile | 1 + drivers/watchdog/atcwdt200_wdt.c | 580 +++++++++++++++++++++++++++++++ 3 files changed, 590 insertions(+) create mode 100644 drivers/watchdog/atcwdt200_wdt.c diff --git a/drivers/watchdog/Kconfig b/drivers/watchdog/Kconfig index d58a0bd0bdeb..eb8eb307e04d 100644 --- a/drivers/watchdog/Kconfig +++ b/drivers/watchdog/Kconfig @@ -2119,6 +2119,15 @@ config WATCHDOG_RTAS # RISC-V Architecture +config ATCWDT200_WATCHDOG + tristate "Andes ATCWDT200 Watchdog support" + depends on ARCH_ANDES || COMPILE_TEST + help + Driver for the Andes ATCWDT200 watchdog timer. It provides access to + programmable reset and interrupt counters with clock-source dependent + timing. The driver automatically detects the supported IntTime bit-width + and is fully integrated with the Linux Watchdog Framework. + config STARFIVE_WATCHDOG tristate "StarFive Watchdog support" depends on ARCH_STARFIVE || COMPILE_TEST diff --git a/drivers/watchdog/Makefile b/drivers/watchdog/Makefile index ff1dc6fd5867..a4341b251bdf 100644 --- a/drivers/watchdog/Makefile +++ b/drivers/watchdog/Makefile @@ -198,6 +198,7 @@ obj-$(CONFIG_PSERIES_WDT) += pseries-wdt.o obj-$(CONFIG_WATCHDOG_RTAS) += wdrtas.o # RISC-V Architecture +obj-$(CONFIG_ATCWDT200_WATCHDOG) += atcwdt200_wdt.o obj-$(CONFIG_STARFIVE_WATCHDOG) += starfive-wdt.o # S390 Architecture diff --git a/drivers/watchdog/atcwdt200_wdt.c b/drivers/watchdog/atcwdt200_wdt.c new file mode 100644 index 000000000000..8e3b18aea368 --- /dev/null +++ b/drivers/watchdog/atcwdt200_wdt.c @@ -0,0 +1,580 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Andes ATCWDT200 watchdog timer driver. + * + * Copyright (C) 2025 Andes Technology Corporation + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* Register definitions */ +#define REG_CTRL 0x10 +#define REG_RESTART 0x14 +#define REG_WRITE_EN 0x18 +#define REG_STATUS 0x1C + +/* Control Register */ +#define CTRL_RST_TIME_MSK GENMASK(10, 8) +#define CTRL_RST_TIME_SET(x) FIELD_PREP(CTRL_RST_TIME_MSK, x) +#define CTRL_INT_TIME_MSK GENMASK(7, 4) +#define CTRL_INT_TIME_SET(x) FIELD_PREP(CTRL_INT_TIME_MSK, x) +#define CTRL_INT_TIME_GET(x) FIELD_GET(CTRL_INT_TIME_MSK, x) +#define CTRL_RST_EN BIT(3) +#define CTRL_CLK_SEL BIT(1) +#define CTRL_CLK_SEL_PCLK 1 +#define CTRL_CLK_SEL_SET(x) FIELD_PREP(CTRL_CLK_SEL, x) +#define CTRL_WDT_EN BIT(0) + +/* Restart Register */ +#define RESTART_MAGIC 0xCAFE + +/* Write Enable Register */ +#define WRITE_EN_MAGIC 0x5AA5 + +/* Status Register */ +#define STATUS_INT_EXPIRED BIT(1) + +/* The default timeout value in seconds */ +#define ATCWDT_TIMEOUT 4 + +/* Define the array size for each timer type */ +#define TMR_SZ_RST 8 +#define TMR_SZ_INT_16 8 +#define TMR_SZ_INT_32 16 + +#define DRV_NAME "atcwdt200" +/** + * enum timer_type - Supported timer types for ATCWDT200 watchdog driver + * @TMR_RST: Reset timer (non-interrupt). + * @TMR_INT_16: 16-bit interrupt timer supported by hardware. + * @TMR_INT_32: 32-bit interrupt timer supported by hardware. + * @TMR_UNKNOWN: Timer type cannot be determined. + */ +enum timer_type { + TMR_RST, + TMR_INT_16, + TMR_INT_32, + TMR_UNKNOWN +}; + +static unsigned int timeout = ATCWDT_TIMEOUT; +static bool nowayout = WATCHDOG_NOWAYOUT; + +/** + * struct atcwdt_drv - ATCWDT200 watchdog driver private data + * @wdt_dev: Watchdog device used by the watchdog framework. + * @regmap: Register map for accessing hardware registers. + * @clk: Hardware clock used by the watchdog timer. + * @lock: Spinlock protecting register accesses and driver state. + * @clk_freq: Input clock frequency of the ATCWDT200. + * @clk_src: Selected clock source for the watchdog timer. + * @int_timer_type: Detected interrupt timer type (16-bit, 32-bit, or unknown). + */ +struct atcwdt_drv { + struct watchdog_device wdt_dev; + struct regmap *regmap; + struct clk *clk; + spinlock_t lock; + unsigned int clk_freq; + unsigned char clk_src; + unsigned char int_timer_type; +}; + +static const struct watchdog_info atcwdt_info = { + .identity = DRV_NAME, + .options = WDIOF_SETTIMEOUT | + WDIOF_KEEPALIVEPING | + WDIOF_MAGICCLOSE, +}; + +/** + * atcwdt_get_index - Get the interval value for the specified timer type + * @index: The index of the interval in the array + * @timer_type: The type of timer, which can be TMR_RST, TMR_INT_16, or + * TMR_INT_32. + * + * This function retrieves the interval value based on the timer type and + * ensures the index stays within the valid range for the given timer type. + * For TMR_RST: + * - The maximum array size is 8 (index range: 0-7). + * For TMR_INT_16: + * - The maximum array size is 8 (index range: 0-7). + * For TMR_INT_32: + * - The maximum array size is 16 (index range: 0-15). + * + * If the index exceeds the maximum array size, the function will return + * the last element of the respective array. + */ +static inline unsigned char atcwdt_get_index(unsigned char index, + enum timer_type timer_type) +{ + static const unsigned char rst_timer_interval[TMR_SZ_RST] = { + 7, 8, 9, 10, 11, 12, 13, 14}; + static const unsigned char int_timer_interval[TMR_SZ_INT_32] = { + 6, 8, 10, 11, 12, 13, 14, 15, 17, 19, 21, 23, 25, 27, 29, 31}; + unsigned char array_index; + + if (timer_type == TMR_RST) { + array_index = min(index, TMR_SZ_RST - 1); + return rst_timer_interval[array_index]; + } + + if (timer_type == TMR_INT_32) + array_index = min(index, TMR_SZ_INT_32 - 1); + else + array_index = min(index, TMR_SZ_INT_16 - 1); + + return int_timer_interval[array_index]; +} + +/** + * atcwdt_get_clock_period - Calculate the closest clock period based on a + * given tick count + * @tick: The target tick count to match + * @timer_type: The type of timer, which can be TMR_RST, TMR_INT_16, or + * TMR_INT_32. + * @index: Pointer to store the index of the selected parameter + * + * This function calculates the closest clock period to the given tick count + * by iterating through the timer parameters and selecting the one that + * minimizes the difference between the target tick count and the calculated + * clock period. The function determines the index of the closest parameter + * and returns the difference between the target tick count and the selected + * clock period. + * + * Return: The difference between the target tick count and the selected + * clock period. + */ +static long long atcwdt_get_clock_period(long long tick, + enum timer_type timer_type, + unsigned char *index) +{ + long long result; + unsigned char size; + char i; + + if (timer_type == TMR_RST) + size = TMR_SZ_RST; + else if (timer_type == TMR_INT_32) + size = TMR_SZ_INT_32; + else + size = TMR_SZ_INT_16; + + *index = size - 1; + for (i = 0; i < size; i++) { + result = tick - (1LL << atcwdt_get_index(i, timer_type)); + + if (result <= 1) { + *index = i; + break; + } + } + + return result; +} + +/** + * atcwdt_get_timeout_params - Calculate optimal parameters for Watchdog Timer + * @drv_data: Pointer to the Watchdog driver data structure + * @timeout: Desired timeout value (in seconds) + * @int_timer_params: Pointer to store the calculated interrupt timer + * parameter index + * @rst_timer_params: Pointer to store the calculated reset timer parameter + * index + * + * This function calculates the optimal parameter combination for the + * interrupt timer and reset timer of the Watchdog Timer to achieve a + * timeout value closest to, but not less than the specified timeout. + * + * Algorithm: + * 1. The parameters for both the interrupt timer and reset timer are + * predefined as a series of options represented as powers of 2. + * 2. The function first determines the interrupt timer's parameter index + * that provides a time closest to and not exceeding the desired timeout. + * 3. Based on the selected interrupt timer, it calculates the required + * reset timer parameter to ensure the total timeout matches the target. + * + * Return: The calculated parameter indices are stored in the provided + * pointers. + */ +static void atcwdt_get_timeout_params(struct atcwdt_drv *drv_data, + unsigned int timeout, + unsigned char *int_timer_params, + unsigned char *rst_timer_params) +{ + long long rest_time_ms; + long long result; + long long tick; + unsigned char rst_index; + unsigned char int_index; + unsigned char above; + unsigned char below; + + tick = (long long)timeout * drv_data->clk_freq; + result = atcwdt_get_clock_period(tick, + drv_data->int_timer_type, + &above); + if (result == 0 || above == 0) { + *int_timer_params = above; + *rst_timer_params = 0; + return; + } + below = above - 1; + + int_index = atcwdt_get_index(below, drv_data->int_timer_type); + rest_time_ms = timeout * 1000LL + - div64_s64(1000LL << int_index, drv_data->clk_freq); + + result = atcwdt_get_clock_period(rest_time_ms * drv_data->clk_freq, + TMR_RST, + &rst_index); + + if (result > 1) { + *int_timer_params = above; + *rst_timer_params = 0; + } else { + *int_timer_params = below; + *rst_timer_params = rst_index; + } +} + +/** + * atcwdt_get_int_timer_type - Get the supported interrupt timer type. + * @drv_data: Pointer to the watchdog driver data structure. + * + * This function tests the writable bits in the IntTime field of the control + * register to determine the interrupt timer type supported by the hardware. + * + * Note: This function must only be called when the ATCWDT200 watchdog is + * disabled. If the watchdog is enabled, this function returns TMR_UNKNOWN. + * + * Returns: The interrupt timer type supported by the hardware. + */ +static int atcwdt_get_int_timer_type(struct atcwdt_drv *drv_data) +{ + struct device *dev = drv_data->wdt_dev.parent; + unsigned int val; + int ret = 0; + + spin_lock(&drv_data->lock); + regmap_read(drv_data->regmap, REG_CTRL, &val); + if (val & CTRL_WDT_EN) { + spin_unlock(&drv_data->lock); + return TMR_UNKNOWN; + } + + /* + * Configures the IntTime field with the maximum mask value + * (CTRL_INT_TIME_MSK), reads its value from the control register + * to identify the maximum writable bits. + */ + regmap_write(drv_data->regmap, REG_WRITE_EN, WRITE_EN_MAGIC); + regmap_write(drv_data->regmap, REG_CTRL, CTRL_INT_TIME_MSK); + regmap_read(drv_data->regmap, REG_CTRL, &val); + spin_unlock(&drv_data->lock); + + val = CTRL_INT_TIME_GET(val); + switch (val) { + case 7: + drv_data->int_timer_type = TMR_INT_16; + break; + case 15: + drv_data->int_timer_type = TMR_INT_32; + break; + default: + drv_data->int_timer_type = TMR_UNKNOWN; + ret = dev_err_probe(dev, -ENODEV, + "Failed to detect interrupt timer type\n"); + } + + return ret; +} + +static int atcwdt_ping(struct watchdog_device *wdt_dev) +{ + struct atcwdt_drv *drv_data = watchdog_get_drvdata(wdt_dev); + + spin_lock(&drv_data->lock); + regmap_write(drv_data->regmap, REG_WRITE_EN, WRITE_EN_MAGIC); + regmap_write(drv_data->regmap, REG_RESTART, RESTART_MAGIC); + regmap_update_bits(drv_data->regmap, REG_STATUS, STATUS_INT_EXPIRED, + STATUS_INT_EXPIRED); + spin_unlock(&drv_data->lock); + + return 0; +} + +static int atcwdt_set_timeout(struct watchdog_device *wdt_dev, + unsigned int timeout) +{ + struct atcwdt_drv *drv_data = watchdog_get_drvdata(wdt_dev); + unsigned int value; + unsigned char rst_val; + unsigned char int_val; + + wdt_dev->timeout = timeout; + atcwdt_get_timeout_params(drv_data, timeout, &int_val, &rst_val); + + spin_lock(&drv_data->lock); + regmap_write(drv_data->regmap, REG_WRITE_EN, WRITE_EN_MAGIC); + + value = CTRL_RST_TIME_SET(rst_val) | + CTRL_INT_TIME_SET(int_val) | + CTRL_CLK_SEL_SET(drv_data->clk_src); + regmap_update_bits(drv_data->regmap, + REG_CTRL, + CTRL_RST_TIME_MSK | + CTRL_INT_TIME_MSK | + CTRL_CLK_SEL, + value); + + spin_unlock(&drv_data->lock); + atcwdt_ping(wdt_dev); + + return 0; +} + +static int atcwdt_start(struct watchdog_device *wdt_dev) +{ + struct atcwdt_drv *drv_data = watchdog_get_drvdata(wdt_dev); + + atcwdt_set_timeout(wdt_dev, wdt_dev->timeout); + + spin_lock(&drv_data->lock); + regmap_write(drv_data->regmap, REG_WRITE_EN, WRITE_EN_MAGIC); + regmap_update_bits(drv_data->regmap, + REG_CTRL, + CTRL_RST_EN | CTRL_WDT_EN, + CTRL_RST_EN | CTRL_WDT_EN); + + spin_unlock(&drv_data->lock); + + return 0; +} + +static int atcwdt_stop(struct watchdog_device *wdt_dev) +{ + struct atcwdt_drv *drv_data = watchdog_get_drvdata(wdt_dev); + + spin_lock(&drv_data->lock); + regmap_write(drv_data->regmap, REG_WRITE_EN, WRITE_EN_MAGIC); + regmap_update_bits(drv_data->regmap, + REG_CTRL, + CTRL_RST_EN | CTRL_WDT_EN, + 0); + spin_unlock(&drv_data->lock); + + return 0; +} + +static int atcwdt_restart(struct watchdog_device *wdt_dev, + unsigned long action, void *data) +{ + struct atcwdt_drv *drv_data = watchdog_get_drvdata(wdt_dev); + + atcwdt_set_timeout(wdt_dev, 0); + + spin_lock(&drv_data->lock); + regmap_write(drv_data->regmap, REG_WRITE_EN, WRITE_EN_MAGIC); + regmap_update_bits(drv_data->regmap, + REG_CTRL, + CTRL_RST_EN | CTRL_WDT_EN, + CTRL_RST_EN | CTRL_WDT_EN); + spin_unlock(&drv_data->lock); + + return 0; +} + +static const struct watchdog_ops atcwdt_ops = { + .owner = THIS_MODULE, + .start = atcwdt_start, + .stop = atcwdt_stop, + .ping = atcwdt_ping, + .set_timeout = atcwdt_set_timeout, + .restart = atcwdt_restart, +}; + +static int atcwdt_init_resource(struct platform_device *pdev, + struct atcwdt_drv *drv_data) +{ + struct device *dev = &pdev->dev; + void __iomem *base; + const struct regmap_config cfg = { + .name = "atcwdt", + .reg_bits = 32, + .val_bits = 32, + .cache_type = REGCACHE_NONE, + .reg_stride = 4, + .max_register = REG_STATUS, + }; + + base = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(base)) + return dev_err_probe(dev, PTR_ERR(base), + "Failed to ioremap I/O resource\n"); + + drv_data->regmap = devm_regmap_init_mmio(dev, base, &cfg); + if (IS_ERR(drv_data->regmap)) + return dev_err_probe(dev, PTR_ERR(drv_data->regmap), + "Failed to create regmap\n"); + + return 0; +} + +static int atcwdt_enable_clk(struct atcwdt_drv *drv_data) +{ + struct device *dev = drv_data->wdt_dev.parent; + unsigned int val; + int clk_src; + + drv_data->clk = devm_clk_get_enabled(dev, NULL); + if (IS_ERR(drv_data->clk)) + return dev_err_probe(dev, PTR_ERR(drv_data->clk), + "Failed to get watchdog clock\n"); + + drv_data->clk_freq = clk_get_rate(drv_data->clk); + if (!drv_data->clk_freq) + return dev_err_probe(dev, -EINVAL, + "Failed to get clock rate\n"); + + clk_src = device_property_read_u32(dev, "andestech,clock-source", &val); + drv_data->clk_src = (!clk_src && val != 0) ? CTRL_CLK_SEL_PCLK : 0; + + return 0; +} + +static int atcwdt_init_wdt_device(struct device *dev, + struct atcwdt_drv *drv_data) +{ + struct watchdog_device *wdd = &drv_data->wdt_dev; + + wdd->parent = dev; + wdd->info = &atcwdt_info; + wdd->ops = &atcwdt_ops; + wdd->timeout = ATCWDT_TIMEOUT; + wdd->min_timeout = 1; + + watchdog_set_nowayout(wdd, nowayout); + watchdog_set_drvdata(wdd, drv_data); + + return 0; +} + +static void atcwdt_calc_max_timeout(struct atcwdt_drv *drv_data) +{ + unsigned char rst_idx = atcwdt_get_index(0xFF, TMR_RST); + unsigned char int_idx = atcwdt_get_index(0xFF, + drv_data->int_timer_type); + + drv_data->wdt_dev.max_timeout = + ((1U << rst_idx) + (1U << int_idx)) / drv_data->clk_freq; +} + +static int atcwdt_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct atcwdt_drv *drv_data; + int ret; + + drv_data = devm_kzalloc(dev, sizeof(*drv_data), GFP_KERNEL); + if (!drv_data) + return -ENOMEM; + + platform_set_drvdata(pdev, drv_data); + spin_lock_init(&drv_data->lock); + + ret = atcwdt_init_wdt_device(dev, drv_data); + if (ret) + return ret; + + ret = atcwdt_init_resource(pdev, drv_data); + if (ret) + return ret; + + ret = atcwdt_enable_clk(drv_data); + if (ret) + return ret; + + ret = atcwdt_get_int_timer_type(drv_data); + if (ret) + return ret; + + atcwdt_calc_max_timeout(drv_data); + + ret = devm_watchdog_register_device(dev, &drv_data->wdt_dev); + + return ret; +} + +static int atcwdt_suspend(struct device *dev) +{ + struct atcwdt_drv *drv_data = dev_get_drvdata(dev); + + if (watchdog_active(&drv_data->wdt_dev)) { + atcwdt_stop(&drv_data->wdt_dev); + clk_disable_unprepare(drv_data->clk); + } + + return 0; +} + +static int atcwdt_resume(struct device *dev) +{ + struct atcwdt_drv *drv_data = dev_get_drvdata(dev); + int ret = 0; + + if (watchdog_active(&drv_data->wdt_dev)) { + ret = clk_prepare_enable(drv_data->clk); + if (ret) + return ret; + atcwdt_start(&drv_data->wdt_dev); + atcwdt_ping(&drv_data->wdt_dev); + } + + return ret; +} + +static const struct of_device_id atcwdt_match[] = { + { .compatible = "andestech,ae350-wdt" }, + { /* sentinel */ }, +}; +MODULE_DEVICE_TABLE(of, atcwdt_match); + +static DEFINE_SIMPLE_DEV_PM_OPS(atcwdt_pm_ops, atcwdt_suspend, atcwdt_resume); + +static struct platform_driver atcwdt_driver = { + .probe = atcwdt_probe, + .driver = { + .name = DRV_NAME, + .of_match_table = atcwdt_match, + .pm = pm_sleep_ptr(&atcwdt_pm_ops), + }, +}; + +module_platform_driver(atcwdt_driver); + +module_param(timeout, uint, 0); +MODULE_PARM_DESC(timeout, "Watchdog timeout in seconds (default=" + __MODULE_STRING(ATCWDT_TIMEOUT) ")"); + +module_param(nowayout, bool, 0); +MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default=" + __MODULE_STRING(WATCHDOG_NOWAYOUT) ")"); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("CL Wang "); +MODULE_DESCRIPTION("Andes ATCWDT200 Watchdog timer driver"); From b5998b4bc54888528a087db1c2744c69748bd822 Mon Sep 17 00:00:00 2001 From: CL Wang Date: Thu, 15 Jan 2026 16:14:44 +0800 Subject: [PATCH 30/58] MAINTAINERS: Add entry for Andes ATCWDT200 Add a MAINTAINERS entry for the Andes ATCWDT200 watchdog driver and its associated Device Tree bindings. Signed-off-by: CL Wang Link: https://lore.kernel.org/r/20260115081444.2452357-4-cl634@andestech.com Signed-off-by: Guenter Roeck --- MAINTAINERS | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/MAINTAINERS b/MAINTAINERS index f35e1769fa72..358eb89484e5 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -1874,6 +1874,12 @@ S: Supported F: Documentation/devicetree/bindings/spi/andestech,ae350-spi.yaml F: drivers/spi/spi-atcspi200.c +ANDES ATCWDT200 WATCHDOG DRIVER +M: CL Wang +S: Supported +F: Documentation/devicetree/bindings/watchdog/andestech,ae350-wdt.yaml +F: drivers/watchdog/atcwdt200_wdt.c + ANDROID DRIVERS M: Greg Kroah-Hartman M: Arve Hjønnevåg From 7b25feb19ce8073193fff6a05a43b5a5c2854b4b Mon Sep 17 00:00:00 2001 From: Mark Pearson Date: Tue, 28 Apr 2026 08:49:44 -0400 Subject: [PATCH 31/58] watchdog: lenovo_se10_wdt: Add support for SE10 Gen 2 platform The Lenovo SE10 Gen 2 platform uses a watchdog chip from the same family. Watchdog functionality is the same, so update the driver with the new chip ID. Add the Gen 2 MTM's to enable support on the platform. Tested on SE10 G2. Signed-off-by: Mark Pearson Link: https://lore.kernel.org/r/20260428124954.1193450-1-mpearson-lenovo@squebb.ca Signed-off-by: Guenter Roeck --- drivers/watchdog/lenovo_se10_wdt.c | 66 +++++++++++++++++++++++++++++- 1 file changed, 65 insertions(+), 1 deletion(-) diff --git a/drivers/watchdog/lenovo_se10_wdt.c b/drivers/watchdog/lenovo_se10_wdt.c index cd0500e5080b..99ff01af4124 100644 --- a/drivers/watchdog/lenovo_se10_wdt.c +++ b/drivers/watchdog/lenovo_se10_wdt.c @@ -178,7 +178,7 @@ static int se10_wdt_probe(struct platform_device *pdev) return -EBUSY; chip_id = get_chipID(); - if (chip_id != 0x5632) { + if (chip_id != 0x5632 && chip_id != 0x5652) { release_region(CFG_PORT, CFG_SIZE); return -ENODEV; } @@ -280,6 +280,70 @@ static const struct dmi_system_id se10_dmi_table[] __initconst = { }, .callback = se10_create_platform_device, }, + { + .ident = "LENOVO-SE10-G2", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"), + DMI_MATCH(DMI_PRODUCT_NAME, "13LJ"), + }, + .callback = se10_create_platform_device, + }, + { + .ident = "LENOVO-SE10-G2", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"), + DMI_MATCH(DMI_PRODUCT_NAME, "13LK"), + }, + .callback = se10_create_platform_device, + }, + { + .ident = "LENOVO-SE10-G2", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"), + DMI_MATCH(DMI_PRODUCT_NAME, "13S1"), + }, + .callback = se10_create_platform_device, + }, + { + .ident = "LENOVO-SE10-G2", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"), + DMI_MATCH(DMI_PRODUCT_NAME, "13S2"), + }, + .callback = se10_create_platform_device, + }, + { + .ident = "LENOVO-SE10-G2", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"), + DMI_MATCH(DMI_PRODUCT_NAME, "13S3"), + }, + .callback = se10_create_platform_device, + }, + { + .ident = "LENOVO-SE10-G2", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"), + DMI_MATCH(DMI_PRODUCT_NAME, "13S4"), + }, + .callback = se10_create_platform_device, + }, + { + .ident = "LENOVO-SE10-G2", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"), + DMI_MATCH(DMI_PRODUCT_NAME, "13S5"), + }, + .callback = se10_create_platform_device, + }, + { + .ident = "LENOVO-SE10-G2", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"), + DMI_MATCH(DMI_PRODUCT_NAME, "13S6"), + }, + .callback = se10_create_platform_device, + }, {} }; MODULE_DEVICE_TABLE(dmi, se10_dmi_table); From 8ff498b2501205964af23ed3949042acfabce5aa Mon Sep 17 00:00:00 2001 From: Deepti Jaggi Date: Mon, 4 May 2026 16:16:43 +0800 Subject: [PATCH 32/58] dt-bindings: watchdog: qcom-wdt: Add compatible for Nord watchdog Document Krait Processor Sub-system (KPSS) Watchdog timer on Nord SoC. Signed-off-by: Deepti Jaggi Signed-off-by: Shawn Guo Acked-by: Krzysztof Kozlowski Link: https://lore.kernel.org/r/20260504081643.826038-1-shengchao.guo@oss.qualcomm.com Signed-off-by: Guenter Roeck --- Documentation/devicetree/bindings/watchdog/qcom-wdt.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/Documentation/devicetree/bindings/watchdog/qcom-wdt.yaml b/Documentation/devicetree/bindings/watchdog/qcom-wdt.yaml index 1d5d01c2547a..47dea1a29ea8 100644 --- a/Documentation/devicetree/bindings/watchdog/qcom-wdt.yaml +++ b/Documentation/devicetree/bindings/watchdog/qcom-wdt.yaml @@ -28,6 +28,7 @@ properties: - qcom,apss-wdt-msm8226 - qcom,apss-wdt-msm8974 - qcom,apss-wdt-msm8994 + - qcom,apss-wdt-nord - qcom,apss-wdt-qcm2290 - qcom,apss-wdt-qcs404 - qcom,apss-wdt-qcs615 From d15a35c059ec88642794729225050eedaeec9ff0 Mon Sep 17 00:00:00 2001 From: Philipp Hahn Date: Tue, 5 May 2026 11:26:12 +0200 Subject: [PATCH 33/58] watchdog: Change suffix .txt to .rst in references Fix link to documentation, which has already been converted to reST. Also remove apostrophes which are no longer needed. Signed-off-by: Philipp Hahn Link: https://lore.kernel.org/r/bb1aa3129d5e52bc4c8e1ec3340b88f80f726fef.1777972790.git.phahn-oss@avm.de Signed-off-by: Guenter Roeck --- Documentation/watchdog/convert_drivers_to_kernel_api.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Documentation/watchdog/convert_drivers_to_kernel_api.rst b/Documentation/watchdog/convert_drivers_to_kernel_api.rst index e83609a5d007..9eddb962f8e4 100644 --- a/Documentation/watchdog/convert_drivers_to_kernel_api.rst +++ b/Documentation/watchdog/convert_drivers_to_kernel_api.rst @@ -114,7 +114,7 @@ Add the watchdog operations --------------------------- All possible callbacks are defined in 'struct watchdog_ops'. You can find it -explained in 'watchdog-kernel-api.txt' in this directory. start() and +explained in watchdog-kernel-api.rst in this directory. start() and owner must be set, the rest are optional. You will easily find corresponding functions in the old driver. Note that you will now get a pointer to the watchdog_device as a parameter to these functions, so you probably have to @@ -153,7 +153,7 @@ Add the watchdog device Now we need to create a 'struct watchdog_device' and populate it with the necessary information for the framework. The struct is also explained in detail -in 'watchdog-kernel-api.txt' in this directory. We pass it the mandatory +in watchdog-kernel-api.rst in this directory. We pass it the mandatory watchdog_info struct and the newly created watchdog_ops. Often, old drivers have their own record-keeping for things like bootstatus and timeout using static variables. Those have to be converted to use the members in From e40714929f638c3b68547260bace7f533fe9f987 Mon Sep 17 00:00:00 2001 From: Philipp Hahn Date: Tue, 5 May 2026 11:26:13 +0200 Subject: [PATCH 34/58] watchdog: Move `struct` before name Write `struct ` before the structure name as Sphinx otherwise uses the following word after it. See https://docs.kernel.org/watchdog/watchdog-api.html#environmental-monitoring Signed-off-by: Philipp Hahn Link: https://lore.kernel.org/r/e66ec58f3b8252b3676cc9fe68818af95123210c.1777972790.git.phahn-oss@avm.de Signed-off-by: Guenter Roeck --- .../convert_drivers_to_kernel_api.rst | 24 +++++++++---------- Documentation/watchdog/watchdog-api.rst | 2 +- .../watchdog/watchdog-kernel-api.rst | 2 +- 3 files changed, 14 insertions(+), 14 deletions(-) diff --git a/Documentation/watchdog/convert_drivers_to_kernel_api.rst b/Documentation/watchdog/convert_drivers_to_kernel_api.rst index 9eddb962f8e4..004fe6ef76e9 100644 --- a/Documentation/watchdog/convert_drivers_to_kernel_api.rst +++ b/Documentation/watchdog/convert_drivers_to_kernel_api.rst @@ -11,12 +11,12 @@ This document shall guide you for this task. The necessary steps are described as well as things to look out for. -Remove the file_operations struct +Remove the struct file_operations --------------------------------- Old drivers define their own file_operations for actions like open(), write(), etc... These are now handled by the framework and just call the driver when -needed. So, in general, the 'file_operations' struct and assorted functions can +needed. So, in general, the struct file_operations and assorted functions can go. Only very few driver-specific details have to be moved to other functions. Here is a overview of the functions and probably needed actions: @@ -36,7 +36,7 @@ Here is a overview of the functions and probably needed actions: from the driver: WDIOC_GETSUPPORT: - Returns the mandatory watchdog_info struct from the driver + Returns the mandatory struct watchdog_info from the driver WDIOC_GETSTATUS: Needs the status-callback defined, otherwise returns 0 @@ -88,8 +88,8 @@ refactoring. The rest can go. Remove the miscdevice --------------------- -Since the file_operations are gone now, you can also remove the 'struct -miscdevice'. The framework will create it on watchdog_dev_register() called by +Since the file_operations are gone now, you can also remove the struct +miscdevice. The framework will create it on watchdog_dev_register() called by watchdog_register_device():: -static struct miscdevice s3c2410wdt_miscdev = { @@ -113,10 +113,10 @@ them. Includes can be removed, too. For example:: Add the watchdog operations --------------------------- -All possible callbacks are defined in 'struct watchdog_ops'. You can find it +All possible callbacks are defined in struct watchdog_ops. You can find it explained in watchdog-kernel-api.rst in this directory. start() and owner must be set, the rest are optional. You will easily find corresponding -functions in the old driver. Note that you will now get a pointer to the +functions in the old driver. Note that you will now get a pointer to the struct watchdog_device as a parameter to these functions, so you probably have to change the function header. Other changes are most likely not needed, because here simply happens the direct hardware access. If you have device-specific @@ -151,12 +151,12 @@ A typical function-header change looks like:: Add the watchdog device ----------------------- -Now we need to create a 'struct watchdog_device' and populate it with the -necessary information for the framework. The struct is also explained in detail +Now we need to create a struct watchdog_device and populate it with the +necessary information for the framework. The structure is also explained in detail in watchdog-kernel-api.rst in this directory. We pass it the mandatory -watchdog_info struct and the newly created watchdog_ops. Often, old drivers +struct watchdog_info and the newly created struct watchdog_ops. Often, old drivers have their own record-keeping for things like bootstatus and timeout using -static variables. Those have to be converted to use the members in +static variables. Those have to be converted to use the members in struct watchdog_device. Note that the timeout values are unsigned int. Some drivers use signed int, so this has to be converted, too. @@ -174,7 +174,7 @@ Handle the 'nowayout' feature A few drivers use nowayout statically, i.e. there is no module parameter for it and only CONFIG_WATCHDOG_NOWAYOUT determines if the feature is going to be used. This needs to be converted by initializing the status variable of the -watchdog_device like this:: +struct watchdog_device like this:: .status = WATCHDOG_NOWAYOUT_INIT_STATUS, diff --git a/Documentation/watchdog/watchdog-api.rst b/Documentation/watchdog/watchdog-api.rst index 984f7362d5fb..f732cd6e37ae 100644 --- a/Documentation/watchdog/watchdog-api.rst +++ b/Documentation/watchdog/watchdog-api.rst @@ -158,7 +158,7 @@ available to ask what the device can do:: struct watchdog_info ident; ioctl(fd, WDIOC_GETSUPPORT, &ident); -the fields returned in the ident struct are: +the fields returned in the struct watchdog_info are: ================ ============================================= identity a string identifying the watchdog driver diff --git a/Documentation/watchdog/watchdog-kernel-api.rst b/Documentation/watchdog/watchdog-kernel-api.rst index 5649c54cf6fb..e2c1386c9550 100644 --- a/Documentation/watchdog/watchdog-kernel-api.rst +++ b/Documentation/watchdog/watchdog-kernel-api.rst @@ -257,7 +257,7 @@ bit-operations. The status bits that are defined are: To set the WDOG_NO_WAY_OUT status bit (before registering your watchdog timer device) you can either: - * set it statically in your watchdog_device struct with + * set it statically in your struct watchdog_device with .status = WATCHDOG_NOWAYOUT_INIT_STATUS, From e46082426b09a019862714a6518e792ef16cc72a Mon Sep 17 00:00:00 2001 From: Philipp Hahn Date: Tue, 5 May 2026 11:26:14 +0200 Subject: [PATCH 35/58] watchdog: Replace intermixed tab/space indent Consistently indent all lines with tabs instead of having one line using blanks. Signed-off-by: Philipp Hahn Link: https://lore.kernel.org/r/e1ccbfb0efb9a9f508d1afe10263af1578a1aa46.1777972790.git.phahn-oss@avm.de Signed-off-by: Guenter Roeck --- Documentation/watchdog/watchdog-api.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Documentation/watchdog/watchdog-api.rst b/Documentation/watchdog/watchdog-api.rst index f732cd6e37ae..9acf7fe1fb58 100644 --- a/Documentation/watchdog/watchdog-api.rst +++ b/Documentation/watchdog/watchdog-api.rst @@ -161,7 +161,7 @@ available to ask what the device can do:: the fields returned in the struct watchdog_info are: ================ ============================================= - identity a string identifying the watchdog driver + identity a string identifying the watchdog driver firmware_version the firmware version of the card if available options a flags describing what the device supports ================ ============================================= From dfc93986a5cbb376fa6c4d35ce092c947fa77943 Mon Sep 17 00:00:00 2001 From: Philipp Hahn Date: Tue, 5 May 2026 11:26:15 +0200 Subject: [PATCH 36/58] watchdog: Separate kind of documentation Currently there are several (sub-)documents for "Generic kernel infrastructure API" and several "driver specific" documents. Put each one into its own sub-section. Signed-off-by: Philipp Hahn Link: https://lore.kernel.org/r/7d1b722205bab83603832e66750f7b5f1f73eaa5.1777972790.git.phahn-oss@avm.de Signed-off-by: Guenter Roeck --- Documentation/watchdog/index.rst | 20 +++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) diff --git a/Documentation/watchdog/index.rst b/Documentation/watchdog/index.rst index 1cea24681e6b..293ed05ba6b8 100644 --- a/Documentation/watchdog/index.rst +++ b/Documentation/watchdog/index.rst @@ -4,15 +4,25 @@ Watchdog Support ================ +Kernel infrastructure +===================== + .. toctree:: :maxdepth: 1 - hpwdt - mlx-wdt - pcwd-watchdog watchdog-api watchdog-kernel-api - watchdog-parameters watchdog-pm - wdt convert_drivers_to_kernel_api + +Driver specific +=============== + +.. toctree:: + :maxdepth: 1 + + watchdog-parameters + hpwdt + wdt + mlx-wdt + pcwd-watchdog From 44c94a7e11a52e915731458a636537bc4d13cb95 Mon Sep 17 00:00:00 2001 From: Philipp Hahn Date: Tue, 5 May 2026 11:26:16 +0200 Subject: [PATCH 37/58] watchdog: Prefix WDT with ICS for clarity `wdt.rst` is only about the Watchdog from "Industrial Computer Source" (ICS). Change the title and rename the file to better express this. Add missing SPDX license identifier `GPL-2.0-or-later` same as code to silence `checkpatch`. Fix wrong link to sample driver in drivers/watchdog/smsc37b787_wdt.c. Signed-off-by: Philipp Hahn Link: https://lore.kernel.org/r/5a71979d8e8ab8e0a30de33f6aa2540b3b5dc1ee.1777972790.git.phahn-oss@avm.de Signed-off-by: Guenter Roeck --- Documentation/watchdog/{wdt.rst => ics-wdt.rst} | 8 +++++--- Documentation/watchdog/index.rst | 2 +- drivers/watchdog/Kconfig | 10 ++++++---- drivers/watchdog/smsc37b787_wdt.c | 2 +- 4 files changed, 13 insertions(+), 9 deletions(-) rename Documentation/watchdog/{wdt.rst => ics-wdt.rst} (91%) diff --git a/Documentation/watchdog/wdt.rst b/Documentation/watchdog/ics-wdt.rst similarity index 91% rename from Documentation/watchdog/wdt.rst rename to Documentation/watchdog/ics-wdt.rst index d97b0361535b..b0dc29e56358 100644 --- a/Documentation/watchdog/wdt.rst +++ b/Documentation/watchdog/ics-wdt.rst @@ -1,6 +1,8 @@ -============================================================ -WDT Watchdog Timer Interfaces For The Linux Operating System -============================================================ +.. SPDX-License-Identifier: GPL-2.0-or-later + +========================================== +Industrial Computer Source Watchdog Driver +========================================== Last Reviewed: 10/05/2007 diff --git a/Documentation/watchdog/index.rst b/Documentation/watchdog/index.rst index 293ed05ba6b8..dbc702b31a43 100644 --- a/Documentation/watchdog/index.rst +++ b/Documentation/watchdog/index.rst @@ -23,6 +23,6 @@ Driver specific watchdog-parameters hpwdt - wdt + ics-wdt mlx-wdt pcwd-watchdog diff --git a/drivers/watchdog/Kconfig b/drivers/watchdog/Kconfig index eb8eb307e04d..4c204951bc95 100644 --- a/drivers/watchdog/Kconfig +++ b/drivers/watchdog/Kconfig @@ -2266,10 +2266,11 @@ config MIXCOMWD Most people will say N. config WDT - tristate "WDT Watchdog timer" + tristate "ICS WDT500P/501P Watchdog timer" depends on ISA help - If you have a WDT500P or WDT501P watchdog board, say Y here, + If you have an Industrial Computer Source (ICS) WDT500P or WDT501P + watchdog board, say Y here, otherwise N. It is not possible to probe for this board, which means that you have to inform the kernel about the IO port and IRQ that is needed (you can do this via the io and irq parameters) @@ -2300,10 +2301,11 @@ config PCIPCWATCHDOG Most people will say N. config WDTPCI - tristate "PCI-WDT500/501 Watchdog timer" + tristate "ICS PCI-WDT500/501 Watchdog timer" depends on PCI && HAS_IOPORT help - If you have a PCI-WDT500/501 watchdog board, say Y here, otherwise N. + If you have an Industrial Computer Source (ICS) PCI-WDT500/501 watchdog + board, say Y here, otherwise N. If you have a PCI-WDT501 watchdog board then you can enable the temperature sensor by setting the type parameter to 501. diff --git a/drivers/watchdog/smsc37b787_wdt.c b/drivers/watchdog/smsc37b787_wdt.c index 3011e1af00f9..9f2c2ab4e2f1 100644 --- a/drivers/watchdog/smsc37b787_wdt.c +++ b/drivers/watchdog/smsc37b787_wdt.c @@ -36,7 +36,7 @@ * mknod /dev/watchdog c 10 130 * * For an example userspace keep-alive daemon, see: - * Documentation/watchdog/wdt.rst + * samples/watchdog/watchdog-simple.c */ #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt From 5e5b01ae7a70859fc6ab8a62ac922c232de70970 Mon Sep 17 00:00:00 2001 From: Komal Bajaj Date: Fri, 1 May 2026 23:35:49 +0530 Subject: [PATCH 38/58] dt-bindings: watchdog: qcom-wdt: Document Shikra watchdog Add devicetree binding for watchdog present on Qualcomm's Shikra SoC Signed-off-by: Komal Bajaj Reviewed-by: Krzysztof Kozlowski Link: https://lore.kernel.org/r/20260501-shikra-wdog-binding-v1-1-fd8c14bc2d20@oss.qualcomm.com Signed-off-by: Guenter Roeck --- Documentation/devicetree/bindings/watchdog/qcom-wdt.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/Documentation/devicetree/bindings/watchdog/qcom-wdt.yaml b/Documentation/devicetree/bindings/watchdog/qcom-wdt.yaml index 47dea1a29ea8..b3f8f063f282 100644 --- a/Documentation/devicetree/bindings/watchdog/qcom-wdt.yaml +++ b/Documentation/devicetree/bindings/watchdog/qcom-wdt.yaml @@ -42,6 +42,7 @@ properties: - qcom,apss-wdt-sdm845 - qcom,apss-wdt-sdx55 - qcom,apss-wdt-sdx65 + - qcom,apss-wdt-shikra - qcom,apss-wdt-sm6115 - qcom,apss-wdt-sm6350 - qcom,apss-wdt-sm8150 From 3394e894880a60338a2981dd688fd25e88d79667 Mon Sep 17 00:00:00 2001 From: Mark Pearson Date: Mon, 4 May 2026 14:01:42 -0400 Subject: [PATCH 39/58] watchdog: lenovo_se10_wdt: Fix use-after-free and resource leak risk Review by sashiko.dev highlighted potential use after free and resource leak instances. Set se10_pdev to null to prevent use after free Remove DMI call back and instead directly call se10_create_platform_device. Handle error cases appropriately Link: https://sashiko.dev/#/patchset/20260428124954.1193450-1-mpearson-lenovo%40squebb.ca Signed-off-by: Mark Pearson Link: https://lore.kernel.org/r/20260504180159.999189-1-mpearson-lenovo@squebb.ca Signed-off-by: Guenter Roeck --- drivers/watchdog/lenovo_se10_wdt.c | 32 ++++++++++++++---------------- 1 file changed, 15 insertions(+), 17 deletions(-) diff --git a/drivers/watchdog/lenovo_se10_wdt.c b/drivers/watchdog/lenovo_se10_wdt.c index 99ff01af4124..503e220263f9 100644 --- a/drivers/watchdog/lenovo_se10_wdt.c +++ b/drivers/watchdog/lenovo_se10_wdt.c @@ -224,7 +224,7 @@ static struct platform_driver se10_wdt_driver = { .probe = se10_wdt_probe, }; -static int se10_create_platform_device(const struct dmi_system_id *id) +static int se10_create_platform_device(void) { int err; @@ -233,9 +233,10 @@ static int se10_create_platform_device(const struct dmi_system_id *id) return -ENOMEM; err = platform_device_add(se10_pdev); - if (err) + if (err) { platform_device_put(se10_pdev); - + se10_pdev = NULL; + } return err; } @@ -246,7 +247,6 @@ static const struct dmi_system_id se10_dmi_table[] __initconst = { DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"), DMI_MATCH(DMI_PRODUCT_NAME, "12NH"), }, - .callback = se10_create_platform_device, }, { .ident = "LENOVO-SE10", @@ -254,7 +254,6 @@ static const struct dmi_system_id se10_dmi_table[] __initconst = { DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"), DMI_MATCH(DMI_PRODUCT_NAME, "12NJ"), }, - .callback = se10_create_platform_device, }, { .ident = "LENOVO-SE10", @@ -262,7 +261,6 @@ static const struct dmi_system_id se10_dmi_table[] __initconst = { DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"), DMI_MATCH(DMI_PRODUCT_NAME, "12NK"), }, - .callback = se10_create_platform_device, }, { .ident = "LENOVO-SE10", @@ -270,7 +268,6 @@ static const struct dmi_system_id se10_dmi_table[] __initconst = { DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"), DMI_MATCH(DMI_PRODUCT_NAME, "12NL"), }, - .callback = se10_create_platform_device, }, { .ident = "LENOVO-SE10", @@ -278,7 +275,6 @@ static const struct dmi_system_id se10_dmi_table[] __initconst = { DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"), DMI_MATCH(DMI_PRODUCT_NAME, "12NM"), }, - .callback = se10_create_platform_device, }, { .ident = "LENOVO-SE10-G2", @@ -286,7 +282,6 @@ static const struct dmi_system_id se10_dmi_table[] __initconst = { DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"), DMI_MATCH(DMI_PRODUCT_NAME, "13LJ"), }, - .callback = se10_create_platform_device, }, { .ident = "LENOVO-SE10-G2", @@ -294,7 +289,6 @@ static const struct dmi_system_id se10_dmi_table[] __initconst = { DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"), DMI_MATCH(DMI_PRODUCT_NAME, "13LK"), }, - .callback = se10_create_platform_device, }, { .ident = "LENOVO-SE10-G2", @@ -302,7 +296,6 @@ static const struct dmi_system_id se10_dmi_table[] __initconst = { DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"), DMI_MATCH(DMI_PRODUCT_NAME, "13S1"), }, - .callback = se10_create_platform_device, }, { .ident = "LENOVO-SE10-G2", @@ -310,7 +303,6 @@ static const struct dmi_system_id se10_dmi_table[] __initconst = { DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"), DMI_MATCH(DMI_PRODUCT_NAME, "13S2"), }, - .callback = se10_create_platform_device, }, { .ident = "LENOVO-SE10-G2", @@ -318,7 +310,6 @@ static const struct dmi_system_id se10_dmi_table[] __initconst = { DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"), DMI_MATCH(DMI_PRODUCT_NAME, "13S3"), }, - .callback = se10_create_platform_device, }, { .ident = "LENOVO-SE10-G2", @@ -326,7 +317,6 @@ static const struct dmi_system_id se10_dmi_table[] __initconst = { DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"), DMI_MATCH(DMI_PRODUCT_NAME, "13S4"), }, - .callback = se10_create_platform_device, }, { .ident = "LENOVO-SE10-G2", @@ -334,7 +324,6 @@ static const struct dmi_system_id se10_dmi_table[] __initconst = { DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"), DMI_MATCH(DMI_PRODUCT_NAME, "13S5"), }, - .callback = se10_create_platform_device, }, { .ident = "LENOVO-SE10-G2", @@ -342,7 +331,6 @@ static const struct dmi_system_id se10_dmi_table[] __initconst = { DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"), DMI_MATCH(DMI_PRODUCT_NAME, "13S6"), }, - .callback = se10_create_platform_device, }, {} }; @@ -350,10 +338,20 @@ MODULE_DEVICE_TABLE(dmi, se10_dmi_table); static int __init se10_wdt_init(void) { + int err; + if (!dmi_check_system(se10_dmi_table)) return -ENODEV; - return platform_driver_register(&se10_wdt_driver); + err = platform_driver_register(&se10_wdt_driver); + if (err) + return err; + + err = se10_create_platform_device(); + if (err) + platform_driver_unregister(&se10_wdt_driver); + + return err; } static void __exit se10_wdt_exit(void) From 550c3bcdda0e9e3b3258b2fa9da040a96db058fb Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Tue, 5 May 2026 09:23:43 -0700 Subject: [PATCH 40/58] watchdog: Remove AMD Elan SC520 processor watchdog driver AMD Elan support was removed from the upstream kernel with commit 8b793a92d862 ("x86/cpu: Remove M486/M486SX/ELAN support"). Its watchdog driver can no longer be enabled except for test builds. Remove it. Signed-off-by: Guenter Roeck --- .../watchdog/watchdog-parameters.rst | 9 - drivers/watchdog/Kconfig | 13 - drivers/watchdog/Makefile | 1 - drivers/watchdog/sc520_wdt.c | 430 ------------------ 4 files changed, 453 deletions(-) delete mode 100644 drivers/watchdog/sc520_wdt.c diff --git a/Documentation/watchdog/watchdog-parameters.rst b/Documentation/watchdog/watchdog-parameters.rst index 173adee71901..8482cc976ce0 100644 --- a/Documentation/watchdog/watchdog-parameters.rst +++ b/Documentation/watchdog/watchdog-parameters.rst @@ -522,15 +522,6 @@ sc1200wdt: ------------------------------------------------- -sc520_wdt: - timeout: - Watchdog timeout in seconds. (1 <= timeout <= 3600, default=30) - nowayout: - Watchdog cannot be stopped once started - (default=kernel config parameter) - -------------------------------------------------- - sch311x_wdt: force_id: Override the detected device ID diff --git a/drivers/watchdog/Kconfig b/drivers/watchdog/Kconfig index 4c204951bc95..d1d2cdbd1995 100644 --- a/drivers/watchdog/Kconfig +++ b/drivers/watchdog/Kconfig @@ -1270,19 +1270,6 @@ config GEODE_WDT You can compile this driver directly into the kernel, or use it as a module. The module will be called geodewdt. -config SC520_WDT - tristate "AMD Elan SC520 processor Watchdog" - depends on MELAN || COMPILE_TEST - help - This is the driver for the hardware watchdog built in to the - AMD "Elan" SC520 microcomputer commonly used in embedded systems. - This watchdog simply watches your kernel to make sure it doesn't - freeze, and if it does, it reboots your computer after a certain - amount of time. - - You can compile this driver directly into the kernel, or use - it as a module. The module will be called sc520_wdt. - config SBC_FITPC2_WATCHDOG tristate "Compulab SBC-FITPC2 watchdog" depends on (X86 || COMPILE_TEST) && HAS_IOPORT diff --git a/drivers/watchdog/Makefile b/drivers/watchdog/Makefile index a4341b251bdf..bc1d52220f22 100644 --- a/drivers/watchdog/Makefile +++ b/drivers/watchdog/Makefile @@ -117,7 +117,6 @@ obj-$(CONFIG_EXAR_WDT) += exar_wdt.o obj-$(CONFIG_F71808E_WDT) += f71808e_wdt.o obj-$(CONFIG_SP5100_TCO) += sp5100_tco.o obj-$(CONFIG_GEODE_WDT) += geodewdt.o -obj-$(CONFIG_SC520_WDT) += sc520_wdt.o obj-$(CONFIG_SBC_FITPC2_WATCHDOG) += sbc_fitpc2_wdt.o obj-$(CONFIG_EUROTECH_WDT) += eurotechwdt.o obj-$(CONFIG_IB700_WDT) += ib700wdt.o diff --git a/drivers/watchdog/sc520_wdt.c b/drivers/watchdog/sc520_wdt.c deleted file mode 100644 index 005f62e4a4fb..000000000000 --- a/drivers/watchdog/sc520_wdt.c +++ /dev/null @@ -1,430 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -/* - * AMD Elan SC520 processor Watchdog Timer driver - * - * Based on acquirewdt.c by Alan Cox, - * and sbc60xxwdt.c by Jakob Oestergaard - * - * The authors do NOT admit liability nor provide warranty for - * any of this software. This material is provided "AS-IS" in - * the hope that it may be useful for others. - * - * (c) Copyright 2001 Scott Jennings - * 9/27 - 2001 [Initial release] - * - * Additional fixes Alan Cox - * - Fixed formatting - * - Removed debug printks - * - Fixed SMP built kernel deadlock - * - Switched to private locks not lock_kernel - * - Used ioremap/writew/readw - * - Added NOWAYOUT support - * 4/12 - 2002 Changes by Rob Radez - * - Change comments - * - Eliminate fop_llseek - * - Change CONFIG_WATCHDOG_NOWAYOUT semantics - * - Add KERN_* tags to printks - * - fix possible wdt_is_open race - * - Report proper capabilities in watchdog_info - * - Add WDIOC_{GETSTATUS, GETBOOTSTATUS, SETTIMEOUT, - * GETTIMEOUT, SETOPTIONS} ioctls - * 09/8 - 2003 Changes by Wim Van Sebroeck - * - cleanup of trailing spaces - * - added extra printk's for startup problems - * - use module_param - * - made timeout (the emulated heartbeat) a module_param - * - made the keepalive ping an internal subroutine - * 3/27 - 2004 Changes by Sean Young - * - set MMCR_BASE to 0xfffef000 - * - CBAR does not need to be read - * - removed debugging printks - * - * This WDT driver is different from most other Linux WDT - * drivers in that the driver will ping the watchdog by itself, - * because this particular WDT has a very short timeout (1.6 - * seconds) and it would be insane to count on any userspace - * daemon always getting scheduled within that time frame. - * - * This driver uses memory mapped IO, and spinlock. - */ - -#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - - -/* - * The AMD Elan SC520 timeout value is 492us times a power of 2 (0-7) - * - * 0: 492us 2: 1.01s 4: 4.03s 6: 16.22s - * 1: 503ms 3: 2.01s 5: 8.05s 7: 32.21s - * - * We will program the SC520 watchdog for a timeout of 2.01s. - * If we reset the watchdog every ~250ms we should be safe. - */ - -#define WDT_INTERVAL (HZ/4+1) - -/* - * We must not require too good response from the userspace daemon. - * Here we require the userspace daemon to send us a heartbeat - * char to /dev/watchdog every 30 seconds. - */ - -#define WATCHDOG_TIMEOUT 30 /* 30 sec default timeout */ -/* in seconds, will be multiplied by HZ to get seconds to wait for a ping */ -static int timeout = WATCHDOG_TIMEOUT; -module_param(timeout, int, 0); -MODULE_PARM_DESC(timeout, - "Watchdog timeout in seconds. (1 <= timeout <= 3600, default=" - __MODULE_STRING(WATCHDOG_TIMEOUT) ")"); - -static bool nowayout = WATCHDOG_NOWAYOUT; -module_param(nowayout, bool, 0); -MODULE_PARM_DESC(nowayout, - "Watchdog cannot be stopped once started (default=" - __MODULE_STRING(WATCHDOG_NOWAYOUT) ")"); - -/* - * AMD Elan SC520 - Watchdog Timer Registers - */ -#define MMCR_BASE 0xfffef000 /* The default base address */ -#define OFFS_WDTMRCTL 0xCB0 /* Watchdog Timer Control Register */ - -/* WDT Control Register bit definitions */ -#define WDT_EXP_SEL_01 0x0001 /* [01] Time-out = 496 us (with 33 Mhz clk). */ -#define WDT_EXP_SEL_02 0x0002 /* [02] Time-out = 508 ms (with 33 Mhz clk). */ -#define WDT_EXP_SEL_03 0x0004 /* [03] Time-out = 1.02 s (with 33 Mhz clk). */ -#define WDT_EXP_SEL_04 0x0008 /* [04] Time-out = 2.03 s (with 33 Mhz clk). */ -#define WDT_EXP_SEL_05 0x0010 /* [05] Time-out = 4.07 s (with 33 Mhz clk). */ -#define WDT_EXP_SEL_06 0x0020 /* [06] Time-out = 8.13 s (with 33 Mhz clk). */ -#define WDT_EXP_SEL_07 0x0040 /* [07] Time-out = 16.27s (with 33 Mhz clk). */ -#define WDT_EXP_SEL_08 0x0080 /* [08] Time-out = 32.54s (with 33 Mhz clk). */ -#define WDT_IRQ_FLG 0x1000 /* [12] Interrupt Request Flag */ -#define WDT_WRST_ENB 0x4000 /* [14] Watchdog Timer Reset Enable */ -#define WDT_ENB 0x8000 /* [15] Watchdog Timer Enable */ - -static __u16 __iomem *wdtmrctl; - -static void wdt_timer_ping(struct timer_list *); -static DEFINE_TIMER(timer, wdt_timer_ping); -static unsigned long next_heartbeat; -static unsigned long wdt_is_open; -static char wdt_expect_close; -static DEFINE_SPINLOCK(wdt_spinlock); - -/* - * Whack the dog - */ - -static void wdt_timer_ping(struct timer_list *unused) -{ - /* If we got a heartbeat pulse within the WDT_US_INTERVAL - * we agree to ping the WDT - */ - if (time_before(jiffies, next_heartbeat)) { - /* Ping the WDT */ - spin_lock(&wdt_spinlock); - writew(0xAAAA, wdtmrctl); - writew(0x5555, wdtmrctl); - spin_unlock(&wdt_spinlock); - - /* Re-set the timer interval */ - mod_timer(&timer, jiffies + WDT_INTERVAL); - } else - pr_warn("Heartbeat lost! Will not ping the watchdog\n"); -} - -/* - * Utility routines - */ - -static void wdt_config(int writeval) -{ - unsigned long flags; - - /* buy some time (ping) */ - spin_lock_irqsave(&wdt_spinlock, flags); - readw(wdtmrctl); /* ensure write synchronization */ - writew(0xAAAA, wdtmrctl); - writew(0x5555, wdtmrctl); - /* unlock WDT = make WDT configuration register writable one time */ - writew(0x3333, wdtmrctl); - writew(0xCCCC, wdtmrctl); - /* write WDT configuration register */ - writew(writeval, wdtmrctl); - spin_unlock_irqrestore(&wdt_spinlock, flags); -} - -static int wdt_startup(void) -{ - next_heartbeat = jiffies + (timeout * HZ); - - /* Start the timer */ - mod_timer(&timer, jiffies + WDT_INTERVAL); - - /* Start the watchdog */ - wdt_config(WDT_ENB | WDT_WRST_ENB | WDT_EXP_SEL_04); - - pr_info("Watchdog timer is now enabled\n"); - return 0; -} - -static int wdt_turnoff(void) -{ - /* Stop the timer */ - timer_delete_sync(&timer); - - /* Stop the watchdog */ - wdt_config(0); - - pr_info("Watchdog timer is now disabled...\n"); - return 0; -} - -static int wdt_keepalive(void) -{ - /* user land ping */ - next_heartbeat = jiffies + (timeout * HZ); - return 0; -} - -static int wdt_set_heartbeat(int t) -{ - if ((t < 1) || (t > 3600)) /* arbitrary upper limit */ - return -EINVAL; - - timeout = t; - return 0; -} - -/* - * /dev/watchdog handling - */ - -static ssize_t fop_write(struct file *file, const char __user *buf, - size_t count, loff_t *ppos) -{ - /* See if we got the magic character 'V' and reload the timer */ - if (count) { - if (!nowayout) { - size_t ofs; - - /* note: just in case someone wrote the magic character - * five months ago... */ - wdt_expect_close = 0; - - /* now scan */ - for (ofs = 0; ofs != count; ofs++) { - char c; - if (get_user(c, buf + ofs)) - return -EFAULT; - if (c == 'V') - wdt_expect_close = 42; - } - } - - /* Well, anyhow someone wrote to us, we should - return that favour */ - wdt_keepalive(); - } - return count; -} - -static int fop_open(struct inode *inode, struct file *file) -{ - /* Just in case we're already talking to someone... */ - if (test_and_set_bit(0, &wdt_is_open)) - return -EBUSY; - if (nowayout) - __module_get(THIS_MODULE); - - /* Good, fire up the show */ - wdt_startup(); - return stream_open(inode, file); -} - -static int fop_close(struct inode *inode, struct file *file) -{ - if (wdt_expect_close == 42) - wdt_turnoff(); - else { - pr_crit("Unexpected close, not stopping watchdog!\n"); - wdt_keepalive(); - } - clear_bit(0, &wdt_is_open); - wdt_expect_close = 0; - return 0; -} - -static long fop_ioctl(struct file *file, unsigned int cmd, unsigned long arg) -{ - void __user *argp = (void __user *)arg; - int __user *p = argp; - static const struct watchdog_info ident = { - .options = WDIOF_KEEPALIVEPING | WDIOF_SETTIMEOUT - | WDIOF_MAGICCLOSE, - .firmware_version = 1, - .identity = "SC520", - }; - - switch (cmd) { - case WDIOC_GETSUPPORT: - return copy_to_user(argp, &ident, sizeof(ident)) ? -EFAULT : 0; - case WDIOC_GETSTATUS: - case WDIOC_GETBOOTSTATUS: - return put_user(0, p); - case WDIOC_SETOPTIONS: - { - int new_options, retval = -EINVAL; - - if (get_user(new_options, p)) - return -EFAULT; - - if (new_options & WDIOS_DISABLECARD) { - wdt_turnoff(); - retval = 0; - } - - if (new_options & WDIOS_ENABLECARD) { - wdt_startup(); - retval = 0; - } - - return retval; - } - case WDIOC_KEEPALIVE: - wdt_keepalive(); - return 0; - case WDIOC_SETTIMEOUT: - { - int new_timeout; - - if (get_user(new_timeout, p)) - return -EFAULT; - - if (wdt_set_heartbeat(new_timeout)) - return -EINVAL; - - wdt_keepalive(); - } - fallthrough; - case WDIOC_GETTIMEOUT: - return put_user(timeout, p); - default: - return -ENOTTY; - } -} - -static const struct file_operations wdt_fops = { - .owner = THIS_MODULE, - .write = fop_write, - .open = fop_open, - .release = fop_close, - .unlocked_ioctl = fop_ioctl, - .compat_ioctl = compat_ptr_ioctl, -}; - -static struct miscdevice wdt_miscdev = { - .minor = WATCHDOG_MINOR, - .name = "watchdog", - .fops = &wdt_fops, -}; - -/* - * Notifier for system down - */ - -static int wdt_notify_sys(struct notifier_block *this, unsigned long code, - void *unused) -{ - if (code == SYS_DOWN || code == SYS_HALT) - wdt_turnoff(); - return NOTIFY_DONE; -} - -/* - * The WDT needs to learn about soft shutdowns in order to - * turn the timebomb registers off. - */ - -static struct notifier_block wdt_notifier = { - .notifier_call = wdt_notify_sys, -}; - -static void __exit sc520_wdt_unload(void) -{ - if (!nowayout) - wdt_turnoff(); - - /* Deregister */ - misc_deregister(&wdt_miscdev); - unregister_reboot_notifier(&wdt_notifier); - iounmap(wdtmrctl); -} - -static int __init sc520_wdt_init(void) -{ - int rc = -EBUSY; - - /* Check that the timeout value is within it's range ; - if not reset to the default */ - if (wdt_set_heartbeat(timeout)) { - wdt_set_heartbeat(WATCHDOG_TIMEOUT); - pr_info("timeout value must be 1 <= timeout <= 3600, using %d\n", - WATCHDOG_TIMEOUT); - } - - wdtmrctl = ioremap(MMCR_BASE + OFFS_WDTMRCTL, 2); - if (!wdtmrctl) { - pr_err("Unable to remap memory\n"); - rc = -ENOMEM; - goto err_out_region2; - } - - rc = register_reboot_notifier(&wdt_notifier); - if (rc) { - pr_err("cannot register reboot notifier (err=%d)\n", rc); - goto err_out_ioremap; - } - - rc = misc_register(&wdt_miscdev); - if (rc) { - pr_err("cannot register miscdev on minor=%d (err=%d)\n", - WATCHDOG_MINOR, rc); - goto err_out_notifier; - } - - pr_info("WDT driver for SC520 initialised. timeout=%d sec (nowayout=%d)\n", - timeout, nowayout); - - return 0; - -err_out_notifier: - unregister_reboot_notifier(&wdt_notifier); -err_out_ioremap: - iounmap(wdtmrctl); -err_out_region2: - return rc; -} - -module_init(sc520_wdt_init); -module_exit(sc520_wdt_unload); - -MODULE_AUTHOR("Scott and Bill Jennings"); -MODULE_DESCRIPTION( - "Driver for watchdog timer in AMD \"Elan\" SC520 uProcessor"); -MODULE_LICENSE("GPL"); From 63e07a8ecb1ea9ac5cdcb267244716d38c942b30 Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Wed, 6 May 2026 10:16:00 +0200 Subject: [PATCH 41/58] watchdog: convert the Kconfig dependency on OF_GPIO to OF OF_GPIO is selected automatically on all OF systems. Any symbols it controls also provide stubs so there's really no reason to select it explicitly. We could simply remove the dependency but in order to avoid a new symbol popping up for everyone in make config - just convert it to requiring CONFIG_OF. Reviewed-by: Guenter Roeck Signed-off-by: Bartosz Golaszewski Link: https://lore.kernel.org/r/20260506081600.4921-1-bartosz.golaszewski@oss.qualcomm.com [groeck: Resolved conflict; updated dependencies to require OF _or_ ACPI] Signed-off-by: Guenter Roeck --- drivers/watchdog/Kconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/watchdog/Kconfig b/drivers/watchdog/Kconfig index d1d2cdbd1995..a8fc1d7a5031 100644 --- a/drivers/watchdog/Kconfig +++ b/drivers/watchdog/Kconfig @@ -250,7 +250,7 @@ config DA9062_WATCHDOG config GPIO_WATCHDOG tristate "Watchdog device controlled through GPIO-line" - depends on (ACPI && GPIOLIB) || OF_GPIO + depends on GPIOLIB && (ACPI || OF) select WATCHDOG_CORE help If you say yes here you get support for watchdog device From 29d71b732bdc43f0c13a4e7258c007bf841a2d29 Mon Sep 17 00:00:00 2001 From: Jonas Karlman Date: Wed, 6 May 2026 11:24:20 +0200 Subject: [PATCH 42/58] dt-bindings: watchdog: Add watchdog compatible for RK3528 The RK3528 uses the same watchdog block as all previous Rockchip SoCs. So add a compatible for it to the soc-list. Signed-off-by: Jonas Karlman Signed-off-by: Heiko Stuebner Acked-by: Conor Dooley Link: https://lore.kernel.org/r/20260506092420.3320031-1-heiko@sntech.de Signed-off-by: Guenter Roeck --- Documentation/devicetree/bindings/watchdog/snps,dw-wdt.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/Documentation/devicetree/bindings/watchdog/snps,dw-wdt.yaml b/Documentation/devicetree/bindings/watchdog/snps,dw-wdt.yaml index 141c72909b15..9d25f5e497e2 100644 --- a/Documentation/devicetree/bindings/watchdog/snps,dw-wdt.yaml +++ b/Documentation/devicetree/bindings/watchdog/snps,dw-wdt.yaml @@ -29,6 +29,7 @@ properties: - rockchip,rk3368-wdt - rockchip,rk3399-wdt - rockchip,rk3506-wdt + - rockchip,rk3528-wdt - rockchip,rk3562-wdt - rockchip,rk3568-wdt - rockchip,rk3576-wdt From fafd036b80a7c85aa86763e70a1a608aa398a144 Mon Sep 17 00:00:00 2001 From: Wolfram Sang Date: Thu, 7 May 2026 12:24:08 +0200 Subject: [PATCH 43/58] watchdog: rzn1: remove now obsolete interrupt support Previously, it was overlooked that the watchdog could reset the system directly. So, a workaround using the interrupt which called emergency_restart() was implemented. We now configure the controller when booting properly to allow watchdog resets directly. Thus, remove the interrupt workaround. Signed-off-by: Wolfram Sang Reviewed-by: Herve Codina Link: https://lore.kernel.org/r/20260507102410.43384-4-wsa+renesas@sang-engineering.com Signed-off-by: Guenter Roeck --- drivers/watchdog/rzn1_wdt.c | 19 ------------------- 1 file changed, 19 deletions(-) diff --git a/drivers/watchdog/rzn1_wdt.c b/drivers/watchdog/rzn1_wdt.c index 48d5afef62a5..4fdc5363ba98 100644 --- a/drivers/watchdog/rzn1_wdt.c +++ b/drivers/watchdog/rzn1_wdt.c @@ -79,14 +79,6 @@ static int rzn1_wdt_start(struct watchdog_device *w) return 0; } -static irqreturn_t rzn1_wdt_irq(int irq, void *_wdt) -{ - pr_crit("RZN1 Watchdog. Initiating system reboot\n"); - emergency_restart(); - - return IRQ_HANDLED; -} - static struct watchdog_info rzn1_wdt_info = { .identity = "RZ/N1 Watchdog", .options = WDIOF_MAGICCLOSE | WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING, @@ -101,12 +93,10 @@ static const struct watchdog_ops rzn1_wdt_ops = { static int rzn1_wdt_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; - struct device_node *np = dev->of_node; struct rzn1_watchdog *wdt; unsigned long clk_rate; struct clk *clk; int ret; - int irq; wdt = devm_kzalloc(dev, sizeof(*wdt), GFP_KERNEL); if (!wdt) @@ -116,15 +106,6 @@ static int rzn1_wdt_probe(struct platform_device *pdev) if (IS_ERR(wdt->base)) return PTR_ERR(wdt->base); - irq = platform_get_irq(pdev, 0); - if (irq < 0) - return irq; - - ret = devm_request_irq(dev, irq, rzn1_wdt_irq, 0, - np->name, wdt); - if (ret) - return dev_err_probe(dev, ret, "failed to request irq %d\n", irq); - clk = devm_clk_get_enabled(dev, NULL); if (IS_ERR(clk)) return dev_err_probe(dev, PTR_ERR(clk), "failed to get the clock\n"); From 14ca4868886f2188401fe06cd7bf01a330b3fb99 Mon Sep 17 00:00:00 2001 From: Janne Grunau Date: Wed, 31 Dec 2025 13:07:21 +0100 Subject: [PATCH 44/58] watchdog: apple: Add "apple,t8103-wdt" compatible After discussion with the devicetree maintainers we agreed to not extend lists with the generic compatible "apple,wdt" anymore [1]. Use "apple,t8103-wdt" as base compatible as it is the SoC the driver and bindings were written for. [1]: https://lore.kernel.org/asahi/12ab93b7-1fc2-4ce0-926e-c8141cfe81bf@kernel.org/ Fixes: 4ed224aeaf66 ("watchdog: Add Apple SoC watchdog driver") Cc: stable@vger.kernel.org Reviewed-by: Neal Gompa Signed-off-by: Janne Grunau Link: https://lore.kernel.org/r/20251231-watchdog-apple-t8103-base-compat-v1-1-1702a02e0c45@jannau.net Signed-off-by: Guenter Roeck --- drivers/watchdog/apple_wdt.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/watchdog/apple_wdt.c b/drivers/watchdog/apple_wdt.c index 66a158f67a71..6b9b0f9b05ce 100644 --- a/drivers/watchdog/apple_wdt.c +++ b/drivers/watchdog/apple_wdt.c @@ -218,6 +218,7 @@ static int apple_wdt_suspend(struct device *dev) static DEFINE_SIMPLE_DEV_PM_OPS(apple_wdt_pm_ops, apple_wdt_suspend, apple_wdt_resume); static const struct of_device_id apple_wdt_of_match[] = { + { .compatible = "apple,t8103-wdt" }, { .compatible = "apple,wdt" }, {}, }; From cb0c1fd6e64d53d29f449fd149defc94aee37af4 Mon Sep 17 00:00:00 2001 From: Janne Grunau Date: Thu, 7 May 2026 09:33:08 +0200 Subject: [PATCH 45/58] dt-bindings: watchdog: apple,wdt: Add t8122 compatible The watchdog on the Apple silicon t8122 (M3) SoC is compatible with the existing driver. Add "apple,t8122-wdt" as SoC specific compatible under "apple,t8103-wdt" used by the driver. Acked-by: Rob Herring (Arm) Reviewed-by: Joshua Peisach Reviewed-by: Neal Gompa Signed-off-by: Janne Grunau Link: https://lore.kernel.org/r/20260507-apple-m3-initial-devicetrees-v3-2-ca07c81b5dc7@jannau.net Signed-off-by: Guenter Roeck --- Documentation/devicetree/bindings/watchdog/apple,wdt.yaml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/Documentation/devicetree/bindings/watchdog/apple,wdt.yaml b/Documentation/devicetree/bindings/watchdog/apple,wdt.yaml index 05602678c070..845b5e8b5abc 100644 --- a/Documentation/devicetree/bindings/watchdog/apple,wdt.yaml +++ b/Documentation/devicetree/bindings/watchdog/apple,wdt.yaml @@ -16,7 +16,9 @@ properties: compatible: oneOf: - items: - - const: apple,t6020-wdt + - enum: + - apple,t6020-wdt + - apple,t8122-wdt - const: apple,t8103-wdt - items: - enum: From 679b7a57fda5543b447cb17503d4a8a26f0725e1 Mon Sep 17 00:00:00 2001 From: Wolfram Sang Date: Thu, 7 May 2026 12:24:09 +0200 Subject: [PATCH 46/58] dt-bindings: watchdog: renesas,rzn1-wdt: interrupts are not required It is now understood how the watchdog can do its job without the need of an interrupt. So, it is not required anymore but optional. Signed-off-by: Wolfram Sang Reviewed-by: Herve Codina Reviewed-by: Guenter Roeck Acked-by: Rob Herring (Arm) Link: https://lore.kernel.org/r/20260507102410.43384-5-wsa+renesas@sang-engineering.com Signed-off-by: Guenter Roeck --- Documentation/devicetree/bindings/watchdog/renesas,rzn1-wdt.yaml | 1 - 1 file changed, 1 deletion(-) diff --git a/Documentation/devicetree/bindings/watchdog/renesas,rzn1-wdt.yaml b/Documentation/devicetree/bindings/watchdog/renesas,rzn1-wdt.yaml index 7e3ee533cd56..0e4b5b529e9c 100644 --- a/Documentation/devicetree/bindings/watchdog/renesas,rzn1-wdt.yaml +++ b/Documentation/devicetree/bindings/watchdog/renesas,rzn1-wdt.yaml @@ -29,7 +29,6 @@ properties: required: - compatible - reg - - interrupts - clocks allOf: From f83da91c7e038c8753c44a85dc9fe2fcf68db17a Mon Sep 17 00:00:00 2001 From: Kathiravan Thirumoorthy Date: Mon, 11 May 2026 20:10:23 +0530 Subject: [PATCH 47/58] dt-bindings: watchdog: qcom-wdt: Document IPQ9650 watchdog Document the watchdog device found on the Qualcomm IPQ9650 SoC. Signed-off-by: Kathiravan Thirumoorthy Acked-by: Krzysztof Kozlowski Link: https://lore.kernel.org/r/20260511-ipq9650_wdt_binding-v2-1-dca22892d3d3@oss.qualcomm.com Signed-off-by: Guenter Roeck --- Documentation/devicetree/bindings/watchdog/qcom-wdt.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/Documentation/devicetree/bindings/watchdog/qcom-wdt.yaml b/Documentation/devicetree/bindings/watchdog/qcom-wdt.yaml index b3f8f063f282..d647bf5f19a4 100644 --- a/Documentation/devicetree/bindings/watchdog/qcom-wdt.yaml +++ b/Documentation/devicetree/bindings/watchdog/qcom-wdt.yaml @@ -24,6 +24,7 @@ properties: - qcom,apss-wdt-ipq5332 - qcom,apss-wdt-ipq5424 - qcom,apss-wdt-ipq9574 + - qcom,apss-wdt-ipq9650 - qcom,apss-wdt-kaanapali - qcom,apss-wdt-msm8226 - qcom,apss-wdt-msm8974 From 05d871c5194bef01b525ca9289efb736bbfd6ece Mon Sep 17 00:00:00 2001 From: Sander Vanheule Date: Fri, 15 May 2026 23:23:50 +0200 Subject: [PATCH 48/58] watchdog: realtek-otto: prevent PHASE2 underflows For small pretimeout values, ((timeout - pretimeout) / tick) might be rounded up to the same value as (timeout / tick). As a result, the number of PHASE2 ticks may be zero, causing an underflow when subtracting 1 to configure the hardware. While this results in a longer-than-expected time to system reset, the duration of PHASE1 and minimum ping interval for the watchdog would still be correct. As the watchdog core ensures pretimeout is strictly less than timeout, ceil(timeout / tick) is strictly greater than floor(pretimeout / tick) and the number of PHASE1 ticks cannot be 0. So instead of rounding up the number of PHASE1 ticks, we can round down the number of PHASE2 ticks, maintaining the current behavior while avoiding underflows. The original helper function is now inlined, as it doesn't save any duplication anymore. Signed-off-by: Sander Vanheule Link: https://lore.kernel.org/r/20260515212351.752054-2-sander@svanheule.net Signed-off-by: Guenter Roeck --- drivers/watchdog/realtek_otto_wdt.c | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-) diff --git a/drivers/watchdog/realtek_otto_wdt.c b/drivers/watchdog/realtek_otto_wdt.c index 2c30ddd574c5..1495e8a8cbb5 100644 --- a/drivers/watchdog/realtek_otto_wdt.c +++ b/drivers/watchdog/realtek_otto_wdt.c @@ -114,12 +114,6 @@ static int otto_wdt_tick_ms(struct otto_wdt_ctrl *ctrl, int prescale) * the value stored in those fields. This means each phase will run for at least * one tick, so small values need to be clamped to correctly reflect the timeout. */ -static inline unsigned int div_round_ticks(unsigned int val, unsigned int tick_duration, - unsigned int min_ticks) -{ - return max(min_ticks, DIV_ROUND_UP(val, tick_duration)); -} - static int otto_wdt_determine_timeouts(struct watchdog_device *wdev, unsigned int timeout, unsigned int pretimeout) { @@ -140,9 +134,9 @@ static int otto_wdt_determine_timeouts(struct watchdog_device *wdev, unsigned in return -EINVAL; tick_ms = otto_wdt_tick_ms(ctrl, prescale); - total_ticks = div_round_ticks(timeout_ms, tick_ms, 2); - phase1_ticks = div_round_ticks(timeout_ms - pretimeout_ms, tick_ms, 1); - phase2_ticks = total_ticks - phase1_ticks; + total_ticks = max(2, DIV_ROUND_UP(timeout_ms, tick_ms)); + phase2_ticks = max(1, pretimeout_ms / tick_ms); + phase1_ticks = total_ticks - phase2_ticks; prescale_next++; } while (phase1_ticks > OTTO_WDT_PHASE_TICKS_MAX From 1147eb0dc29bd33aa3499f9d02777d4165587575 Mon Sep 17 00:00:00 2001 From: Sander Vanheule Date: Fri, 15 May 2026 23:23:51 +0200 Subject: [PATCH 49/58] watchdog: realtek-otto: enable clock before using I/O As the watchdog is normally on the same bus as the UART peripheral, the bootloader will have ensured the bus' clock is up and running before the watchdog driver is probed. Nevertheless, let's do things the right way and enable the watchdog's clock before performing I/O accesses. Signed-off-by: Sander Vanheule Link: https://lore.kernel.org/r/20260515212351.752054-3-sander@svanheule.net Signed-off-by: Guenter Roeck --- drivers/watchdog/realtek_otto_wdt.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/watchdog/realtek_otto_wdt.c b/drivers/watchdog/realtek_otto_wdt.c index 1495e8a8cbb5..01b3ef89bacf 100644 --- a/drivers/watchdog/realtek_otto_wdt.c +++ b/drivers/watchdog/realtek_otto_wdt.c @@ -296,15 +296,15 @@ static int otto_wdt_probe(struct platform_device *pdev) if (IS_ERR(ctrl->base)) return PTR_ERR(ctrl->base); + ret = otto_wdt_probe_clk(ctrl); + if (ret) + return ret; + /* Clear any old interrupts and reset initial state */ iowrite32(OTTO_WDT_INTR_PHASE_1 | OTTO_WDT_INTR_PHASE_2, ctrl->base + OTTO_WDT_REG_INTR); iowrite32(OTTO_WDT_CTRL_DEFAULT, ctrl->base + OTTO_WDT_REG_CTRL); - ret = otto_wdt_probe_clk(ctrl); - if (ret) - return ret; - ctrl->irq_phase1 = platform_get_irq_byname(pdev, "phase1"); if (ctrl->irq_phase1 < 0) return ctrl->irq_phase1; From c574bdb524095d24169e229b2e3b9318c72e733a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Uwe=20Kleine-K=C3=B6nig=20=28The=20Capable=20Hub=29?= Date: Mon, 18 May 2026 19:19:01 +0200 Subject: [PATCH 50/58] watchdog: ziirave_wdt: Use named initializers for struct i2c_device_id MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit While being less compact, using named initializers allows to more easily see which members of the structs are assigned which value without having to lookup the declaration of the struct. And it's also more robust against changes to the struct definition. This patch doesn't modify the compiled arrays, only their representation in source form benefits. The former was confirmed with x86 and arm64 builds. Signed-off-by: Uwe Kleine-König (The Capable Hub) Link: https://lore.kernel.org/r/20260518171901.904094-2-u.kleine-koenig@baylibre.com Signed-off-by: Guenter Roeck --- drivers/watchdog/ziirave_wdt.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/watchdog/ziirave_wdt.c b/drivers/watchdog/ziirave_wdt.c index 5c6e3fa001d8..f3bb935c08c2 100644 --- a/drivers/watchdog/ziirave_wdt.c +++ b/drivers/watchdog/ziirave_wdt.c @@ -718,7 +718,7 @@ static void ziirave_wdt_remove(struct i2c_client *client) } static const struct i2c_device_id ziirave_wdt_id[] = { - { "rave-wdt" }, + { .name = "rave-wdt" }, { } }; MODULE_DEVICE_TABLE(i2c, ziirave_wdt_id); From 0f3999f528715d4d8f45802e9527c54b92350413 Mon Sep 17 00:00:00 2001 From: Randy Dunlap Date: Fri, 29 May 2026 14:20:20 -0700 Subject: [PATCH 51/58] watchdog: add devm_watchdog_register_device() to watchdog-kernel-api devm_watchdog_register_device() is not documented. Add it to the current kernel API documentation. Signed-off-by: Randy Dunlap Signed-off-by: Guenter Roeck --- Documentation/watchdog/watchdog-kernel-api.rst | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/Documentation/watchdog/watchdog-kernel-api.rst b/Documentation/watchdog/watchdog-kernel-api.rst index e2c1386c9550..7a907cb6f3d3 100644 --- a/Documentation/watchdog/watchdog-kernel-api.rst +++ b/Documentation/watchdog/watchdog-kernel-api.rst @@ -42,6 +42,14 @@ The watchdog subsystem includes an registration deferral mechanism, which allows you to register an watchdog as early as you wish during the boot process. +There is also a resource-managed watchdog_register_device(), +devm_watchdog_register_device(). If you use this to register a watchdog +device, watchdog_unregister_device() is called automatically on driver +detach:: + + int devm_watchdog_register_device(struct device *dev, + struct watchdog_device *wdd); + The watchdog device structure looks like this:: struct watchdog_device { From 4d72cac328911a0ceb2292678b1f6163adeffca1 Mon Sep 17 00:00:00 2001 From: Randy Dunlap Date: Fri, 29 May 2026 14:20:21 -0700 Subject: [PATCH 52/58] watchdog: linux/watchdog.h: repair kernel-doc comments Convert struct comments to correct kernel-doc format and add one missing struct member description. Signed-off-by: Randy Dunlap Signed-off-by: Guenter Roeck --- include/linux/watchdog.h | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/include/linux/watchdog.h b/include/linux/watchdog.h index 62cdd26fd025..29cd03686154 100644 --- a/include/linux/watchdog.h +++ b/include/linux/watchdog.h @@ -26,7 +26,8 @@ struct watchdog_device; struct watchdog_core_data; struct watchdog_governor; -/** struct watchdog_ops - The watchdog-devices operations +/** + * struct watchdog_ops - The watchdog-devices operations * * @owner: The module owner. * @start: The routine for starting the watchdog device. @@ -59,7 +60,8 @@ struct watchdog_ops { long (*ioctl)(struct watchdog_device *, unsigned int, unsigned long); }; -/** struct watchdog_device - The structure that defines a watchdog device +/** + * struct watchdog_device - The structure that defines a watchdog device * * @id: The watchdog's ID. (Allocated by watchdog_register_device) * @parent: The parent bus device @@ -83,6 +85,8 @@ struct watchdog_ops { * Replaces max_timeout if specified. * @reboot_nb: The notifier block to stop watchdog on reboot. * @restart_nb: The notifier block to register a restart function. + * @pm_nb: The notifier block to stop watchdog on suspend and restart it + * on resume. * @driver_data:Pointer to the drivers private data. * @wd_data: Pointer to watchdog core internal data. * @status: Field that contains the devices internal status bits. From 38ecabc9af8be21fcb07f19634b56b6ec2d70268 Mon Sep 17 00:00:00 2001 From: Randy Dunlap Date: Fri, 29 May 2026 14:20:22 -0700 Subject: [PATCH 53/58] watchdog: uapi: add comments for what bit masks apply to Add comments similar to those in include/linux/watchdog.h so that the reader/user doesn't have to dig into the API documentation files for this. Signed-off-by: Randy Dunlap Signed-off-by: Guenter Roeck --- include/uapi/linux/watchdog.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/include/uapi/linux/watchdog.h b/include/uapi/linux/watchdog.h index b15cde5c9054..fc73c17ee182 100644 --- a/include/uapi/linux/watchdog.h +++ b/include/uapi/linux/watchdog.h @@ -36,6 +36,7 @@ struct watchdog_info { #define WDIOF_UNKNOWN -1 /* Unknown flag error */ #define WDIOS_UNKNOWN -1 /* Unknown status error */ +/* Bit masks for watchdog_info.options, GETSTATUS and GETBOOTSTATUS ioctls */ #define WDIOF_OVERHEAT 0x0001 /* Reset due to CPU overheat */ #define WDIOF_FANFAULT 0x0002 /* Fan failed */ #define WDIOF_EXTERN1 0x0004 /* External relay 1 */ @@ -50,6 +51,7 @@ struct watchdog_info { other external alarm not a reboot */ #define WDIOF_KEEPALIVEPING 0x8000 /* Keep alive ping reply */ +/* Bit masks for WDIOC_SETOPTIONS ioctl */ #define WDIOS_DISABLECARD 0x0001 /* Turn off the watchdog timer */ #define WDIOS_ENABLECARD 0x0002 /* Turn on the watchdog timer */ #define WDIOS_TEMPPANIC 0x0004 /* Kernel panic on temperature trip */ From c5be907887350812624f8bac4671a0f7e6c22c92 Mon Sep 17 00:00:00 2001 From: Randy Dunlap Date: Fri, 29 May 2026 14:20:23 -0700 Subject: [PATCH 54/58] watchdog: core: clean up some comments Fix some grammar typos and bulleted kernel-doc comment format. Signed-off-by: Randy Dunlap Signed-off-by: Guenter Roeck --- drivers/watchdog/watchdog_core.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/drivers/watchdog/watchdog_core.c b/drivers/watchdog/watchdog_core.c index 8300520688d0..866874b26819 100644 --- a/drivers/watchdog/watchdog_core.c +++ b/drivers/watchdog/watchdog_core.c @@ -55,9 +55,9 @@ MODULE_PARM_DESC(stop_on_reboot, "Stop watchdogs on reboot (0=keep watching, 1=s * for example when it's impossible to disable it. To do so, * raising the initcall level of the watchdog driver is a solution. * But in such case, the miscdev is maybe not ready (subsys_initcall), and - * watchdog_core need miscdev to register the watchdog as a char device. + * watchdog_core needs miscdev to register the watchdog as a char device. * - * The deferred registration infrastructure offer a way for the watchdog + * The deferred registration infrastructure offers a way for the watchdog * subsystem to register a watchdog properly, even before miscdev is ready. */ @@ -222,11 +222,11 @@ static int watchdog_pm_notifier(struct notifier_block *nb, unsigned long mode, * watchdog_set_restart_priority - Change priority of restart handler * @wdd: watchdog device * @priority: priority of the restart handler, should follow these guidelines: - * 0: use watchdog's restart function as last resort, has limited restart - * capabilies - * 128: default restart handler, use if no other handler is expected to be + * * 0: use watchdog's restart function as last resort, has limited restart + * capabilities + * * 128: default restart handler, use if no other handler is expected to be * available and/or if restart is sufficient to restart the entire system - * 255: preempt all other handlers + * * 255: preempt all other handlers * * If a wdd->ops->restart function is provided when watchdog_register_device is * called, it will be registered as a restart handler with the priority given From 975599591fefd356fc67f9d7675f425bcf3eabf5 Mon Sep 17 00:00:00 2001 From: Randy Dunlap Date: Fri, 29 May 2026 14:20:24 -0700 Subject: [PATCH 55/58] watchdog: dev: convert to kernel-doc comments Convert multiple functions to kernel-doc format. Signed-off-by: Randy Dunlap Signed-off-by: Guenter Roeck --- drivers/watchdog/watchdog_dev.c | 30 +++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/drivers/watchdog/watchdog_dev.c b/drivers/watchdog/watchdog_dev.c index 834f65f4b59a..d7895009a2de 100644 --- a/drivers/watchdog/watchdog_dev.c +++ b/drivers/watchdog/watchdog_dev.c @@ -176,7 +176,7 @@ static int __watchdog_ping(struct watchdog_device *wdd) return err; } -/* +/** * watchdog_ping - ping the watchdog * @wdd: The watchdog device to ping * @@ -236,7 +236,7 @@ static enum hrtimer_restart watchdog_timer_expired(struct hrtimer *timer) return HRTIMER_NORESTART; } -/* +/** * watchdog_start - wrapper to start the watchdog * @wdd: The watchdog device to start * @@ -279,7 +279,7 @@ static int watchdog_start(struct watchdog_device *wdd) return err; } -/* +/** * watchdog_stop - wrapper to stop the watchdog * @wdd: The watchdog device to stop * @@ -319,7 +319,7 @@ static int watchdog_stop(struct watchdog_device *wdd) return err; } -/* +/** * watchdog_get_status - wrapper to get the watchdog status * @wdd: The watchdog device to get the status from * @@ -356,7 +356,7 @@ static unsigned int watchdog_get_status(struct watchdog_device *wdd) return status; } -/* +/** * watchdog_set_timeout - set the watchdog timer timeout * @wdd: The watchdog device to set the timeout for * @timeout: Timeout to set in seconds @@ -391,7 +391,7 @@ static int watchdog_set_timeout(struct watchdog_device *wdd, return err; } -/* +/** * watchdog_set_pretimeout - set the watchdog timer pretimeout * @wdd: The watchdog device to set the timeout for * @timeout: pretimeout to set in seconds @@ -417,7 +417,7 @@ static int watchdog_set_pretimeout(struct watchdog_device *wdd, return err; } -/* +/** * watchdog_get_timeleft - wrapper to get the time left before a reboot * @wdd: The watchdog device to get the remaining time from * @timeleft: The time that's left @@ -659,7 +659,7 @@ __ATTRIBUTE_GROUPS(wdt); #define wdt_groups NULL #endif -/* +/** * watchdog_ioctl_op - call the watchdog drivers ioctl op if defined * @wdd: The watchdog device to do the ioctl on * @cmd: Watchdog command @@ -678,7 +678,7 @@ static int watchdog_ioctl_op(struct watchdog_device *wdd, unsigned int cmd, return wdd->ops->ioctl(wdd, cmd, arg); } -/* +/** * watchdog_write - writes to the watchdog * @file: File from VFS * @data: User address of data @@ -732,7 +732,7 @@ static ssize_t watchdog_write(struct file *file, const char __user *data, return len; } -/* +/** * watchdog_ioctl - handle the different ioctl's for the watchdog device * @file: File handle to the device * @cmd: Watchdog command @@ -845,7 +845,7 @@ static long watchdog_ioctl(struct file *file, unsigned int cmd, return err; } -/* +/** * watchdog_open - open the /dev/watchdog* devices * @inode: Inode of device * @file: File handle to device @@ -923,7 +923,7 @@ static void watchdog_core_data_release(struct device *dev) kfree(wd_data); } -/* +/** * watchdog_release - release the watchdog device * @inode: Inode of device * @file: File handle to device @@ -932,7 +932,7 @@ static void watchdog_core_data_release(struct device *dev) * stop the watchdog when we have received the magic char (and nowayout * was not set), else the watchdog will keep running. * - * Always returns 0. + * Returns: Always 0. */ static int watchdog_release(struct inode *inode, struct file *file) { @@ -1004,7 +1004,7 @@ static const struct class watchdog_class = { .dev_groups = wdt_groups, }; -/* +/** * watchdog_cdev_register - register watchdog character device * @wdd: Watchdog device * @@ -1105,7 +1105,7 @@ static int watchdog_cdev_register(struct watchdog_device *wdd) return 0; } -/* +/** * watchdog_cdev_unregister - unregister watchdog character device * @wdd: Watchdog device * From 942d413e9ce7896ff80b15a3ca75af44207f618b Mon Sep 17 00:00:00 2001 From: Kathiravan Thirumoorthy Date: Mon, 11 May 2026 16:19:13 +0530 Subject: [PATCH 56/58] dt-bindings: watchdog: qcom-wdt: Document IPQ5210 watchdog Document the watchdog device found on the Qualcomm IPQ5210 SoC. Signed-off-by: Kathiravan Thirumoorthy Acked-by: Rob Herring (Arm) Link: https://lore.kernel.org/r/20260511-ipq5210_wdt_binding-v1-1-859003d48274@oss.qualcomm.com Signed-off-by: Guenter Roeck --- Documentation/devicetree/bindings/watchdog/qcom-wdt.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/Documentation/devicetree/bindings/watchdog/qcom-wdt.yaml b/Documentation/devicetree/bindings/watchdog/qcom-wdt.yaml index d647bf5f19a4..74117f5726a7 100644 --- a/Documentation/devicetree/bindings/watchdog/qcom-wdt.yaml +++ b/Documentation/devicetree/bindings/watchdog/qcom-wdt.yaml @@ -21,6 +21,7 @@ properties: - qcom,apss-wdt-hawi - qcom,kpss-wdt-ipq4019 - qcom,apss-wdt-ipq5018 + - qcom,apss-wdt-ipq5210 - qcom,apss-wdt-ipq5332 - qcom,apss-wdt-ipq5424 - qcom,apss-wdt-ipq9574 From a298c7302ee9584a7a1ac1e8acbede8d98ab51a4 Mon Sep 17 00:00:00 2001 From: Yuho Choi Date: Mon, 1 Jun 2026 15:20:05 -0400 Subject: [PATCH 57/58] watchdog: unregister PM notifier on watchdog unregister watchdog_register_device() registers wdd->pm_nb when WDOG_NO_PING_ON_SUSPEND is set, but watchdog_unregister_device() does not remove it. This leaves an embedded notifier block on the PM notifier chain after the watchdog device has been unregistered. A later suspend/resume notification can then call watchdog_pm_notifier() with a stale watchdog_device pointer, or at minimum after wdd->wd_data has been cleared by watchdog_dev_unregister(). Unregister the PM notifier before tearing down the watchdog device. Fixes: 60bcd91aafd2 ("watchdog: introduce watchdog_dev_suspend/resume") Signed-off-by: Yuho Choi Link: https://lore.kernel.org/r/20260601192005.1970805-1-dbgh9129@gmail.com Signed-off-by: Guenter Roeck --- drivers/watchdog/watchdog_core.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/watchdog/watchdog_core.c b/drivers/watchdog/watchdog_core.c index 866874b26819..f4097aefaf49 100644 --- a/drivers/watchdog/watchdog_core.c +++ b/drivers/watchdog/watchdog_core.c @@ -391,6 +391,9 @@ static void __watchdog_unregister_device(struct watchdog_device *wdd) if (test_bit(WDOG_STOP_ON_REBOOT, &wdd->status)) unregister_reboot_notifier(&wdd->reboot_nb); + if (test_bit(WDOG_NO_PING_ON_SUSPEND, &wdd->status)) + unregister_pm_notifier(&wdd->pm_nb); + watchdog_dev_unregister(wdd); ida_free(&watchdog_ida, wdd->id); } From 54c726fbb53341a2553bf234d519d0cb22a2ce3d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Uwe=20Kleine-K=C3=B6nig=20=28The=20Capable=20Hub=29?= Date: Wed, 10 Jun 2026 10:48:22 +0200 Subject: [PATCH 58/58] watchdog: sc1200: Drop unused assignment of pnp_device_id driver data MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The driver explicitly sets the .driver_data member of struct pnp_device_id to zero without relying on that value. Drop this unused assignments. While touching this array simplify the list terminator. This patch doesn't modify the compiled array, only its representation in source form benefits. The former was confirmed with builds on x86 and arm64. Signed-off-by: Uwe Kleine-König (The Capable Hub) Link: https://lore.kernel.org/r/0793f81a854f9e5880ad38f54c8583b3d56e5d60.1781081216.git.u.kleine-koenig@baylibre.com Signed-off-by: Guenter Roeck --- drivers/watchdog/sc1200wdt.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/watchdog/sc1200wdt.c b/drivers/watchdog/sc1200wdt.c index 76a58715f665..1d3300c98d43 100644 --- a/drivers/watchdog/sc1200wdt.c +++ b/drivers/watchdog/sc1200wdt.c @@ -339,8 +339,8 @@ static int __init sc1200wdt_probe(void) static const struct pnp_device_id scl200wdt_pnp_devices[] = { /* National Semiconductor PC87307/PC97307 watchdog component */ - {.id = "NSC0800", .driver_data = 0}, - {.id = ""}, + { .id = "NSC0800" }, + { } }; static int scl200wdt_pnp_probe(struct pnp_dev *dev,