mirror of
https://github.com/torvalds/linux.git
synced 2026-09-11 20:13:02 +02:00
watchdog: sunxi_wdt: preserve boot-enabled watchdog
sunxi_wdt_probe() unconditionally stops the watchdog even when firmware
left it running. This opens an unprotected interval during boot and
prevents CONFIG_WATCHDOG_HANDLE_BOOT_ENABLED from taking over the active
watchdog.
Detect an enabled watchdog and decode its programmed interval. Preserve
representable timeouts, and round the 0.5-second interval up to the
minimum representable one-second timeout. Use the configured timeout for
reserved interval encodings. Set the Linux reset mode and ping the
watchdog without clearing its enable bit, then mark it hardware-running
before registration so the watchdog core services it until userspace
takes control. Leave disabled watchdogs untouched.
Fixes: d00680ed00 ("watchdog: sunxi: New watchdog driver for Allwinner A10/A13")
Cc: stable@vger.kernel.org
Signed-off-by: James Hilliard <james.hilliard1@gmail.com>
Link: https://patch.msgid.link/20260827-submit-sunxi-wdt-boot-enabled-v1-v2-1-610d37dccc97@gmail.com
Signed-off-by: Guenter Roeck <linux@roeck-us.net>
This commit is contained in:
parent
cee9395acd
commit
aab55360fa
|
|
@ -128,6 +128,38 @@ static int sunxi_wdt_ping(struct watchdog_device *wdt_dev)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static bool sunxi_wdt_is_running(struct watchdog_device *wdt_dev)
|
||||
{
|
||||
struct sunxi_wdt_dev *sunxi_wdt = watchdog_get_drvdata(wdt_dev);
|
||||
const struct sunxi_wdt_reg *regs = sunxi_wdt->wdt_regs;
|
||||
|
||||
return readl(sunxi_wdt->wdt_base + regs->wdt_mode) & WDT_MODE_EN;
|
||||
}
|
||||
|
||||
static unsigned int sunxi_wdt_get_timeout(struct watchdog_device *wdt_dev)
|
||||
{
|
||||
struct sunxi_wdt_dev *sunxi_wdt = watchdog_get_drvdata(wdt_dev);
|
||||
const struct sunxi_wdt_reg *regs = sunxi_wdt->wdt_regs;
|
||||
unsigned int timeout;
|
||||
u32 interval;
|
||||
|
||||
interval = readl(sunxi_wdt->wdt_base + regs->wdt_mode);
|
||||
interval >>= regs->wdt_timeout_shift;
|
||||
interval &= WDT_TIMEOUT_MASK;
|
||||
/* Round the 0.5-second interval up to the minimum representable timeout. */
|
||||
if (!interval)
|
||||
return WDT_MIN_TIMEOUT;
|
||||
|
||||
for (timeout = WDT_MIN_TIMEOUT;
|
||||
timeout < ARRAY_SIZE(wdt_timeout_map); timeout++) {
|
||||
if (wdt_timeout_map[timeout] == interval)
|
||||
return timeout;
|
||||
}
|
||||
|
||||
/* Reserved interval encoding. */
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int sunxi_wdt_set_timeout(struct watchdog_device *wdt_dev,
|
||||
unsigned int timeout)
|
||||
{
|
||||
|
|
@ -259,6 +291,7 @@ static int sunxi_wdt_probe(struct platform_device *pdev)
|
|||
{
|
||||
struct device *dev = &pdev->dev;
|
||||
struct sunxi_wdt_dev *sunxi_wdt;
|
||||
unsigned int running_timeout;
|
||||
int err;
|
||||
|
||||
sunxi_wdt = devm_kzalloc(dev, sizeof(*sunxi_wdt), GFP_KERNEL);
|
||||
|
|
@ -286,7 +319,17 @@ static int sunxi_wdt_probe(struct platform_device *pdev)
|
|||
|
||||
watchdog_set_drvdata(&sunxi_wdt->wdt_dev, sunxi_wdt);
|
||||
|
||||
sunxi_wdt_stop(&sunxi_wdt->wdt_dev);
|
||||
if (sunxi_wdt_is_running(&sunxi_wdt->wdt_dev)) {
|
||||
running_timeout = sunxi_wdt_get_timeout(&sunxi_wdt->wdt_dev);
|
||||
if (running_timeout)
|
||||
sunxi_wdt->wdt_dev.timeout = running_timeout;
|
||||
|
||||
err = sunxi_wdt_start(&sunxi_wdt->wdt_dev);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
set_bit(WDOG_HW_RUNNING, &sunxi_wdt->wdt_dev.status);
|
||||
}
|
||||
|
||||
watchdog_stop_on_reboot(&sunxi_wdt->wdt_dev);
|
||||
err = devm_watchdog_register_device(dev, &sunxi_wdt->wdt_dev);
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user