Skip to content

Commit 05e487d

Browse files
Josh CartwrightWim Van Sebroeck
authored andcommitted
watchdog: qcom: register a restart notifier
The WDT's BITE_TIME warm-reset behavior can be leveraged as a last resort mechanism for triggering chip reset. Usually, other restart methods (such as PS_HOLD) are preferrable for issuing a more complete reset of the chip. As such, keep the priority of the watchdog notifier low. Signed-off-by: Josh Cartwright <[email protected]> Reviewed-by: Guenter Roeck <[email protected]> Signed-off-by: Wim Van Sebroeck <[email protected]>
1 parent f286e13 commit 05e487d

File tree

1 file changed

+38
-0
lines changed

1 file changed

+38
-0
lines changed

drivers/watchdog/qcom-wdt.c

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,11 +11,13 @@
1111
*
1212
*/
1313
#include <linux/clk.h>
14+
#include <linux/delay.h>
1415
#include <linux/io.h>
1516
#include <linux/kernel.h>
1617
#include <linux/module.h>
1718
#include <linux/of.h>
1819
#include <linux/platform_device.h>
20+
#include <linux/reboot.h>
1921
#include <linux/watchdog.h>
2022

2123
#define WDT_RST 0x0
@@ -26,6 +28,7 @@ struct qcom_wdt {
2628
struct watchdog_device wdd;
2729
struct clk *clk;
2830
unsigned long rate;
31+
struct notifier_block restart_nb;
2932
void __iomem *base;
3033
};
3134

@@ -84,6 +87,32 @@ static const struct watchdog_info qcom_wdt_info = {
8487
.identity = KBUILD_MODNAME,
8588
};
8689

90+
static int qcom_wdt_restart(struct notifier_block *nb, unsigned long action,
91+
void *data)
92+
{
93+
struct qcom_wdt *wdt = container_of(nb, struct qcom_wdt, restart_nb);
94+
u32 timeout;
95+
96+
/*
97+
* Trigger watchdog bite:
98+
* Setup BITE_TIME to be 128ms, and enable WDT.
99+
*/
100+
timeout = 128 * wdt->rate / 1000;
101+
102+
writel(0, wdt->base + WDT_EN);
103+
writel(1, wdt->base + WDT_RST);
104+
writel(timeout, wdt->base + WDT_BITE_TIME);
105+
writel(1, wdt->base + WDT_EN);
106+
107+
/*
108+
* Actually make sure the above sequence hits hardware before sleeping.
109+
*/
110+
wmb();
111+
112+
msleep(150);
113+
return NOTIFY_DONE;
114+
}
115+
87116
static int qcom_wdt_probe(struct platform_device *pdev)
88117
{
89118
struct qcom_wdt *wdt;
@@ -147,6 +176,14 @@ static int qcom_wdt_probe(struct platform_device *pdev)
147176
goto err_clk_unprepare;
148177
}
149178

179+
/*
180+
* WDT restart notifier has priority 0 (use as a last resort)
181+
*/
182+
wdt->restart_nb.notifier_call = qcom_wdt_restart;
183+
ret = register_restart_handler(&wdt->restart_nb);
184+
if (ret)
185+
dev_err(&pdev->dev, "failed to setup restart handler\n");
186+
150187
platform_set_drvdata(pdev, wdt);
151188
return 0;
152189

@@ -159,6 +196,7 @@ static int qcom_wdt_remove(struct platform_device *pdev)
159196
{
160197
struct qcom_wdt *wdt = platform_get_drvdata(pdev);
161198

199+
unregister_restart_handler(&wdt->restart_nb);
162200
watchdog_unregister_device(&wdt->wdd);
163201
clk_disable_unprepare(wdt->clk);
164202
return 0;

0 commit comments

Comments
 (0)