Commit f29a72c24ad4927027e77e4eed431a61bc8335b2

Authored by Guenter Roeck
Committed by Wim Van Sebroeck
1 parent 15013ad813

watchdog: dw_wdt: Convert to use watchdog infrastructure

Convert driver to use watchdog infrastructure. This includes
infrastructure support to handle watchdog keepalive if the watchdog
is running while the watchdog device is closed.

Signed-off-by: Guenter Roeck <linux@roeck-us.net>
Tested-by: Douglas Anderson <dianders@chromium.org>
Signed-off-by: Wim Van Sebroeck <wim@iguana.be>

Showing 2 changed files with 117 additions and 207 deletions Side-by-side Diff

drivers/watchdog/Kconfig
... ... @@ -350,6 +350,7 @@
350 350 config DW_WATCHDOG
351 351 tristate "Synopsys DesignWare watchdog"
352 352 depends on HAS_IOMEM
  353 + select WATCHDOG_CORE
353 354 help
354 355 Say Y here if to include support for the Synopsys DesignWare
355 356 watchdog timer found in many chips.
drivers/watchdog/dw_wdt.c
... ... @@ -12,9 +12,8 @@
12 12 * and these are a function of the input clock frequency.
13 13 *
14 14 * The DesignWare watchdog cannot be stopped once it has been started so we
15   - * use a software timer to implement a ping that will keep the watchdog alive.
16   - * If we receive an expected close for the watchdog then we keep the timer
17   - * running, otherwise the timer is stopped and the watchdog will expire.
  15 + * do not implement a stop function. The watchdog core will continue to send
  16 + * heartbeat requests after the watchdog device has been closed.
18 17 */
19 18  
20 19 #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
21 20  
22 21  
... ... @@ -22,12 +21,9 @@
22 21 #include <linux/bitops.h>
23 22 #include <linux/clk.h>
24 23 #include <linux/delay.h>
25   -#include <linux/device.h>
26 24 #include <linux/err.h>
27   -#include <linux/fs.h>
28 25 #include <linux/io.h>
29 26 #include <linux/kernel.h>
30   -#include <linux/miscdevice.h>
31 27 #include <linux/module.h>
32 28 #include <linux/moduleparam.h>
33 29 #include <linux/notifier.h>
... ... @@ -35,8 +31,6 @@
35 31 #include <linux/pm.h>
36 32 #include <linux/platform_device.h>
37 33 #include <linux/reboot.h>
38   -#include <linux/timer.h>
39   -#include <linux/uaccess.h>
40 34 #include <linux/watchdog.h>
41 35  
42 36 #define WDOG_CONTROL_REG_OFFSET 0x00
43 37  
44 38  
45 39  
46 40  
47 41  
48 42  
49 43  
50 44  
51 45  
52 46  
53 47  
54 48  
55 49  
56 50  
57 51  
... ... @@ -57,53 +51,50 @@
57 51 MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started "
58 52 "(default=" __MODULE_STRING(WATCHDOG_NOWAYOUT) ")");
59 53  
60   -#define WDT_TIMEOUT (HZ / 2)
61   -
62   -static struct {
  54 +struct dw_wdt {
63 55 void __iomem *regs;
64 56 struct clk *clk;
65   - unsigned long in_use;
66   - unsigned long next_heartbeat;
67   - struct timer_list timer;
68   - int expect_close;
69 57 struct notifier_block restart_handler;
70   -} dw_wdt;
  58 + struct watchdog_device wdd;
  59 +};
71 60  
72   -static inline int dw_wdt_is_enabled(void)
  61 +#define to_dw_wdt(wdd) container_of(wdd, struct dw_wdt, wdd)
  62 +
  63 +static inline int dw_wdt_is_enabled(struct dw_wdt *dw_wdt)
73 64 {
74   - return readl(dw_wdt.regs + WDOG_CONTROL_REG_OFFSET) &
  65 + return readl(dw_wdt->regs + WDOG_CONTROL_REG_OFFSET) &
75 66 WDOG_CONTROL_REG_WDT_EN_MASK;
76 67 }
77 68  
78   -static inline int dw_wdt_top_in_seconds(unsigned top)
  69 +static inline int dw_wdt_top_in_seconds(struct dw_wdt *dw_wdt, unsigned top)
