Skip to content

Commit 40aebd1

Browse files
committed
NVIDIA: SAUCE: watchdog: sbsa_gwdt: stop the watchdog across the whole system-sleep transition
The driver stops a running watchdog in its own device suspend callback and restarts it in its resume callback. That leaves the watchdog armed, with nobody refreshing it, for the entire early part of suspend entry: userspace freeze, kernel thread freeze, and every device suspend callback that runs before this device's own. The same window exists at the tail end of resume. When the watchdog is running from boot (early_enable=1, previously force_enable=1 downstream; 10 s default timeout) and any device stalls its suspend callback past the timeout, the watchdog resets the system in the middle of suspend entry. On N1x (Yukon) this fired on about 7% of suspend attempts in a randomized stress run (9 resets in 124 suspends; the serial console shows the board dropping into the boot ROM mid-entry with the watchdog reset status set in NONRST_REG2). Two elimination runs confirm the mechanism: the identical stress matrix with the parameter off produced zero resets in 118 suspends, and with the first version of this change (notifier plus the original device callbacks) applied and the watchdog force-enabled, zero resets in 198 suspends across four runs (the baseline rate predicts about 14). The version here keeps that mechanism, removes the device resume callback and adds the locking described below; it is compile tested. Stop the watchdog from a PM notifier at the *_PREPARE events, before tasks are frozen and device callbacks run, and restart it at the PM_POST_* events, after everything has resumed. The driver state (armed, stopped for sleep) lives under a lock shared with the watchdog ops, so a userspace stop or magic close after thaw cannot race the restart, and a start requested while the transition is in progress is deferred until PM_POST_* instead of arming hardware nobody can refresh. The notifier is registered before anything can arm the watchdog and its failure fails the probe. A suspend-only device callback remains as the final guard for a device whose probe overlapped the *_PREPARE event; it has no resume counterpart, so nothing re-arms the watchdog during device resume, before PM_POST_SUSPEND. This is also upstream-relevant as a companion to the early_enable parameter: the armed-during-entry window exists for any system running the SBSA watchdog from boot. Fixes: 57d2caa ("Watchdog: introduce ARM SBSA watchdog driver") Signed-off-by: David Cemin <dcemin@nvidia.com>
1 parent f8c1047 commit 40aebd1

1 file changed

Lines changed: 142 additions & 32 deletions

File tree

‎drivers/watchdog/sbsa_gwdt.c‎

Lines changed: 142 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,8 @@
4747
#include <linux/module.h>
4848
#include <linux/moduleparam.h>
4949
#include <linux/platform_device.h>
50+
#include <linux/spinlock.h>
51+
#include <linux/suspend.h>
5052
#include <linux/uaccess.h>
5153
#include <linux/watchdog.h>
5254
#include <asm/arch_timer.h>
@@ -88,6 +90,13 @@
8890
* indicate whether to adjust wdd->timeout to avoid a race with WS0
8991
* @refresh_base: Virtual address of the watchdog refresh frame
9092
* @control_base: Virtual address of the watchdog control frame
93+
* @lock: Serializes the watchdog ops against the system sleep hooks
94+
* @hw_armed: The watchdog is logically running (started by firmware,
95+
* early_enable or userspace); the hardware follows it except
96+
* while a system sleep transition is in progress
97+
* @pm_stopped: A system sleep transition stopped the hardware and owes
98+
* a restart at PM_POST_*
99+
* @pm_nb: PM notifier stopping the watchdog across system sleep
91100
*/
92101
struct sbsa_gwdt {
93102
struct watchdog_device wdd;
@@ -96,6 +105,10 @@ struct sbsa_gwdt {
96105
bool need_ws0_race_workaround;
97106
void __iomem *refresh_base;
98107
void __iomem *control_base;
108+
spinlock_t lock; /* hw_armed, pm_stopped */
109+
bool hw_armed;
110+
bool pm_stopped;
111+
struct notifier_block pm_nb;
99112
};
100113

101114
#define DEFAULT_TIMEOUT 10 /* seconds */
@@ -250,22 +263,48 @@ static void sbsa_gwdt_get_version(struct watchdog_device *wdd)
250263
!action && (impl == SBSA_GWDT_IMPL_MEDIATEK);
251264
}
252265

