From d4011cda7ff23b0c6561d6edbe6dfd13099169a4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Uwe=20Kleine-K=C3=B6nig=20=28The=20Capable=20Hub=29?= Date: Tue, 16 Jun 2026 17:25:10 +0200 Subject: [PATCH 01/40] watchdog: Use named initializers for platform_device_id arrays MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Named initializers are better readable and more robust to changes of the struct definition. This robustness is relevant for a planned change to struct platform_device_id replacing .driver_data by an anonymous union. For one driver drop the unused assignment to .driver_data instead. While touching these arrays unify spacing and usage of commas. There is no effect on the compiled arrays. Signed-off-by: Uwe Kleine-König (The Capable Hub) Link: https://lore.kernel.org/r/22bc09d0c9c8dfe75a205b0a9ccc98ccfba1de10.1781622532.git.u.kleine-koenig@baylibre.com Signed-off-by: Guenter Roeck --- drivers/watchdog/cros_ec_wdt.c | 4 ++-- drivers/watchdog/max63xx_wdt.c | 14 +++++++------- drivers/watchdog/max77620_wdt.c | 6 +++--- 3 files changed, 12 insertions(+), 12 deletions(-) diff --git a/drivers/watchdog/cros_ec_wdt.c b/drivers/watchdog/cros_ec_wdt.c index 9a4a59b39ed9..2283419d8b38 100644 --- a/drivers/watchdog/cros_ec_wdt.c +++ b/drivers/watchdog/cros_ec_wdt.c @@ -178,8 +178,8 @@ static int __maybe_unused cros_ec_wdt_resume(struct platform_device *pdev) } static const struct platform_device_id cros_ec_wdt_id[] = { - { DRV_NAME, 0 }, - {} + { .name = DRV_NAME }, + { } }; static struct platform_driver cros_ec_wdt_driver = { diff --git a/drivers/watchdog/max63xx_wdt.c b/drivers/watchdog/max63xx_wdt.c index 3b4f3134d1c4..e9073df1a7de 100644 --- a/drivers/watchdog/max63xx_wdt.c +++ b/drivers/watchdog/max63xx_wdt.c @@ -245,13 +245,13 @@ static int max63xx_wdt_probe(struct platform_device *pdev) } static const struct platform_device_id max63xx_id_table[] = { - { "max6369_wdt", (kernel_ulong_t)max6369_table, }, - { "max6370_wdt", (kernel_ulong_t)max6369_table, }, - { "max6371_wdt", (kernel_ulong_t)max6371_table, }, - { "max6372_wdt", (kernel_ulong_t)max6371_table, }, - { "max6373_wdt", (kernel_ulong_t)max6373_table, }, - { "max6374_wdt", (kernel_ulong_t)max6373_table, }, - { }, + { .name = "max6369_wdt", .driver_data = (kernel_ulong_t)max6369_table }, + { .name = "max6370_wdt", .driver_data = (kernel_ulong_t)max6369_table }, + { .name = "max6371_wdt", .driver_data = (kernel_ulong_t)max6371_table }, + { .name = "max6372_wdt", .driver_data = (kernel_ulong_t)max6371_table }, + { .name = "max6373_wdt", .driver_data = (kernel_ulong_t)max6373_table }, + { .name = "max6374_wdt", .driver_data = (kernel_ulong_t)max6373_table }, + { } }; MODULE_DEVICE_TABLE(platform, max63xx_id_table); diff --git a/drivers/watchdog/max77620_wdt.c b/drivers/watchdog/max77620_wdt.c index 6ce435741d97..ed5a98cea525 100644 --- a/drivers/watchdog/max77620_wdt.c +++ b/drivers/watchdog/max77620_wdt.c @@ -235,9 +235,9 @@ static int max77620_wdt_probe(struct platform_device *pdev) } static const struct platform_device_id max77620_wdt_devtype[] = { - { "max77620-watchdog", (kernel_ulong_t)&max77620_wdt_data }, - { "max77714-watchdog", (kernel_ulong_t)&max77714_wdt_data }, - { }, + { .name = "max77620-watchdog", .driver_data = (kernel_ulong_t)&max77620_wdt_data }, + { .name = "max77714-watchdog", .driver_data = (kernel_ulong_t)&max77714_wdt_data }, + { } }; MODULE_DEVICE_TABLE(platform, max77620_wdt_devtype); From c6bbe157d8a2a408eae412171991426fb72b8c8a Mon Sep 17 00:00:00 2001 From: Jingyi Wang Date: Mon, 29 Jun 2026 00:09:05 -0700 Subject: [PATCH 02/40] dt-bindings: watchdog: Document Qualcomm Maili watchdog Add devicetree binding for watchdog present on Qualcomm Maili SoC. Signed-off-by: Jingyi Wang Acked-by: Rob Herring (Arm) Link: https://lore.kernel.org/r/20260629-maili-watchdog-v2-1-5cb9c83a581c@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 74117f5726a7..4ff61102e407 100644 --- a/Documentation/devicetree/bindings/watchdog/qcom-wdt.yaml +++ b/Documentation/devicetree/bindings/watchdog/qcom-wdt.yaml @@ -27,6 +27,7 @@ properties: - qcom,apss-wdt-ipq9574 - qcom,apss-wdt-ipq9650 - qcom,apss-wdt-kaanapali + - qcom,apss-wdt-maili - qcom,apss-wdt-msm8226 - qcom,apss-wdt-msm8974 - qcom,apss-wdt-msm8994 From dac35bdc5c257fd320451ed5c82ddf0f7739768c Mon Sep 17 00:00:00 2001 From: Yureka Lilian Date: Sun, 5 Jul 2026 15:17:22 +0200 Subject: [PATCH 03/40] dt-bindings: watchdog: apple,wdt: Add t8132 compatible The watchdog on the Apple silicon t8132 (M4) SoC is compatible with the existing driver. Add "apple,t8132-wdt" as SoC specific compatible under "apple,t8103-wdt" used by the driver. The M4 wdt block additionally has a secondary watchdog, which is disabled by m1n1 and can safely be ignored by Linux. Signed-off-by: Yureka Lilian Acked-by: Krzysztof Kozlowski Link: https://lore.kernel.org/r/20260705-apple-m4-initial-devicetrees-v1-3-e5655ee56523@cyberchaos.dev Signed-off-by: Guenter Roeck --- Documentation/devicetree/bindings/watchdog/apple,wdt.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/Documentation/devicetree/bindings/watchdog/apple,wdt.yaml b/Documentation/devicetree/bindings/watchdog/apple,wdt.yaml index 845b5e8b5abc..b99e7913e398 100644 --- a/Documentation/devicetree/bindings/watchdog/apple,wdt.yaml +++ b/Documentation/devicetree/bindings/watchdog/apple,wdt.yaml @@ -19,6 +19,7 @@ properties: - enum: - apple,t6020-wdt - apple,t8122-wdt + - apple,t8132-wdt - const: apple,t8103-wdt - items: - enum: From 8bb763d4c3bee266de891f2121703910fdfd3fa1 Mon Sep 17 00:00:00 2001 From: Tzung-Bi Shih Date: Tue, 7 Jul 2026 10:21:05 +0000 Subject: [PATCH 04/40] watchdog: pretimeout: Add "dump" pretimeout governor Add a new "dump" pretimeout governor that triggers a backtrace of all CPUs (via trigger_all_cpu_backtrace()) to the kernel log buffer. This provides diagnostic information right before the hardware watchdog fires. In addition, update the WATCHDOG_PRETIMEOUT_GOV_SEL Kconfig logic to fall back to the "panic" governor only when both "noop" and "dump" governors are disabled. Signed-off-by: Tzung-Bi Shih Link: https://lore.kernel.org/r/20260707102105.3600275-1-tzungbi@kernel.org Signed-off-by: Guenter Roeck --- drivers/watchdog/Kconfig | 19 ++++++++++- drivers/watchdog/Makefile | 1 + drivers/watchdog/pretimeout_dump.c | 45 ++++++++++++++++++++++++++ drivers/watchdog/watchdog_pretimeout.h | 4 ++- 4 files changed, 67 insertions(+), 2 deletions(-) create mode 100644 drivers/watchdog/pretimeout_dump.c diff --git a/drivers/watchdog/Kconfig b/drivers/watchdog/Kconfig index 08cb8612d41f..c93bc0d285f0 100644 --- a/drivers/watchdog/Kconfig +++ b/drivers/watchdog/Kconfig @@ -93,10 +93,19 @@ config WATCHDOG_PRETIMEOUT_GOV_SEL tristate depends on WATCHDOG_PRETIMEOUT_GOV default m - select WATCHDOG_PRETIMEOUT_GOV_PANIC if WATCHDOG_PRETIMEOUT_GOV_NOOP=n + select WATCHDOG_PRETIMEOUT_GOV_PANIC if \ + WATCHDOG_PRETIMEOUT_GOV_NOOP=n && WATCHDOG_PRETIMEOUT_GOV_DUMP=n if WATCHDOG_PRETIMEOUT_GOV +config WATCHDOG_PRETIMEOUT_GOV_DUMP + bool "Dump watchdog pretimeout governor" + depends on WATCHDOG_CORE=y + default WATCHDOG_CORE + help + Dump watchdog pretimeout governor, all cpu backtrace is + added to kernel log buffer. + config WATCHDOG_PRETIMEOUT_GOV_NOOP tristate "Noop watchdog pretimeout governor" depends on WATCHDOG_CORE @@ -121,6 +130,14 @@ choice The governor takes its action, if a watchdog is capable to report a pretimeout event. +config WATCHDOG_PRETIMEOUT_DEFAULT_GOV_DUMP + bool "dump" + depends on WATCHDOG_PRETIMEOUT_GOV_DUMP + help + Use dump watchdog pretimeout governor by default. If dump + governor is selected by a user, dump all cpu backtrace to + the kernel log buffer and don't do any system changes. + config WATCHDOG_PRETIMEOUT_DEFAULT_GOV_NOOP bool "noop" depends on WATCHDOG_PRETIMEOUT_GOV_NOOP diff --git a/drivers/watchdog/Makefile b/drivers/watchdog/Makefile index bc1d52220f22..598556f03bc3 100644 --- a/drivers/watchdog/Makefile +++ b/drivers/watchdog/Makefile @@ -11,6 +11,7 @@ watchdog-objs += watchdog_core.o watchdog_dev.o watchdog-$(CONFIG_WATCHDOG_PRETIMEOUT_GOV) += watchdog_pretimeout.o watchdog-$(CONFIG_WATCHDOG_HRTIMER_PRETIMEOUT) += watchdog_hrtimer_pretimeout.o +obj-$(CONFIG_WATCHDOG_PRETIMEOUT_GOV_DUMP) += pretimeout_dump.o obj-$(CONFIG_WATCHDOG_PRETIMEOUT_GOV_NOOP) += pretimeout_noop.o obj-$(CONFIG_WATCHDOG_PRETIMEOUT_GOV_PANIC) += pretimeout_panic.o diff --git a/drivers/watchdog/pretimeout_dump.c b/drivers/watchdog/pretimeout_dump.c new file mode 100644 index 000000000000..c5d3dac2606c --- /dev/null +++ b/drivers/watchdog/pretimeout_dump.c @@ -0,0 +1,45 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright 2026 Google LLC + */ + +#include +#include +#include +#include + +#include "watchdog_pretimeout.h" + +/** + * pretimeout_dump - Dump on watchdog pretimeout event + * @wdd: watchdog_device + * + * Dump all cpu backtrace on pretimeout event. + */ +static void pretimeout_dump(struct watchdog_device *wdd) +{ + pr_alert("watchdog%d: pretimeout event\n", wdd->id); + if (!trigger_all_cpu_backtrace()) + pr_alert("trigger_all_cpu_backtrace() isn't available\n"); +} + +static struct watchdog_governor watchdog_gov_dump = { + .name = "dump", + .pretimeout = pretimeout_dump, +}; + +static int __init watchdog_gov_dump_register(void) +{ + return watchdog_register_governor(&watchdog_gov_dump); +} + +static void __exit watchdog_gov_dump_unregister(void) +{ + watchdog_unregister_governor(&watchdog_gov_dump); +} +module_init(watchdog_gov_dump_register); +module_exit(watchdog_gov_dump_unregister); + +MODULE_AUTHOR("Tzung-Bi Shih "); +MODULE_DESCRIPTION("Dump watchdog pretimeout governor"); +MODULE_LICENSE("GPL"); diff --git a/drivers/watchdog/watchdog_pretimeout.h b/drivers/watchdog/watchdog_pretimeout.h index a3f1abc68839..57aa790e2669 100644 --- a/drivers/watchdog/watchdog_pretimeout.h +++ b/drivers/watchdog/watchdog_pretimeout.h @@ -24,7 +24,9 @@ int watchdog_pretimeout_governor_get(struct watchdog_device *wdd, char *buf); int watchdog_pretimeout_governor_set(struct watchdog_device *wdd, const char *buf); -#if IS_ENABLED(CONFIG_WATCHDOG_PRETIMEOUT_DEFAULT_GOV_NOOP) +#if IS_ENABLED(CONFIG_WATCHDOG_PRETIMEOUT_DEFAULT_GOV_DUMP) +#define WATCHDOG_PRETIMEOUT_DEFAULT_GOV "dump" +#elif IS_ENABLED(CONFIG_WATCHDOG_PRETIMEOUT_DEFAULT_GOV_NOOP) #define WATCHDOG_PRETIMEOUT_DEFAULT_GOV "noop" #elif IS_ENABLED(CONFIG_WATCHDOG_PRETIMEOUT_DEFAULT_GOV_PANIC) #define WATCHDOG_PRETIMEOUT_DEFAULT_GOV "panic" From aad2cb54573fc002f84e4fbb22724aacf97fbc16 Mon Sep 17 00:00:00 2001 From: Paul Louvel Date: Tue, 7 Jul 2026 17:45:01 +0200 Subject: [PATCH 05/40] watchdog: w83627hf_wdt: Correctly identify Nuvoton NCT6126 chip Currently, the NCT6126 is probed as a NCT6116 because the NCT6126 shares the same high-byte chip ID as the NCT6116. Read the low byte of the chip ID as well to distinguish between the two devices and identify the NCT6126 correctly. Signed-off-by: Paul Louvel Link: https://lore.kernel.org/r/20260707-w83627hf_wdt-nct6126d-v4-1-829e5f63d548@bootlin.com Signed-off-by: Guenter Roeck --- drivers/watchdog/w83627hf_wdt.c | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/drivers/watchdog/w83627hf_wdt.c b/drivers/watchdog/w83627hf_wdt.c index bc33b63c5a5d..a6dfa9d01702 100644 --- a/drivers/watchdog/w83627hf_wdt.c +++ b/drivers/watchdog/w83627hf_wdt.c @@ -49,7 +49,7 @@ static int wdt_cfg_leave = 0xAA;/* key to lock configuration space */ enum chips { w83627hf, w83627s, w83697hf, w83697ug, w83637hf, w83627thf, w83687thf, w83627ehf, w83627dhg, w83627uhg, w83667hg, w83627dhg_p, w83667hg_b, nct6775, nct6776, nct6779, nct6791, nct6792, nct6793, - nct6795, nct6796, nct6102, nct6116 }; + nct6795, nct6796, nct6102, nct6116, nct6126 }; static int timeout; /* in seconds */ module_param(timeout, int, 0); @@ -94,7 +94,9 @@ MODULE_PARM_DESC(early_disable, "Disable watchdog at boot time (default=0)"); #define NCT6775_ID 0xb4 #define NCT6776_ID 0xc3 #define NCT6102_ID 0xc4 -#define NCT6116_ID 0xd2 +#define NCT6116_ID 0xd2 /* also NCT6126D */ +#define NCT6126_VER_A_LOW_ID 0x83 /* ... version A */ +#define NCT6126_VER_B_LOW_ID 0x84 /* ... version B */ #define NCT6779_ID 0xc5 #define NCT6791_ID 0xc8 #define NCT6792_ID 0xc9 @@ -217,6 +219,7 @@ static int w83627hf_init(struct watchdog_device *wdog, enum chips chip) case nct6796: case nct6102: case nct6116: + case nct6126: /* * These chips have a fixed WDTO# output pin (W83627UHG), * or support more than one WDTO# output pin. @@ -428,7 +431,12 @@ static int wdt_find(int addr) cr_wdt_csr = NCT6102D_WDT_CSR; break; case NCT6116_ID: - ret = nct6116; + val = superio_inb(0x21); + if (val == NCT6126_VER_A_LOW_ID || val == NCT6126_VER_B_LOW_ID) + ret = nct6126; + else + ret = nct6116; + cr_wdt_timeout = NCT6102D_WDT_TIMEOUT; cr_wdt_control = NCT6102D_WDT_CONTROL; cr_wdt_csr = NCT6102D_WDT_CSR; @@ -499,6 +507,7 @@ static int __init wdt_init(void) "NCT6796", "NCT6102", "NCT6116", + "NCT6126" }; /* Apply system-specific quirks */ From 3d6757a640f8c92b6cb68a3cfdff9b9fa292a7d3 Mon Sep 17 00:00:00 2001 From: Paul Louvel Date: Tue, 7 Jul 2026 17:45:02 +0200 Subject: [PATCH 06/40] watchdog: w83627hf_wdt: Report the probed chip name via WDIOC_GETSUPPORT The watchdog identity string reported via WDIOC_GETSUPPORT has always been hardcoded to "W83627HF Watchdog", even though this driver supports a wide range of Nuvoton/Winbond Super I/O chips. This is misleading, since the driver itself correctly log the chip name in wdt_init(), but could report later a different chip name with WDIOC_GETSUPPORT. Instead format the identity at probe time using the actual chip name that was detected. Signed-off-by: Paul Louvel Link: https://lore.kernel.org/r/20260707-w83627hf_wdt-nct6126d-v4-2-829e5f63d548@bootlin.com Signed-off-by: Guenter Roeck --- drivers/watchdog/w83627hf_wdt.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/drivers/watchdog/w83627hf_wdt.c b/drivers/watchdog/w83627hf_wdt.c index a6dfa9d01702..bfe132cfea87 100644 --- a/drivers/watchdog/w83627hf_wdt.c +++ b/drivers/watchdog/w83627hf_wdt.c @@ -316,9 +316,8 @@ static unsigned int wdt_get_time(struct watchdog_device *wdog) * Kernel Interfaces */ -static const struct watchdog_info wdt_info = { +static struct watchdog_info wdt_info = { .options = WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING | WDIOF_MAGICCLOSE, - .identity = "W83627HF Watchdog", }; static const struct watchdog_ops wdt_ops = { @@ -525,6 +524,9 @@ static int __init wdt_init(void) pr_info("WDT driver for %s Super I/O chip initialising\n", chip_name[chip]); + snprintf(wdt_info.identity, sizeof(wdt_info.identity), "%s Watchdog", + chip_name[chip]); + watchdog_init_timeout(&wdt_dev, timeout, NULL); watchdog_set_nowayout(&wdt_dev, nowayout); watchdog_stop_on_reboot(&wdt_dev); From c9aab8bba225d786ca05f6f8732946b9944dc728 Mon Sep 17 00:00:00 2001 From: Paul Louvel Date: Tue, 7 Jul 2026 17:45:03 +0200 Subject: [PATCH 07/40] watchdog: w83627hf_wdt: Use WDOG_HW_RUNNING for running chip on boot If the watchdog is already running when the driver is initialized, the driver currently refreshes it once using the default timeout of 60 seconds. Since commit ee142889e32f ("watchdog: Introduce WDOG_HW_RUNNING flag"), the watchdog core can kick an already running watchdog alive until userspace open the device. Add the WDOG_HW_RUNNING flag to the watchdog status if the watchdog is already running. Signed-off-by: Paul Louvel Link: https://lore.kernel.org/r/20260707-w83627hf_wdt-nct6126d-v4-3-829e5f63d548@bootlin.com Signed-off-by: Guenter Roeck --- drivers/watchdog/w83627hf_wdt.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/watchdog/w83627hf_wdt.c b/drivers/watchdog/w83627hf_wdt.c index bfe132cfea87..db77599e43a0 100644 --- a/drivers/watchdog/w83627hf_wdt.c +++ b/drivers/watchdog/w83627hf_wdt.c @@ -244,6 +244,7 @@ static int w83627hf_init(struct watchdog_device *wdog, enum chips chip) pr_info("Watchdog already running. Resetting timeout to %d sec\n", wdog->timeout); superio_outb(cr_wdt_timeout, wdog->timeout); + set_bit(WDOG_HW_RUNNING, &wdog->status); } } From d34f921cf44633fd70460d535391a40f255b2692 Mon Sep 17 00:00:00 2001 From: Mark Pearson Date: Mon, 6 Jul 2026 10:54:00 -0400 Subject: [PATCH 08/40] watchdog: lenovo_se30g2_se60_wdt: Watchdog for Lenovo SE30G2 and SE60 Watchdog driver implementation for Lenovo SE30G2 and SE60 platforms. Signed-off-by: Mark Pearson Link: https://lore.kernel.org/r/20260706145412.3833185-1-mpearson-lenovo@squebb.ca Signed-off-by: Guenter Roeck --- drivers/watchdog/Kconfig | 12 + drivers/watchdog/Makefile | 1 + drivers/watchdog/lenovo_se30g2_se60_wdt.c | 289 ++++++++++++++++++++++ 3 files changed, 302 insertions(+) create mode 100644 drivers/watchdog/lenovo_se30g2_se60_wdt.c diff --git a/drivers/watchdog/Kconfig b/drivers/watchdog/Kconfig index c93bc0d285f0..9f013d774897 100644 --- a/drivers/watchdog/Kconfig +++ b/drivers/watchdog/Kconfig @@ -308,6 +308,18 @@ config LENOVO_SE30_WDT This driver can also be built as a module. If so, the module will be called lenovo-se30-wdt. +config LENOVO_SE30G2_SE60_WDT + tristate "Lenovo SE30 Gen 2, SE60 Watchdog" + depends on (X86 && DMI) || COMPILE_TEST + depends on HAS_IOPORT + select WATCHDOG_CORE + help + If you say yes here you get support for the watchdog + functionality for the Lenovo SE30 Gen2 and SE60 platforms. + + This driver can also be built as a module. If so, the module + will be called lenovo-se30g2-se60-wdt. + config MENF21BMC_WATCHDOG tristate "MEN 14F021P00 BMC Watchdog" depends on MFD_MENF21BMC || COMPILE_TEST diff --git a/drivers/watchdog/Makefile b/drivers/watchdog/Makefile index 598556f03bc3..cb78e9932eae 100644 --- a/drivers/watchdog/Makefile +++ b/drivers/watchdog/Makefile @@ -128,6 +128,7 @@ obj-$(CONFIG_IE6XX_WDT) += ie6xx_wdt.o obj-$(CONFIG_ITCO_WDT) += iTCO_wdt.o obj-$(CONFIG_LENOVO_SE10_WDT) += lenovo_se10_wdt.o obj-$(CONFIG_LENOVO_SE30_WDT) += lenovo_se30_wdt.o +obj-$(CONFIG_LENOVO_SE30G2_SE60_WDT) += lenovo_se30g2_se60_wdt.o obj-$(CONFIG_IT8712F_WDT) += it8712f_wdt.o obj-$(CONFIG_IT87_WDT) += it87_wdt.o obj-$(CONFIG_HP_WATCHDOG) += hpwdt.o diff --git a/drivers/watchdog/lenovo_se30g2_se60_wdt.c b/drivers/watchdog/lenovo_se30g2_se60_wdt.c new file mode 100644 index 000000000000..f31ecdb82ad0 --- /dev/null +++ b/drivers/watchdog/lenovo_se30g2_se60_wdt.c @@ -0,0 +1,289 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * WDT driver for Lenovo SE30G2 & SE60. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define CFG_PORT 0x2E +#define CFG_SIZE 2 + +#define CFG_LDN 0x07 +#define CFG_BRAM_LDN 0x10 + +#define BRAM_SIZE 2 + +#define BRAM_WDT_REG 0x48 + +#define DRVNAME "lenovo-se30g2-se60-wdt" + +/*The timeout range is 1-255 seconds*/ +#define MIN_TIMEOUT 1 +#define MAX_TIMEOUT 255 +#define WATCHDOG_TIMEOUT 60 /* 60 sec default timeout */ + +static unsigned short bram_base; +static struct platform_device *se_30g2_60_pdev; + +static int timeout; /* in seconds */ +module_param(timeout, int, 0); +MODULE_PARM_DESC(timeout, + "Watchdog timeout in seconds. 1 <= timeout <= 255, 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) ")"); + +struct se_30g2_60_wdt { + struct watchdog_device wdd; +}; + +static void enter_pnp_mode(void) +{ + outb(0x87, CFG_PORT); + outb(0x01, CFG_PORT); + outb(0x55, CFG_PORT); + outb(0x55, CFG_PORT); +} + +static void exit_pnp_mode(void) +{ + outb(0x2, CFG_PORT); + outb(0x2, CFG_PORT + 1); +} + +static void lpc_write(unsigned char index, unsigned char data) +{ + outb(index, CFG_PORT); + outb(data, CFG_PORT + 1); +} + +static unsigned char lpc_read(unsigned char index) +{ + outb(index, CFG_PORT); + return inb(CFG_PORT + 1); +} + +static unsigned short lpc_chip_id(void) +{ + unsigned char msb, lsb; + + msb = lpc_read(0x20); + lsb = lpc_read(0x21); + + return (msb << 8 | lsb); +} + +static void bram_write(unsigned char reg, unsigned char val) +{ + outb(reg | 0x80, bram_base); + outb(val, bram_base + 1); +} + +static unsigned char bram_read(unsigned char reg) +{ + unsigned char val; + + outb(reg | 0x80, bram_base); + val = inb(bram_base + 1); + return val; +} + +static int wdt_read(unsigned short *val) +{ + if (!request_muxed_region(bram_base, BRAM_SIZE, DRVNAME)) + return -EACCES; + + *val = ((bram_read(BRAM_WDT_REG) & 0xFF) << 8); + *val |= bram_read(BRAM_WDT_REG + 1) & 0xFF; + release_region(bram_base, BRAM_SIZE); + + return 0; +} + +static int wdt_write(unsigned short val) +{ + if (!request_muxed_region(bram_base, BRAM_SIZE, DRVNAME)) + return -EACCES; + + bram_write(BRAM_WDT_REG, (val >> 8) & 0xFF); + bram_write(BRAM_WDT_REG + 1, val & 0xFF); + release_region(bram_base, BRAM_SIZE); + + return 0; +} + +static int wdt_start(struct watchdog_device *wdog) +{ + return wdt_write(wdog->timeout); +} + +static int wdt_stop(struct watchdog_device *wdog) +{ + return wdt_write(0); +} + +static int wdt_ping(struct watchdog_device *wdog) +{ + return wdt_write(wdog->timeout); +} + +static unsigned int wdt_get_timeleft(struct watchdog_device *wdog) +{ + unsigned short val; + int err; + + err = wdt_read(&val); + return err ? 0 : val; +} + +static const struct watchdog_info wdt_info = { + .options = WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING | WDIOF_MAGICCLOSE, + .identity = "Lenovo SE30G2 SE60 WDOG", +}; + +static const struct watchdog_ops se_30g2_60_wdt_ops = { + .owner = THIS_MODULE, + .start = wdt_start, + .stop = wdt_stop, + .ping = wdt_ping, + .get_timeleft = wdt_get_timeleft, +}; + +static int se_30g2_60_wdt_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct se_30g2_60_wdt *priv; + unsigned int chip_id; + int ret; + + if (!request_muxed_region(CFG_PORT, CFG_SIZE, DRVNAME)) + return -EBUSY; + + /* identify the chip */ + enter_pnp_mode(); + chip_id = lpc_chip_id(); + if (chip_id != 0x5782) { + exit_pnp_mode(); + release_region(CFG_PORT, CFG_SIZE); + return -ENODEV; + } + + /* probe the BRAM base address */ + lpc_write(CFG_LDN, CFG_BRAM_LDN); + bram_base = (lpc_read(0x60) << 8) | lpc_read(0x61); + exit_pnp_mode(); + release_region(CFG_PORT, CFG_SIZE); + dev_info(dev, "Found Lenovo SE30G2 SE60 0x%x\n", chip_id); + + priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + watchdog_set_drvdata(&priv->wdd, priv); + + priv->wdd.parent = dev; + priv->wdd.info = &wdt_info; + priv->wdd.ops = &se_30g2_60_wdt_ops; + priv->wdd.timeout = WATCHDOG_TIMEOUT; /* Set default timeout */ + priv->wdd.min_timeout = MIN_TIMEOUT; + priv->wdd.max_timeout = MAX_TIMEOUT; + + watchdog_init_timeout(&priv->wdd, timeout, dev); + watchdog_set_nowayout(&priv->wdd, nowayout); + watchdog_stop_on_reboot(&priv->wdd); + watchdog_stop_on_unregister(&priv->wdd); + + ret = devm_watchdog_register_device(dev, &priv->wdd); + + dev_dbg(&pdev->dev, "initialized. timeout=%d sec (nowayout=%d)\n", + priv->wdd.timeout, nowayout); + + return ret; +} + +static struct platform_driver se_30g2_60_wdt_driver = { + .driver = { + .name = DRVNAME, + }, + .probe = se_30g2_60_wdt_probe, +}; + +static int se_30g2_60_create_device(const struct dmi_system_id *id) +{ + int err; + + se_30g2_60_pdev = platform_device_alloc("lenovo-se30g2-se60-wdt", -1); + if (!se_30g2_60_pdev) + return -ENOMEM; + + err = platform_device_add(se_30g2_60_pdev); + if (err) { + platform_device_put(se_30g2_60_pdev); + se_30g2_60_pdev = NULL; + } + + return err; +} + +static const struct dmi_system_id se_30g2_60[] __initconst = { + { + .ident = "LENOVO-SE30G2", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"), + DMI_MATCH(DMI_BOARD_NAME, "33BD"), + }, + .callback = se_30g2_60_create_device, + }, + { + .ident = "LENOVO-SE60", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"), + DMI_MATCH(DMI_BOARD_NAME, "33BF"), + }, + .callback = se_30g2_60_create_device, + }, + {} +}; +MODULE_DEVICE_TABLE(dmi, se_30g2_60); + +static int __init se_30g2_60_wdt_init(void) +{ + int err; + + if (!dmi_check_system(se_30g2_60)) + return -ENODEV; + + err = platform_driver_register(&se_30g2_60_wdt_driver); + if (err && se_30g2_60_pdev) { + platform_device_unregister(se_30g2_60_pdev); + se_30g2_60_pdev = NULL; + } + + return err; +} + +static void __exit se_30g2_60_wdt_exit(void) +{ + if (se_30g2_60_pdev) + platform_device_unregister(se_30g2_60_pdev); + platform_driver_unregister(&se_30g2_60_wdt_driver); +} + +module_init(se_30g2_60_wdt_init); +module_exit(se_30g2_60_wdt_exit); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Mark Pearson "); +MODULE_DESCRIPTION("WDT driver for Lenovo SE30G2 & SE60"); From 47a159d948c9222e05927e2c43ea8d3002baba6c Mon Sep 17 00:00:00 2001 From: Janne Grunau Date: Wed, 15 Jul 2026 11:11:53 +0200 Subject: [PATCH 09/40] dt-bindings: watchdog: apple,wdt: Add t6030 and t6031 compatibles The watchdog on Apple silicon M3 Pro, Max and Ultra SoCs is compatible with the t8103 (M1) one. Add "apple,t6030-wdt" for M3 Pro and "apple,t6031-wdt" for M3 Max and Ultra as per-SoC compatibles. Acked-by: Conor Dooley Signed-off-by: Janne Grunau Link: https://lore.kernel.org/r/20260715-apple-t603x-initial-devices-v2-6-df65b2485710@jannau.net Signed-off-by: Guenter Roeck --- Documentation/devicetree/bindings/watchdog/apple,wdt.yaml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Documentation/devicetree/bindings/watchdog/apple,wdt.yaml b/Documentation/devicetree/bindings/watchdog/apple,wdt.yaml index b99e7913e398..ca4cf01db59f 100644 --- a/Documentation/devicetree/bindings/watchdog/apple,wdt.yaml +++ b/Documentation/devicetree/bindings/watchdog/apple,wdt.yaml @@ -18,6 +18,8 @@ properties: - items: - enum: - apple,t6020-wdt + - apple,t6030-wdt + - apple,t6031-wdt - apple,t8122-wdt - apple,t8132-wdt - const: apple,t8103-wdt From 686a639c02109960c6da9c82d0689568a3f46953 Mon Sep 17 00:00:00 2001 From: Tomer Maimon Date: Mon, 6 Jul 2026 17:48:26 +0300 Subject: [PATCH 10/40] dt-bindings: watchdog: npcm: add GCR syscon property NPCM750 and NPCM845 latch watchdog reset indications in the SoC GCR block rather than in the watchdog block itself. Add the optional nuvoton,sysgcr phandle so watchdog nodes can reference the shared GCR reset-status registers that hold those latched watchdog reset indications. This is needed by the following reset-status support, which reads those latches and reports watchdog-caused resets through bootstatus. Signed-off-by: Tomer Maimon Acked-by: Conor Dooley Link: https://lore.kernel.org/r/20260706144828.3517631-2-tmaimon77@gmail.com Signed-off-by: Guenter Roeck --- .../bindings/watchdog/nuvoton,npcm750-wdt.yaml | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/Documentation/devicetree/bindings/watchdog/nuvoton,npcm750-wdt.yaml b/Documentation/devicetree/bindings/watchdog/nuvoton,npcm750-wdt.yaml index 7aa30f5b5c49..81269fdc81a5 100644 --- a/Documentation/devicetree/bindings/watchdog/nuvoton,npcm750-wdt.yaml +++ b/Documentation/devicetree/bindings/watchdog/nuvoton,npcm750-wdt.yaml @@ -16,6 +16,14 @@ description: allOf: - $ref: watchdog.yaml# + - if: + properties: + compatible: + contains: + const: nuvoton,wpcm450-wdt + then: + properties: + nuvoton,sysgcr: false properties: compatible: @@ -40,6 +48,12 @@ properties: clock-frequency: description: Frequency in Hz of the clock that drives the NPCM timer. + nuvoton,sysgcr: + $ref: /schemas/types.yaml#/definitions/phandle + description: + a phandle to access the GCR reset-status registers that latch + watchdog reset indications on NPCM750 and NPCM845. + required: - compatible - reg From 016d840a7ec5909ed97ef086df4b06d179760ee2 Mon Sep 17 00:00:00 2001 From: Tomer Maimon Date: Mon, 6 Jul 2026 17:48:27 +0300 Subject: [PATCH 11/40] docs: watchdog: npcm: Add reset status description Add documentation describing how the NPCM watchdog driver reports reset causes through bootstatus on NPCM750 and NPCM845 systems. Document the reset flag mapping, the watchdog instance mapping for WDIOF_CARDRESET, and the NPCM750/NPCM845 latch handling. Also mention sysfs bootstatus reporting when watchdog sysfs support is enabled. Signed-off-by: Tomer Maimon Link: https://lore.kernel.org/r/20260706144828.3517631-3-tmaimon77@gmail.com Signed-off-by: Guenter Roeck --- Documentation/watchdog/index.rst | 1 + Documentation/watchdog/npcm_wdt.rst | 71 +++++++++++++++++++++++++++++ 2 files changed, 72 insertions(+) create mode 100644 Documentation/watchdog/npcm_wdt.rst diff --git a/Documentation/watchdog/index.rst b/Documentation/watchdog/index.rst index dbc702b31a43..eb9e76275fa0 100644 --- a/Documentation/watchdog/index.rst +++ b/Documentation/watchdog/index.rst @@ -25,4 +25,5 @@ Driver specific hpwdt ics-wdt mlx-wdt + npcm_wdt pcwd-watchdog diff --git a/Documentation/watchdog/npcm_wdt.rst b/Documentation/watchdog/npcm_wdt.rst new file mode 100644 index 000000000000..cdc45ade9e0d --- /dev/null +++ b/Documentation/watchdog/npcm_wdt.rst @@ -0,0 +1,71 @@ +.. SPDX-License-Identifier: GPL-2.0 + +============= +NPCM Watchdog +============= + +The NPCM watchdog driver can report reset-cause information on +``nuvoton,npcm750-wdt`` and ``nuvoton,npcm845-wdt`` systems. + +Userspace can read the latched reset cause through +``WDIOC_GETBOOTSTATUS``. When ``CONFIG_WATCHDOG_SYSFS`` is enabled, the +same value is also visible through ``/sys/class/watchdog/watchdogN/bootstatus``. + +The mapping is fixed in the driver. It exposes the SoC reset indications +through the generic watchdog bootstatus flags and is not configurable from +Device Tree. + +.. list-table:: Reset-cause mapping + :header-rows: 1 + + * - Platform + - Reset indication + - Bootstatus flag + - Reported meaning + * - NPCM750 and NPCM845 + - ``PORST`` + - ``WDIOF_OVERHEAT`` + - power-on reset + * - NPCM750 and NPCM845 + - ``CORST`` + - ``WDIOF_FANFAULT`` + - core reset + * - NPCM750 and NPCM845 + - ``SWR1RST`` + - ``WDIOF_EXTERN1`` + - software reset source 1 + * - NPCM750 and NPCM845 + - ``SWR2RST`` + - ``WDIOF_EXTERN2`` + - software reset source 2 + * - NPCM750 and NPCM845 + - ``SWR3RST`` + - ``WDIOF_POWERUNDER`` + - software reset source 3 + * - NPCM750 + - ``SWR4RST`` + - ``WDIOF_POWEROVER`` + - software reset source 4 + * - NPCM845 + - ``TIP reset`` (``INTCR2[25]``) + - ``WDIOF_POWEROVER`` + - TIP reset + +``WDIOF_CARDRESET`` is reported only for the watchdog instance whose own +reset-status bit is latched. On systems with three watchdog instances, this +maps ``WD0RST``, ``WD1RST``, and ``WD2RST`` to ``watchdog0``, ``watchdog1``, +and ``watchdog2`` respectively. + +The driver may report ``WDIOF_CARDRESET`` together with one or more of the +reset-cause flags listed above. + +On NPCM750, the driver samples ``RESSR``. When reset bits are still latched, +it clears them and stores the sampled value in ``SCRPAD2`` so later watchdog +probes can report the same boot-time state. + +On NPCM845, the driver samples ``INTCR2``. When reset bits are still latched, +it clears them and stores the sampled value in ``SCRPAD10`` so later watchdog +probes can report the same boot-time state. + +The WPCM450 watchdog continues to operate without this reset-indication +mapping. From 7762911c0d8c6c7ca7b08b6861372b105990b165 Mon Sep 17 00:00:00 2001 From: Tomer Maimon Date: Mon, 6 Jul 2026 17:48:28 +0300 Subject: [PATCH 12/40] watchdog: npcm: add bootstatus support The NPCM750 uses RESSR and the NPCM845 uses INTCR2 to latch reset indications. Read those bits during probe and map them into watchdog bootstatus flags. For NPCM845, cache the sampled INTCR2 state in SCRPAD10 after the reset status bits are cleared so later probes can report the same boot-time state. Also report WDIOF_CARDRESET for the watchdog instance whose reset bit is latched, while leaving WPCM450 behavior unchanged. Signed-off-by: Tomer Maimon Link: https://lore.kernel.org/r/20260706144828.3517631-4-tmaimon77@gmail.com [groeck: Declare npcm750_data and npcm845_data with __maybe_unused] Signed-off-by: Guenter Roeck --- drivers/watchdog/npcm_wdt.c | 217 +++++++++++++++++++++++++++++++++++- 1 file changed, 215 insertions(+), 2 deletions(-) diff --git a/drivers/watchdog/npcm_wdt.c b/drivers/watchdog/npcm_wdt.c index e62ea054bc61..51348969bf49 100644 --- a/drivers/watchdog/npcm_wdt.c +++ b/drivers/watchdog/npcm_wdt.c @@ -7,14 +7,52 @@ #include #include #include +#include #include #include #include +#include #include #include #define NPCM_WTCR 0x1C +/* NPCM GCR module */ +#define NPCM_RESSR_OFFSET 0x6C +#define NPCM_INTCR2_OFFSET 0x60 +#define NPCM7XX_SCRPAD2_OFFSET 0x84 +#define NPCM8XX_SCRPAD10_OFFSET 0xE28 + +#define NPCM_PORST BIT(31) +#define NPCM_CORST BIT(30) +#define NPCM_WD0RST BIT(29) +#define NPCM_SWR1RST BIT(28) +#define NPCM_SWR2RST BIT(27) +#define NPCM_SWR3RST BIT(26) +#define NPCM_SWR4RST BIT(25) +#define NPCM_WD1RST BIT(24) +#define NPCM_WD2RST BIT(23) +#define NPCM_RST GENMASK(31, 23) +#define NPCM8XX_TIP_RESET BIT(25) /* Replaces SWRST4 on NPCM8xx */ + +/* Per-instance mapping of MMIO base address to its RESSR/INTCR2 reset bit. */ +struct npcm_wdt_rst_map { + phys_addr_t base; + u32 rst_bit; +}; + +struct npcm_wdt_status_map { + u32 rst_bit; + u32 wdiof_flag; +}; + +struct npcm_wdt_data { + const struct npcm_wdt_rst_map *rst_map; + unsigned int rst_map_size; + const struct npcm_wdt_status_map *status_map; + unsigned int status_map_size; +}; + #define NPCM_WTCLK (BIT(10) | BIT(11)) /* Clock divider */ #define NPCM_WTE BIT(7) /* Enable */ #define NPCM_WTIE BIT(6) /* Enable irq */ @@ -47,6 +85,50 @@ struct npcm_wdt { struct clk *clk; }; +static const struct npcm_wdt_rst_map npcm750_rst_map[] = { + { 0xf000801c, NPCM_WD0RST }, + { 0xf000901c, NPCM_WD1RST }, + { 0xf000a01c, NPCM_WD2RST }, +}; + +static const struct npcm_wdt_status_map npcm750_status_map[] = { + { NPCM_PORST, WDIOF_OVERHEAT }, + { NPCM_CORST, WDIOF_FANFAULT }, + { NPCM_SWR1RST, WDIOF_EXTERN1 }, + { NPCM_SWR2RST, WDIOF_EXTERN2 }, + { NPCM_SWR3RST, WDIOF_POWERUNDER }, + { NPCM_SWR4RST, WDIOF_POWEROVER }, +}; + +static const struct npcm_wdt_data __maybe_unused npcm750_data = { + .rst_map = npcm750_rst_map, + .rst_map_size = ARRAY_SIZE(npcm750_rst_map), + .status_map = npcm750_status_map, + .status_map_size = ARRAY_SIZE(npcm750_status_map), +}; + +static const struct npcm_wdt_rst_map npcm845_rst_map[] = { + { 0xf000801c, NPCM_WD0RST }, + { 0xf000901c, NPCM_WD1RST }, + { 0xf000a01c, NPCM_WD2RST }, +}; + +static const struct npcm_wdt_status_map npcm845_status_map[] = { + { NPCM_PORST, WDIOF_OVERHEAT }, + { NPCM_CORST, WDIOF_FANFAULT }, + { NPCM_SWR1RST, WDIOF_EXTERN1 }, + { NPCM_SWR2RST, WDIOF_EXTERN2 }, + { NPCM_SWR3RST, WDIOF_POWERUNDER }, + { NPCM8XX_TIP_RESET, WDIOF_POWEROVER }, +}; + +static const struct npcm_wdt_data __maybe_unused npcm845_data = { + .rst_map = npcm845_rst_map, + .rst_map_size = ARRAY_SIZE(npcm845_rst_map), + .status_map = npcm845_status_map, + .status_map_size = ARRAY_SIZE(npcm845_status_map), +}; + static inline struct npcm_wdt *to_npcm_wdt(struct watchdog_device *wdd) { return container_of(wdd, struct npcm_wdt, wdd); @@ -169,6 +251,111 @@ static bool npcm_is_running(struct watchdog_device *wdd) return readl(wdt->reg) & NPCM_WTE; } +static void npcm_get_reset_status(struct npcm_wdt *wdt, struct device *dev, + const struct npcm_wdt_data *data, + resource_size_t start) +{ + struct regmap *gcr_regmap; + u32 rstval = 0; + unsigned int i; + int ret; + + if (!data) + return; + + gcr_regmap = syscon_regmap_lookup_by_phandle(dev->of_node, + "nuvoton,sysgcr"); + if (IS_ERR(gcr_regmap)) { + dev_warn(dev, + "Failed to find nuvoton,sysgcr, WD reset status not supported\n"); + return; + } + + if (of_device_is_compatible(dev->of_node, "nuvoton,npcm845-wdt")) { + ret = regmap_read(gcr_regmap, NPCM_INTCR2_OFFSET, &rstval); + if (ret) { + dev_warn(dev, "Failed to read INTCR2 reset status: %d\n", + ret); + return; + } + + if (rstval & NPCM_RST) { + ret = regmap_write(gcr_regmap, NPCM_INTCR2_OFFSET, + rstval & ~NPCM_RST); + if (ret) { + dev_warn(dev, + "Failed to clear INTCR2 reset status: %d\n", + ret); + return; + } + + ret = regmap_write(gcr_regmap, NPCM8XX_SCRPAD10_OFFSET, + rstval); + if (ret) { + dev_warn(dev, + "Failed to cache reset status in SCRPAD10: %d\n", + ret); + return; + } + } else { + ret = regmap_read(gcr_regmap, NPCM8XX_SCRPAD10_OFFSET, + &rstval); + if (ret) { + dev_warn(dev, + "Failed to read cached reset status from SCRPAD10: %d\n", + ret); + return; + } + } + } else if (of_device_is_compatible(dev->of_node, "nuvoton,npcm750-wdt")) { + ret = regmap_read(gcr_regmap, NPCM_RESSR_OFFSET, &rstval); + if (ret) { + dev_warn(dev, "Failed to read RESSR reset status: %d\n", + ret); + return; + } + + if (rstval & NPCM_RST) { + ret = regmap_write(gcr_regmap, NPCM_RESSR_OFFSET, + rstval & ~NPCM_RST); + if (ret) { + dev_warn(dev, "Failed to clear RESSR reset status: %d\n", ret); + return; + } + + ret = regmap_write(gcr_regmap, NPCM7XX_SCRPAD2_OFFSET, + rstval); + if (ret) { + dev_warn(dev, + "Failed to cache reset status in SCRPAD2: %d\n", ret); + return; + } + } else { + ret = regmap_read(gcr_regmap, NPCM7XX_SCRPAD2_OFFSET, + &rstval); + if (ret) { + dev_warn(dev, + "Failed to read cached reset status from SCRPAD2: %d\n", + ret); + return; + } + } + } + + for (i = 0; i < data->status_map_size; i++) { + if (rstval & data->status_map[i].rst_bit) + wdt->wdd.bootstatus |= data->status_map[i].wdiof_flag; + } + + for (i = 0; i < data->rst_map_size; i++) { + if (data->rst_map[i].base == start && + rstval & data->rst_map[i].rst_bit) { + wdt->wdd.bootstatus |= WDIOF_CARDRESET; + break; + } + } +} + static const struct watchdog_info npcm_wdt_info = { .identity = KBUILD_MODNAME, .options = WDIOF_SETTIMEOUT @@ -176,6 +363,20 @@ static const struct watchdog_info npcm_wdt_info = { | WDIOF_MAGICCLOSE, }; +static const struct watchdog_info npcm_wdt_rst_info = { + .identity = KBUILD_MODNAME, + .options = WDIOF_SETTIMEOUT + | WDIOF_KEEPALIVEPING + | WDIOF_MAGICCLOSE + | WDIOF_CARDRESET + | WDIOF_OVERHEAT + | WDIOF_FANFAULT + | WDIOF_EXTERN1 + | WDIOF_EXTERN2 + | WDIOF_POWERUNDER + | WDIOF_POWEROVER, +}; + static const struct watchdog_ops npcm_wdt_ops = { .owner = THIS_MODULE, .start = npcm_wdt_start, @@ -188,7 +389,10 @@ static const struct watchdog_ops npcm_wdt_ops = { static int npcm_wdt_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; + const struct npcm_wdt_data *data = device_get_match_data(dev); + struct resource *res; struct npcm_wdt *wdt; + resource_size_t start; int irq; int ret; @@ -196,10 +400,16 @@ static int npcm_wdt_probe(struct platform_device *pdev) if (!wdt) return -ENOMEM; + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) + return -EINVAL; + wdt->reg = devm_platform_ioremap_resource(pdev, 0); if (IS_ERR(wdt->reg)) return PTR_ERR(wdt->reg); + start = res->start; + wdt->clk = devm_clk_get_optional(&pdev->dev, NULL); if (IS_ERR(wdt->clk)) return PTR_ERR(wdt->clk); @@ -208,7 +418,7 @@ static int npcm_wdt_probe(struct platform_device *pdev) if (irq < 0) return irq; - wdt->wdd.info = &npcm_wdt_info; + wdt->wdd.info = data ? &npcm_wdt_rst_info : &npcm_wdt_info; wdt->wdd.ops = &npcm_wdt_ops; wdt->wdd.min_timeout = 1; wdt->wdd.max_timeout = 2750; @@ -220,6 +430,8 @@ static int npcm_wdt_probe(struct platform_device *pdev) /* Ensure timeout is able to be represented by the hardware */ npcm_wdt_set_timeout(&wdt->wdd, wdt->wdd.timeout); + npcm_get_reset_status(wdt, dev, data, start); + if (npcm_is_running(&wdt->wdd)) { /* Restart with the default or device-tree specified timeout */ npcm_wdt_start(&wdt->wdd); @@ -243,7 +455,8 @@ static int npcm_wdt_probe(struct platform_device *pdev) #ifdef CONFIG_OF static const struct of_device_id npcm_wdt_match[] = { {.compatible = "nuvoton,wpcm450-wdt"}, - {.compatible = "nuvoton,npcm750-wdt"}, + {.compatible = "nuvoton,npcm750-wdt", .data = &npcm750_data}, + {.compatible = "nuvoton,npcm845-wdt", .data = &npcm845_data}, {}, }; MODULE_DEVICE_TABLE(of, npcm_wdt_match); From 969ca8f12dd215a71e1dbc34410a9080495c2b21 Mon Sep 17 00:00:00 2001 From: Rustam Adilov Date: Fri, 10 Jul 2026 12:43:16 +0500 Subject: [PATCH 13/40] watchdog: realtek-otto: Change to use regmap API To make the realtek watchdog driver functional when SWAP_IO_SPACE config is enabled, change all of the register access to be done by regmap API which helps us to tweak endianness with big-endian or little-endian property from within the device tree node. Add the REGMAP_MMIO as a select to REALTEK_OTTO_WDT now that the regmap is used. Signed-off-by: Rustam Adilov Link: https://lore.kernel.org/r/20260710074316.46643-2-adilov@disroot.org Signed-off-by: Guenter Roeck --- drivers/watchdog/Kconfig | 1 + drivers/watchdog/realtek_otto_wdt.c | 74 +++++++++++++++-------------- 2 files changed, 39 insertions(+), 36 deletions(-) diff --git a/drivers/watchdog/Kconfig b/drivers/watchdog/Kconfig index 9f013d774897..e40eb44f590d 100644 --- a/drivers/watchdog/Kconfig +++ b/drivers/watchdog/Kconfig @@ -1105,6 +1105,7 @@ config REALTEK_OTTO_WDT depends on MACH_REALTEK_RTL || COMPILE_TEST depends on COMMON_CLK select WATCHDOG_CORE + select REGMAP_MMIO default MACH_REALTEK_RTL help Say Y here to include support for the watchdog timer on Realtek diff --git a/drivers/watchdog/realtek_otto_wdt.c b/drivers/watchdog/realtek_otto_wdt.c index 9094f2189f55..5c48ffba70a2 100644 --- a/drivers/watchdog/realtek_otto_wdt.c +++ b/drivers/watchdog/realtek_otto_wdt.c @@ -27,6 +27,7 @@ #include #include #include +#include #include #define OTTO_WDT_REG_CNTR 0x0 @@ -65,7 +66,7 @@ struct otto_wdt_ctrl { struct watchdog_device wdev; struct device *dev; - void __iomem *base; + struct regmap *regmap; unsigned int clk_rate_khz; int irq_phase1; }; @@ -73,24 +74,17 @@ struct otto_wdt_ctrl { static int otto_wdt_start(struct watchdog_device *wdev) { struct otto_wdt_ctrl *ctrl = watchdog_get_drvdata(wdev); - u32 v; - - v = ioread32(ctrl->base + OTTO_WDT_REG_CTRL); - v |= OTTO_WDT_CTRL_ENABLE; - iowrite32(v, ctrl->base + OTTO_WDT_REG_CTRL); + regmap_set_bits(ctrl->regmap, OTTO_WDT_REG_CTRL, OTTO_WDT_CTRL_ENABLE); return 0; } static int otto_wdt_stop(struct watchdog_device *wdev) { struct otto_wdt_ctrl *ctrl = watchdog_get_drvdata(wdev); - u32 v; - - v = ioread32(ctrl->base + OTTO_WDT_REG_CTRL); - v &= ~OTTO_WDT_CTRL_ENABLE; - iowrite32(v, ctrl->base + OTTO_WDT_REG_CTRL); + regmap_clear_bits(ctrl->regmap, OTTO_WDT_REG_CTRL, + OTTO_WDT_CTRL_ENABLE); return 0; } @@ -98,8 +92,7 @@ static int otto_wdt_ping(struct watchdog_device *wdev) { struct otto_wdt_ctrl *ctrl = watchdog_get_drvdata(wdev); - iowrite32(OTTO_WDT_CNTR_PING, ctrl->base + OTTO_WDT_REG_CNTR); - + regmap_write(ctrl->regmap, OTTO_WDT_REG_CNTR, OTTO_WDT_CNTR_PING); return 0; } @@ -125,7 +118,7 @@ static int otto_wdt_determine_timeouts(struct watchdog_device *wdev, unsigned in unsigned int total_ticks; unsigned int prescale; unsigned int tick_ms; - u32 v; + u32 mask, val; do { prescale = prescale_next; @@ -141,14 +134,11 @@ static int otto_wdt_determine_timeouts(struct watchdog_device *wdev, unsigned in } while (phase1_ticks > OTTO_WDT_PHASE_TICKS_MAX || phase2_ticks > OTTO_WDT_PHASE_TICKS_MAX); - v = ioread32(ctrl->base + OTTO_WDT_REG_CTRL); - - v &= ~(OTTO_WDT_CTRL_PRESCALE | OTTO_WDT_CTRL_PHASE1 | OTTO_WDT_CTRL_PHASE2); - v |= FIELD_PREP(OTTO_WDT_CTRL_PHASE1, phase1_ticks - 1); - v |= FIELD_PREP(OTTO_WDT_CTRL_PHASE2, phase2_ticks - 1); - v |= FIELD_PREP(OTTO_WDT_CTRL_PRESCALE, prescale); - - iowrite32(v, ctrl->base + OTTO_WDT_REG_CTRL); + mask = OTTO_WDT_CTRL_PRESCALE | OTTO_WDT_CTRL_PHASE1 | OTTO_WDT_CTRL_PHASE2; + val = FIELD_PREP(OTTO_WDT_CTRL_PHASE1, phase1_ticks - 1); + val |= FIELD_PREP(OTTO_WDT_CTRL_PHASE2, phase2_ticks - 1); + val |= FIELD_PREP(OTTO_WDT_CTRL_PRESCALE, prescale); + regmap_update_bits(ctrl->regmap, OTTO_WDT_REG_CTRL, mask, val); timeout_ms = total_ticks * tick_ms; ctrl->wdev.timeout = timeout_ms / 1000; @@ -192,7 +182,7 @@ static int otto_wdt_restart(struct watchdog_device *wdev, unsigned long reboot_m /* Configure for shortest timeout and wait for reset to occur */ v = FIELD_PREP(OTTO_WDT_CTRL_RST_MODE, reset_mode) | OTTO_WDT_CTRL_ENABLE; - iowrite32(v, ctrl->base + OTTO_WDT_REG_CTRL); + regmap_write(ctrl->regmap, OTTO_WDT_REG_CTRL, v); mdelay(3 * otto_wdt_tick_ms(ctrl, 0)); @@ -203,7 +193,7 @@ static irqreturn_t otto_wdt_phase1_isr(int irq, void *dev_id) { struct otto_wdt_ctrl *ctrl = dev_id; - iowrite32(OTTO_WDT_INTR_PHASE_1, ctrl->base + OTTO_WDT_REG_INTR); + regmap_write(ctrl->regmap, OTTO_WDT_REG_INTR, OTTO_WDT_INTR_PHASE_1); dev_crit(ctrl->dev, "phase 1 timeout\n"); watchdog_notify_pretimeout(&ctrl->wdev); @@ -249,7 +239,6 @@ static int otto_wdt_probe_reset_mode(struct otto_wdt_ctrl *ctrl) const struct fwnode_handle *node = ctrl->dev->fwnode; int mode_count; u32 mode; - u32 v; if (!node) return -ENXIO; @@ -271,19 +260,25 @@ static int otto_wdt_probe_reset_mode(struct otto_wdt_ctrl *ctrl) else return -EINVAL; - v = ioread32(ctrl->base + OTTO_WDT_REG_CTRL); - v &= ~OTTO_WDT_CTRL_RST_MODE; - v |= FIELD_PREP(OTTO_WDT_CTRL_RST_MODE, mode); - iowrite32(v, ctrl->base + OTTO_WDT_REG_CTRL); - + regmap_update_bits(ctrl->regmap, OTTO_WDT_REG_CTRL, + OTTO_WDT_CTRL_RST_MODE, + FIELD_PREP(OTTO_WDT_CTRL_RST_MODE, mode)); return 0; } +static const struct regmap_config realtek_otto_wdt_regmap_config = { + .reg_bits = 32, + .reg_stride = 4, + .val_bits = 32, + .disable_locking = true, +}; + static int otto_wdt_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; struct otto_wdt_ctrl *ctrl; unsigned int max_tick_ms; + void __iomem *base; int ret; ctrl = devm_kzalloc(dev, sizeof(*ctrl), GFP_KERNEL); @@ -291,18 +286,25 @@ static int otto_wdt_probe(struct platform_device *pdev) return -ENOMEM; ctrl->dev = dev; - ctrl->base = devm_platform_ioremap_resource(pdev, 0); - if (IS_ERR(ctrl->base)) - return PTR_ERR(ctrl->base); + base = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(base)) + return PTR_ERR(base); + + ctrl->regmap = devm_regmap_init_mmio(dev, base, + &realtek_otto_wdt_regmap_config); + if (IS_ERR(ctrl->regmap)) { + dev_err(dev, "regmap init failed\n"); + return PTR_ERR(ctrl->regmap); + } 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); + regmap_write(ctrl->regmap, OTTO_WDT_REG_INTR, + OTTO_WDT_INTR_PHASE_1 | OTTO_WDT_INTR_PHASE_2); + regmap_write(ctrl->regmap, OTTO_WDT_REG_CTRL, OTTO_WDT_CTRL_DEFAULT); ctrl->irq_phase1 = platform_get_irq_byname(pdev, "phase1"); if (ctrl->irq_phase1 < 0) From 4e6b7a26ff35044b38265ed14d7439bb2ada94b4 Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Tue, 14 Jul 2026 13:18:57 -0600 Subject: [PATCH 14/40] dt-bindings: watchdog: snps,dw-wdt: Add RV1106 compatible Add the compatible for the watchdog of the Rockchip RV1106, which is compatible with the Synopsys DesignWare watchdog. Signed-off-by: Simon Glass Acked-by: Krzysztof Kozlowski Reviewed-by: Heiko Stuebner Link: https://lore.kernel.org/r/20260714131856.v2.1.b5339e64b3fe4338b3924ebd9dc0096904699744@changeid 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 9d25f5e497e2..2b16b7c2787b 100644 --- a/Documentation/devicetree/bindings/watchdog/snps,dw-wdt.yaml +++ b/Documentation/devicetree/bindings/watchdog/snps,dw-wdt.yaml @@ -35,6 +35,7 @@ properties: - rockchip,rk3576-wdt - rockchip,rk3588-wdt - rockchip,rv1103b-wdt + - rockchip,rv1106-wdt - rockchip,rv1108-wdt - rockchip,rv1126-wdt - const: snps,dw-wdt From 5e845fe634eb5e796ada5b44384bb77426e7d69f Mon Sep 17 00:00:00 2001 From: Pengpeng Hou Date: Sun, 5 Jul 2026 08:16:13 +0800 Subject: [PATCH 15/40] watchdog: via_wdt: add missing MODULE_DEVICE_TABLE() The driver has a match table for the pci bus wired into its driver structure, but the table is not exported with MODULE_DEVICE_TABLE(). Add the missing MODULE_DEVICE_TABLE() entry so module alias information is generated for automatic module loading. This is a source-level fix. It does not claim dynamic hardware reproduction; the evidence is the driver-owned match table, its use by the driver registration structure, and the missing module alias publication. Signed-off-by: Pengpeng Hou Link: https://lore.kernel.org/r/20260705001613.68871-1-pengpeng@iscas.ac.cn Signed-off-by: Guenter Roeck --- drivers/watchdog/via_wdt.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/watchdog/via_wdt.c b/drivers/watchdog/via_wdt.c index f55576392651..be4ca619da65 100644 --- a/drivers/watchdog/via_wdt.c +++ b/drivers/watchdog/via_wdt.c @@ -247,6 +247,7 @@ static const struct pci_device_id wdt_pci_table[] = { { PCI_DEVICE(PCI_VENDOR_ID_VIA, PCI_DEVICE_ID_VIA_VX855) }, { 0 } }; +MODULE_DEVICE_TABLE(pci, wdt_pci_table); static struct pci_driver wdt_driver = { .name = "via_wdt", From 19ff50d05f584ecabc6d1b9d0f38bf08f45750bb Mon Sep 17 00:00:00 2001 From: Rasmus Villemoes Date: Tue, 14 Jul 2026 12:53:56 +0200 Subject: [PATCH 16/40] watchdog: take all OF aliases into account when assigning id If some, but not all, watchdog devices have device tree aliases, those without aliases might (depending on probe order) be assigned an id which would otherwise be assigned to one of those with an alias. This is problematic when for example watchdog0 is an alias for an always-running gpio watchdog that userspace must handle, but the SOC's watchdog device(s) get probed first and thus one of those become /dev/watchdog0, and then at some point later, the gpio watchdog device gets probed and becomes /dev/watchdog5. Ensure that ids for devices without a device tree alias are allocated from among those where no watchdogX alias exists. Signed-off-by: Rasmus Villemoes Link: https://lore.kernel.org/r/20260714105356.2931450-1-linux@rasmusvillemoes.dk [groeck: Use scnprintf() instead of snprintf() to make gcc happy] Signed-off-by: Guenter Roeck --- drivers/watchdog/watchdog_core.c | 26 +++++++++++++++++++++++--- 1 file changed, 23 insertions(+), 3 deletions(-) diff --git a/drivers/watchdog/watchdog_core.c b/drivers/watchdog/watchdog_core.c index f4097aefaf49..38c1e63a8e2c 100644 --- a/drivers/watchdog/watchdog_core.c +++ b/drivers/watchdog/watchdog_core.c @@ -240,7 +240,9 @@ EXPORT_SYMBOL_GPL(watchdog_set_restart_priority); static int ___watchdog_register_device(struct watchdog_device *wdd) { - int ret, id = -1; + int ret, min_id, id = -1; + struct device_node *np; + char alias[16]; if (wdd == NULL || wdd->info == NULL || wdd->ops == NULL) return -EINVAL; @@ -265,8 +267,26 @@ static int ___watchdog_register_device(struct watchdog_device *wdd) GFP_KERNEL); } - if (id < 0) - id = ida_alloc_max(&watchdog_ida, MAX_DOGS - 1, GFP_KERNEL); + /* + * Find an id which is not pre-assigned via a DT alias to some + * other, possibly not yet probed, watchdog device. + */ + if (id < 0) { + np = of_find_node_by_path("/aliases"); + + for (min_id = 0; ; min_id = id + 1) { + id = ida_alloc_range(&watchdog_ida, min_id, MAX_DOGS - 1, + GFP_KERNEL); + if (!np || id < 0) + break; + + scnprintf(alias, sizeof(alias), "watchdog%d", id); + if (!of_get_property(np, alias, NULL)) + break; + ida_free(&watchdog_ida, id); + } + of_node_put(np); + } if (id < 0) return id; From 010993649a699d961c30faea38103099cdcb16aa Mon Sep 17 00:00:00 2001 From: Pan Chuang Date: Wed, 22 Jul 2026 16:42:09 +0800 Subject: [PATCH 17/40] watchdog: bd96801_wdt: Remove redundant dev_err_probe() Since commit 55b48e23f5c4 ("genirq/devres: Add error handling in devm_request_*_irq()"), devm_request_threaded_irq() automatically logs detailed error messages on failure. Remove the now-redundant driver-specific dev_err_probe() call. Signed-off-by: Pan Chuang Link: https://lore.kernel.org/r/20260722084218.79073-2-panchuang@vivo.com Signed-off-by: Guenter Roeck --- drivers/watchdog/bd96801_wdt.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/watchdog/bd96801_wdt.c b/drivers/watchdog/bd96801_wdt.c index a25b7cf1488b..e5a56f53b419 100644 --- a/drivers/watchdog/bd96801_wdt.c +++ b/drivers/watchdog/bd96801_wdt.c @@ -389,8 +389,7 @@ static int bd96801_wdt_probe(struct platform_device *pdev) IRQF_ONESHOT, "bd96801-wdg", NULL); if (ret) - return dev_err_probe(&pdev->dev, ret, - "Failed to register IRQ\n"); + return ret; } return devm_watchdog_register_device(&pdev->dev, &w->wdt); From 767d2416d8134b66a53f56ed70900526c6f0894d Mon Sep 17 00:00:00 2001 From: Pan Chuang Date: Wed, 22 Jul 2026 16:42:10 +0800 Subject: [PATCH 18/40] watchdog: cadence_wdt: Remove redundant dev_err() Since commit 55b48e23f5c4 ("genirq/devres: Add error handling in devm_request_*_irq()"), devm_request_irq() automatically logs detailed error messages on failure. Remove the now-redundant driver-specific dev_err() call. Signed-off-by: Pan Chuang Link: https://lore.kernel.org/r/20260722084218.79073-3-panchuang@vivo.com Signed-off-by: Guenter Roeck --- drivers/watchdog/cadence_wdt.c | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/drivers/watchdog/cadence_wdt.c b/drivers/watchdog/cadence_wdt.c index 23d41043863f..ad46b33ce5f2 100644 --- a/drivers/watchdog/cadence_wdt.c +++ b/drivers/watchdog/cadence_wdt.c @@ -312,12 +312,8 @@ static int cdns_wdt_probe(struct platform_device *pdev) if (!wdt->rst && irq >= 0) { ret = devm_request_irq(dev, irq, cdns_wdt_irq_handler, 0, pdev->name, pdev); - if (ret) { - dev_err(dev, - "cannot register interrupt handler err=%d\n", - ret); + if (ret) return ret; - } } /* Initialize the members of cdns_wdt structure */ From 762d7d93ee5d05693e8842bf7872ac9756bb75d7 Mon Sep 17 00:00:00 2001 From: Pan Chuang Date: Wed, 22 Jul 2026 16:42:11 +0800 Subject: [PATCH 19/40] watchdog: intel-mid_wdt: Remove redundant dev_err() Since commit 55b48e23f5c4 ("genirq/devres: Add error handling in devm_request_*_irq()"), devm_request_irq() automatically logs detailed error messages on failure. Remove the now-redundant driver-specific dev_err() call. Signed-off-by: Pan Chuang Link: https://lore.kernel.org/r/20260722084218.79073-4-panchuang@vivo.com Signed-off-by: Guenter Roeck --- drivers/watchdog/intel-mid_wdt.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/drivers/watchdog/intel-mid_wdt.c b/drivers/watchdog/intel-mid_wdt.c index 756d262dc580..b1e1b77ed190 100644 --- a/drivers/watchdog/intel-mid_wdt.c +++ b/drivers/watchdog/intel-mid_wdt.c @@ -165,10 +165,8 @@ static int mid_wdt_probe(struct platform_device *pdev) ret = devm_request_irq(dev, pdata->irq, mid_wdt_irq, IRQF_SHARED | IRQF_NO_SUSPEND, "watchdog", wdt_dev); - if (ret) { - dev_err(dev, "error requesting warning irq %d\n", pdata->irq); + if (ret) return ret; - } /* * The firmware followed by U-Boot leaves the watchdog running From 9e34598b92ae561283bdfa66309d9c711ed6e6c3 Mon Sep 17 00:00:00 2001 From: Pan Chuang Date: Wed, 22 Jul 2026 16:42:12 +0800 Subject: [PATCH 20/40] watchdog: keembay: Remove redundant dev_err_probe() Since commit 55b48e23f5c4 ("genirq/devres: Add error handling in devm_request_*_irq()"), devm_request_irq() automatically logs detailed error messages on failure. Remove the now-redundant driver-specific dev_err_probe() calls. Signed-off-by: Pan Chuang Link: https://lore.kernel.org/r/20260722084218.79073-5-panchuang@vivo.com Signed-off-by: Guenter Roeck --- drivers/watchdog/keembay_wdt.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/watchdog/keembay_wdt.c b/drivers/watchdog/keembay_wdt.c index 3854249c7455..7c8e53921e40 100644 --- a/drivers/watchdog/keembay_wdt.c +++ b/drivers/watchdog/keembay_wdt.c @@ -214,7 +214,7 @@ static int keembay_wdt_probe(struct platform_device *pdev) ret = devm_request_irq(dev, wdt->th_irq, keembay_wdt_th_isr, 0, "keembay-wdt", wdt); if (ret) - return dev_err_probe(dev, ret, "Failed to request IRQ for threshold\n"); + return ret; wdt->to_irq = platform_get_irq_byname(pdev, "timeout"); if (wdt->to_irq < 0) @@ -223,7 +223,7 @@ static int keembay_wdt_probe(struct platform_device *pdev) ret = devm_request_irq(dev, wdt->to_irq, keembay_wdt_to_isr, 0, "keembay-wdt", wdt); if (ret) - return dev_err_probe(dev, ret, "Failed to request IRQ for timeout\n"); + return ret; wdt->wdd.parent = dev; wdt->wdd.info = &keembay_wdt_info; From c19929a8d8d9612373009513754fbc20b7ef7f0f Mon Sep 17 00:00:00 2001 From: Pan Chuang Date: Wed, 22 Jul 2026 16:42:13 +0800 Subject: [PATCH 21/40] watchdog: marvell_gti: Remove redundant dev_err_probe() Since commit 55b48e23f5c4 ("genirq/devres: Add error handling in devm_request_*_irq()"), devm_request_irq() automatically logs detailed error messages on failure. Remove the now-redundant driver-specific dev_err_probe() call. Signed-off-by: Pan Chuang Link: https://lore.kernel.org/r/20260722084218.79073-6-panchuang@vivo.com Signed-off-by: Guenter Roeck --- drivers/watchdog/marvell_gti_wdt.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/watchdog/marvell_gti_wdt.c b/drivers/watchdog/marvell_gti_wdt.c index 298089d45ab8..31167034963c 100644 --- a/drivers/watchdog/marvell_gti_wdt.c +++ b/drivers/watchdog/marvell_gti_wdt.c @@ -321,7 +321,7 @@ static int gti_wdt_probe(struct platform_device *pdev) err = devm_request_irq(dev, irq, gti_wdt_interrupt, 0, pdev->name, &priv->wdev); if (err) - return dev_err_probe(dev, err, "Failed to register interrupt handler\n"); + return err; dev_info(dev, "Watchdog enabled (timeout=%d sec)\n", wdog_dev->timeout); return 0; From 329b7e841fea0410dcfcd76acdc10687f41caf4f Mon Sep 17 00:00:00 2001 From: Pan Chuang Date: Wed, 22 Jul 2026 16:42:14 +0800 Subject: [PATCH 22/40] watchdog: orion: Remove redundant dev_err() Since commit 55b48e23f5c4 ("genirq/devres: Add error handling in devm_request_*_irq()"), devm_request_irq() automatically logs detailed error messages on failure. Remove the now-redundant driver-specific dev_err() calls. Signed-off-by: Pan Chuang Link: https://lore.kernel.org/r/20260722084218.79073-7-panchuang@vivo.com Signed-off-by: Guenter Roeck --- drivers/watchdog/orion_wdt.c | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/drivers/watchdog/orion_wdt.c b/drivers/watchdog/orion_wdt.c index 0e145f762f6f..1802634cc7cd 100644 --- a/drivers/watchdog/orion_wdt.c +++ b/drivers/watchdog/orion_wdt.c @@ -613,10 +613,8 @@ static int orion_wdt_probe(struct platform_device *pdev) */ ret = devm_request_irq(&pdev->dev, irq, orion_wdt_irq, 0, pdev->name, dev); - if (ret < 0) { - dev_err(&pdev->dev, "failed to request IRQ\n"); + if (ret < 0) goto disable_clk; - } } /* Optional 2nd interrupt for pretimeout */ @@ -625,10 +623,8 @@ static int orion_wdt_probe(struct platform_device *pdev) orion_wdt_info.options |= WDIOF_PRETIMEOUT; ret = devm_request_irq(&pdev->dev, irq, orion_wdt_pre_irq, 0, pdev->name, dev); - if (ret < 0) { - dev_err(&pdev->dev, "failed to request IRQ\n"); + if (ret < 0) goto disable_clk; - } } From d7c4cef982656ecc64618ce58651f854ed22fd81 Mon Sep 17 00:00:00 2001 From: Pan Chuang Date: Wed, 22 Jul 2026 16:42:15 +0800 Subject: [PATCH 23/40] watchdog: realtek_otto: Remove redundant dev_err_probe() Since commit 55b48e23f5c4 ("genirq/devres: Add error handling in devm_request_*_irq()"), devm_request_irq() automatically logs detailed error messages on failure. Remove the now-redundant driver-specific dev_err_probe() call. Signed-off-by: Pan Chuang Link: https://lore.kernel.org/r/20260722084218.79073-8-panchuang@vivo.com Signed-off-by: Guenter Roeck --- drivers/watchdog/realtek_otto_wdt.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/watchdog/realtek_otto_wdt.c b/drivers/watchdog/realtek_otto_wdt.c index 5c48ffba70a2..35d34834ce04 100644 --- a/drivers/watchdog/realtek_otto_wdt.c +++ b/drivers/watchdog/realtek_otto_wdt.c @@ -313,7 +313,7 @@ static int otto_wdt_probe(struct platform_device *pdev) ret = devm_request_irq(dev, ctrl->irq_phase1, otto_wdt_phase1_isr, 0, "realtek-otto-wdt", ctrl); if (ret) - return dev_err_probe(dev, ret, "Failed to get IRQ for phase1\n"); + return ret; ret = otto_wdt_probe_reset_mode(ctrl); if (ret) From 0e2d63f85a3118d8e748d685ba3a15a52d610ae9 Mon Sep 17 00:00:00 2001 From: Pan Chuang Date: Wed, 22 Jul 2026 16:42:16 +0800 Subject: [PATCH 24/40] watchdog: sama5d4: Remove redundant dev_err() Since commit 55b48e23f5c4 ("genirq/devres: Add error handling in devm_request_*_irq()"), devm_request_irq() automatically logs detailed error messages on failure. Remove the now-redundant driver-specific dev_err() call. Signed-off-by: Pan Chuang Acked-by: Alexandre Belloni Link: https://lore.kernel.org/r/20260722084218.79073-9-panchuang@vivo.com Signed-off-by: Guenter Roeck --- drivers/watchdog/sama5d4_wdt.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/drivers/watchdog/sama5d4_wdt.c b/drivers/watchdog/sama5d4_wdt.c index 704b786cc2ec..ecdd9aeab79c 100644 --- a/drivers/watchdog/sama5d4_wdt.c +++ b/drivers/watchdog/sama5d4_wdt.c @@ -291,10 +291,8 @@ static int sama5d4_wdt_probe(struct platform_device *pdev) ret = devm_request_irq(dev, irq, sama5d4_wdt_irq_handler, IRQF_SHARED | IRQF_IRQPOLL | IRQF_NO_SUSPEND, pdev->name, pdev); - if (ret) { - dev_err(dev, "cannot register interrupt handler\n"); + if (ret) return ret; - } } watchdog_init_timeout(wdd, wdt_timeout, dev); From 7aa968415cd2fd7d5a345f5c6a9547c9d6b102b8 Mon Sep 17 00:00:00 2001 From: Pan Chuang Date: Wed, 22 Jul 2026 16:42:17 +0800 Subject: [PATCH 25/40] watchdog: sprd: Remove redundant dev_err() Since commit 55b48e23f5c4 ("genirq/devres: Add error handling in devm_request_*_irq()"), devm_request_irq() automatically logs detailed error messages on failure. Remove the now-redundant driver-specific dev_err() call. Signed-off-by: Pan Chuang Link: https://lore.kernel.org/r/20260722084218.79073-10-panchuang@vivo.com Signed-off-by: Guenter Roeck --- drivers/watchdog/sprd_wdt.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/drivers/watchdog/sprd_wdt.c b/drivers/watchdog/sprd_wdt.c index aacf04616fef..616b4d7f21d7 100644 --- a/drivers/watchdog/sprd_wdt.c +++ b/drivers/watchdog/sprd_wdt.c @@ -293,10 +293,8 @@ static int sprd_wdt_probe(struct platform_device *pdev) ret = devm_request_irq(dev, wdt->irq, sprd_wdt_isr, IRQF_NO_SUSPEND, "sprd-wdt", (void *)wdt); - if (ret) { - dev_err(dev, "failed to register irq\n"); + if (ret) return ret; - } wdt->wdd.info = &sprd_wdt_info; wdt->wdd.ops = &sprd_wdt_ops; From 0595f74ea1d3092284ec5bcb8eeea09face23de9 Mon Sep 17 00:00:00 2001 From: Kathiravan Thirumoorthy Date: Tue, 28 Jul 2026 16:28:02 +0530 Subject: [PATCH 26/40] watchdog: qcom: report WDIOF_POWERUNDER in bootstatus On IPQ platforms, identify the restart reason as POWERUNDER, similar to CARDRESET, and expose it via the bootstatus sysfs interface. Reviewed-by: Konrad Dybcio Signed-off-by: Kathiravan Thirumoorthy Link: https://lore.kernel.org/r/20260728-qcom_wdt_powerunder-v1-1-de8d9e964e7d@oss.qualcomm.com Signed-off-by: Guenter Roeck --- drivers/watchdog/qcom-wdt.c | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/drivers/watchdog/qcom-wdt.c b/drivers/watchdog/qcom-wdt.c index 49bd04841f0c..8e1fa1347343 100644 --- a/drivers/watchdog/qcom-wdt.c +++ b/drivers/watchdog/qcom-wdt.c @@ -44,6 +44,7 @@ struct qcom_wdt_match_data { bool pretimeout; u32 max_tick_count; u32 wdt_reason_val; + u32 powerunder_reason_val; }; struct qcom_wdt { @@ -164,7 +165,8 @@ static const struct watchdog_info qcom_wdt_info = { .options = WDIOF_KEEPALIVEPING | WDIOF_MAGICCLOSE | WDIOF_SETTIMEOUT - | WDIOF_CARDRESET, + | WDIOF_CARDRESET + | WDIOF_POWERUNDER, .identity = KBUILD_MODNAME, }; @@ -173,7 +175,8 @@ static const struct watchdog_info qcom_wdt_pt_info = { | WDIOF_MAGICCLOSE | WDIOF_SETTIMEOUT | WDIOF_PRETIMEOUT - | WDIOF_CARDRESET, + | WDIOF_CARDRESET + | WDIOF_POWERUNDER, .identity = KBUILD_MODNAME, }; @@ -188,6 +191,7 @@ static const struct qcom_wdt_match_data match_data_ipq5424 = { .pretimeout = true, .max_tick_count = 0xFFFFFU, .wdt_reason_val = 5, + .powerunder_reason_val = 1, }; static const struct qcom_wdt_match_data match_data_kpss = { @@ -197,12 +201,13 @@ static const struct qcom_wdt_match_data match_data_kpss = { }; static int qcom_wdt_get_bootstatus(struct device *dev, struct qcom_wdt *wdt, - u32 val) + const struct qcom_wdt_match_data *data) { struct device_node *imem; struct resource res; void __iomem *addr; int ret; + int val; imem = of_parse_phandle(dev->of_node, "sram", 0); if (!imem) { @@ -222,8 +227,11 @@ static int qcom_wdt_get_bootstatus(struct device *dev, struct qcom_wdt *wdt, if (!addr) return -ENOMEM; - if (readl(addr) == val) + val = readl(addr); + if (val == data->wdt_reason_val) wdt->wdd.bootstatus = WDIOF_CARDRESET; + else if (val == data->powerunder_reason_val) + wdt->wdd.bootstatus = WDIOF_POWERUNDER; iounmap(addr); @@ -310,7 +318,7 @@ static int qcom_wdt_probe(struct platform_device *pdev) wdt->wdd.parent = dev; wdt->layout = data->offset; - ret = qcom_wdt_get_bootstatus(dev, wdt, data->wdt_reason_val); + ret = qcom_wdt_get_bootstatus(dev, wdt, data); if (ret) dev_err(dev, "failed to get the bootstatus, %d\n", ret); From 6a393b0ea3cc3032ded9186d7a6d1f021911c84b Mon Sep 17 00:00:00 2001 From: Kathiravan Thirumoorthy Date: Tue, 28 Jul 2026 16:28:03 +0530 Subject: [PATCH 27/40] watchdog: qcom: report bootstatus on IPQ9574 and IPQ5332 Report bootstatus on IPQ5332 and IPQ9574. These SoCs use different restart-reason values from IPQ5424 for watchdog reset and powerunder reset events, so add separate match data for them and use it when decoding the restart reason. Reviewed-by: Konrad Dybcio Signed-off-by: Kathiravan Thirumoorthy Link: https://lore.kernel.org/r/20260728-qcom_wdt_powerunder-v1-2-de8d9e964e7d@oss.qualcomm.com Signed-off-by: Guenter Roeck --- drivers/watchdog/qcom-wdt.c | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/drivers/watchdog/qcom-wdt.c b/drivers/watchdog/qcom-wdt.c index 8e1fa1347343..a425902b9f68 100644 --- a/drivers/watchdog/qcom-wdt.c +++ b/drivers/watchdog/qcom-wdt.c @@ -194,6 +194,14 @@ static const struct qcom_wdt_match_data match_data_ipq5424 = { .powerunder_reason_val = 1, }; +static const struct qcom_wdt_match_data match_data_ipq9574 = { + .offset = reg_offset_data_kpss, + .pretimeout = true, + .max_tick_count = 0xFFFFFU, + .wdt_reason_val = 1, + .powerunder_reason_val = 32, +}; + static const struct qcom_wdt_match_data match_data_kpss = { .offset = reg_offset_data_kpss, .pretimeout = true, @@ -374,7 +382,9 @@ static const struct dev_pm_ops qcom_wdt_pm_ops = { }; static const struct of_device_id qcom_wdt_of_table[] = { + { .compatible = "qcom,apss-wdt-ipq5332", .data = &match_data_ipq9574 }, { .compatible = "qcom,apss-wdt-ipq5424", .data = &match_data_ipq5424 }, + { .compatible = "qcom,apss-wdt-ipq9574", .data = &match_data_ipq9574 }, { .compatible = "qcom,kpss-timer", .data = &match_data_apcs_tmr }, { .compatible = "qcom,scss-timer", .data = &match_data_apcs_tmr }, { .compatible = "qcom,kpss-wdt", .data = &match_data_kpss }, From 7397d13027dcd97868ea4f20d1912c681641fbc9 Mon Sep 17 00:00:00 2001 From: Zi-Yu Chen Date: Tue, 4 Aug 2026 15:03:49 +0800 Subject: [PATCH 28/40] dt-bindings: watchdog: Add MA35D1 Watchdog Add device tree binding documentation for the watchdog timer (WDT) controller found on Nuvoton MA35D1 SoC. Reviewed-by: Krzysztof Kozlowski Signed-off-by: Zi-Yu Chen Link: https://lore.kernel.org/r/20260804070351.1846165-2-zychennvt@gmail.com Signed-off-by: Guenter Roeck --- .../bindings/watchdog/nuvoton,ma35d1-wdt.yaml | 55 +++++++++++++++++++ 1 file changed, 55 insertions(+) create mode 100644 Documentation/devicetree/bindings/watchdog/nuvoton,ma35d1-wdt.yaml diff --git a/Documentation/devicetree/bindings/watchdog/nuvoton,ma35d1-wdt.yaml b/Documentation/devicetree/bindings/watchdog/nuvoton,ma35d1-wdt.yaml new file mode 100644 index 000000000000..aced7d77a7d3 --- /dev/null +++ b/Documentation/devicetree/bindings/watchdog/nuvoton,ma35d1-wdt.yaml @@ -0,0 +1,55 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/watchdog/nuvoton,ma35d1-wdt.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Nuvoton MA35D1 Watchdog Timer + +maintainers: + - Zi-Yu Chen + +allOf: + - $ref: watchdog.yaml# + +properties: + compatible: + enum: + - nuvoton,ma35d1-wdt + + reg: + maxItems: 1 + + interrupts: + maxItems: 1 + + clocks: + maxItems: 1 + + wakeup-source: true + +required: + - compatible + - reg + - interrupts + - clocks + +unevaluatedProperties: false + +examples: + - | + #include + #include + #include + + soc { + #address-cells = <2>; + #size-cells = <2>; + wdt1: watchdog@40440000 { + compatible = "nuvoton,ma35d1-wdt"; + reg = <0x0 0x40440000 0x0 0x100>; + interrupts = ; + clocks = <&clk WDT1_GATE>; + }; + }; +... From 80141041bdb6e606b286656b16a62f5aedc122e4 Mon Sep 17 00:00:00 2001 From: Zi-Yu Chen Date: Tue, 4 Aug 2026 15:03:50 +0800 Subject: [PATCH 29/40] watchdog: Add Nuvoton MA35D1 watchdog driver support Add driver support for the Watchdog Timer (WDT) integrated in Nuvoton MA35D1 SoC. This driver provides standard watchdog functionality, including timeout configuration, ping, and system reset support via the watchdog framework. Signed-off-by: Zi-Yu Chen Link: https://lore.kernel.org/r/20260804070351.1846165-3-zychennvt@gmail.com Signed-off-by: Guenter Roeck --- drivers/watchdog/Kconfig | 10 + drivers/watchdog/Makefile | 1 + drivers/watchdog/ma35d1_wdt.c | 363 ++++++++++++++++++++++++++++++++++ 3 files changed, 374 insertions(+) create mode 100644 drivers/watchdog/ma35d1_wdt.c diff --git a/drivers/watchdog/Kconfig b/drivers/watchdog/Kconfig index e40eb44f590d..aaa41ce7dd55 100644 --- a/drivers/watchdog/Kconfig +++ b/drivers/watchdog/Kconfig @@ -749,6 +749,16 @@ config STMP3XXX_RTC_WATCHDOG To compile this driver as a module, choose M here: the module will be called stmp3xxx_rtc_wdt. +config MA35D1_WDT + tristate "Nuvoton MA35D1 Watchdog Timer" + depends on ARCH_MA35 || COMPILE_TEST + select WATCHDOG_CORE + help + Say Y here to include support for the watchdog timer + for the Nuvoton MA35D1 series microprocessor. + To compile this driver as a module, choose M here: the + module will be called ma35d1_wdt. + config TS4800_WATCHDOG tristate "TS-4800 Watchdog" depends on HAS_IOMEM && OF diff --git a/drivers/watchdog/Makefile b/drivers/watchdog/Makefile index cb78e9932eae..9ac406677aba 100644 --- a/drivers/watchdog/Makefile +++ b/drivers/watchdog/Makefile @@ -105,6 +105,7 @@ 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 +obj-$(CONFIG_MA35D1_WDT) += ma35d1_wdt.o # X86 (i386 + ia64 + x86_64) Architecture obj-$(CONFIG_ACQUIRE_WDT) += acquirewdt.o diff --git a/drivers/watchdog/ma35d1_wdt.c b/drivers/watchdog/ma35d1_wdt.c new file mode 100644 index 000000000000..119d58f338bf --- /dev/null +++ b/drivers/watchdog/ma35d1_wdt.c @@ -0,0 +1,363 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (c) 2026 Nuvoton technology corporation. + * + * Author: Zi-Yu Chen + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define DRV_NAME "ma35d1-wdt" + +#define REG_WDT_CTL 0x00 +#define REG_WDT_RSTCNT 0x08 + +#define TOUTSEL GENMASK(11, 8) +#define WDTEN BIT(7) +#define INTEN BIT(6) +#define WKF BIT(5) +#define WKEN BIT(4) +#define IF BIT(3) +#define RSTF BIT(2) +#define RSTEN BIT(1) +#define SYNC BIT(30) + +#define WDT_DEFAULT_TIMEOUT 32 +#define RESET_COUNTER 0x00005AA5 + +static bool nowayout = WATCHDOG_NOWAYOUT; +static unsigned int timeout; + +struct ma35d1_wdt_dev { + struct watchdog_device wdt_dev; + spinlock_t lock; + void __iomem *wdt_base; + struct clk *clk; + unsigned long clk_rate; + int irq; +}; + +static int ma35d1_wdt_wait_sync(struct ma35d1_wdt_dev *ma35d1_wdt) +{ + unsigned int val; + + return readl_relaxed_poll_timeout_atomic(ma35d1_wdt->wdt_base + + REG_WDT_CTL, val, !(val & SYNC), 10, 125); +} + +/** + * ma35d1_wdt_stop - Stop the watchdog. + * + * @wdt_dev: watchdog device + * + * Read the contents of the CTL register, clear the WDTEN bit + * in the register and set the access key for successful write. + * + * Return: 0 on success, negative error otherwise. + */ +static int ma35d1_wdt_stop(struct watchdog_device *wdt_dev) +{ + struct ma35d1_wdt_dev *ma35d1_wdt = watchdog_get_drvdata(wdt_dev); + unsigned int val; + int ret; + + guard(spinlock_irqsave)(&ma35d1_wdt->lock); + val = readl_relaxed(ma35d1_wdt->wdt_base + REG_WDT_CTL); + val &= ~WDTEN; + writel_relaxed(val, ma35d1_wdt->wdt_base + REG_WDT_CTL); + ret = ma35d1_wdt_wait_sync(ma35d1_wdt); + if (ret) { + dev_err(wdt_dev->parent, "Wait for WDTEN SYNC timeout!\n"); + return ret; + } + return 0; +} + +static int ma35d1_wdt_ping(struct watchdog_device *wdt_dev) +{ + struct ma35d1_wdt_dev *ma35d1_wdt = watchdog_get_drvdata(wdt_dev); + + writel_relaxed(RESET_COUNTER, ma35d1_wdt->wdt_base + REG_WDT_RSTCNT); + + return 0; +} + +static int ma35d1_wdt_set_timeout(struct watchdog_device *wdt_dev, + unsigned int timeout) +{ + struct ma35d1_wdt_dev *ma35d1_wdt = watchdog_get_drvdata(wdt_dev); + unsigned long target_ticks; + unsigned int val, i; + static const uint8_t toutsel_shifts[] = { 4, 6, 8, 10, 12, + 14, 16, 18, 20 }; + + if (timeout < (ma35d1_wdt->wdt_dev.max_hw_heartbeat_ms / 1000)) { + target_ticks = (unsigned long)timeout * ma35d1_wdt->clk_rate; + + for (i = 0; i < ARRAY_SIZE(toutsel_shifts); i++) { + if ((1UL << toutsel_shifts[i]) >= target_ticks) + break; + } + /* To avoid truncation errors (0 seconds) during division. */ + wdt_dev->timeout = + (1UL << toutsel_shifts[i]) / ma35d1_wdt->clk_rate; + if (wdt_dev->timeout == 0) + wdt_dev->timeout = 1; + + } else { + i = ARRAY_SIZE(toutsel_shifts) - 1; + wdt_dev->timeout = timeout; + } + + guard(spinlock_irqsave)(&ma35d1_wdt->lock); + val = readl_relaxed(ma35d1_wdt->wdt_base + REG_WDT_CTL); + val &= ~TOUTSEL; + val |= FIELD_PREP(TOUTSEL, i); + writel_relaxed(val, ma35d1_wdt->wdt_base + REG_WDT_CTL); + + ma35d1_wdt_ping(wdt_dev); + return 0; +} + +static int ma35d1_wdt_start(struct watchdog_device *wdt_dev) +{ + struct ma35d1_wdt_dev *ma35d1_wdt = watchdog_get_drvdata(wdt_dev); + void __iomem *wdt_base = ma35d1_wdt->wdt_base; + unsigned int val; + int ret; + + ret = ma35d1_wdt_set_timeout(wdt_dev, wdt_dev->timeout); + if (ret < 0) + return ret; + + guard(spinlock_irqsave)(&ma35d1_wdt->lock); + val = readl_relaxed(wdt_base + REG_WDT_CTL); + val |= (WDTEN | RSTEN); + + writel_relaxed(val, wdt_base + REG_WDT_CTL); + ret = ma35d1_wdt_wait_sync(ma35d1_wdt); + if (ret) { + dev_err(wdt_dev->parent, "Wait for WDTEN SYNC timeout!\n"); + return ret; + } + + writel_relaxed(RESET_COUNTER, wdt_base + REG_WDT_RSTCNT); + + return 0; +} + +static const struct watchdog_info ma35d1_wdt_info = { + .identity = DRV_NAME, + .options = WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING | WDIOF_MAGICCLOSE | + WDIOF_CARDRESET, +}; + +static const struct watchdog_ops ma35d1_wdt_ops = { + .owner = THIS_MODULE, + .start = ma35d1_wdt_start, + .stop = ma35d1_wdt_stop, + .ping = ma35d1_wdt_ping, + .set_timeout = ma35d1_wdt_set_timeout, +}; + +static irqreturn_t ma35d1_wdt_isr(int irq, void *dev_id) +{ + struct ma35d1_wdt_dev *ma35d1_wdt = dev_id; + unsigned int val; + + /* Clear the flag if set */ + guard(spinlock)(&ma35d1_wdt->lock); + val = readl_relaxed(ma35d1_wdt->wdt_base + REG_WDT_CTL); + writel_relaxed(val, ma35d1_wdt->wdt_base + REG_WDT_CTL); + + return IRQ_HANDLED; +} + +static int ma35d1_wdt_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct ma35d1_wdt_dev *ma35d1_wdt; + unsigned long clk_rate, val; + int ret; + + ma35d1_wdt = devm_kzalloc(dev, sizeof(*ma35d1_wdt), GFP_KERNEL); + if (!ma35d1_wdt) + return -ENOMEM; + + spin_lock_init(&ma35d1_wdt->lock); + platform_set_drvdata(pdev, ma35d1_wdt); + + ma35d1_wdt->wdt_base = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(ma35d1_wdt->wdt_base)) + return PTR_ERR(ma35d1_wdt->wdt_base); + + ma35d1_wdt->clk = devm_clk_get_enabled(dev, NULL); + if (IS_ERR(ma35d1_wdt->clk)) + return dev_err_probe(dev, PTR_ERR(ma35d1_wdt->clk), + "Can't get Watchdog clock\n"); + + clk_rate = clk_get_rate(ma35d1_wdt->clk); + if (!clk_rate) + return -EINVAL; + ma35d1_wdt->clk_rate = clk_rate; + + ma35d1_wdt->irq = platform_get_irq(pdev, 0); + if (ma35d1_wdt->irq < 0) + return dev_err_probe(dev, ma35d1_wdt->irq, + "failed to get irq\n"); + + ma35d1_wdt->wdt_dev.info = &ma35d1_wdt_info; + ma35d1_wdt->wdt_dev.ops = &ma35d1_wdt_ops; + ma35d1_wdt->wdt_dev.timeout = WDT_DEFAULT_TIMEOUT; + ma35d1_wdt->wdt_dev.min_timeout = 1; + ma35d1_wdt->wdt_dev.max_hw_heartbeat_ms = (1U << 20) * 1000 / clk_rate; + ma35d1_wdt->wdt_dev.parent = dev; + + val = readl_relaxed(ma35d1_wdt->wdt_base + REG_WDT_CTL); + if (val & RSTF) { + ma35d1_wdt->wdt_dev.bootstatus = WDIOF_CARDRESET; + writel_relaxed(val, ma35d1_wdt->wdt_base + REG_WDT_CTL); + } + + if (val & WDTEN) + set_bit(WDOG_HW_RUNNING, &ma35d1_wdt->wdt_dev.status); + + watchdog_set_drvdata(&ma35d1_wdt->wdt_dev, ma35d1_wdt); + watchdog_set_nowayout(&ma35d1_wdt->wdt_dev, nowayout); + watchdog_init_timeout(&ma35d1_wdt->wdt_dev, timeout, &pdev->dev); + + ma35d1_wdt_set_timeout(&ma35d1_wdt->wdt_dev, + ma35d1_wdt->wdt_dev.timeout); + + ret = devm_request_irq(dev, ma35d1_wdt->irq, ma35d1_wdt_isr, 0, + dev_name(dev), ma35d1_wdt); + if (ret) + return dev_err_probe(dev, ret, "cannot claim IRQ %d\n", + ma35d1_wdt->irq); + + if (device_property_read_bool(dev, "wakeup-source")) { + ret = devm_device_init_wakeup(dev); + if (ret) + return ret; + + ret = devm_pm_set_wake_irq(dev, ma35d1_wdt->irq); + if (ret) + return ret; + } + + watchdog_stop_on_reboot(&ma35d1_wdt->wdt_dev); + watchdog_stop_on_unregister(&ma35d1_wdt->wdt_dev); + ret = devm_watchdog_register_device(dev, &ma35d1_wdt->wdt_dev); + if (ret) + return ret; + + return 0; +} + +static int ma35d1_wdt_suspend(struct device *dev) +{ + struct ma35d1_wdt_dev *ma35d1_wdt = dev_get_drvdata(dev); + + if (watchdog_active(&ma35d1_wdt->wdt_dev) || + watchdog_hw_running(&ma35d1_wdt->wdt_dev)) { + u32 val; + int ret; + + guard(spinlock_irqsave)(&ma35d1_wdt->lock); + val = readl_relaxed(ma35d1_wdt->wdt_base + REG_WDT_CTL); + + if (device_may_wakeup(dev)) { + val &= ~RSTEN; + val |= (INTEN | WKEN); + } else { + val &= ~(WDTEN | RSTEN); + } + writel_relaxed(val, ma35d1_wdt->wdt_base + REG_WDT_CTL); + ret = ma35d1_wdt_wait_sync(ma35d1_wdt); + if (ret) { + dev_err(dev, "Wait for WDTEN SYNC timeout!\n"); + return ret; + } + } + + return 0; +} + +static int ma35d1_wdt_resume(struct device *dev) +{ + struct ma35d1_wdt_dev *ma35d1_wdt = dev_get_drvdata(dev); + + if (watchdog_active(&ma35d1_wdt->wdt_dev) || + watchdog_hw_running(&ma35d1_wdt->wdt_dev)) { + u32 val; + int ret; + + guard(spinlock_irqsave)(&ma35d1_wdt->lock); + val = readl_relaxed(ma35d1_wdt->wdt_base + REG_WDT_CTL); + + if (device_may_wakeup(dev)) { + val |= RSTEN; + val &= ~(INTEN | WKEN); + } else { + val |= (WDTEN | RSTEN); + } + writel_relaxed(val, ma35d1_wdt->wdt_base + REG_WDT_CTL); + writel_relaxed(RESET_COUNTER, + ma35d1_wdt->wdt_base + REG_WDT_RSTCNT); + ret = ma35d1_wdt_wait_sync(ma35d1_wdt); + if (ret) { + dev_err(dev, "Wait for WDTEN SYNC timeout!\n"); + return ret; + } + } + return 0; +} + +static DEFINE_SIMPLE_DEV_PM_OPS(ma35d1_wdt_pm_ops, ma35d1_wdt_suspend, + ma35d1_wdt_resume); + +static const struct of_device_id ma35d1_wdt_dt_ids[] = { + { .compatible = "nuvoton,ma35d1-wdt" }, + { /* sentinel */ }, +}; +MODULE_DEVICE_TABLE(of, ma35d1_wdt_dt_ids); + +static struct platform_driver ma35d1_wdt_driver = { + .probe = ma35d1_wdt_probe, + .driver = { + .name = DRV_NAME, + .pm = pm_ptr(&ma35d1_wdt_pm_ops), + .of_match_table = ma35d1_wdt_dt_ids, + }, +}; + +module_platform_driver(ma35d1_wdt_driver); + +module_param(timeout, uint, 0); +MODULE_PARM_DESC(timeout, "Watchdog heartbeat in seconds"); + +module_param(nowayout, bool, 0); +MODULE_PARM_DESC( + nowayout, + "Watchdog cannot be stopped once started (default=" __MODULE_STRING( + WATCHDOG_NOWAYOUT) ")"); + +MODULE_AUTHOR("Zi-Yu Chen "); +MODULE_DESCRIPTION("Nuvoton MA35D1 Watchdog Timer Driver"); +MODULE_LICENSE("GPL"); From 0ab46a83fa124a71e95728dd31cf0feab4da7c12 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=AA=20de=20Souza=20Pinto?= Date: Tue, 4 Aug 2026 12:39:57 +0200 Subject: [PATCH 30/40] watchdog: wdat_wdt: map registers that fall inside ACPI NVS MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Some firmwares describe WDAT registers inside memory ranges marked as ACPI NVS in the E820 map, failing with -EBUSY during probe, leaving the hardware watchdog unserviced and triggering periodic system resets. This issue was observed on a OnLogic Karbon 524 device (when watchdog is enabled in BIOS): wdat_wdt wdat_wdt: error -EBUSY: can't request region for resource [mem 0x63df7a98] wdat_wdt wdat_wdt: probe with driver wdat_wdt failed with error -16 Check whether the region falls inside ACPI NVS before requesting it and, if so, map it without reservation. Signed-off-by: Renê de Souza Pinto Link: https://lore.kernel.org/r/20260804103958.3684195-1-rene@renesp.com.br Signed-off-by: Guenter Roeck --- drivers/watchdog/wdat_wdt.c | 27 ++++++++++++++++++++++++++- 1 file changed, 26 insertions(+), 1 deletion(-) diff --git a/drivers/watchdog/wdat_wdt.c b/drivers/watchdog/wdat_wdt.c index dd3c2d69c9df..55ea16e73652 100644 --- a/drivers/watchdog/wdat_wdt.c +++ b/drivers/watchdog/wdat_wdt.c @@ -7,7 +7,9 @@ */ #include +#include #include +#include #include #include #include @@ -309,6 +311,29 @@ static struct watchdog_ops wdat_wdt_ops = { .set_timeout = wdat_wdt_set_timeout, }; +static void __iomem *wdat_wdt_map_mem(struct device *dev, struct resource *res) +{ + resource_size_t size = resource_size(res); + void *addr; + + /* Map memory region without reserving it if it falls inside ACPI NVS */ + if (region_intersects(res->start, size, IORESOURCE_MEM, + IORES_DESC_ACPI_NV_STORAGE) == REGION_INTERSECTS) { + dev_warn(dev, "%pR is inside ACPI NVS, mapping without reservation\n", + res); + + addr = devm_memremap(dev, res->start, size, MEMREMAP_WB); + if (IS_ERR(addr)) { + dev_err(dev, "failed to map resource %pR\n", res); + return IOMEM_ERR_PTR(PTR_ERR(addr)); + } + + return (void __iomem __force *)addr; + } + + return devm_ioremap_resource(dev, res); +} + static int wdat_wdt_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; @@ -362,7 +387,7 @@ static int wdat_wdt_probe(struct platform_device *pdev) res = &pdev->resource[i]; if (resource_type(res) == IORESOURCE_MEM) { - reg = devm_ioremap_resource(dev, res); + reg = wdat_wdt_map_mem(dev, res); if (IS_ERR(reg)) { ret = PTR_ERR(reg); goto out_put_table; From b583ce85417befdc3d8a13458ed9b81e9421b665 Mon Sep 17 00:00:00 2001 From: Babanpreet Singh Date: Thu, 30 Jul 2026 06:06:17 +0000 Subject: [PATCH 31/40] watchdog: booke_wdt: Document unused parameter of __booke_wdt_disable() scripts/kernel-doc -none reports: Warning: drivers/watchdog/booke_wdt.c:158 function parameter 'data' not described in '__booke_wdt_disable' __booke_wdt_disable() is invoked through on_each_cpu(), so it takes a void * argument that it does not use. Its kernel-doc comment does not describe the parameter at all. Document it as unused. Assisted-by: Claude:claude-opus-5 Signed-off-by: Babanpreet Singh Link: https://lore.kernel.org/r/20260730060617.7-3-bbnpreetsingh@gmail.com Signed-off-by: Guenter Roeck --- drivers/watchdog/booke_wdt.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/watchdog/booke_wdt.c b/drivers/watchdog/booke_wdt.c index 932a03f4436a..1b1fb389b89b 100644 --- a/drivers/watchdog/booke_wdt.c +++ b/drivers/watchdog/booke_wdt.c @@ -149,6 +149,7 @@ static void __booke_wdt_enable(void *data) /** * __booke_wdt_disable - disable the watchdog on the given CPU + * @data: unused * * This function is called on each CPU. It disables the watchdog on that CPU. * From bd26ae0caa20de77328f85a2de4860d8494f1217 Mon Sep 17 00:00:00 2001 From: Mayank Rungta Date: Thu, 30 Jul 2026 15:00:21 -0700 Subject: [PATCH 32/40] nmi: Export CPU backtrace APIs for loadable modules Currently, CPU backtrace functions cannot be called from loadable modules because the underlying helper arch_trigger_cpumask_backtrace() is not exported. Instead of exporting arch_trigger_cpumask_backtrace() individually across every supported architecture, introduce and export a common helper, cpumask_backtrace(), in lib/nmi_backtrace.c. Update the four inline CPU backtrace macros in include/linux/nmi.h to route through this centralized helper. Signed-off-by: Mayank Rungta Reviewed-by: Douglas Anderson Reviewed-by: Andrew Morton Link: https://lore.kernel.org/r/20260730-export-cpu-backtrace-apis-v1-1-bace8e1cb817@google.com Signed-off-by: Guenter Roeck --- include/linux/nmi.h | 10 ++++++---- lib/nmi_backtrace.c | 7 +++++++ 2 files changed, 13 insertions(+), 4 deletions(-) diff --git a/include/linux/nmi.h b/include/linux/nmi.h index bc1162895f35..a394656bdb6d 100644 --- a/include/linux/nmi.h +++ b/include/linux/nmi.h @@ -157,27 +157,29 @@ static inline void touch_nmi_watchdog(void) * to allow calling code to fall back to some other mechanism: */ #ifdef arch_trigger_cpumask_backtrace +void cpumask_backtrace(const cpumask_t *mask, int exclude_cpu); + static inline bool trigger_all_cpu_backtrace(void) { - arch_trigger_cpumask_backtrace(cpu_online_mask, -1); + cpumask_backtrace(cpu_online_mask, -1); return true; } static inline bool trigger_allbutcpu_cpu_backtrace(int exclude_cpu) { - arch_trigger_cpumask_backtrace(cpu_online_mask, exclude_cpu); + cpumask_backtrace(cpu_online_mask, exclude_cpu); return true; } static inline bool trigger_cpumask_backtrace(struct cpumask *mask) { - arch_trigger_cpumask_backtrace(mask, -1); + cpumask_backtrace(mask, -1); return true; } static inline bool trigger_single_cpu_backtrace(int cpu) { - arch_trigger_cpumask_backtrace(cpumask_of(cpu), -1); + cpumask_backtrace(cpumask_of(cpu), -1); return true; } diff --git a/lib/nmi_backtrace.c b/lib/nmi_backtrace.c index a3bfa9360b23..f39c9eca8888 100644 --- a/lib/nmi_backtrace.c +++ b/lib/nmi_backtrace.c @@ -19,6 +19,7 @@ #include #include #include +#include #include #ifdef arch_trigger_cpumask_backtrace @@ -129,4 +130,10 @@ bool nmi_cpu_backtrace(struct pt_regs *regs) return false; } NOKPROBE_SYMBOL(nmi_cpu_backtrace); + +void cpumask_backtrace(const cpumask_t *mask, int exclude_cpu) +{ + arch_trigger_cpumask_backtrace(mask, exclude_cpu); +} +EXPORT_SYMBOL_GPL(cpumask_backtrace); #endif From 382a3cd59e24e452f94717bc9ccab053d634a48f Mon Sep 17 00:00:00 2001 From: Mayank Rungta Date: Thu, 30 Jul 2026 15:00:22 -0700 Subject: [PATCH 33/40] watchdog: pretimeout: Convert dump pretimeout governor to tristate Commit 645ad41da8b2 ("watchdog: pretimeout: Add "dump" pretimeout governor") added the "dump" watchdog pretimeout governor, but restricted it to built-in code because trigger_all_cpu_backtrace() was not exported to loadable modules. Now that CPU backtrace APIs are supported for loadable kernel modules via cpumask_backtrace(), convert WATCHDOG_PRETIMEOUT_GOV_DUMP to tristate. This allows kernels to deliver the pretimeout governor as a loadable kernel module. Signed-off-by: Mayank Rungta Reviewed-by: Douglas Anderson Reviewed-by: Tzung-Bi Shih Acked-by: Guenter Roeck Reviewed-by: Andrew Morton Link: https://lore.kernel.org/r/20260730-export-cpu-backtrace-apis-v1-2-bace8e1cb817@google.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 aaa41ce7dd55..f7d0a0c2c0ef 100644 --- a/drivers/watchdog/Kconfig +++ b/drivers/watchdog/Kconfig @@ -99,8 +99,8 @@ config WATCHDOG_PRETIMEOUT_GOV_SEL if WATCHDOG_PRETIMEOUT_GOV config WATCHDOG_PRETIMEOUT_GOV_DUMP - bool "Dump watchdog pretimeout governor" - depends on WATCHDOG_CORE=y + tristate "Dump watchdog pretimeout governor" + depends on WATCHDOG_CORE default WATCHDOG_CORE help Dump watchdog pretimeout governor, all cpu backtrace is From 799ef16e4607ba9ebc7fa2ca7d984940481fb79a Mon Sep 17 00:00:00 2001 From: Christophe JAILLET Date: Sun, 9 Aug 2026 15:49:10 +0200 Subject: [PATCH 34/40] watchdog: apple: Constify some structures 'struct watchdog_ops' and 'struct watchdog_info' are not modified in this driver. Constifying these structure moves some data to a read-only section, so increases overall security, especially when the structure holds some function pointers. On a x86_64, with allmodconfig: Before: ====== text data bss dec hex filename 7319 1768 0 9087 237f drivers/watchdog/apple_wdt.o After: ===== text data bss dec hex filename 7543 1544 0 9087 237f drivers/watchdog/apple_wdt.o Signed-off-by: Christophe JAILLET Link: https://lore.kernel.org/r/99019b7e560c2d9c087be340e68f9cce4db1988e.1786283336.git.christophe.jaillet@wanadoo.fr Signed-off-by: Guenter Roeck --- drivers/watchdog/apple_wdt.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/watchdog/apple_wdt.c b/drivers/watchdog/apple_wdt.c index 6b9b0f9b05ce..f533953294f6 100644 --- a/drivers/watchdog/apple_wdt.c +++ b/drivers/watchdog/apple_wdt.c @@ -139,7 +139,7 @@ static int apple_wdt_restart(struct watchdog_device *wdd, unsigned long mode, return 0; } -static struct watchdog_ops apple_wdt_ops = { +static const struct watchdog_ops apple_wdt_ops = { .owner = THIS_MODULE, .start = apple_wdt_start, .stop = apple_wdt_stop, @@ -149,7 +149,7 @@ static struct watchdog_ops apple_wdt_ops = { .restart = apple_wdt_restart, }; -static struct watchdog_info apple_wdt_info = { +static const struct watchdog_info apple_wdt_info = { .identity = "Apple SoC Watchdog", .options = WDIOF_MAGICCLOSE | WDIOF_KEEPALIVEPING | WDIOF_SETTIMEOUT, }; From 7528abb5154ad18d18315b80fded00eb903d35cc Mon Sep 17 00:00:00 2001 From: bui duc phuc Date: Fri, 7 Aug 2026 15:16:50 +0700 Subject: [PATCH 35/40] watchdog: mediatek: Propagate errors from optional IRQ lookup platform_get_irq_optional() returns a positive IRQ number on success or a negative error code on failure. For an optional IRQ, -ENXIO indicates that no IRQ is available, while other errors should be propagated. Instead of only checking for -EPROBE_DEFER, propagate all error codes returned by platform_get_irq_optional() other than -ENXIO, so that failures are properly reported to the caller. Signed-off-by: bui duc phuc Link: https://lore.kernel.org/r/20260807081652.38692-2-phucduc.bui@gmail.com Signed-off-by: Guenter Roeck --- drivers/watchdog/mtk_wdt.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/watchdog/mtk_wdt.c b/drivers/watchdog/mtk_wdt.c index 91d110646e16..d9c30e4c80e3 100644 --- a/drivers/watchdog/mtk_wdt.c +++ b/drivers/watchdog/mtk_wdt.c @@ -422,8 +422,8 @@ static int mtk_wdt_probe(struct platform_device *pdev) mtk_wdt->wdt_dev.info = &mtk_wdt_pt_info; mtk_wdt->wdt_dev.pretimeout = WDT_MAX_TIMEOUT / 2; } else { - if (irq == -EPROBE_DEFER) - return -EPROBE_DEFER; + if (irq != -ENXIO) + return irq; mtk_wdt->wdt_dev.info = &mtk_wdt_info; } From ac4bf51d4dc5343793587bfa8dc91a9089c622f8 Mon Sep 17 00:00:00 2001 From: bui duc phuc Date: Fri, 7 Aug 2026 15:16:51 +0700 Subject: [PATCH 36/40] watchdog: dw_wdt: Propagate errors from optional IRQ lookup platform_get_irq_optional() returns a positive IRQ number on success or a negative error code on failure. For an optional IRQ, -ENXIO indicates that no IRQ is available, while other errors should be propagated. Instead of only checking for -EPROBE_DEFER, propagate all error codes returned by platform_get_irq_optional() other than -ENXIO, so that failures are properly reported to the caller. Signed-off-by: bui duc phuc Link: https://lore.kernel.org/r/20260807081652.38692-3-phucduc.bui@gmail.com Signed-off-by: Guenter Roeck --- drivers/watchdog/dw_wdt.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/watchdog/dw_wdt.c b/drivers/watchdog/dw_wdt.c index c3fbb6068c52..39031445d754 100644 --- a/drivers/watchdog/dw_wdt.c +++ b/drivers/watchdog/dw_wdt.c @@ -611,7 +611,7 @@ static int dw_wdt_drv_probe(struct platform_device *pdev) dw_wdt->wdd.info = &dw_wdt_pt_ident; } else { - if (ret == -EPROBE_DEFER) + if (ret != -ENXIO) return ret; dw_wdt->wdd.info = &dw_wdt_ident; From 553edb793e5be81a0d9bdb7b5aa2f9f1e0eb237b Mon Sep 17 00:00:00 2001 From: bui duc phuc Date: Fri, 7 Aug 2026 15:04:47 +0700 Subject: [PATCH 37/40] watchdog: stm32_iwdg: Propagate errors from optional IRQ lookup platform_get_irq_optional() returns a positive IRQ number on success or a negative error code on failure. For an optional IRQ, -ENXIO indicates that no IRQ is available, while other errors should be propagated. Propagate errors such as -EPROBE_DEFER and -EINVAL instead of continuing probe without the IRQ. Signed-off-by: bui duc phuc Link: https://lore.kernel.org/r/20260807080447.35479-2-phucduc.bui@gmail.com Signed-off-by: Guenter Roeck --- drivers/watchdog/stm32_iwdg.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/drivers/watchdog/stm32_iwdg.c b/drivers/watchdog/stm32_iwdg.c index b356a272ff9a..a8d2db2d3cf8 100644 --- a/drivers/watchdog/stm32_iwdg.c +++ b/drivers/watchdog/stm32_iwdg.c @@ -287,8 +287,11 @@ static int stm32_iwdg_irq_init(struct platform_device *pdev, return 0; irq = platform_get_irq_optional(pdev, 0); - if (irq <= 0) + if (irq < 0) { + if (irq != -ENXIO) + return irq; return 0; + } if (of_property_read_bool(np, "wakeup-source")) { ret = devm_device_init_wakeup(dev); From 1f3c3fa76ddbc2eb92b60c5d4db24d7f702a7811 Mon Sep 17 00:00:00 2001 From: bui duc phuc Date: Fri, 7 Aug 2026 15:16:52 +0700 Subject: [PATCH 38/40] watchdog: aspeed: Propagate errors from optional IRQ lookup platform_get_irq_optional() returns a positive IRQ number on success or a negative error code on failure. For an optional IRQ, -ENXIO indicates that no IRQ is available, while other errors should be propagated. Propagate errors such as -EPROBE_DEFER and -EINVAL instead of continuing probe without the IRQ. Signed-off-by: bui duc phuc Link: https://lore.kernel.org/r/20260807081652.38692-4-phucduc.bui@gmail.com Signed-off-by: Guenter Roeck --- drivers/watchdog/aspeed_wdt.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/watchdog/aspeed_wdt.c b/drivers/watchdog/aspeed_wdt.c index c9e79851504c..48454ac86074 100644 --- a/drivers/watchdog/aspeed_wdt.c +++ b/drivers/watchdog/aspeed_wdt.c @@ -429,6 +429,8 @@ static int aspeed_wdt_probe(struct platform_device *pdev) if (wdt->cfg->irq_mask) { int irq = platform_get_irq_optional(pdev, 0); + if (irq < 0 && irq != -ENXIO) + return irq; if (irq > 0) { ret = devm_request_irq(dev, irq, aspeed_wdt_irq, IRQF_SHARED, dev_name(dev), From 2bab791e56d977b9e4676ef284c00a11a272d61c Mon Sep 17 00:00:00 2001 From: bui duc phuc Date: Mon, 10 Aug 2026 17:51:01 +0700 Subject: [PATCH 39/40] watchdog: qcom: Propagate errors from optional IRQ lookup platform_get_irq_optional() returns a positive IRQ number on success or a negative error code on failure. For an optional IRQ, -ENXIO indicates that no IRQ is available, while other errors should be propagated. Instead of only checking for -EPROBE_DEFER, propagate all error codes returned by platform_get_irq_optional() other than -ENXIO, so that failures are properly reported to the caller. Signed-off-by: bui duc phuc Link: https://lore.kernel.org/r/20260810105101.55945-1-phucduc.bui@gmail.com Signed-off-by: Guenter Roeck --- drivers/watchdog/qcom-wdt.c | 27 ++++++++++++++------------- 1 file changed, 14 insertions(+), 13 deletions(-) diff --git a/drivers/watchdog/qcom-wdt.c b/drivers/watchdog/qcom-wdt.c index a425902b9f68..4eb1bf979012 100644 --- a/drivers/watchdog/qcom-wdt.c +++ b/drivers/watchdog/qcom-wdt.c @@ -303,21 +303,22 @@ static int qcom_wdt_probe(struct platform_device *pdev) return -EINVAL; } + wdt->wdd.info = &qcom_wdt_info; + /* check if there is pretimeout support */ - irq = platform_get_irq_optional(pdev, 0); - if (data->pretimeout && irq > 0) { - ret = devm_request_irq(dev, irq, qcom_wdt_isr, 0, - "wdt_bark", &wdt->wdd); - if (ret) - return ret; + if (data->pretimeout) { + irq = platform_get_irq_optional(pdev, 0); + if (irq < 0 && irq != -ENXIO) + return irq; + if (irq > 0) { + ret = devm_request_irq(dev, irq, qcom_wdt_isr, 0, + "wdt_bark", &wdt->wdd); + if (ret) + return ret; - wdt->wdd.info = &qcom_wdt_pt_info; - wdt->wdd.pretimeout = 1; - } else { - if (irq == -EPROBE_DEFER) - return -EPROBE_DEFER; - - wdt->wdd.info = &qcom_wdt_info; + wdt->wdd.info = &qcom_wdt_pt_info; + wdt->wdd.pretimeout = 1; + } } wdt->wdd.ops = &qcom_wdt_ops; From 3bbbeb7fddf54a7d7116b2defe0a63a9b583c84e Mon Sep 17 00:00:00 2001 From: bui duc phuc Date: Mon, 10 Aug 2026 15:26:05 +0700 Subject: [PATCH 40/40] watchdog: orion_wdt: Propagate errors from optional IRQ lookup platform_get_irq_optional() returns a positive IRQ number on success or a negative error code on failure. For an optional IRQ, -ENXIO indicates that no IRQ is available, while other errors should be propagated. Propagate errors such as -EPROBE_DEFER and -EINVAL instead of continuing probe without the IRQ. Signed-off-by: bui duc phuc Link: https://lore.kernel.org/r/20260810082606.42623-1-phucduc.bui@gmail.com Signed-off-by: Guenter Roeck --- drivers/watchdog/orion_wdt.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/drivers/watchdog/orion_wdt.c b/drivers/watchdog/orion_wdt.c index 1802634cc7cd..0ea946823b0a 100644 --- a/drivers/watchdog/orion_wdt.c +++ b/drivers/watchdog/orion_wdt.c @@ -606,6 +606,10 @@ static int orion_wdt_probe(struct platform_device *pdev) /* Request the IRQ only after the watchdog is disabled */ irq = platform_get_irq_optional(pdev, 0); + if (irq < 0 && irq != -ENXIO) { + ret = irq; + goto disable_clk; + } if (irq > 0) { /* * Not all supported platforms specify an interrupt for the @@ -619,6 +623,10 @@ static int orion_wdt_probe(struct platform_device *pdev) /* Optional 2nd interrupt for pretimeout */ irq = platform_get_irq_optional(pdev, 1); + if (irq < 0 && irq != -ENXIO) { + ret = irq; + goto disable_clk; + } if (irq > 0) { orion_wdt_info.options |= WDIOF_PRETIMEOUT; ret = devm_request_irq(&pdev->dev, irq, orion_wdt_pre_irq,