diff options
author | Josh Cartwright <joshc@codeaurora.org> | 2014-09-25 18:51:04 -0400 |
---|---|---|
committer | Wim Van Sebroeck <wim@iguana.be> | 2014-10-20 15:04:19 -0400 |
commit | 05e487d905ab29b5756d6d1e47e27eefa6693fb3 (patch) | |
tree | b84870947ec139b72c8ee1615ff427fd2deb25a1 /drivers/watchdog | |
parent | f286e1335f579dfd970c7fc3f62b248773a47a5c (diff) |
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 <joshc@codeaurora.org>
Reviewed-by: Guenter Roeck <linux@roeck-us.net>
Signed-off-by: Wim Van Sebroeck <wim@iguana.be>
Diffstat (limited to 'drivers/watchdog')
-rw-r--r-- | drivers/watchdog/qcom-wdt.c | 38 |
1 files changed, 38 insertions, 0 deletions
diff --git a/drivers/watchdog/qcom-wdt.c b/drivers/watchdog/qcom-wdt.c index 68db322341bc..aa85618c4d03 100644 --- a/drivers/watchdog/qcom-wdt.c +++ b/drivers/watchdog/qcom-wdt.c | |||
@@ -11,11 +11,13 @@ | |||
11 | * | 11 | * |
12 | */ | 12 | */ |
13 | #include <linux/clk.h> | 13 | #include <linux/clk.h> |
14 | #include <linux/delay.h> | ||
14 | #include <linux/io.h> | 15 | #include <linux/io.h> |
15 | #include <linux/kernel.h> | 16 | #include <linux/kernel.h> |
16 | #include <linux/module.h> | 17 | #include <linux/module.h> |
17 | #include <linux/of.h> | 18 | #include <linux/of.h> |
18 | #include <linux/platform_device.h> | 19 | #include <linux/platform_device.h> |
20 | #include <linux/reboot.h> | ||
19 | #include <linux/watchdog.h> | 21 | #include <linux/watchdog.h> |
20 | 22 | ||
21 | #define WDT_RST 0x0 | 23 | #define WDT_RST 0x0 |
@@ -26,6 +28,7 @@ struct qcom_wdt { | |||
26 | struct watchdog_device wdd; | 28 | struct watchdog_device wdd; |
27 | struct clk *clk; | 29 | struct clk *clk; |
28 | unsigned long rate; | 30 | unsigned long rate; |
31 | struct notifier_block restart_nb; | ||
29 | void __iomem *base; | 32 | void __iomem *base; |
30 | }; | 33 | }; |
31 | 34 | ||
@@ -84,6 +87,32 @@ static const struct watchdog_info qcom_wdt_info = { | |||
84 | .identity = KBUILD_MODNAME, | 87 | .identity = KBUILD_MODNAME, |
85 | }; | 88 | }; |
86 | 89 | ||
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 | |||
87 | static int qcom_wdt_probe(struct platform_device *pdev) | 116 | static int qcom_wdt_probe(struct platform_device *pdev) |
88 | { | 117 | { |
89 | struct qcom_wdt *wdt; | 118 | struct qcom_wdt *wdt; |
@@ -147,6 +176,14 @@ static int qcom_wdt_probe(struct platform_device *pdev) | |||
147 | goto err_clk_unprepare; | 176 | goto err_clk_unprepare; |
148 | } | 177 | } |
149 | 178 | ||
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 | |||
150 | platform_set_drvdata(pdev, wdt); | 187 | platform_set_drvdata(pdev, wdt); |
151 | return 0; | 188 | return 0; |
152 | 189 | ||
@@ -159,6 +196,7 @@ static int qcom_wdt_remove(struct platform_device *pdev) | |||
159 | { | 196 | { |
160 | struct qcom_wdt *wdt = platform_get_drvdata(pdev); | 197 | struct qcom_wdt *wdt = platform_get_drvdata(pdev); |
161 | 198 | ||
199 | unregister_restart_handler(&wdt->restart_nb); | ||
162 | watchdog_unregister_device(&wdt->wdd); | 200 | watchdog_unregister_device(&wdt->wdd); |
163 | clk_disable_unprepare(wdt->clk); | 201 | clk_disable_unprepare(wdt->clk); |
164 | return 0; | 202 | return 0; |