266+
static void sbsa_gwdt_hw_start(struct sbsa_gwdt *gwdt)
267+
{
268+
/* writing WCS will cause an explicit watchdog refresh */
269+
writel(SBSA_GWDT_WCS_EN, gwdt->control_base + SBSA_GWDT_WCS);
270+
}
271+
272+
static void sbsa_gwdt_hw_stop(struct sbsa_gwdt *gwdt)
273+
{
274+
/* Simply write 0 to WCS to clean WCS_EN bit */
275+
writel(0, gwdt->control_base + SBSA_GWDT_WCS);
276+
}
277+
253278
static int sbsa_gwdt_start(struct watchdog_device *wdd)
254279
{
255280
struct sbsa_gwdt *gwdt = watchdog_get_drvdata(wdd);
281+
unsigned long flags;
256282

257-
/* writing WCS will cause an explicit watchdog refresh */
258-
writel(SBSA_GWDT_WCS_EN, gwdt->control_base + SBSA_GWDT_WCS);
283+
spin_lock_irqsave(&gwdt->lock, flags);
284+
gwdt->hw_armed = true;
285+
/*
286+
* While a system sleep transition is in progress nobody can refresh
287+
* the watchdog: leave the hardware stopped and let the PM_POST_*
288+
* notifier arm it once everything has resumed.
289+
*/
290+
if (!gwdt->pm_stopped)
291+
sbsa_gwdt_hw_start(gwdt);
292+
spin_unlock_irqrestore(&gwdt->lock, flags);
259293

260294
return 0;
261295
}
262296

263297
static int sbsa_gwdt_stop(struct watchdog_device *wdd)
264298
{
265299
struct sbsa_gwdt *gwdt = watchdog_get_drvdata(wdd);
300+
unsigned long flags;
266301

267-
/* Simply write 0 to WCS to clean WCS_EN bit */
268-
writel(0, gwdt->control_base + SBSA_GWDT_WCS);
302+
spin_lock_irqsave(&gwdt->lock, flags);
303+
gwdt->hw_armed = false;
304+
/* A stop during the transition also cancels the pending restart. */
305+
gwdt->pm_stopped = false;
306+
sbsa_gwdt_hw_stop(gwdt);
307+
spin_unlock_irqrestore(&gwdt->lock, flags);
269308

270309
return 0;
271310
}
@@ -294,6 +333,86 @@ static const struct watchdog_ops sbsa_gwdt_ops = {
294333
.get_timeleft = sbsa_gwdt_get_timeleft,
295334
};
296335

