diff options
| author | Mark Brown <broonie@kernel.org> | 2026-07-26 22:26:11 +0100 |
|---|---|---|
| committer | Mark Brown <broonie@kernel.org> | 2026-07-26 22:26:11 +0100 |
| commit | 56e7504157bc4e7e1a7bbda575895924e154c3f8 (patch) | |
| tree | 93319da3db282711f81d0f78a1ec96ba48e71b6e | |
| parent | 9f96b774350e2cf9949fa81e350903936bd98678 (diff) | |
| parent | 65204d0605c0c50e3051279a919a2f80b8fa4ba1 (diff) | |
| download | linux-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
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" |
