Linux Kernel  3.7.1
 All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros Groups Pages
mpcore_wdt.c
Go to the documentation of this file.
1 /*
2  * Watchdog driver for the mpcore watchdog timer
3  *
4  * (c) Copyright 2004 ARM Limited
5  *
6  * Based on the SoftDog driver:
7  * (c) Copyright 1996 Alan Cox <[email protected]>,
8  * All Rights Reserved.
9  *
10  * This program is free software; you can redistribute it and/or
11  * modify it under the terms of the GNU General Public License
12  * as published by the Free Software Foundation; either version
13  * 2 of the License, or (at your option) any later version.
14  *
15  * Neither Alan Cox nor CymruNet Ltd. admit liability nor provide
16  * warranty for any of this software. This material is provided
17  * "AS-IS" and at no charge.
18  *
19  * (c) Copyright 1995 Alan Cox <[email protected]>
20  *
21  */
22 
23 #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
24 
25 #include <linux/module.h>
26 #include <linux/moduleparam.h>
27 #include <linux/types.h>
28 #include <linux/miscdevice.h>
29 #include <linux/watchdog.h>
30 #include <linux/fs.h>
31 #include <linux/reboot.h>
32 #include <linux/init.h>
33 #include <linux/interrupt.h>
34 #include <linux/platform_device.h>
35 #include <linux/uaccess.h>
36 #include <linux/slab.h>
37 #include <linux/io.h>
38 
39 #include <asm/smp_twd.h>
40 
41 struct mpcore_wdt {
42  unsigned long timer_alive;
43  struct device *dev;
44  void __iomem *base;
45  int irq;
46  unsigned int perturb;
48 };
49 
50 static struct platform_device *mpcore_wdt_pdev;
51 static DEFINE_SPINLOCK(wdt_lock);
52 
53 #define TIMER_MARGIN 60
54 static int mpcore_margin = TIMER_MARGIN;
55 module_param(mpcore_margin, int, 0);
56 MODULE_PARM_DESC(mpcore_margin,
57  "MPcore timer margin in seconds. (0 < mpcore_margin < 65536, default="
59 
60 static bool nowayout = WATCHDOG_NOWAYOUT;
61 module_param(nowayout, bool, 0);
62 MODULE_PARM_DESC(nowayout,
63  "Watchdog cannot be stopped once started (default="
65 
66 #define ONLY_TESTING 0
67 static int mpcore_noboot = ONLY_TESTING;
68 module_param(mpcore_noboot, int, 0);
69 MODULE_PARM_DESC(mpcore_noboot, "MPcore watchdog action, "
70  "set to 1 to ignore reboots, 0 to reboot (default="
72 
73 /*
74  * This is the interrupt handler. Note that we only use this
75  * in testing mode, so don't actually do a reboot here.
76  */
77 static irqreturn_t mpcore_wdt_fire(int irq, void *arg)
78 {
79  struct mpcore_wdt *wdt = arg;
80 
81  /* Check it really was our interrupt */
82  if (readl(wdt->base + TWD_WDOG_INTSTAT)) {
83  dev_printk(KERN_CRIT, wdt->dev,
84  "Triggered - Reboot ignored.\n");
85  /* Clear the interrupt on the watchdog */
86  writel(1, wdt->base + TWD_WDOG_INTSTAT);
87  return IRQ_HANDLED;
88  }
89  return IRQ_NONE;
90 }
91 
92 /*
93  * mpcore_wdt_keepalive - reload the timer
94  *
95  * Note that the spec says a DIFFERENT value must be written to the reload
96  * register each time. The "perturb" variable deals with this by adding 1
97  * to the count every other time the function is called.
98  */
99 static void mpcore_wdt_keepalive(struct mpcore_wdt *wdt)
100 {
101  unsigned long count;
102 
103  spin_lock(&wdt_lock);
104  /* Assume prescale is set to 256 */
105  count = __raw_readl(wdt->base + TWD_WDOG_COUNTER);
106  count = (0xFFFFFFFFU - count) * (HZ / 5);
107  count = (count / 256) * mpcore_margin;
108 
109  /* Reload the counter */
110  writel(count + wdt->perturb, wdt->base + TWD_WDOG_LOAD);
111  wdt->perturb = wdt->perturb ? 0 : 1;
112  spin_unlock(&wdt_lock);
113 }
114 
115 static void mpcore_wdt_stop(struct mpcore_wdt *wdt)
116 {
117  spin_lock(&wdt_lock);
118  writel(0x12345678, wdt->base + TWD_WDOG_DISABLE);
119  writel(0x87654321, wdt->base + TWD_WDOG_DISABLE);
120  writel(0x0, wdt->base + TWD_WDOG_CONTROL);
121  spin_unlock(&wdt_lock);
122 }
123 
124 static void mpcore_wdt_start(struct mpcore_wdt *wdt)
125 {
126  dev_printk(KERN_INFO, wdt->dev, "enabling watchdog.\n");
127 
128  /* This loads the count register but does NOT start the count yet */
129  mpcore_wdt_keepalive(wdt);
130 
131  if (mpcore_noboot) {
132  /* Enable watchdog - prescale=256, watchdog mode=0, enable=1 */
133  writel(0x0000FF01, wdt->base + TWD_WDOG_CONTROL);
134  } else {
135  /* Enable watchdog - prescale=256, watchdog mode=1, enable=1 */
136  writel(0x0000FF09, wdt->base + TWD_WDOG_CONTROL);
137  }
138 }
139 
140 static int mpcore_wdt_set_heartbeat(int t)
141 {
142  if (t < 0x0001 || t > 0xFFFF)
143  return -EINVAL;
144 
145  mpcore_margin = t;
146  return 0;
147 }
148 
149 /*
150  * /dev/watchdog handling
151  */
152 static int mpcore_wdt_open(struct inode *inode, struct file *file)
153 {
154  struct mpcore_wdt *wdt = platform_get_drvdata(mpcore_wdt_pdev);
155 
156  if (test_and_set_bit(0, &wdt->timer_alive))
157  return -EBUSY;
158 
159  if (nowayout)
160  __module_get(THIS_MODULE);
161 
162  file->private_data = wdt;
163 
164  /*
165  * Activate timer
166  */
167  mpcore_wdt_start(wdt);
168 
169  return nonseekable_open(inode, file);
170 }
171 
172 static int mpcore_wdt_release(struct inode *inode, struct file *file)
173 {
174  struct mpcore_wdt *wdt = file->private_data;
175 
176  /*
177  * Shut off the timer.
178  * Lock it in if it's a module and we set nowayout
179  */
180  if (wdt->expect_close == 42)
181  mpcore_wdt_stop(wdt);
182  else {
183  dev_printk(KERN_CRIT, wdt->dev,
184  "unexpected close, not stopping watchdog!\n");
185  mpcore_wdt_keepalive(wdt);
186  }
187  clear_bit(0, &wdt->timer_alive);
188  wdt->expect_close = 0;
189  return 0;
190 }
191 
192 static ssize_t mpcore_wdt_write(struct file *file, const char *data,
193  size_t len, loff_t *ppos)
194 {
195  struct mpcore_wdt *wdt = file->private_data;
196 
197  /*
198  * Refresh the timer.
199  */
200  if (len) {
201  if (!nowayout) {
202  size_t i;
203 
204  /* In case it was set long ago */
205  wdt->expect_close = 0;
206 
207  for (i = 0; i != len; i++) {
208  char c;
209 
210  if (get_user(c, data + i))
211  return -EFAULT;
212  if (c == 'V')
213  wdt->expect_close = 42;
214  }
215  }
216  mpcore_wdt_keepalive(wdt);
217  }
218  return len;
219 }
220 
221 static const struct watchdog_info ident = {
222  .options = WDIOF_SETTIMEOUT |
225  .identity = "MPcore Watchdog",
226 };
227 
228 static long mpcore_wdt_ioctl(struct file *file, unsigned int cmd,
229  unsigned long arg)
230 {
231  struct mpcore_wdt *wdt = file->private_data;
232  int ret;
233  union {
234  struct watchdog_info ident;
235  int i;
236  } uarg;
237 
238  if (_IOC_DIR(cmd) && _IOC_SIZE(cmd) > sizeof(uarg))
239  return -ENOTTY;
240 
241  if (_IOC_DIR(cmd) & _IOC_WRITE) {
242  ret = copy_from_user(&uarg, (void __user *)arg, _IOC_SIZE(cmd));
243  if (ret)
244  return -EFAULT;
245  }
246 
247  switch (cmd) {
248  case WDIOC_GETSUPPORT:
249  uarg.ident = ident;
250  ret = 0;
251  break;
252 
253  case WDIOC_GETSTATUS:
254  case WDIOC_GETBOOTSTATUS:
255  uarg.i = 0;
256  ret = 0;
257  break;
258 
259  case WDIOC_SETOPTIONS:
260  ret = -EINVAL;
261  if (uarg.i & WDIOS_DISABLECARD) {
262  mpcore_wdt_stop(wdt);
263  ret = 0;
264  }
265  if (uarg.i & WDIOS_ENABLECARD) {
266  mpcore_wdt_start(wdt);
267  ret = 0;
268  }
269  break;
270 
271  case WDIOC_KEEPALIVE:
272  mpcore_wdt_keepalive(wdt);
273  ret = 0;
274  break;
275 
276  case WDIOC_SETTIMEOUT:
277  ret = mpcore_wdt_set_heartbeat(uarg.i);
278  if (ret)
279  break;
280 
281  mpcore_wdt_keepalive(wdt);
282  /* Fall */
283  case WDIOC_GETTIMEOUT:
284  uarg.i = mpcore_margin;
285  ret = 0;
286  break;
287 
288  default:
289  return -ENOTTY;
290  }
291 
292  if (ret == 0 && _IOC_DIR(cmd) & _IOC_READ) {
293  ret = copy_to_user((void __user *)arg, &uarg, _IOC_SIZE(cmd));
294  if (ret)
295  ret = -EFAULT;
296  }
297  return ret;
298 }
299 
300 /*
301  * System shutdown handler. Turn off the watchdog if we're
302  * restarting or halting the system.
303  */
304 static void mpcore_wdt_shutdown(struct platform_device *pdev)
305 {
306  struct mpcore_wdt *wdt = platform_get_drvdata(pdev);
307 
309  mpcore_wdt_stop(wdt);
310 }
311 
312 /*
313  * Kernel Interfaces
314  */
315 static const struct file_operations mpcore_wdt_fops = {
316  .owner = THIS_MODULE,
317  .llseek = no_llseek,
318  .write = mpcore_wdt_write,
319  .unlocked_ioctl = mpcore_wdt_ioctl,
320  .open = mpcore_wdt_open,
321  .release = mpcore_wdt_release,
322 };
323 
324 static struct miscdevice mpcore_wdt_miscdev = {
325  .minor = WATCHDOG_MINOR,
326  .name = "watchdog",
327  .fops = &mpcore_wdt_fops,
328 };
329 
330 static int __devinit mpcore_wdt_probe(struct platform_device *pdev)
331 {
332  struct mpcore_wdt *wdt;
333  struct resource *res;
334  int ret;
335 
336  /* We only accept one device, and it must have an id of -1 */
337  if (pdev->id != -1)
338  return -ENODEV;
339 
340  res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
341  if (!res)
342  return -ENODEV;
343 
344  wdt = devm_kzalloc(&pdev->dev, sizeof(struct mpcore_wdt), GFP_KERNEL);
345  if (!wdt)
346  return -ENOMEM;
347 
348  wdt->dev = &pdev->dev;
349  wdt->irq = platform_get_irq(pdev, 0);
350  if (wdt->irq >= 0) {
351  ret = devm_request_irq(wdt->dev, wdt->irq, mpcore_wdt_fire, 0,
352  "mpcore_wdt", wdt);
353  if (ret) {
354  dev_printk(KERN_ERR, wdt->dev,
355  "cannot register IRQ%d for watchdog\n",
356  wdt->irq);
357  return ret;
358  }
359  }
360 
361  wdt->base = devm_ioremap(wdt->dev, res->start, resource_size(res));
362  if (!wdt->base)
363  return -ENOMEM;
364 
365  mpcore_wdt_miscdev.parent = &pdev->dev;
366  ret = misc_register(&mpcore_wdt_miscdev);
367  if (ret) {
368  dev_printk(KERN_ERR, wdt->dev,
369  "cannot register miscdev on minor=%d (err=%d)\n",
370  WATCHDOG_MINOR, ret);
371  return ret;
372  }
373 
374  mpcore_wdt_stop(wdt);
375  platform_set_drvdata(pdev, wdt);
376  mpcore_wdt_pdev = pdev;
377 
378  return 0;
379 }
380 
381 static int __devexit mpcore_wdt_remove(struct platform_device *pdev)
382 {
383  platform_set_drvdata(pdev, NULL);
384 
385  misc_deregister(&mpcore_wdt_miscdev);
386 
387  mpcore_wdt_pdev = NULL;
388 
389  return 0;
390 }
391 
392 #ifdef CONFIG_PM
393 static int mpcore_wdt_suspend(struct platform_device *pdev, pm_message_t msg)
394 {
395  struct mpcore_wdt *wdt = platform_get_drvdata(pdev);
396  mpcore_wdt_stop(wdt); /* Turn the WDT off */
397  return 0;
398 }
399 
400 static int mpcore_wdt_resume(struct platform_device *pdev)
401 {
402  struct mpcore_wdt *wdt = platform_get_drvdata(pdev);
403  /* re-activate timer */
404  if (test_bit(0, &wdt->timer_alive))
405  mpcore_wdt_start(wdt);
406  return 0;
407 }
408 #else
409 #define mpcore_wdt_suspend NULL
410 #define mpcore_wdt_resume NULL
411 #endif
412 
413 /* work with hotplug and coldplug */
414 MODULE_ALIAS("platform:mpcore_wdt");
415 
416 static struct platform_driver mpcore_wdt_driver = {
417  .probe = mpcore_wdt_probe,
418  .remove = __devexit_p(mpcore_wdt_remove),
419  .suspend = mpcore_wdt_suspend,
420  .resume = mpcore_wdt_resume,
421  .shutdown = mpcore_wdt_shutdown,
422  .driver = {
423  .owner = THIS_MODULE,
424  .name = "mpcore_wdt",
425  },
426 };
427 
428 static int __init mpcore_wdt_init(void)
429 {
430  /*
431  * Check that the margin value is within it's range;
432  * if not reset to the default
433  */
434  if (mpcore_wdt_set_heartbeat(mpcore_margin)) {
435  mpcore_wdt_set_heartbeat(TIMER_MARGIN);
436  pr_info("mpcore_margin value must be 0 < mpcore_margin < 65536, using %d\n",
437  TIMER_MARGIN);
438  }
439 
440  pr_info("MPcore Watchdog Timer: 0.1. mpcore_noboot=%d mpcore_margin=%d sec (nowayout= %d)\n",
441  mpcore_noboot, mpcore_margin, nowayout);
442 
443  return platform_driver_register(&mpcore_wdt_driver);
444 }
445 
446 static void __exit mpcore_wdt_exit(void)
447 {
448  platform_driver_unregister(&mpcore_wdt_driver);
449 }
450 
451 module_init(mpcore_wdt_init);
452 module_exit(mpcore_wdt_exit);
453 
454 MODULE_AUTHOR("ARM Limited");
455 MODULE_DESCRIPTION("MPcore Watchdog Device Driver");
456 MODULE_LICENSE("GPL");