79 70 {
80 71 /*
81 72 * There are 16 possible timeout values in 0..15 where the number of
82 73 * cycles is 2 ^ (16 + i) and the watchdog counts down.
83 74 */
84   - return (1U << (16 + top)) / clk_get_rate(dw_wdt.clk);
  75 + return (1U << (16 + top)) / clk_get_rate(dw_wdt->clk);
85 76 }
86 77  
87   -static int dw_wdt_get_top(void)
  78 +static int dw_wdt_get_top(struct dw_wdt *dw_wdt)
88 79 {
89   - int top = readl(dw_wdt.regs + WDOG_TIMEOUT_RANGE_REG_OFFSET) & 0xF;
  80 + int top = readl(dw_wdt->regs + WDOG_TIMEOUT_RANGE_REG_OFFSET) & 0xF;
90 81  
91   - return dw_wdt_top_in_seconds(top);
  82 + return dw_wdt_top_in_seconds(dw_wdt, top);
92 83 }
93 84  
94   -static inline void dw_wdt_set_next_heartbeat(void)
  85 +static int dw_wdt_ping(struct watchdog_device *wdd)
95 86 {
96   - dw_wdt.next_heartbeat = jiffies + dw_wdt_get_top() * HZ;
97   -}
  87 + struct dw_wdt *dw_wdt = to_dw_wdt(wdd);
98 88  
99   -static void dw_wdt_keepalive(void)
100   -{
101   - writel(WDOG_COUNTER_RESTART_KICK_VALUE, dw_wdt.regs +
  89 + writel(WDOG_COUNTER_RESTART_KICK_VALUE, dw_wdt->regs +
102 90 WDOG_COUNTER_RESTART_REG_OFFSET);
  91 +
  92 + return 0;
103 93 }
104 94  
105   -static int dw_wdt_set_top(unsigned top_s)
  95 +static int dw_wdt_set_timeout(struct watchdog_device *wdd, unsigned int top_s)
106 96 {
  97 + struct dw_wdt *dw_wdt = to_dw_wdt(wdd);
107 98 int i, top_val = DW_WDT_MAX_TOP;
108 99  
109 100 /*
... ... @@ -111,7 +102,7 @@
111 102 * always look for >=.
112 103 */
113 104 for (i = 0; i <= DW_WDT_MAX_TOP; ++i)
114   - if (dw_wdt_top_in_seconds(i) >= top_s) {
  105 + if (dw_wdt_top_in_seconds(dw_wdt, i) >= top_s) {
115 106 top_val = i;
116 107 break;
117 108 }
118 109  
119 110  
120 111  
121 112  
122 113  
123 114  
124 115  
125 116  
... ... @@ -123,33 +114,43 @@
123 114 * effectively get a pat of the watchdog right here.
124 115 */
125 116 writel(top_val | top_val << WDOG_TIMEOUT_RANGE_TOPINIT_SHIFT,
126   - dw_wdt.regs + WDOG_TIMEOUT_RANGE_REG_OFFSET);
  117 + dw_wdt->regs + WDOG_TIMEOUT_RANGE_REG_OFFSET);
127 118  
128   - /*
129   - * Add an explicit pat to handle versions of the watchdog that
130   - * don't have TOPINIT. This won't hurt on versions that have
131   - * it.
132   - */
133   - dw_wdt_keepalive();
  119 + wdd->timeout = dw_wdt_top_in_seconds(dw_wdt, top_val);
134 120  
135   - dw_wdt_set_next_heartbeat();
  121 + return 0;
  122 +}
136 123  
137   - return dw_wdt_top_in_seconds(top_val);
  124 +static int dw_wdt_start(struct watchdog_device *wdd)
  125 +{
  126 + struct dw_wdt *dw_wdt = to_dw_wdt(wdd);
  127 +
  128 + dw_wdt_set_timeout(wdd, wdd->timeout);
  129 +
  130 + set_bit(WDOG_HW_RUNNING, &wdd->status);
  131 +
  132 + writel(WDOG_CONTROL_REG_WDT_EN_MASK,
  133 + dw_wdt->regs + WDOG_CONTROL_REG_OFFSET);
  134 +
  135 + return 0;
138 136 }
139 137  
140 138 static int dw_wdt_restart_handle(struct notifier_block *this,
141   - unsigned long mode, void *cmd)
  139 + unsigned long mode, void *cmd)
