* [PATCH v2 1/8] watchdog: w83627hf_wdt: Replace magic numbers with descriptive macros
2026-07-26 18:44 [PATCH v2 0/8] watchdog: w83627hf_wdt: Convert driver to the driver model and add minute support Paul Louvel
@ 2026-07-26 18:44 ` Paul Louvel
2026-07-26 18:44 ` [PATCH v2 2/8] watchdog: w83627hf_wdt: Convert to platform driver model Paul Louvel
` (6 subsequent siblings)
7 siblings, 0 replies; 14+ messages in thread
From: Paul Louvel @ 2026-07-26 18:44 UTC (permalink / raw)
To: Wim Van Sebroeck, Guenter Roeck
Cc: linux-watchdog, linux-kernel, Paul Louvel, Thomas Petazzoni
Raw hex values for Super I/O register addresses are difficult to understand
without constantly consulting the datasheet. Use named macros instead.
Also, replace macros value that contains bit position with the BIT()
macro.
Signed-off-by: Paul Louvel <paul.louvel@bootlin.com>
---
drivers/watchdog/w83627hf_wdt.c | 43 ++++++++++++++++++++++++++---------------
1 file changed, 27 insertions(+), 16 deletions(-)
diff --git a/drivers/watchdog/w83627hf_wdt.c b/drivers/watchdog/w83627hf_wdt.c
index db77599e43a0..1529a4e16820 100644
--- a/drivers/watchdog/w83627hf_wdt.c
+++ b/drivers/watchdog/w83627hf_wdt.c
@@ -27,6 +27,7 @@
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+#include <linux/bits.h>
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/types.h>
@@ -71,6 +72,12 @@ MODULE_PARM_DESC(early_disable, "Disable watchdog at boot time (default=0)");
* Kernel methods.
*/
+#define SIO_REG_LDSEL 0x07 /* Logical device select */
+#define SIO_REG_DEVID 0x20 /* Device ID (1 or 2 bytes) */
+#define SIO_REG_ENABLE 0x30 /* Logical device enable */
+#define SIO_REG_CONF_ADDR0 0x2E
+#define SIO_REG_CONF_ADDR1 0x4E
+
#define WDT_EFER (wdt_io+0) /* Extended Function Enable Registers */
#define WDT_EFIR (wdt_io+0) /* Extended Function Index Register
(same as EFER) */
@@ -115,9 +122,12 @@ MODULE_PARM_DESC(early_disable, "Disable watchdog at boot time (default=0)");
#define W836X7HF_WDT_CSR 0xf7
#define NCT6102D_WDT_CSR 0xf2
-#define WDT_CSR_STATUS 0x10
-#define WDT_CSR_KBD 0x40
-#define WDT_CSR_MOUSE 0x80
+#define WDT_CSR_STATUS BIT(4)
+#define WDT_CSR_KBD_INT_RESET BIT(6)
+#define WDT_CSR_MOUSE_INT_RESET BIT(7)
+
+#define WDT_CTRL_RISING_EDGE_KBD_RESET BIT(2)
+#define WDT_CTRL_MINUTE_MODE BIT(3)
static void superio_outb(int reg, int val)
{
@@ -144,7 +154,7 @@ static int superio_enter(void)
static void superio_select(int ld)
{
- superio_outb(0x07, ld);
+ superio_outb(SIO_REG_LDSEL, ld);
}
static void superio_exit(void)
@@ -165,9 +175,9 @@ static int w83627hf_init(struct watchdog_device *wdog, enum chips chip)
superio_select(W83627HF_LD_WDT);
/* set CR30 bit 0 to activate GPIO2 */
- t = superio_inb(0x30);
+ t = superio_inb(SIO_REG_ENABLE);
if (!(t & 0x01))
- superio_outb(0x30, t | 0x01);
+ superio_outb(SIO_REG_ENABLE, t | 0x01);
switch (chip) {
case w83627hf:
@@ -248,16 +258,17 @@ static int w83627hf_init(struct watchdog_device *wdog, enum chips chip)
}
}
- /* set second mode & disable keyboard turning off watchdog */
- t = superio_inb(cr_wdt_control) & ~0x0C;
+ /* set second mode & disable keyboard reset turning off watchdog */
+ t = superio_inb(cr_wdt_control) &
+ ~(WDT_CTRL_MINUTE_MODE | WDT_CTRL_RISING_EDGE_KBD_RESET);
superio_outb(cr_wdt_control, t);
t = superio_inb(cr_wdt_csr);
if (t & WDT_CSR_STATUS)
wdog->bootstatus |= WDIOF_CARDRESET;
- /* reset status, disable keyboard & mouse turning off watchdog */
- t &= ~(WDT_CSR_STATUS | WDT_CSR_KBD | WDT_CSR_MOUSE);
+ /* reset status, disable keyboard & mouse interrupt turning off watchdog */
+ t &= ~(WDT_CSR_STATUS | WDT_CSR_KBD_INT_RESET | WDT_CSR_MOUSE_INT_RESET);
superio_outb(cr_wdt_csr, t);
superio_exit();
@@ -355,7 +366,7 @@ static int wdt_find(int addr)
if (ret)
return ret;
superio_select(W83627HF_LD_WDT);
- val = superio_inb(0x20);
+ val = superio_inb(SIO_REG_DEVID);
switch (val) {
case W83627HF_ID:
ret = w83627hf;
@@ -431,7 +442,7 @@ static int wdt_find(int addr)
cr_wdt_csr = NCT6102D_WDT_CSR;
break;
case NCT6116_ID:
- val = superio_inb(0x21);
+ val = superio_inb(SIO_REG_DEVID + 1);
if (val == NCT6126_VER_A_LOW_ID || val == NCT6126_VER_B_LOW_ID)
ret = nct6126;
else
@@ -513,11 +524,11 @@ static int __init wdt_init(void)
/* Apply system-specific quirks */
dmi_check_system(wdt_dmi_table);
- wdt_io = 0x2e;
- chip = wdt_find(0x2e);
+ wdt_io = SIO_REG_CONF_ADDR0;
+ chip = wdt_find(SIO_REG_CONF_ADDR0);
if (chip < 0) {
- wdt_io = 0x4e;
- chip = wdt_find(0x4e);
+ wdt_io = SIO_REG_CONF_ADDR1;
+ chip = wdt_find(SIO_REG_CONF_ADDR1);
if (chip < 0)
return chip;
}
--
2.55.0
^ permalink raw reply related [flat|nested] 14+ messages in thread* [PATCH v2 2/8] watchdog: w83627hf_wdt: Convert to platform driver model
2026-07-26 18:44 [PATCH v2 0/8] watchdog: w83627hf_wdt: Convert driver to the driver model and add minute support Paul Louvel
2026-07-26 18:44 ` [PATCH v2 1/8] watchdog: w83627hf_wdt: Replace magic numbers with descriptive macros Paul Louvel
@ 2026-07-26 18:44 ` Paul Louvel
2026-07-26 18:57 ` sashiko-bot
2026-07-26 18:44 ` [PATCH v2 3/8] watchdog: w83627hf_wdt: Use private driver data structure Paul Louvel
` (5 subsequent siblings)
7 siblings, 1 reply; 14+ messages in thread
From: Paul Louvel @ 2026-07-26 18:44 UTC (permalink / raw)
To: Wim Van Sebroeck, Guenter Roeck
Cc: linux-watchdog, linux-kernel, Paul Louvel, Thomas Petazzoni
Convert the driver to the Linux driver model with a platform driver /
device.
Signed-off-by: Paul Louvel <paul.louvel@bootlin.com>
---
drivers/watchdog/w83627hf_wdt.c | 126 +++++++++++++++++++++++++---------------
1 file changed, 79 insertions(+), 47 deletions(-)
diff --git a/drivers/watchdog/w83627hf_wdt.c b/drivers/watchdog/w83627hf_wdt.c
index 1529a4e16820..206ab91fdb2a 100644
--- a/drivers/watchdog/w83627hf_wdt.c
+++ b/drivers/watchdog/w83627hf_wdt.c
@@ -29,6 +29,7 @@
#include <linux/bits.h>
#include <linux/module.h>
+#include <linux/platform_device.h>
#include <linux/moduleparam.h>
#include <linux/types.h>
#include <linux/watchdog.h>
@@ -464,6 +465,36 @@ static int wdt_find(int addr)
return ret;
}
+static int wdt_probe(struct platform_device *pdev)
+{
+ const struct platform_device_id *id = platform_get_device_id(pdev);
+ enum chips chip = id->driver_data;
+ struct device *dev = &pdev->dev;
+ int ret;
+
+ dev_info(dev, "WDT driver initialising\n");
+
+ snprintf(wdt_info.identity, sizeof(wdt_info.identity), "%s Watchdog",
+ id->name);
+
+ watchdog_init_timeout(&wdt_dev, timeout, NULL);
+ watchdog_set_nowayout(&wdt_dev, nowayout);
+ watchdog_stop_on_reboot(&wdt_dev);
+
+ ret = w83627hf_init(&wdt_dev, chip);
+ if (ret)
+ return dev_err_probe(dev, ret, "failed to initialize watchdog\n");
+
+ ret = devm_watchdog_register_device(&pdev->dev, &wdt_dev);
+ if (ret)
+ return ret;
+
+ dev_info(dev, "initialized. timeout=%d sec (nowayout=%d)\n",
+ wdt_dev.timeout, nowayout);
+
+ return ret;
+}
+
/*
* On some systems, the NCT6791D comes with a companion chip and the
* watchdog function is in this companion chip. We must use a different
@@ -490,36 +521,48 @@ static const struct dmi_system_id wdt_dmi_table[] __initconst = {
{}
};
+static const struct platform_device_id wdt_ids[] = {
+ { .name = "W83627HF", .driver_data = w83627hf },
+ { .name = "W83627S", .driver_data = w83627s },
+ { .name = "W83697HF", .driver_data = w83697hf },
+ { .name = "W83697UG", .driver_data = w83697ug },
+ { .name = "W83637HF", .driver_data = w83637hf },
+ { .name = "W83627THF", .driver_data = w83627thf },
+ { .name = "W83687THF", .driver_data = w83687thf },
+ { .name = "W83627EHF", .driver_data = w83627ehf },
+ { .name = "W83627DHG", .driver_data = w83627dhg },
+ { .name = "W83627UHG", .driver_data = w83627uhg },
+ { .name = "W83667HG", .driver_data = w83667hg },
+ { .name = "W83667DHG-P", .driver_data = w83627dhg_p },
+ { .name = "W83667HG-B", .driver_data = w83667hg_b },
+ { .name = "NCT6775", .driver_data = nct6775 },
+ { .name = "NCT6776", .driver_data = nct6776 },
+ { .name = "NCT6779", .driver_data = nct6779 },
+ { .name = "NCT6791", .driver_data = nct6791 },
+ { .name = "NCT6792", .driver_data = nct6792 },
+ { .name = "NCT6793", .driver_data = nct6793 },
+ { .name = "NCT6795", .driver_data = nct6795 },
+ { .name = "NCT6796", .driver_data = nct6796 },
+ { .name = "NCT6102", .driver_data = nct6102 },
+ { .name = "NCT6116", .driver_data = nct6116 },
+ { .name = "NCT6126", .driver_data = nct6126 },
+ {},
+};
+
+static struct platform_driver wdt_driver = {
+ .probe = wdt_probe,
+ .id_table = wdt_ids,
+ .driver = {
+ .name = KBUILD_MODNAME,
+ },
+};
+
+static struct platform_device *wdt_pdev;
+
static int __init wdt_init(void)
{
int ret;
int chip;
- static const char * const chip_name[] = {
- "W83627HF",
- "W83627S",
- "W83697HF",
- "W83697UG",
- "W83637HF",
- "W83627THF",
- "W83687THF",
- "W83627EHF",
- "W83627DHG",
- "W83627UHG",
- "W83667HG",
- "W83667DHG-P",
- "W83667HG-B",
- "NCT6775",
- "NCT6776",
- "NCT6779",
- "NCT6791",
- "NCT6792",
- "NCT6793",
- "NCT6795",
- "NCT6796",
- "NCT6102",
- "NCT6116",
- "NCT6126"
- };
/* Apply system-specific quirks */
dmi_check_system(wdt_dmi_table);
@@ -533,35 +576,24 @@ static int __init wdt_init(void)
return chip;
}
- 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);
-
- ret = w83627hf_init(&wdt_dev, chip);
- if (ret) {
- pr_err("failed to initialize watchdog (err=%d)\n", ret);
- return ret;
- }
-
- ret = watchdog_register_device(&wdt_dev);
+ ret = platform_driver_register(&wdt_driver);
if (ret)
return ret;
- pr_info("initialized. timeout=%d sec (nowayout=%d)\n",
- wdt_dev.timeout, nowayout);
+ wdt_pdev = platform_device_register_data(NULL, wdt_ids[chip].name,
+ PLATFORM_DEVID_NONE, NULL, 0);
+ if (IS_ERR(wdt_pdev)) {
+ platform_driver_unregister(&wdt_driver);
+ return PTR_ERR(wdt_pdev);
+ }
- return ret;
+ return 0;
}
static void __exit wdt_exit(void)
{
- watchdog_unregister_device(&wdt_dev);
+ platform_device_unregister(wdt_pdev);
+ platform_driver_unregister(&wdt_driver);
}
module_init(wdt_init);
--
2.55.0
^ permalink raw reply related [flat|nested] 14+ messages in thread* Re: [PATCH v2 2/8] watchdog: w83627hf_wdt: Convert to platform driver model
2026-07-26 18:44 ` [PATCH v2 2/8] watchdog: w83627hf_wdt: Convert to platform driver model Paul Louvel
@ 2026-07-26 18:57 ` sashiko-bot
0 siblings, 0 replies; 14+ messages in thread
From: sashiko-bot @ 2026-07-26 18:57 UTC (permalink / raw)
To: Paul Louvel; +Cc: linux-watchdog
Thank you for your contribution! Sashiko AI review found 4 potential issue(s) to consider:
New issues:
- [High] Potential NULL pointer dereference in `wdt_probe()` when the device is bound via sysfs `driver_override`.
- [High] Unbinding and rebinding the platform driver leaks watchdog status state, permanently breaking the watchdog.
- [Medium] The watchdog device's parent pointer is not initialized before registration, breaking the sysfs device hierarchy.
Pre-existing issues:
- [High] Hardware watchdog is left running if `devm_watchdog_register_device()` fails, guaranteeing an unintended system reboot.
--
--- Patch [2]: [PATCH v2 2/8] watchdog: w83627hf_wdt: Convert to platform driver model ---
commit d9d8da674e5ddb501516445617ff593614dca863
Author: Paul Louvel <paul.louvel@bootlin.com>
watchdog: w83627hf_wdt: Convert to platform driver model
This commit converts the w83627hf_wdt driver to the Linux driver model by
using a platform driver and platform device instead of the legacy module
init registration.
> diff --git a/drivers/watchdog/w83627hf_wdt.c b/drivers/watchdog/w83627hf_wdt.c
> index 1529a4e168209..206ab91fdb2a9 100644
> --- a/drivers/watchdog/w83627hf_wdt.c
> +++ b/drivers/watchdog/w83627hf_wdt.c
[ ... ]
> @@ -464,6 +465,36 @@ static int wdt_find(int addr)
> return ret;
> }
>
> +static int wdt_probe(struct platform_device *pdev)
> +{
> + const struct platform_device_id *id = platform_get_device_id(pdev);
> + enum chips chip = id->driver_data;
[Severity: High]
Can platform_get_device_id() return NULL here if the device is bound via
sysfs driver_override? If the driver core's platform_match() succeeds by
matching the driver name and bypasses the id_table, id would be NULL,
causing a NULL pointer dereference when accessing id->driver_data.
> + struct device *dev = &pdev->dev;
> + int ret;
> +
> + dev_info(dev, "WDT driver initialising\n");
> +
> + snprintf(wdt_info.identity, sizeof(wdt_info.identity), "%s Watchdog",
> + id->name);
> +
> + watchdog_init_timeout(&wdt_dev, timeout, NULL);
> + watchdog_set_nowayout(&wdt_dev, nowayout);
> + watchdog_stop_on_reboot(&wdt_dev);
> +
> + ret = w83627hf_init(&wdt_dev, chip);
> + if (ret)
> + return dev_err_probe(dev, ret, "failed to initialize watchdog\n");
> +
> + ret = devm_watchdog_register_device(&pdev->dev, &wdt_dev);
[Severity: Medium]
Should wdt_dev.parent be set to dev before calling
devm_watchdog_register_device()? Without this assignment, the watchdog
device is parented to the virtual sysfs root rather than the physical
platform device, which breaks udev hardware association rules.
[Severity: High]
Does unbinding and rebinding this platform driver leak watchdog status
state? Since wdt_dev is statically allocated and not zeroed during probe,
any state set during a previous bind (like the WDOG_ACTIVE bit) is
preserved. If an administrator unbinds and rebinds the driver, the core
will see WDOG_ACTIVE is already set and return success without calling
the hardware start routine, silently breaking the watchdog.
> + if (ret)
> + return ret;
[Severity: High]
This is a pre-existing issue, but does a failure in
devm_watchdog_register_device() leave the hardware watchdog running? If
the BIOS started the hardware watchdog before boot, w83627hf_init() resets
its timeout and leaves it ticking. Because probe returns an error and the
device node is never created, user-space has no way to ping the watchdog,
guaranteeing an unintended system reboot.
> +
> + dev_info(dev, "initialized. timeout=%d sec (nowayout=%d)\n",
> + wdt_dev.timeout, nowayout);
> +
> + return ret;
> +}
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260726-w83627hf_wdt-improvements-v2-0-3645a2a6c022@bootlin.com?part=2
^ permalink raw reply [flat|nested] 14+ messages in thread
* [PATCH v2 3/8] watchdog: w83627hf_wdt: Use private driver data structure
2026-07-26 18:44 [PATCH v2 0/8] watchdog: w83627hf_wdt: Convert driver to the driver model and add minute support Paul Louvel
2026-07-26 18:44 ` [PATCH v2 1/8] watchdog: w83627hf_wdt: Replace magic numbers with descriptive macros Paul Louvel
2026-07-26 18:44 ` [PATCH v2 2/8] watchdog: w83627hf_wdt: Convert to platform driver model Paul Louvel
@ 2026-07-26 18:44 ` Paul Louvel
2026-07-26 18:44 ` [PATCH v2 4/8] watchdog: w83627hf_wdt: Move register offsets into driver data Paul Louvel
` (4 subsequent siblings)
7 siblings, 0 replies; 14+ messages in thread
From: Paul Louvel @ 2026-07-26 18:44 UTC (permalink / raw)
To: Wim Van Sebroeck, Guenter Roeck
Cc: linux-watchdog, linux-kernel, Paul Louvel, Thomas Petazzoni
Allocating a per-device data structure is a common practice in the Linux
driver model. Add one that will be used to store chip-specific register
offsets, and configuration.
Signed-off-by: Paul Louvel <paul.louvel@bootlin.com>
---
drivers/watchdog/w83627hf_wdt.c | 49 ++++++++++++++++++++++++-----------------
1 file changed, 29 insertions(+), 20 deletions(-)
diff --git a/drivers/watchdog/w83627hf_wdt.c b/drivers/watchdog/w83627hf_wdt.c
index 206ab91fdb2a..38cbc6d14f4c 100644
--- a/drivers/watchdog/w83627hf_wdt.c
+++ b/drivers/watchdog/w83627hf_wdt.c
@@ -130,6 +130,11 @@ MODULE_PARM_DESC(early_disable, "Disable watchdog at boot time (default=0)");
#define WDT_CTRL_RISING_EDGE_KBD_RESET BIT(2)
#define WDT_CTRL_MINUTE_MODE BIT(3)
+struct w83627hf_data {
+ struct watchdog_device wdd;
+ struct watchdog_info info;
+};
+
static void superio_outb(int reg, int val)
{
outb(reg, WDT_EFER);
@@ -329,10 +334,6 @@ static unsigned int wdt_get_time(struct watchdog_device *wdog)
* Kernel Interfaces
*/
-static struct watchdog_info wdt_info = {
- .options = WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING | WDIOF_MAGICCLOSE,
-};
-
static const struct watchdog_ops wdt_ops = {
.owner = THIS_MODULE,
.start = wdt_start,
@@ -341,14 +342,6 @@ static const struct watchdog_ops wdt_ops = {
.get_timeleft = wdt_get_time,
};
-static struct watchdog_device wdt_dev = {
- .info = &wdt_info,
- .ops = &wdt_ops,
- .timeout = WATCHDOG_TIMEOUT,
- .min_timeout = 1,
- .max_timeout = 255,
-};
-
/*
* The WDT needs to learn about soft shutdowns in order to
* turn the timebomb registers off.
@@ -470,27 +463,43 @@ static int wdt_probe(struct platform_device *pdev)
const struct platform_device_id *id = platform_get_device_id(pdev);
enum chips chip = id->driver_data;
struct device *dev = &pdev->dev;
+ struct watchdog_device *wdd;
+ struct w83627hf_data *data;
int ret;
dev_info(dev, "WDT driver initialising\n");
- snprintf(wdt_info.identity, sizeof(wdt_info.identity), "%s Watchdog",
- id->name);
+ data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL);
+ if (!data)
+ return -ENOMEM;
+
+ data->info.options = WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING | WDIOF_MAGICCLOSE;
+ snprintf(data->info.identity, sizeof(data->info.identity),
+ "%s Watchdog", id->name);
+
+ wdd = &data->wdd;
+
+ wdd->info = &data->info;
+ wdd->ops = &wdt_ops;
+ wdd->timeout = WATCHDOG_TIMEOUT;
+ wdd->min_timeout = 1;
+ wdd->max_timeout = 255;
- watchdog_init_timeout(&wdt_dev, timeout, NULL);
- watchdog_set_nowayout(&wdt_dev, nowayout);
- watchdog_stop_on_reboot(&wdt_dev);
+ watchdog_set_drvdata(wdd, data);
+ watchdog_init_timeout(wdd, timeout, NULL);
+ watchdog_set_nowayout(wdd, nowayout);
+ watchdog_stop_on_reboot(wdd);
- ret = w83627hf_init(&wdt_dev, chip);
+ ret = w83627hf_init(wdd, chip);
if (ret)
return dev_err_probe(dev, ret, "failed to initialize watchdog\n");
- ret = devm_watchdog_register_device(&pdev->dev, &wdt_dev);
+ ret = devm_watchdog_register_device(&pdev->dev, wdd);
if (ret)
return ret;
dev_info(dev, "initialized. timeout=%d sec (nowayout=%d)\n",
- wdt_dev.timeout, nowayout);
+ wdd->timeout, nowayout);
return ret;
}
--
2.55.0
^ permalink raw reply related [flat|nested] 14+ messages in thread* [PATCH v2 4/8] watchdog: w83627hf_wdt: Move register offsets into driver data
2026-07-26 18:44 [PATCH v2 0/8] watchdog: w83627hf_wdt: Convert driver to the driver model and add minute support Paul Louvel
` (2 preceding siblings ...)
2026-07-26 18:44 ` [PATCH v2 3/8] watchdog: w83627hf_wdt: Use private driver data structure Paul Louvel
@ 2026-07-26 18:44 ` Paul Louvel
2026-07-26 18:44 ` [PATCH v2 5/8] watchdog: w83627hf_wdt: Add Super I/O configuration port as a platform device resource Paul Louvel
` (3 subsequent siblings)
7 siblings, 0 replies; 14+ messages in thread
From: Paul Louvel @ 2026-07-26 18:44 UTC (permalink / raw)
To: Wim Van Sebroeck, Guenter Roeck
Cc: linux-watchdog, linux-kernel, Paul Louvel, Thomas Petazzoni
Register offsets for the watchdog timer, control, and status registers
differ across Super I/O chip variants. Store them in the driver data
instead of global variables.
Signed-off-by: Paul Louvel <paul.louvel@bootlin.com>
---
drivers/watchdog/w83627hf_wdt.c | 73 ++++++++++++++++++++++-------------------
1 file changed, 39 insertions(+), 34 deletions(-)
diff --git a/drivers/watchdog/w83627hf_wdt.c b/drivers/watchdog/w83627hf_wdt.c
index 38cbc6d14f4c..69d4230d8f6c 100644
--- a/drivers/watchdog/w83627hf_wdt.c
+++ b/drivers/watchdog/w83627hf_wdt.c
@@ -42,9 +42,6 @@
#define WATCHDOG_TIMEOUT 60 /* 60 sec default timeout */
static int wdt_io;
-static int cr_wdt_timeout; /* WDT timeout register */
-static int cr_wdt_control; /* WDT control register */
-static int cr_wdt_csr; /* WDT control & status register */
static int wdt_cfg_enter = 0x87;/* key to unlock configuration space */
static int wdt_cfg_leave = 0xAA;/* key to lock configuration space */
@@ -133,6 +130,11 @@ MODULE_PARM_DESC(early_disable, "Disable watchdog at boot time (default=0)");
struct w83627hf_data {
struct watchdog_device wdd;
struct watchdog_info info;
+ struct {
+ int control;
+ int timeout;
+ int csr;
+ } reg;
};
static void superio_outb(int reg, int val)
@@ -171,6 +173,7 @@ static void superio_exit(void)
static int w83627hf_init(struct watchdog_device *wdog, enum chips chip)
{
+ struct w83627hf_data *data = watchdog_get_drvdata(wdog);
int ret;
unsigned char t;
@@ -210,10 +213,10 @@ static int w83627hf_init(struct watchdog_device *wdog, enum chips chip)
case w83627dhg_p:
t = superio_inb(0x2D) & ~0x01; /* PIN77 -> WDT0# */
superio_outb(0x2D, t); /* set GPIO5 to WDT0 */
- t = superio_inb(cr_wdt_control);
+ t = superio_inb(data->reg.control);
t |= 0x02; /* enable the WDTO# output low pulse
* to the KBRST# pin */
- superio_outb(cr_wdt_control, t);
+ superio_outb(data->reg.control, t);
break;
case w83637hf:
break;
@@ -242,48 +245,49 @@ static int w83627hf_init(struct watchdog_device *wdog, enum chips chip)
* Don't touch its configuration, and hope the BIOS
* does the right thing.
*/
- t = superio_inb(cr_wdt_control);
+ t = superio_inb(data->reg.control);
t |= 0x02; /* enable the WDTO# output low pulse
* to the KBRST# pin */
- superio_outb(cr_wdt_control, t);
+ superio_outb(data->reg.control, t);
break;
default:
break;
}
- t = superio_inb(cr_wdt_timeout);
+ t = superio_inb(data->reg.timeout);
if (t != 0) {
if (early_disable) {
pr_warn("Stopping previously enabled watchdog until userland kicks in\n");
- superio_outb(cr_wdt_timeout, 0);
+ superio_outb(data->reg.timeout, 0);
} else {
pr_info("Watchdog already running. Resetting timeout to %d sec\n",
wdog->timeout);
- superio_outb(cr_wdt_timeout, wdog->timeout);
+ superio_outb(data->reg.timeout, wdog->timeout);
set_bit(WDOG_HW_RUNNING, &wdog->status);
}
}
/* set second mode & disable keyboard reset turning off watchdog */
- t = superio_inb(cr_wdt_control) &
+ t = superio_inb(data->reg.control) &
~(WDT_CTRL_MINUTE_MODE | WDT_CTRL_RISING_EDGE_KBD_RESET);
- superio_outb(cr_wdt_control, t);
+ superio_outb(data->reg.control, t);
- t = superio_inb(cr_wdt_csr);
+ t = superio_inb(data->reg.csr);
if (t & WDT_CSR_STATUS)
wdog->bootstatus |= WDIOF_CARDRESET;
/* reset status, disable keyboard & mouse interrupt turning off watchdog */
t &= ~(WDT_CSR_STATUS | WDT_CSR_KBD_INT_RESET | WDT_CSR_MOUSE_INT_RESET);
- superio_outb(cr_wdt_csr, t);
+ superio_outb(data->reg.csr, t);
superio_exit();
return 0;
}
-static int wdt_set_time(unsigned int timeout)
+static int wdt_set_time(struct watchdog_device *wdog, unsigned int timeout)
{
+ struct w83627hf_data *data = watchdog_get_drvdata(wdog);
int ret;
ret = superio_enter();
@@ -291,7 +295,7 @@ static int wdt_set_time(unsigned int timeout)
return ret;
superio_select(W83627HF_LD_WDT);
- superio_outb(cr_wdt_timeout, timeout);
+ superio_outb(data->reg.timeout, timeout);
superio_exit();
return 0;
@@ -299,12 +303,12 @@ static int wdt_set_time(unsigned int timeout)
static int wdt_start(struct watchdog_device *wdog)
{
- return wdt_set_time(wdog->timeout);
+ return wdt_set_time(wdog, wdog->timeout);
}
static int wdt_stop(struct watchdog_device *wdog)
{
- return wdt_set_time(0);
+ return wdt_set_time(wdog, 0);
}
static int wdt_set_timeout(struct watchdog_device *wdog, unsigned int timeout)
@@ -316,6 +320,7 @@ static int wdt_set_timeout(struct watchdog_device *wdog, unsigned int timeout)
static unsigned int wdt_get_time(struct watchdog_device *wdog)
{
+ struct w83627hf_data *data = watchdog_get_drvdata(wdog);
unsigned int timeleft;
int ret;
@@ -324,7 +329,7 @@ static unsigned int wdt_get_time(struct watchdog_device *wdog)
return 0;
superio_select(W83627HF_LD_WDT);
- timeleft = superio_inb(cr_wdt_timeout);
+ timeleft = superio_inb(data->reg.timeout);
superio_exit();
return timeleft;
@@ -352,10 +357,6 @@ static int wdt_find(int addr)
u8 val;
int ret;
- cr_wdt_timeout = W83627HF_WDT_TIMEOUT;
- cr_wdt_control = W83627HF_WDT_CONTROL;
- cr_wdt_csr = W836X7HF_WDT_CSR;
-
ret = superio_enter();
if (ret)
return ret;
@@ -370,13 +371,9 @@ static int wdt_find(int addr)
break;
case W83697HF_ID:
ret = w83697hf;
- cr_wdt_timeout = W83697HF_WDT_TIMEOUT;
- cr_wdt_control = W83697HF_WDT_CONTROL;
break;
case W83697UG_ID:
ret = w83697ug;
- cr_wdt_timeout = W83697HF_WDT_TIMEOUT;
- cr_wdt_control = W83697HF_WDT_CONTROL;
break;
case W83637HF_ID:
ret = w83637hf;
@@ -431,9 +428,6 @@ static int wdt_find(int addr)
break;
case NCT6102_ID:
ret = nct6102;
- cr_wdt_timeout = NCT6102D_WDT_TIMEOUT;
- cr_wdt_control = NCT6102D_WDT_CONTROL;
- cr_wdt_csr = NCT6102D_WDT_CSR;
break;
case NCT6116_ID:
val = superio_inb(SIO_REG_DEVID + 1);
@@ -441,10 +435,6 @@ static int wdt_find(int addr)
ret = nct6126;
else
ret = nct6116;
-
- cr_wdt_timeout = NCT6102D_WDT_TIMEOUT;
- cr_wdt_control = NCT6102D_WDT_CONTROL;
- cr_wdt_csr = NCT6102D_WDT_CSR;
break;
case 0xff:
ret = -ENODEV;
@@ -485,6 +475,21 @@ static int wdt_probe(struct platform_device *pdev)
wdd->min_timeout = 1;
wdd->max_timeout = 255;
+ data->reg.timeout = W83627HF_WDT_TIMEOUT;
+ data->reg.control = W83627HF_WDT_CONTROL;
+ data->reg.csr = W836X7HF_WDT_CSR;
+
+ if (chip == nct6102 || chip == nct6116 || chip == nct6126) {
+ data->reg.timeout = NCT6102D_WDT_TIMEOUT;
+ data->reg.control = NCT6102D_WDT_CONTROL;
+ data->reg.csr = NCT6102D_WDT_CSR;
+ }
+
+ if (chip == w83697hf || chip == w83697ug) {
+ data->reg.timeout = W83697HF_WDT_TIMEOUT;
+ data->reg.control = W83697HF_WDT_CONTROL;
+ }
+
watchdog_set_drvdata(wdd, data);
watchdog_init_timeout(wdd, timeout, NULL);
watchdog_set_nowayout(wdd, nowayout);
--
2.55.0
^ permalink raw reply related [flat|nested] 14+ messages in thread* [PATCH v2 5/8] watchdog: w83627hf_wdt: Add Super I/O configuration port as a platform device resource
2026-07-26 18:44 [PATCH v2 0/8] watchdog: w83627hf_wdt: Convert driver to the driver model and add minute support Paul Louvel
` (3 preceding siblings ...)
2026-07-26 18:44 ` [PATCH v2 4/8] watchdog: w83627hf_wdt: Move register offsets into driver data Paul Louvel
@ 2026-07-26 18:44 ` Paul Louvel
2026-07-26 18:56 ` sashiko-bot
2026-07-26 18:44 ` [PATCH v2 6/8] watchdog: w83627hf_wdt: Store Super I/O unlocking sequence in platform data Paul Louvel
` (2 subsequent siblings)
7 siblings, 1 reply; 14+ messages in thread
From: Paul Louvel @ 2026-07-26 18:44 UTC (permalink / raw)
To: Wim Van Sebroeck, Guenter Roeck
Cc: linux-watchdog, linux-kernel, Paul Louvel, Thomas Petazzoni
The Super I/O configuration port address is a global variable.
Instead, use a platform device resource.
Signed-off-by: Paul Louvel <paul.louvel@bootlin.com>
---
drivers/watchdog/w83627hf_wdt.c | 145 +++++++++++++++++++++-------------------
1 file changed, 77 insertions(+), 68 deletions(-)
diff --git a/drivers/watchdog/w83627hf_wdt.c b/drivers/watchdog/w83627hf_wdt.c
index 69d4230d8f6c..a067b8ea6b28 100644
--- a/drivers/watchdog/w83627hf_wdt.c
+++ b/drivers/watchdog/w83627hf_wdt.c
@@ -41,7 +41,6 @@
#define WATCHDOG_NAME "w83627hf/thf/hg/dhg WDT"
#define WATCHDOG_TIMEOUT 60 /* 60 sec default timeout */
-static int wdt_io;
static int wdt_cfg_enter = 0x87;/* key to unlock configuration space */
static int wdt_cfg_leave = 0xAA;/* key to lock configuration space */
@@ -76,11 +75,6 @@ MODULE_PARM_DESC(early_disable, "Disable watchdog at boot time (default=0)");
#define SIO_REG_CONF_ADDR0 0x2E
#define SIO_REG_CONF_ADDR1 0x4E
-#define WDT_EFER (wdt_io+0) /* Extended Function Enable Registers */
-#define WDT_EFIR (wdt_io+0) /* Extended Function Index Register
- (same as EFER) */
-#define WDT_EFDR (WDT_EFIR+1) /* Extended Function Data Register */
-
#define W83627HF_LD_WDT 0x08
#define W83627HF_ID 0x52
@@ -135,40 +129,41 @@ struct w83627hf_data {
int timeout;
int csr;
} reg;
+ int sioaddr;
};
-static void superio_outb(int reg, int val)
+static void superio_outb(int base, int reg, int val)
{
- outb(reg, WDT_EFER);
- outb(val, WDT_EFDR);
+ outb(reg, base);
+ outb(val, base + 1);
}
-static inline int superio_inb(int reg)
+static inline int superio_inb(int base, int reg)
{
- outb(reg, WDT_EFER);
- return inb(WDT_EFDR);
+ outb(reg, base);
+ return inb(base + 1);
}
-static int superio_enter(void)
+static int superio_enter(int base)
{
- if (!request_muxed_region(wdt_io, 2, WATCHDOG_NAME))
+ if (!request_muxed_region(base, 2, WATCHDOG_NAME))
return -EBUSY;
- outb_p(wdt_cfg_enter, WDT_EFER); /* Enter extended function mode */
- outb_p(wdt_cfg_enter, WDT_EFER); /* Again according to manual */
+ outb_p(wdt_cfg_enter, base); /* Enter extended function mode */
+ outb_p(wdt_cfg_enter, base); /* Again according to manual */
return 0;
}
-static void superio_select(int ld)
+static void superio_select(int base, int ld)
{
- superio_outb(SIO_REG_LDSEL, ld);
+ superio_outb(base, SIO_REG_LDSEL, ld);
}
-static void superio_exit(void)
+static void superio_exit(int base)
{
- outb_p(wdt_cfg_leave, WDT_EFER); /* Leave extended function mode */
- release_region(wdt_io, 2);
+ outb_p(wdt_cfg_leave, base); /* Leave extended function mode */
+ release_region(base, 2);
}
static int w83627hf_init(struct watchdog_device *wdog, enum chips chip)
@@ -177,52 +172,52 @@ static int w83627hf_init(struct watchdog_device *wdog, enum chips chip)
int ret;
unsigned char t;
- ret = superio_enter();
+ ret = superio_enter(data->sioaddr);
if (ret)
return ret;
- superio_select(W83627HF_LD_WDT);
+ superio_select(data->sioaddr, W83627HF_LD_WDT);
/* set CR30 bit 0 to activate GPIO2 */
- t = superio_inb(SIO_REG_ENABLE);
+ t = superio_inb(data->sioaddr, SIO_REG_ENABLE);
if (!(t & 0x01))
- superio_outb(SIO_REG_ENABLE, t | 0x01);
+ superio_outb(data->sioaddr, SIO_REG_ENABLE, t | 0x01);
switch (chip) {
case w83627hf:
case w83627s:
- t = superio_inb(0x2B) & ~0x10;
- superio_outb(0x2B, t); /* set GPIO24 to WDT0 */
+ t = superio_inb(data->sioaddr, 0x2B) & ~0x10;
+ superio_outb(data->sioaddr, 0x2B, t); /* set GPIO24 to WDT0 */
break;
case w83697hf:
/* Set pin 119 to WDTO# mode (= CR29, WDT0) */
- t = superio_inb(0x29) & ~0x60;
+ t = superio_inb(data->sioaddr, 0x29) & ~0x60;
t |= 0x20;
- superio_outb(0x29, t);
+ superio_outb(data->sioaddr, 0x29, t);
break;
case w83697ug:
/* Set pin 118 to WDTO# mode */
- t = superio_inb(0x2b) & ~0x04;
- superio_outb(0x2b, t);
+ t = superio_inb(data->sioaddr, 0x2b) & ~0x04;
+ superio_outb(data->sioaddr, 0x2b, t);
break;
case w83627thf:
- t = (superio_inb(0x2B) & ~0x08) | 0x04;
- superio_outb(0x2B, t); /* set GPIO3 to WDT0 */
+ t = (superio_inb(data->sioaddr, 0x2B) & ~0x08) | 0x04;
+ superio_outb(data->sioaddr, 0x2B, t); /* set GPIO3 to WDT0 */
break;
case w83627dhg:
case w83627dhg_p:
- t = superio_inb(0x2D) & ~0x01; /* PIN77 -> WDT0# */
- superio_outb(0x2D, t); /* set GPIO5 to WDT0 */
- t = superio_inb(data->reg.control);
+ t = superio_inb(data->sioaddr, 0x2D) & ~0x01; /* PIN77 -> WDT0# */
+ superio_outb(data->sioaddr, 0x2D, t); /* set GPIO5 to WDT0 */
+ t = superio_inb(data->sioaddr, data->reg.control);
t |= 0x02; /* enable the WDTO# output low pulse
* to the KBRST# pin */
- superio_outb(data->reg.control, t);
+ superio_outb(data->sioaddr, data->reg.control, t);
break;
case w83637hf:
break;
case w83687thf:
- t = superio_inb(0x2C) & ~0x80; /* PIN47 -> WDT0# */
- superio_outb(0x2C, t);
+ t = superio_inb(data->sioaddr, 0x2C) & ~0x80; /* PIN47 -> WDT0# */
+ superio_outb(data->sioaddr, 0x2C, t);
break;
case w83627ehf:
case w83627uhg:
@@ -245,42 +240,42 @@ static int w83627hf_init(struct watchdog_device *wdog, enum chips chip)
* Don't touch its configuration, and hope the BIOS
* does the right thing.
*/
- t = superio_inb(data->reg.control);
+ t = superio_inb(data->sioaddr, data->reg.control);
t |= 0x02; /* enable the WDTO# output low pulse
* to the KBRST# pin */
- superio_outb(data->reg.control, t);
+ superio_outb(data->sioaddr, data->reg.control, t);
break;
default:
break;
}
- t = superio_inb(data->reg.timeout);
+ t = superio_inb(data->sioaddr, data->reg.timeout);
if (t != 0) {
if (early_disable) {
pr_warn("Stopping previously enabled watchdog until userland kicks in\n");
- superio_outb(data->reg.timeout, 0);
+ superio_outb(data->sioaddr, data->reg.timeout, 0);
} else {
pr_info("Watchdog already running. Resetting timeout to %d sec\n",
wdog->timeout);
- superio_outb(data->reg.timeout, wdog->timeout);
+ superio_outb(data->sioaddr, data->reg.timeout, wdog->timeout);
set_bit(WDOG_HW_RUNNING, &wdog->status);
}
}
/* set second mode & disable keyboard reset turning off watchdog */
- t = superio_inb(data->reg.control) &
+ t = superio_inb(data->sioaddr, data->reg.control) &
~(WDT_CTRL_MINUTE_MODE | WDT_CTRL_RISING_EDGE_KBD_RESET);
- superio_outb(data->reg.control, t);
+ superio_outb(data->sioaddr, data->reg.control, t);
- t = superio_inb(data->reg.csr);
+ t = superio_inb(data->sioaddr, data->reg.csr);
if (t & WDT_CSR_STATUS)
wdog->bootstatus |= WDIOF_CARDRESET;
/* reset status, disable keyboard & mouse interrupt turning off watchdog */
t &= ~(WDT_CSR_STATUS | WDT_CSR_KBD_INT_RESET | WDT_CSR_MOUSE_INT_RESET);
- superio_outb(data->reg.csr, t);
+ superio_outb(data->sioaddr, data->reg.csr, t);
- superio_exit();
+ superio_exit(data->sioaddr);
return 0;
}
@@ -290,13 +285,13 @@ static int wdt_set_time(struct watchdog_device *wdog, unsigned int timeout)
struct w83627hf_data *data = watchdog_get_drvdata(wdog);
int ret;
- ret = superio_enter();
+ ret = superio_enter(data->sioaddr);
if (ret)
return ret;
- superio_select(W83627HF_LD_WDT);
- superio_outb(data->reg.timeout, timeout);
- superio_exit();
+ superio_select(data->sioaddr, W83627HF_LD_WDT);
+ superio_outb(data->sioaddr, data->reg.timeout, timeout);
+ superio_exit(data->sioaddr);
return 0;
}
@@ -324,13 +319,13 @@ static unsigned int wdt_get_time(struct watchdog_device *wdog)
unsigned int timeleft;
int ret;
- ret = superio_enter();
+ ret = superio_enter(data->sioaddr);
if (ret)
return 0;
- superio_select(W83627HF_LD_WDT);
- timeleft = superio_inb(data->reg.timeout);
- superio_exit();
+ superio_select(data->sioaddr, W83627HF_LD_WDT);
+ timeleft = superio_inb(data->sioaddr, data->reg.timeout);
+ superio_exit(data->sioaddr);
return timeleft;
}
@@ -357,11 +352,11 @@ static int wdt_find(int addr)
u8 val;
int ret;
- ret = superio_enter();
+ ret = superio_enter(addr);
if (ret)
return ret;
- superio_select(W83627HF_LD_WDT);
- val = superio_inb(SIO_REG_DEVID);
+ superio_select(addr, W83627HF_LD_WDT);
+ val = superio_inb(addr, SIO_REG_DEVID);
switch (val) {
case W83627HF_ID:
ret = w83627hf;
@@ -430,7 +425,7 @@ static int wdt_find(int addr)
ret = nct6102;
break;
case NCT6116_ID:
- val = superio_inb(SIO_REG_DEVID + 1);
+ val = superio_inb(addr, SIO_REG_DEVID + 1);
if (val == NCT6126_VER_A_LOW_ID || val == NCT6126_VER_B_LOW_ID)
ret = nct6126;
else
@@ -444,7 +439,7 @@ static int wdt_find(int addr)
pr_err("Unsupported chip ID: 0x%02x\n", val);
break;
}
- superio_exit();
+ superio_exit(addr);
return ret;
}
@@ -455,6 +450,7 @@ static int wdt_probe(struct platform_device *pdev)
struct device *dev = &pdev->dev;
struct watchdog_device *wdd;
struct w83627hf_data *data;
+ struct resource *res;
int ret;
dev_info(dev, "WDT driver initialising\n");
@@ -463,6 +459,10 @@ static int wdt_probe(struct platform_device *pdev)
if (!data)
return -ENOMEM;
+ res = platform_get_resource(pdev, IORESOURCE_IO, 0);
+ if (!res)
+ return -ENXIO;
+
data->info.options = WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING | WDIOF_MAGICCLOSE;
snprintf(data->info.identity, sizeof(data->info.identity),
"%s Watchdog", id->name);
@@ -475,6 +475,7 @@ static int wdt_probe(struct platform_device *pdev)
wdd->min_timeout = 1;
wdd->max_timeout = 255;
+ data->sioaddr = res->start;
data->reg.timeout = W83627HF_WDT_TIMEOUT;
data->reg.control = W83627HF_WDT_CONTROL;
data->reg.csr = W836X7HF_WDT_CSR;
@@ -575,17 +576,19 @@ static struct platform_device *wdt_pdev;
static int __init wdt_init(void)
{
+ struct resource res = {};
+ int sioaddr;
int ret;
int chip;
/* Apply system-specific quirks */
dmi_check_system(wdt_dmi_table);
- wdt_io = SIO_REG_CONF_ADDR0;
- chip = wdt_find(SIO_REG_CONF_ADDR0);
+ sioaddr = SIO_REG_CONF_ADDR0;
+ chip = wdt_find(sioaddr);
if (chip < 0) {
- wdt_io = SIO_REG_CONF_ADDR1;
- chip = wdt_find(SIO_REG_CONF_ADDR1);
+ sioaddr = SIO_REG_CONF_ADDR1;
+ chip = wdt_find(sioaddr);
if (chip < 0)
return chip;
}
@@ -594,8 +597,14 @@ static int __init wdt_init(void)
if (ret)
return ret;
- wdt_pdev = platform_device_register_data(NULL, wdt_ids[chip].name,
- PLATFORM_DEVID_NONE, NULL, 0);
+ res.name = "Super I/O port";
+ res.flags = IORESOURCE_IO;
+ res.start = sioaddr;
+ res.end = sioaddr + 1;
+
+ wdt_pdev = platform_device_register_resndata(NULL, wdt_ids[chip].name,
+ PLATFORM_DEVID_NONE, &res,
+ 1, NULL, 0);
if (IS_ERR(wdt_pdev)) {
platform_driver_unregister(&wdt_driver);
return PTR_ERR(wdt_pdev);
--
2.55.0
^ permalink raw reply related [flat|nested] 14+ messages in thread* Re: [PATCH v2 5/8] watchdog: w83627hf_wdt: Add Super I/O configuration port as a platform device resource
2026-07-26 18:44 ` [PATCH v2 5/8] watchdog: w83627hf_wdt: Add Super I/O configuration port as a platform device resource Paul Louvel
@ 2026-07-26 18:56 ` sashiko-bot
2026-07-27 1:08 ` Guenter Roeck
0 siblings, 1 reply; 14+ messages in thread
From: sashiko-bot @ 2026-07-26 18:56 UTC (permalink / raw)
To: Paul Louvel; +Cc: linux-watchdog
Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
- [Critical] Claiming the shared Super I/O configuration port as a static platform device resource causes a Use-After-Free and breaks global mutual exclusion upon module unload.
--
--- Patch [5]: [PATCH v2 5/8] watchdog: w83627hf_wdt: Add Super I/O configuration port as a platform device resource ---
commit 7147d011852b779a7e376729ecb29c06b86e9282
Author: Paul Louvel <paul.louvel@bootlin.com>
watchdog: w83627hf_wdt: Add Super I/O configuration port as a platform device resource
This commit changes the Super I/O configuration port address from a
global variable to a platform device resource. It uses
platform_device_register_resndata() to assign the IO resource during
module initialization.
> diff --git a/drivers/watchdog/w83627hf_wdt.c b/drivers/watchdog/w83627hf_wdt.c
> index 69d4230d8f6cc..a067b8ea6b289 100644
> --- a/drivers/watchdog/w83627hf_wdt.c
> +++ b/drivers/watchdog/w83627hf_wdt.c
[ ... ]
> @@ -594,8 +597,14 @@ static int __init wdt_init(void)
> if (ret)
> return ret;
>
> - wdt_pdev = platform_device_register_data(NULL, wdt_ids[chip].name,
> - PLATFORM_DEVID_NONE, NULL, 0);
> + res.name = "Super I/O port";
> + res.flags = IORESOURCE_IO;
> + res.start = sioaddr;
> + res.end = sioaddr + 1;
> +
> + wdt_pdev = platform_device_register_resndata(NULL, wdt_ids[chip].name,
> + PLATFORM_DEVID_NONE, &res,
> + 1, NULL, 0);
[Severity: Critical]
Does claiming the shared Super I/O configuration port as a static platform
device resource cause a use-after-free regression and break mutual exclusion
on module unload?
If this port is registered as a non-busy I/O resource, it becomes a
container node in the global ioport_resource tree. When another driver
calls request_muxed_region() for the same port, it might sleep on
muxed_resource_wait while holding a parent pointer to this container node.
If the wdt module is unloaded while that thread is sleeping,
platform_device_del() unlinks the container node and
platform_device_release() frees its memory. When the sleeping thread
wakes up, will it dereference the freed parent pointer in
__request_resource()?
Additionally, it appears that unlinking the container node orphans any
active child reservations from the tree. Could this allow concurrent
hardware access to the shared Super I/O port?
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260726-w83627hf_wdt-improvements-v2-0-3645a2a6c022@bootlin.com?part=5
^ permalink raw reply [flat|nested] 14+ messages in thread
* Re: [PATCH v2 5/8] watchdog: w83627hf_wdt: Add Super I/O configuration port as a platform device resource
2026-07-26 18:56 ` sashiko-bot
@ 2026-07-27 1:08 ` Guenter Roeck
0 siblings, 0 replies; 14+ messages in thread
From: Guenter Roeck @ 2026-07-27 1:08 UTC (permalink / raw)
To: sashiko-reviews, Paul Louvel; +Cc: linux-watchdog
On 7/26/26 11:56, sashiko-bot@kernel.org wrote:
> Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
> - [Critical] Claiming the shared Super I/O configuration port as a static platform device resource causes a Use-After-Free and breaks global mutual exclusion upon module unload.
> --
>
> --- Patch [5]: [PATCH v2 5/8] watchdog: w83627hf_wdt: Add Super I/O configuration port as a platform device resource ---
> commit 7147d011852b779a7e376729ecb29c06b86e9282
> Author: Paul Louvel <paul.louvel@bootlin.com>
>
> watchdog: w83627hf_wdt: Add Super I/O configuration port as a platform device resource
>
> This commit changes the Super I/O configuration port address from a
> global variable to a platform device resource. It uses
> platform_device_register_resndata() to assign the IO resource during
> module initialization.
>
>> diff --git a/drivers/watchdog/w83627hf_wdt.c b/drivers/watchdog/w83627hf_wdt.c
>> index 69d4230d8f6cc..a067b8ea6b289 100644
>> --- a/drivers/watchdog/w83627hf_wdt.c
>> +++ b/drivers/watchdog/w83627hf_wdt.c
> [ ... ]
>> @@ -594,8 +597,14 @@ static int __init wdt_init(void)
>> if (ret)
>> return ret;
>>
>> - wdt_pdev = platform_device_register_data(NULL, wdt_ids[chip].name,
>> - PLATFORM_DEVID_NONE, NULL, 0);
>> + res.name = "Super I/O port";
>> + res.flags = IORESOURCE_IO;
>> + res.start = sioaddr;
>> + res.end = sioaddr + 1;
>> +
>> + wdt_pdev = platform_device_register_resndata(NULL, wdt_ids[chip].name,
>> + PLATFORM_DEVID_NONE, &res,
>> + 1, NULL, 0);
>
> [Severity: Critical]
> Does claiming the shared Super I/O configuration port as a static platform
> device resource cause a use-after-free regression and break mutual exclusion
> on module unload?
>
> If this port is registered as a non-busy I/O resource, it becomes a
> container node in the global ioport_resource tree. When another driver
> calls request_muxed_region() for the same port, it might sleep on
> muxed_resource_wait while holding a parent pointer to this container node.
>
> If the wdt module is unloaded while that thread is sleeping,
> platform_device_del() unlinks the container node and
> platform_device_release() frees its memory. When the sleeping thread
> wakes up, will it dereference the freed parent pointer in
> __request_resource()?
>
> Additionally, it appears that unlinking the container node orphans any
> active child reservations from the tree. Could this allow concurrent
> hardware access to the shared Super I/O port?
>
That makes me wonder: Under which authority can the watchdog driver claim
ownership of this resource ? What is the rationale ?
Why should the owner not be, for example, the hardware monitoring driver ?
Or the Bluetooth driver, if the chip has one ? Or some other driver ?
Or some non-existent Super-IO core ?
On a higher level, why even risk the problem outlined by Sashiko above ?
What is the benefit of doing this ? The patch description fails to
explain _why_ this change is made. Actually, that is true for many
of the patches. Quite often the description explain what is done,
but not the reason for it. The reason may be obvious for you, but not
or not always for me - and very much so for this patch.
Please explain why you are making those changes.
Also, I notice that at least in some cases it looks like you did not
address the feedback from Sashiko. Partially that is because problems
in one patch are addressed in a later patch of the series, but I am quite
sure I have seen Sashiko's feedback about the missing parent device
initialization before. Please ensure to address its feedback.
Thanks,
Guenter
^ permalink raw reply [flat|nested] 14+ messages in thread
* [PATCH v2 6/8] watchdog: w83627hf_wdt: Store Super I/O unlocking sequence in platform data
2026-07-26 18:44 [PATCH v2 0/8] watchdog: w83627hf_wdt: Convert driver to the driver model and add minute support Paul Louvel
` (4 preceding siblings ...)
2026-07-26 18:44 ` [PATCH v2 5/8] watchdog: w83627hf_wdt: Add Super I/O configuration port as a platform device resource Paul Louvel
@ 2026-07-26 18:44 ` Paul Louvel
2026-07-26 18:55 ` sashiko-bot
2026-07-26 18:44 ` [PATCH v2 7/8] watchdog: w83627hf_wdt: Add minute mode counting Paul Louvel
2026-07-26 18:44 ` [PATCH v2 8/8] watchdog: w83627hf_wdt: Report all initialization failures in probe Paul Louvel
7 siblings, 1 reply; 14+ messages in thread
From: Paul Louvel @ 2026-07-26 18:44 UTC (permalink / raw)
To: Wim Van Sebroeck, Guenter Roeck
Cc: linux-watchdog, linux-kernel, Paul Louvel, Thomas Petazzoni
Instead of using two global variable, store the unlocking sequence in the
platform data.
Signed-off-by: Paul Louvel <paul.louvel@bootlin.com>
---
drivers/watchdog/w83627hf_wdt.c | 59 ++++++++++++++++++++++++++---------------
1 file changed, 37 insertions(+), 22 deletions(-)
diff --git a/drivers/watchdog/w83627hf_wdt.c b/drivers/watchdog/w83627hf_wdt.c
index a067b8ea6b28..064a99db80ef 100644
--- a/drivers/watchdog/w83627hf_wdt.c
+++ b/drivers/watchdog/w83627hf_wdt.c
@@ -41,9 +41,6 @@
#define WATCHDOG_NAME "w83627hf/thf/hg/dhg WDT"
#define WATCHDOG_TIMEOUT 60 /* 60 sec default timeout */
-static int wdt_cfg_enter = 0x87;/* key to unlock configuration space */
-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,
@@ -121,6 +118,11 @@ MODULE_PARM_DESC(early_disable, "Disable watchdog at boot time (default=0)");
#define WDT_CTRL_RISING_EDGE_KBD_RESET BIT(2)
#define WDT_CTRL_MINUTE_MODE BIT(3)
+struct wdt_pdata {
+ int siocfg_enter;
+ int siocfg_leave;
+};
+
struct w83627hf_data {
struct watchdog_device wdd;
struct watchdog_info info;
@@ -130,6 +132,8 @@ struct w83627hf_data {
int csr;
} reg;
int sioaddr;
+ int siocfg_enter;
+ int siocfg_leave;
};
static void superio_outb(int base, int reg, int val)
@@ -144,13 +148,13 @@ static inline int superio_inb(int base, int reg)
return inb(base + 1);
}
-static int superio_enter(int base)
+static int superio_enter(int base, int enter)
{
if (!request_muxed_region(base, 2, WATCHDOG_NAME))
return -EBUSY;
- outb_p(wdt_cfg_enter, base); /* Enter extended function mode */
- outb_p(wdt_cfg_enter, base); /* Again according to manual */
+ outb_p(enter, base); /* Enter extended function mode */
+ outb_p(enter, base); /* Again according to manual */
return 0;
}
@@ -160,9 +164,9 @@ static void superio_select(int base, int ld)
superio_outb(base, SIO_REG_LDSEL, ld);
}
-static void superio_exit(int base)
+static void superio_exit(int base, int leave)
{
- outb_p(wdt_cfg_leave, base); /* Leave extended function mode */
+ outb_p(leave, base); /* Leave extended function mode */
release_region(base, 2);
}
@@ -172,7 +176,7 @@ static int w83627hf_init(struct watchdog_device *wdog, enum chips chip)
int ret;
unsigned char t;
- ret = superio_enter(data->sioaddr);
+ ret = superio_enter(data->sioaddr, data->siocfg_enter);
if (ret)
return ret;
@@ -275,7 +279,7 @@ static int w83627hf_init(struct watchdog_device *wdog, enum chips chip)
t &= ~(WDT_CSR_STATUS | WDT_CSR_KBD_INT_RESET | WDT_CSR_MOUSE_INT_RESET);
superio_outb(data->sioaddr, data->reg.csr, t);
- superio_exit(data->sioaddr);
+ superio_exit(data->sioaddr, data->siocfg_leave);
return 0;
}
@@ -285,13 +289,13 @@ static int wdt_set_time(struct watchdog_device *wdog, unsigned int timeout)
struct w83627hf_data *data = watchdog_get_drvdata(wdog);
int ret;
- ret = superio_enter(data->sioaddr);
+ ret = superio_enter(data->sioaddr, data->siocfg_enter);
if (ret)
return ret;
superio_select(data->sioaddr, W83627HF_LD_WDT);
superio_outb(data->sioaddr, data->reg.timeout, timeout);
- superio_exit(data->sioaddr);
+ superio_exit(data->sioaddr, data->siocfg_leave);
return 0;
}
@@ -319,13 +323,13 @@ static unsigned int wdt_get_time(struct watchdog_device *wdog)
unsigned int timeleft;
int ret;
- ret = superio_enter(data->sioaddr);
+ ret = superio_enter(data->sioaddr, data->siocfg_enter);
if (ret)
return 0;
superio_select(data->sioaddr, W83627HF_LD_WDT);
timeleft = superio_inb(data->sioaddr, data->reg.timeout);
- superio_exit(data->sioaddr);
+ superio_exit(data->sioaddr, data->siocfg_leave);
return timeleft;
}
@@ -347,12 +351,12 @@ static const struct watchdog_ops wdt_ops = {
* turn the timebomb registers off.
*/
-static int wdt_find(int addr)
+static int wdt_find(int addr, int enter, int leave)
{
u8 val;
int ret;
- ret = superio_enter(addr);
+ ret = superio_enter(addr, enter);
if (ret)
return ret;
superio_select(addr, W83627HF_LD_WDT);
@@ -439,13 +443,14 @@ static int wdt_find(int addr)
pr_err("Unsupported chip ID: 0x%02x\n", val);
break;
}
- superio_exit(addr);
+ superio_exit(addr, leave);
return ret;
}
static int wdt_probe(struct platform_device *pdev)
{
const struct platform_device_id *id = platform_get_device_id(pdev);
+ const struct wdt_pdata *pdata = pdev->dev.platform_data;
enum chips chip = id->driver_data;
struct device *dev = &pdev->dev;
struct watchdog_device *wdd;
@@ -476,6 +481,8 @@ static int wdt_probe(struct platform_device *pdev)
wdd->max_timeout = 255;
data->sioaddr = res->start;
+ data->siocfg_enter = pdata->siocfg_enter;
+ data->siocfg_leave = pdata->siocfg_leave;
data->reg.timeout = W83627HF_WDT_TIMEOUT;
data->reg.control = W83627HF_WDT_CONTROL;
data->reg.csr = W836X7HF_WDT_CSR;
@@ -517,12 +524,16 @@ static int wdt_probe(struct platform_device *pdev)
*/
static int __init wdt_use_alt_key(const struct dmi_system_id *d)
{
- wdt_cfg_enter = 0x88;
- wdt_cfg_leave = 0xBB;
+ struct wdt_pdata *pdata = d->driver_data;
+
+ pdata->siocfg_enter = 0x88;
+ pdata->siocfg_leave = 0xBB;
return 0;
}
+static struct wdt_pdata pdata;
+
static const struct dmi_system_id wdt_dmi_table[] __initconst = {
{
.matches = {
@@ -532,6 +543,7 @@ static const struct dmi_system_id wdt_dmi_table[] __initconst = {
DMI_EXACT_MATCH(DMI_BOARD_NAME, "SHARKBAY"),
},
.callback = wdt_use_alt_key,
+ .driver_data = &pdata,
},
{}
};
@@ -581,14 +593,17 @@ static int __init wdt_init(void)
int ret;
int chip;
+ pdata.siocfg_enter = 0x87;
+ pdata.siocfg_leave = 0xAA;
+
/* Apply system-specific quirks */
dmi_check_system(wdt_dmi_table);
sioaddr = SIO_REG_CONF_ADDR0;
- chip = wdt_find(sioaddr);
+ chip = wdt_find(sioaddr, pdata.siocfg_enter, pdata.siocfg_leave);
if (chip < 0) {
sioaddr = SIO_REG_CONF_ADDR1;
- chip = wdt_find(sioaddr);
+ chip = wdt_find(sioaddr, pdata.siocfg_enter, pdata.siocfg_leave);
if (chip < 0)
return chip;
}
@@ -604,7 +619,7 @@ static int __init wdt_init(void)
wdt_pdev = platform_device_register_resndata(NULL, wdt_ids[chip].name,
PLATFORM_DEVID_NONE, &res,
- 1, NULL, 0);
+ 1, &pdata, sizeof(pdata));
if (IS_ERR(wdt_pdev)) {
platform_driver_unregister(&wdt_driver);
return PTR_ERR(wdt_pdev);
--
2.55.0
^ permalink raw reply related [flat|nested] 14+ messages in thread* Re: [PATCH v2 6/8] watchdog: w83627hf_wdt: Store Super I/O unlocking sequence in platform data
2026-07-26 18:44 ` [PATCH v2 6/8] watchdog: w83627hf_wdt: Store Super I/O unlocking sequence in platform data Paul Louvel
@ 2026-07-26 18:55 ` sashiko-bot
0 siblings, 0 replies; 14+ messages in thread
From: sashiko-bot @ 2026-07-26 18:55 UTC (permalink / raw)
To: Paul Louvel; +Cc: linux-watchdog
Thank you for your contribution! Sashiko AI review found 2 potential issue(s) to consider:
- [High] Missing NULL check for `platform_data` in `wdt_probe` leads to a kernel panic.
- [High] Missing NULL check for `platform_get_device_id()` in `wdt_probe` leads to a NULL pointer dereference.
--
--- Patch [6]: [PATCH v2 6/8] watchdog: w83627hf_wdt: Store Super I/O unlocking sequence in platform data ---
commit 60b05c387ecdfb14df4b8e624ec2e3f22d7a1aa6
Author: Paul Louvel <paul.louvel@bootlin.com>
watchdog: w83627hf_wdt: Store Super I/O unlocking sequence in platform data
This commit replaces the use of two global variables for the Super I/O
unlocking sequence. It instead stores the unlocking sequence in the
platform data.
> diff --git a/drivers/watchdog/w83627hf_wdt.c b/drivers/watchdog/w83627hf_wdt.c
> index a067b8ea6b289..064a99db80efe 100644
> --- a/drivers/watchdog/w83627hf_wdt.c
> +++ b/drivers/watchdog/w83627hf_wdt.c
[ ... ]
> @@ -439,13 +443,14 @@ static int wdt_find(int addr)
> pr_err("Unsupported chip ID: 0x%02x\n", val);
> break;
> }
> - superio_exit(addr);
> + superio_exit(addr, leave);
> return ret;
> }
>
> static int wdt_probe(struct platform_device *pdev)
> {
> const struct platform_device_id *id = platform_get_device_id(pdev);
> + const struct wdt_pdata *pdata = pdev->dev.platform_data;
> enum chips chip = id->driver_data;
[Severity: High]
Can platform_get_device_id() return NULL here if the driver is bound to a
device via sysfs driver_override or fallback name matching? If so, does
accessing id->driver_data cause a NULL pointer dereference?
> struct device *dev = &pdev->dev;
> struct watchdog_device *wdd;
[ ... ]
> @@ -476,6 +481,8 @@ static int wdt_probe(struct platform_device *pdev)
> wdd->max_timeout = 255;
>
> data->sioaddr = res->start;
> + data->siocfg_enter = pdata->siocfg_enter;
> + data->siocfg_leave = pdata->siocfg_leave;
[Severity: High]
What happens if the platform device is instantiated without platform data
and bound to this driver? Would pdev->dev.platform_data be NULL, leading
to a kernel panic when dereferencing pdata here?
> data->reg.timeout = W83627HF_WDT_TIMEOUT;
> data->reg.control = W83627HF_WDT_CONTROL;
> data->reg.csr = W836X7HF_WDT_CSR;
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260726-w83627hf_wdt-improvements-v2-0-3645a2a6c022@bootlin.com?part=6
^ permalink raw reply [flat|nested] 14+ messages in thread
* [PATCH v2 7/8] watchdog: w83627hf_wdt: Add minute mode counting
2026-07-26 18:44 [PATCH v2 0/8] watchdog: w83627hf_wdt: Convert driver to the driver model and add minute support Paul Louvel
` (5 preceding siblings ...)
2026-07-26 18:44 ` [PATCH v2 6/8] watchdog: w83627hf_wdt: Store Super I/O unlocking sequence in platform data Paul Louvel
@ 2026-07-26 18:44 ` Paul Louvel
2026-07-26 18:44 ` [PATCH v2 8/8] watchdog: w83627hf_wdt: Report all initialization failures in probe Paul Louvel
7 siblings, 0 replies; 14+ messages in thread
From: Paul Louvel @ 2026-07-26 18:44 UTC (permalink / raw)
To: Wim Van Sebroeck, Guenter Roeck
Cc: linux-watchdog, linux-kernel, Paul Louvel, Thomas Petazzoni
All of the chips supported by the driver can be set to minute mode,
allowing timeout value up to 255 minutes.
Add it to the driver.
Signed-off-by: Paul Louvel <paul.louvel@bootlin.com>
---
drivers/watchdog/w83627hf_wdt.c | 70 +++++++++++++++++++++++++++++++----------
1 file changed, 53 insertions(+), 17 deletions(-)
diff --git a/drivers/watchdog/w83627hf_wdt.c b/drivers/watchdog/w83627hf_wdt.c
index 064a99db80ef..2957364485eb 100644
--- a/drivers/watchdog/w83627hf_wdt.c
+++ b/drivers/watchdog/w83627hf_wdt.c
@@ -40,6 +40,7 @@
#define WATCHDOG_NAME "w83627hf/thf/hg/dhg WDT"
#define WATCHDOG_TIMEOUT 60 /* 60 sec default timeout */
+#define WATCHDOG_MAX_TIMEOUT (255 * 60)
enum chips { w83627hf, w83627s, w83697hf, w83697ug, w83637hf, w83627thf,
w83687thf, w83627ehf, w83627dhg, w83627uhg, w83667hg, w83627dhg_p,
@@ -49,7 +50,8 @@ enum chips { w83627hf, w83627s, w83697hf, w83697ug, w83637hf, w83627thf,
static int timeout; /* in seconds */
module_param(timeout, int, 0);
MODULE_PARM_DESC(timeout,
- "Watchdog timeout in seconds. 1 <= timeout <= 255, default="
+ "Watchdog timeout in seconds. 1 <= timeout <= "
+ __MODULE_STRING(WATCHDOG_MAX_TIMEOUT) ", default="
__MODULE_STRING(WATCHDOG_TIMEOUT) ".");
static bool nowayout = WATCHDOG_NOWAYOUT;
@@ -134,6 +136,9 @@ struct w83627hf_data {
int sioaddr;
int siocfg_enter;
int siocfg_leave;
+ bool minute_mode;
+ u8 early_timer_val;
+ u8 timer_val;
};
static void superio_outb(int base, int reg, int val)
@@ -253,22 +258,11 @@ static int w83627hf_init(struct watchdog_device *wdog, enum chips chip)
break;
}
- t = superio_inb(data->sioaddr, data->reg.timeout);
- if (t != 0) {
- if (early_disable) {
- pr_warn("Stopping previously enabled watchdog until userland kicks in\n");
- superio_outb(data->sioaddr, data->reg.timeout, 0);
- } else {
- pr_info("Watchdog already running. Resetting timeout to %d sec\n",
- wdog->timeout);
- superio_outb(data->sioaddr, data->reg.timeout, wdog->timeout);
- set_bit(WDOG_HW_RUNNING, &wdog->status);
- }
- }
+ data->early_timer_val = superio_inb(data->sioaddr, data->reg.timeout);
- /* set second mode & disable keyboard reset turning off watchdog */
+ /* disable keyboard reset turning off watchdog */
t = superio_inb(data->sioaddr, data->reg.control) &
- ~(WDT_CTRL_MINUTE_MODE | WDT_CTRL_RISING_EDGE_KBD_RESET);
+ ~WDT_CTRL_RISING_EDGE_KBD_RESET;
superio_outb(data->sioaddr, data->reg.control, t);
t = superio_inb(data->sioaddr, data->reg.csr);
@@ -287,6 +281,7 @@ static int w83627hf_init(struct watchdog_device *wdog, enum chips chip)
static int wdt_set_time(struct watchdog_device *wdog, unsigned int timeout)
{
struct w83627hf_data *data = watchdog_get_drvdata(wdog);
+ unsigned char ctrl;
int ret;
ret = superio_enter(data->sioaddr, data->siocfg_enter);
@@ -294,6 +289,15 @@ static int wdt_set_time(struct watchdog_device *wdog, unsigned int timeout)
return ret;
superio_select(data->sioaddr, W83627HF_LD_WDT);
+
+ ctrl = superio_inb(data->sioaddr, data->reg.control);
+
+ if (data->minute_mode)
+ ctrl |= WDT_CTRL_MINUTE_MODE;
+ else
+ ctrl &= ~WDT_CTRL_MINUTE_MODE;
+
+ superio_outb(data->sioaddr, data->reg.control, ctrl);
superio_outb(data->sioaddr, data->reg.timeout, timeout);
superio_exit(data->sioaddr, data->siocfg_leave);
@@ -302,7 +306,9 @@ static int wdt_set_time(struct watchdog_device *wdog, unsigned int timeout)
static int wdt_start(struct watchdog_device *wdog)
{
- return wdt_set_time(wdog, wdog->timeout);
+ struct w83627hf_data *data = watchdog_get_drvdata(wdog);
+
+ return wdt_set_time(wdog, data->timer_val);
}
static int wdt_stop(struct watchdog_device *wdog)
@@ -312,6 +318,17 @@ static int wdt_stop(struct watchdog_device *wdog)
static int wdt_set_timeout(struct watchdog_device *wdog, unsigned int timeout)
{
+ struct w83627hf_data *data = watchdog_get_drvdata(wdog);
+
+ if (timeout > 255) {
+ data->minute_mode = true;
+ data->timer_val = DIV_ROUND_UP(timeout, 60);
+ timeout = data->timer_val * 60;
+ } else {
+ data->minute_mode = false;
+ data->timer_val = timeout;
+ }
+
wdog->timeout = timeout;
return 0;
@@ -329,6 +346,8 @@ static unsigned int wdt_get_time(struct watchdog_device *wdog)
superio_select(data->sioaddr, W83627HF_LD_WDT);
timeleft = superio_inb(data->sioaddr, data->reg.timeout);
+ if (data->minute_mode)
+ timeleft *= 60;
superio_exit(data->sioaddr, data->siocfg_leave);
return timeleft;
@@ -478,7 +497,7 @@ static int wdt_probe(struct platform_device *pdev)
wdd->ops = &wdt_ops;
wdd->timeout = WATCHDOG_TIMEOUT;
wdd->min_timeout = 1;
- wdd->max_timeout = 255;
+ wdd->max_timeout = WATCHDOG_MAX_TIMEOUT;
data->sioaddr = res->start;
data->siocfg_enter = pdata->siocfg_enter;
@@ -503,10 +522,27 @@ static int wdt_probe(struct platform_device *pdev)
watchdog_set_nowayout(wdd, nowayout);
watchdog_stop_on_reboot(wdd);
+ wdt_set_timeout(wdd, wdd->timeout);
+
ret = w83627hf_init(wdd, chip);
if (ret)
return dev_err_probe(dev, ret, "failed to initialize watchdog\n");
+ if (data->early_timer_val) {
+ if (early_disable) {
+ dev_warn(dev, "Stopping previously enabled watchdog until userland kicks in\n");
+ ret = wdt_stop(wdd);
+ } else {
+ dev_info(dev, "Watchdog already running. Resetting timeout to %d sec\n",
+ wdd->timeout);
+ ret = wdt_start(wdd);
+ set_bit(WDOG_HW_RUNNING, &wdd->status);
+ }
+
+ if (ret)
+ return ret;
+ }
+
ret = devm_watchdog_register_device(&pdev->dev, wdd);
if (ret)
return ret;
--
2.55.0
^ permalink raw reply related [flat|nested] 14+ messages in thread* [PATCH v2 8/8] watchdog: w83627hf_wdt: Report all initialization failures in probe
2026-07-26 18:44 [PATCH v2 0/8] watchdog: w83627hf_wdt: Convert driver to the driver model and add minute support Paul Louvel
` (6 preceding siblings ...)
2026-07-26 18:44 ` [PATCH v2 7/8] watchdog: w83627hf_wdt: Add minute mode counting Paul Louvel
@ 2026-07-26 18:44 ` Paul Louvel
2026-07-26 18:56 ` sashiko-bot
7 siblings, 1 reply; 14+ messages in thread
From: Paul Louvel @ 2026-07-26 18:44 UTC (permalink / raw)
To: Wim Van Sebroeck, Guenter Roeck
Cc: linux-watchdog, linux-kernel, Paul Louvel, Thomas Petazzoni
The driver currently logs an error only if the chip initialization
fails. Extend the error reporting to all failure paths in probe to
improve diagnostics.
Signed-off-by: Paul Louvel <paul.louvel@bootlin.com>
---
drivers/watchdog/w83627hf_wdt.c | 7 ++++---
1 file changed, 4 insertions(+), 3 deletions(-)
diff --git a/drivers/watchdog/w83627hf_wdt.c b/drivers/watchdog/w83627hf_wdt.c
index 2957364485eb..7b50865ae824 100644
--- a/drivers/watchdog/w83627hf_wdt.c
+++ b/drivers/watchdog/w83627hf_wdt.c
@@ -485,7 +485,8 @@ static int wdt_probe(struct platform_device *pdev)
res = platform_get_resource(pdev, IORESOURCE_IO, 0);
if (!res)
- return -ENXIO;
+ return dev_err_probe(dev, -ENXIO,
+ "failed to get I/O resource\n");
data->info.options = WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING | WDIOF_MAGICCLOSE;
snprintf(data->info.identity, sizeof(data->info.identity),
@@ -540,12 +541,12 @@ static int wdt_probe(struct platform_device *pdev)
}
if (ret)
- return ret;
+ return dev_err_probe(dev, ret, "failed to set watchdog timeout\n");
}
ret = devm_watchdog_register_device(&pdev->dev, wdd);
if (ret)
- return ret;
+ return dev_err_probe(dev, ret, "failed to register watchdog\n");
dev_info(dev, "initialized. timeout=%d sec (nowayout=%d)\n",
wdd->timeout, nowayout);
--
2.55.0
^ permalink raw reply related [flat|nested] 14+ messages in thread* Re: [PATCH v2 8/8] watchdog: w83627hf_wdt: Report all initialization failures in probe
2026-07-26 18:44 ` [PATCH v2 8/8] watchdog: w83627hf_wdt: Report all initialization failures in probe Paul Louvel
@ 2026-07-26 18:56 ` sashiko-bot
0 siblings, 0 replies; 14+ messages in thread
From: sashiko-bot @ 2026-07-26 18:56 UTC (permalink / raw)
To: Paul Louvel; +Cc: linux-watchdog
Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
- [High] Unchecked dereferences of `id` and `pdata` in probe can lead to a kernel Oops.
--
--- Patch [8]: [PATCH v2 8/8] watchdog: w83627hf_wdt: Report all initialization failures in probe ---
commit 0129cf43f51e4968c683b359d971eaa0c48a6846
Author: Paul Louvel <paul.louvel@bootlin.com>
watchdog: w83627hf_wdt: Report all initialization failures in probe
The driver currently logs an error only if the chip initialization
fails. Extend the error reporting to all failure paths in probe to
improve diagnostics.
> diff --git a/drivers/watchdog/w83627hf_wdt.c b/drivers/watchdog/w83627hf_wdt.c
> index 2957364485ebf..7b50865ae8248 100644
> --- a/drivers/watchdog/w83627hf_wdt.c
> +++ b/drivers/watchdog/w83627hf_wdt.c
> @@ -485,7 +485,8 @@ static int wdt_probe(struct platform_device *pdev)
>
> res = platform_get_resource(pdev, IORESOURCE_IO, 0);
> if (!res)
> - return -ENXIO;
> + return dev_err_probe(dev, -ENXIO,
> + "failed to get I/O resource\n");
[Severity: High]
Could the dereferences of id and pdata earlier in this function lead to a
kernel Oops?
If a user binds a device to this driver via sysfs (for example, using
driver_override or bind), platform_get_device_id(pdev) will return NULL
because the device ID does not match any entry in the driver's id_table.
This leads to a NULL pointer dereference when initializing chip:
wdt_probe()
const struct platform_device_id *id = platform_get_device_id(pdev);
...
enum chips chip = id->driver_data;
Additionally, binding a platform device this way leaves
pdev->dev.platform_data as NULL, causing a subsequent NULL pointer
dereference when accessing pdata:
wdt_probe()
const struct wdt_pdata *pdata = pdev->dev.platform_data;
...
data->siocfg_enter = pdata->siocfg_enter;
Since this can be triggered from userspace by a user with privileges to
write to sysfs, could this be used to panic the kernel?
[ ... ]
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260726-w83627hf_wdt-improvements-v2-0-3645a2a6c022@bootlin.com?part=8
^ permalink raw reply [flat|nested] 14+ messages in thread