336+
/*
337+
* Per-device suspend/resume callbacks alone would stop the watchdog only
338+
* once this device itself is suspended, one of the last steps of suspend
339+
* entry, and restart it during device resume, before tasks are thawed. A
340+
* watchdog running from boot (early_enable) would therefore be armed, with
341+
* nobody refreshing it, through task freezing and every other device's
342+
* suspend callback on the way down, and again from device resume until
343+
* userspace runs on the way up; anything stalling past the timeout in
344+
* either window resets the system.
345+
*
346+
* Own the transition from a PM notifier instead: stop at the *_PREPARE
347+
* events, before anything is frozen, and restart at PM_POST_*, after
348+
* everything has resumed. The driver state (hw_armed, pm_stopped) is kept
349+
* under a lock shared with the watchdog ops so that a userspace stop or
350+
* magic close after thaw cannot race the restart, and a start requested
351+
* while the transition is in progress is deferred to PM_POST_*. A
352+
* suspend-only device callback remains as the final guard for a device
353+
* whose probe overlapped the *_PREPARE event; it has no resume
354+
* counterpart, so nothing re-arms the hardware before PM_POST_*.
355+
*/
356+
static void sbsa_gwdt_sleep_stop(struct sbsa_gwdt *gwdt)
357+
{
358+
unsigned long flags;
359+
360+
spin_lock_irqsave(&gwdt->lock, flags);
361+
if (gwdt->hw_armed && !gwdt->pm_stopped) {
362+
sbsa_gwdt_hw_stop(gwdt);
363+
gwdt->pm_stopped = true;
364+
}
365+
spin_unlock_irqrestore(&gwdt->lock, flags);
366+
}
367+
368+
static void sbsa_gwdt_sleep_restart(struct sbsa_gwdt *gwdt)
369+
{
370+
unsigned long flags;
371+
372+
spin_lock_irqsave(&gwdt->lock, flags);
373+
if (gwdt->pm_stopped) {
374+
gwdt->pm_stopped = false;
375+
if (gwdt->hw_armed)
376+
sbsa_gwdt_hw_start(gwdt);
377+
}
378+
spin_unlock_irqrestore(&gwdt->lock, flags);
379+
}
380+
381+
static int sbsa_gwdt_pm_notify(struct notifier_block *nb, unsigned long mode,
382+
void *data)
383+
{
384+
struct sbsa_gwdt *gwdt = container_of(nb, struct sbsa_gwdt, pm_nb);
385+
386+
switch (mode) {
387+
case PM_SUSPEND_PREPARE:
388+
case PM_HIBERNATION_PREPARE:
389+
case PM_RESTORE_PREPARE:
390+
sbsa_gwdt_sleep_stop(gwdt);
391+
break;
392+
case PM_POST_SUSPEND:
393+
case PM_POST_HIBERNATION:
394+
case PM_POST_RESTORE:
395+
sbsa_gwdt_sleep_restart(gwdt);
396+
break;
397+
}
398+
399+
return NOTIFY_DONE;
400+
}
401+
402+
static void sbsa_gwdt_unregister_pm_notifier(void *data)
403+
{
404+
unregister_pm_notifier(data);
405+
}
406+
407+
static int sbsa_gwdt_suspend(struct device *dev)
408+
{
409+
sbsa_gwdt_sleep_stop(dev_get_drvdata(dev));
410+
411+
return 0;
412+
}
413+
414+
static DEFINE_SIMPLE_DEV_PM_OPS(sbsa_gwdt_pm_ops, sbsa_gwdt_suspend, NULL);
415+
297416
static int sbsa_gwdt_probe(struct platform_device *pdev)
298417
{
299418
void __iomem *rf_base, *cf_base;
@@ -325,6 +444,21 @@ static int sbsa_gwdt_probe(struct platform_device *pdev)
325444
gwdt->clk = arch_timer_get_cntfrq();
326445
gwdt->refresh_base = rf_base;
327446
gwdt->control_base = cf_base;
447+
spin_lock_init(&gwdt->lock);
448+
449+
/*
450+
* Register the sleep hook before anything can arm the watchdog, and
451+
* treat its failure as fatal: without it a running watchdog would
452+
* survive into system sleep with nobody refreshing it.
453+
*/
454+
gwdt->pm_nb.notifier_call = sbsa_gwdt_pm_notify;
455+
ret = register_pm_notifier(&gwdt->pm_nb);
456+
if (!ret)
457+
ret = devm_add_action_or_reset(dev,
458+
sbsa_gwdt_unregister_pm_notifier,
459+
&gwdt->pm_nb);
460+
if (ret)
461+
return dev_err_probe(dev, ret, "Failed to register PM notifier\n");
328462

329463
wdd = &gwdt->wdd;
330464
wdd->parent = dev;
@@ -354,8 +488,10 @@ static int sbsa_gwdt_probe(struct platform_device *pdev)
354488
dev_warn(dev, "System reset by WDT.\n");
355489
wdd->bootstatus |= WDIOF_CARDRESET;
356490
}
357-
if (status & SBSA_GWDT_WCS_EN)
491+
if (status & SBSA_GWDT_WCS_EN) {
358492
set_bit(WDOG_HW_RUNNING, &wdd->status);
493+
gwdt->hw_armed = true;
494+
}
359495

360496
if (action) {
361497
irq = platform_get_irq(pdev, 0);
@@ -414,32 +550,6 @@ static int sbsa_gwdt_probe(struct platform_device *pdev)
414550
return 0;
415551
}
416552

417-
/* Disable watchdog if it is active during suspend */
418-
static int __maybe_unused sbsa_gwdt_suspend(struct device *dev)
419-
{
420-
struct sbsa_gwdt *gwdt = dev_get_drvdata(dev);
421-
422-
if (watchdog_hw_running(&gwdt->wdd))
423-
sbsa_gwdt_stop(&gwdt->wdd);
424-
425-
return 0;
426-
}
427-
428-
/* Enable watchdog if necessary */
429-
static int __maybe_unused sbsa_gwdt_resume(struct device *dev)
430-
{
431-
struct sbsa_gwdt *gwdt = dev_get_drvdata(dev);
432-
433-
if (watchdog_hw_running(&gwdt->wdd))
434-
sbsa_gwdt_start(&gwdt->wdd);
435-
436-
return 0;
437-
}
438-
439-
static const struct dev_pm_ops sbsa_gwdt_pm_ops = {
440-
SET_SYSTEM_SLEEP_PM_OPS(sbsa_gwdt_suspend, sbsa_gwdt_resume)
441-
};
442-
443553
static const struct of_device_id sbsa_gwdt_of_match[] = {
444554
{ .compatible = "arm,sbsa-gwdt", },
445555
{},
@@ -455,7 +565,7 @@ MODULE_DEVICE_TABLE(platform, sbsa_gwdt_pdev_match);
455565
static struct platform_driver sbsa_gwdt_driver = {
456566
.driver = {
457567
.name = DRV_NAME,
458-
.pm = &sbsa_gwdt_pm_ops,
568+
.pm = pm_sleep_ptr(&sbsa_gwdt_pm_ops),
459569
.of_match_table = sbsa_gwdt_of_match,
460570
},
461571
.probe = sbsa_gwdt_probe,

0 commit comments

Comments
 (0)