142 140 {
  141 + struct dw_wdt *dw_wdt;
143 142 u32 val;
144 143  
145   - writel(0, dw_wdt.regs + WDOG_TIMEOUT_RANGE_REG_OFFSET);
146   - val = readl(dw_wdt.regs + WDOG_CONTROL_REG_OFFSET);
  144 + dw_wdt = container_of(this, struct dw_wdt, restart_handler);
  145 +
  146 + writel(0, dw_wdt->regs + WDOG_TIMEOUT_RANGE_REG_OFFSET);
  147 + val = readl(dw_wdt->regs + WDOG_CONTROL_REG_OFFSET);
147 148 if (val & WDOG_CONTROL_REG_WDT_EN_MASK)
148   - writel(WDOG_COUNTER_RESTART_KICK_VALUE, dw_wdt.regs +
149   - WDOG_COUNTER_RESTART_REG_OFFSET);
  149 + writel(WDOG_COUNTER_RESTART_KICK_VALUE,
  150 + dw_wdt->regs + WDOG_COUNTER_RESTART_REG_OFFSET);
150 151 else
151 152 writel(WDOG_CONTROL_REG_WDT_EN_MASK,
152   - dw_wdt.regs + WDOG_CONTROL_REG_OFFSET);
  153 + dw_wdt->regs + WDOG_CONTROL_REG_OFFSET);
153 154  
154 155 /* wait for reset to assert... */
155 156 mdelay(500);
156 157  
157 158  
158 159  
159 160  
160 161  
161 162  
162 163  
163 164  
164 165  
... ... @@ -157,154 +158,47 @@
157 158 return NOTIFY_DONE;
158 159 }
159 160  
160   -static void dw_wdt_ping(unsigned long data)
  161 +static unsigned int dw_wdt_get_timeleft(struct watchdog_device *wdd)
161 162 {
162   - if (time_before(jiffies, dw_wdt.next_heartbeat) ||
163   - (!nowayout && !dw_wdt.in_use)) {
164   - dw_wdt_keepalive();
165   - mod_timer(&dw_wdt.timer, jiffies + WDT_TIMEOUT);
166   - } else
167   - pr_crit("keepalive missed, machine will reset\n");
168   -}
  163 + struct dw_wdt *dw_wdt = to_dw_wdt(wdd);
