summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMark Brown <broonie@kernel.org>2026-07-26 22:26:11 +0100
committerMark Brown <broonie@kernel.org>2026-07-26 22:26:11 +0100
commit56e7504157bc4e7e1a7bbda575895924e154c3f8 (patch)
tree93319da3db282711f81d0f78a1ec96ba48e71b6e
parent9f96b774350e2cf9949fa81e350903936bd98678 (diff)
parent65204d0605c0c50e3051279a919a2f80b8fa4ba1 (diff)
downloadlinux-next-56e7504157bc4e7e1a7bbda575895924e154c3f8.tar.gz
linux-next-56e7504157bc4e7e1a7bbda575895924e154c3f8.zip
Merge branch 'watchdog-next' of https://git.kernel.org/pub/scm/linux/kernel/git/groeck/linux-staging.git
-rw-r--r--Documentation/devicetree/bindings/watchdog/apple,wdt.yaml3
-rw-r--r--Documentation/devicetree/bindings/watchdog/nuvoton,npcm750-wdt.yaml14
-rw-r--r--Documentation/devicetree/bindings/watchdog/qcom-wdt.yaml1
-rw-r--r--Documentation/devicetree/bindings/watchdog/snps,dw-wdt.yaml1
-rw-r--r--Documentation/watchdog/index.rst1
-rw-r--r--Documentation/watchdog/npcm_wdt.rst71
-rw-r--r--drivers/watchdog/Kconfig32
-rw-r--r--drivers/watchdog/Makefile2
-rw-r--r--drivers/watchdog/bd96801_wdt.c3
-rw-r--r--drivers/watchdog/cadence_wdt.c6
-rw-r--r--drivers/watchdog/cros_ec_wdt.c4
-rw-r--r--drivers/watchdog/intel-mid_wdt.c4
-rw-r--r--drivers/watchdog/keembay_wdt.c4
-rw-r--r--drivers/watchdog/lenovo_se30g2_se60_wdt.c289
-rw-r--r--drivers/watchdog/marvell_gti_wdt.c2
-rw-r--r--drivers/watchdog/max63xx_wdt.c14
-rw-r--r--drivers/watchdog/max77620_wdt.c6
-rw-r--r--drivers/watchdog/npcm_wdt.c217
-rw-r--r--drivers/watchdog/orion_wdt.c8
-rw-r--r--drivers/watchdog/pretimeout_dump.c45
-rw-r--r--drivers/watchdog/realtek_otto_wdt.c76
-rw-r--r--drivers/watchdog/sama5d4_wdt.c4
-rw-r--r--drivers/watchdog/sprd_wdt.c4
-rw-r--r--drivers/watchdog/via_wdt.c1
-rw-r--r--drivers/watchdog/w83627hf_wdt.c22
-rw-r--r--drivers/watchdog/watchdog_core.c26
-rw-r--r--drivers/watchdog/watchdog_pretimeout.h4
27 files changed, 778 insertions, 86 deletions
diff --git a/Documentation/devicetree/bindings/watchdog/apple,wdt.yaml b/Documentation/devicetree/bindings/watchdog/apple,wdt.yaml
index 845b5e8b5abc..ca4cf01db59f 100644
--- a/Documentation/devicetree/bindings/watchdog/apple,wdt.yaml
+++ b/Documentation/devicetree/bindings/watchdog/apple,wdt.yaml
@@ -18,7 +18,10 @@ properties:
- items:
- enum:
- apple,t6020-wdt
+ - apple,t6030-wdt
+ - apple,t6031-wdt
- apple,t8122-wdt
+ - apple,t8132-wdt
- const: apple,t8103-wdt
- items:
- enum:
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
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
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
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.
diff --git a/drivers/watchdog/Kconfig b/drivers/watchdog/Kconfig
index 08cb8612d41f..e40eb44f590d 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
@@ -291,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
@@ -1076,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/Makefile b/drivers/watchdog/Makefile
index bc1d52220f22..cb78e9932eae 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
@@ -127,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/bd96801_wdt.c b/drivers/watchdog/bd96801_wdt.c
index 12b74fd2bc05..6a1c5a4d39bf 100644
--- a/drivers/watchdog/bd96801_wdt.c
+++ b/drivers/watchdog/bd96801_wdt.c
@@ -390,8 +390,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);
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 */
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/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
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;
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 <linux/delay.h>
+#include <linux/dmi.h>
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/platform_device.h>
+#include <linux/string.h>
+#include <linux/types.h>
+#include <linux/watchdog.h>
+
+#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 <mpearson-lenovo@squebb.ca>");
+MODULE_DESCRIPTION("WDT driver for Lenovo SE30G2 & SE60");
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;
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);
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 <linux/delay.h>
#include <linux/interrupt.h>
#include <linux/kernel.h>
+#include <linux/mfd/syscon.h>
#include <linux/module.h>
#include <linux/of_irq.h>
#include <linux/platform_device.h>
+#include <linux/regmap.h>
#include <linux/slab.h>
#include <linux/watchdog.h>
#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);
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;
- }
}
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 <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/nmi.h>
+#include <linux/watchdog.h>
+
+#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 <tzungbi@kernel.org>");
+MODULE_DESCRIPTION("Dump watchdog pretimeout governor");
+MODULE_LICENSE("GPL");
diff --git a/drivers/watchdog/realtek_otto_wdt.c b/drivers/watchdog/realtek_otto_wdt.c
index 9094f2189f55..35d34834ce04 100644
--- a/drivers/watchdog/realtek_otto_wdt.c
+++ b/drivers/watchdog/realtek_otto_wdt.c
@@ -27,6 +27,7 @@
#include <linux/platform_device.h>
#include <linux/property.h>
#include <linux/reboot.h>
+#include <linux/regmap.h>
#include <linux/watchdog.h>
#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)
@@ -311,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)
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);
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;
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",
diff --git a/drivers/watchdog/w83627hf_wdt.c b/drivers/watchdog/w83627hf_wdt.c
index bc33b63c5a5d..db77599e43a0 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.
@@ -241,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);
}
}
@@ -313,9 +317,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 = {
@@ -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 */
@@ -516,6 +525,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);
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;
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"