169 164  
170   -static int dw_wdt_open(struct inode *inode, struct file *filp)
171   -{
172   - if (test_and_set_bit(0, &dw_wdt.in_use))
173   - return -EBUSY;
174   -
175   - /* Make sure we don't get unloaded. */
176   - __module_get(THIS_MODULE);
177   -
178   - if (!dw_wdt_is_enabled()) {
179   - /*
180   - * The watchdog is not currently enabled. Set the timeout to
181   - * something reasonable and then start it.
182   - */
183   - dw_wdt_set_top(DW_WDT_DEFAULT_SECONDS);
184   - writel(WDOG_CONTROL_REG_WDT_EN_MASK,
185   - dw_wdt.regs + WDOG_CONTROL_REG_OFFSET);
186   - }
187   -
188   - dw_wdt_set_next_heartbeat();
189   -
190   - return nonseekable_open(inode, filp);
  165 + return readl(dw_wdt->regs + WDOG_CURRENT_COUNT_REG_OFFSET) /
  166 + clk_get_rate(dw_wdt->clk);
191 167 }
192 168  
193   -static ssize_t dw_wdt_write(struct file *filp, const char __user *buf,
194   - size_t len, loff_t *offset)
195   -{
196   - if (!len)
197   - return 0;
198   -
199   - if (!nowayout) {
200   - size_t i;
201   -
202   - dw_wdt.expect_close = 0;
203   -
204   - for (i = 0; i < len; ++i) {
205   - char c;
206   -
207   - if (get_user(c, buf + i))
208   - return -EFAULT;
209   -
210   - if (c == 'V') {
211   - dw_wdt.expect_close = 1;
212   - break;
213   - }
214   - }
215   - }
216   -
217   - dw_wdt_set_next_heartbeat();
218   - dw_wdt_keepalive();
219   - mod_timer(&dw_wdt.timer, jiffies + WDT_TIMEOUT);
220   -
221   - return len;
222   -}
223   -
224   -static u32 dw_wdt_time_left(void)
225   -{
226   - return readl(dw_wdt.regs + WDOG_CURRENT_COUNT_REG_OFFSET) /
227   - clk_get_rate(dw_wdt.clk);
228   -}
229   -
230 169 static const struct watchdog_info dw_wdt_ident = {
231 170 .options = WDIOF_KEEPALIVEPING | WDIOF_SETTIMEOUT |
232 171 WDIOF_MAGICCLOSE,
233 172 .identity = "Synopsys DesignWare Watchdog",
234 173 };
235 174  
236   -static long dw_wdt_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
237   -{
238   - unsigned long val;
239   - int timeout;
  175 +static const struct watchdog_ops dw_wdt_ops = {
  176 + .owner = THIS_MODULE,
  177 + .start = dw_wdt_start,
  178 + .ping = dw_wdt_ping,
  179 + .set_timeout = dw_wdt_set_timeout,
  180 + .get_timeleft = dw_wdt_get_timeleft,
  181 +};
240 182  
241   - switch (cmd) {
242   - case WDIOC_GETSUPPORT:
243   - return copy_to_user((void __user *)arg, &dw_wdt_ident,
244   - sizeof(dw_wdt_ident)) ? -EFAULT : 0;
245   -
246   - case WDIOC_GETSTATUS:
247   - case WDIOC_GETBOOTSTATUS:
248   - return put_user(0, (int __user *)arg);
249   -
250   - case WDIOC_KEEPALIVE:
251   - dw_wdt_set_next_heartbeat();
252   - return 0;
253   -
254   - case WDIOC_SETTIMEOUT:
255   - if (get_user(val, (int __user *)arg))
256   - return -EFAULT;
257   - timeout = dw_wdt_set_top(val);
258   - return put_user(timeout , (int __user *)arg);
259   -
260   - case WDIOC_GETTIMEOUT:
261   - return put_user(dw_wdt_get_top(), (int __user *)arg);
262   -
263   - case WDIOC_GETTIMELEFT:
264   - /* Get the time left until expiry. */
265   - if (get_user(val, (int __user *)arg))
266   - return -EFAULT;
267   - return put_user(dw_wdt_time_left(), (int __user *)arg);
268   -
269   - default:
270   - return -ENOTTY;
271   - }
272   -}
273   -
274   -static int dw_wdt_release(struct inode *inode, struct file *filp)
275   -{
276   - clear_bit(0, &dw_wdt.in_use);
277   -
278   - if (!dw_wdt.expect_close) {
279   - del_timer(&dw_wdt.timer);
280   -
281   - if (!nowayout)
282   - pr_crit("unexpected close, system will reboot soon\n");
283   - else
284   - pr_crit("watchdog cannot be disabled, system will reboot soon\n");
285   - }
286   -
287   - dw_wdt.expect_close = 0;
288   -
289   - return 0;
290   -}
291   -
292 183 #ifdef CONFIG_PM_SLEEP
293 184 static int dw_wdt_suspend(struct device *dev)
294 185 {
295   - clk_disable_unprepare(dw_wdt.clk);
  186 + struct dw_wdt *dw_wdt = dev_get_drvdata(dev);
296 187  
  188 + clk_disable_unprepare(dw_wdt->clk);
  189 +
297 190 return 0;
298 191 }
299 192  
300 193 static int dw_wdt_resume(struct device *dev)
301 194 {
302   - int err = clk_prepare_enable(dw_wdt.clk);
  195 + struct dw_wdt *dw_wdt = dev_get_drvdata(dev);
  196 + int err = clk_prepare_enable(dw_wdt->clk);
303 197  
304 198 if (err)
305 199 return err;
306 200  
307   - dw_wdt_keepalive();
  201 + dw_wdt_ping(&dw_wdt->wdd);
308 202  
309 203 return 0;
310 204 }
311 205  
312 206  
313 207  
314 208  
315 209  
316 210  
317 211  
318 212  
319 213  
320 214  
321 215  
... ... @@ -312,67 +206,82 @@
312 206  
313 207 static SIMPLE_DEV_PM_OPS(dw_wdt_pm_ops, dw_wdt_suspend, dw_wdt_resume);
314 208  
315   -static const struct file_operations wdt_fops = {
316   - .owner = THIS_MODULE,
317   - .llseek = no_llseek,
318   - .open = dw_wdt_open,
319   - .write = dw_wdt_write,
320   - .unlocked_ioctl = dw_wdt_ioctl,
321   - .release = dw_wdt_release
322   -};
323   -
324   -static struct miscdevice dw_wdt_miscdev = {
325   - .fops = &wdt_fops,
326   - .name = "watchdog",
327   - .minor = WATCHDOG_MINOR,
328   -};
329   -
330 209 static int dw_wdt_drv_probe(struct platform_device *pdev)
331 210 {
  211 + struct device *dev = &pdev->dev;
  212 + struct watchdog_device *wdd;
  213 + struct dw_wdt *dw_wdt;
  214 + struct resource *mem;
332 215 int ret;
333   - struct resource *mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
334 216  
335   - dw_wdt.regs = devm_ioremap_resource(&pdev->dev, mem);
336   - if (IS_ERR(dw_wdt.regs))
337   - return PTR_ERR(dw_wdt.regs);
  217 + dw_wdt = devm_kzalloc(dev, sizeof(*dw_wdt), GFP_KERNEL);
  218 + if (!dw_wdt)
  219 + return -ENOMEM;
338 220  
339   - dw_wdt.clk = devm_clk_get(&pdev->dev, NULL);
340   - if (IS_ERR(dw_wdt.clk))
341   - return PTR_ERR(dw_wdt.clk);
  221 + mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
  222 + dw_wdt->regs = devm_ioremap_resource(dev, mem);
  223 + if (IS_ERR(dw_wdt->regs))
  224 + return PTR_ERR(dw_wdt->regs);
342 225  
343   - ret = clk_prepare_enable(dw_wdt.clk);
  226 + dw_wdt->clk = devm_clk_get(dev, NULL);
  227 + if (IS_ERR(dw_wdt->clk))
  228 + return PTR_ERR(dw_wdt->clk);
  229 +
  230 + ret = clk_prepare_enable(dw_wdt->clk);
344 231 if (ret)
345 232 return ret;
346 233  
347   - ret = misc_register(&dw_wdt_miscdev);
  234 + wdd = &dw_wdt->wdd;
  235 + wdd->info = &dw_wdt_ident;
  236 + wdd->ops = &dw_wdt_ops;
  237 + wdd->min_timeout = 1;
  238 + wdd->max_hw_heartbeat_ms =
  239 + dw_wdt_top_in_seconds(dw_wdt, DW_WDT_MAX_TOP) * 1000;
  240 + wdd->parent = dev;
  241 +
  242 + watchdog_set_drvdata(wdd, dw_wdt);
  243 + watchdog_set_nowayout(wdd, nowayout);
  244 + watchdog_init_timeout(wdd, 0, dev);
  245 +
  246 + /*
  247 + * If the watchdog is already running, use its already configured
  248 + * timeout. Otherwise use the default or the value provided through
  249 + * devicetree.
  250 + */
  251 + if (dw_wdt_is_enabled(dw_wdt)) {
  252 + wdd->timeout = dw_wdt_get_top(dw_wdt);
  253 + set_bit(WDOG_HW_RUNNING, &wdd->status);
  254 + } else {
  255 + wdd->timeout = DW_WDT_DEFAULT_SECONDS;
  256 + watchdog_init_timeout(wdd, 0, dev);
  257 + }
  258 +
  259 + platform_set_drvdata(pdev, dw_wdt);
  260 +
  261 + ret = watchdog_register_device(wdd);
348 262 if (ret)
349 263 goto out_disable_clk;
350 264  
351   - dw_wdt.restart_handler.notifier_call = dw_wdt_restart_handle;
352   - dw_wdt.restart_handler.priority = 128;
353   - ret = register_restart_handler(&dw_wdt.restart_handler);
  265 + dw_wdt->restart_handler.notifier_call = dw_wdt_restart_handle;
  266 + dw_wdt->restart_handler.priority = 128;
  267 + ret = register_restart_handler(&dw_wdt->restart_handler);
354 268 if (ret)
355 269 pr_warn("cannot register restart handler\n");
356 270  
357   - dw_wdt_set_next_heartbeat();
358   - setup_timer(&dw_wdt.timer, dw_wdt_ping, 0);
359   - mod_timer(&dw_wdt.timer, jiffies + WDT_TIMEOUT);
360   -
361 271 return 0;
362 272  
363 273 out_disable_clk:
364   - clk_disable_unprepare(dw_wdt.clk);
365   -
  274 + clk_disable_unprepare(dw_wdt->clk);
366 275 return ret;
367 276 }
368 277  
369 278 static int dw_wdt_drv_remove(struct platform_device *pdev)
370 279 {
371   - unregister_restart_handler(&dw_wdt.restart_handler);
  280 + struct dw_wdt *dw_wdt = platform_get_drvdata(pdev);
372 281  
373   - misc_deregister(&dw_wdt_miscdev);
374   -
375   - clk_disable_unprepare(dw_wdt.clk);
  282 + unregister_restart_handler(&dw_wdt->restart_handler);
  283 + watchdog_unregister_device(&dw_wdt->wdd);
  284 + clk_disable_unprepare(dw_wdt->clk);
376 285  
377 286 return 0;
378 287 }