Linux Kernel  3.7.1
 All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros Groups Pages
bfin_wdt.c
Go to the documentation of this file.
1 /*
2  * Blackfin On-Chip Watchdog Driver
3  *
4  * Originally based on softdog.c
5  * Copyright 2006-2010 Analog Devices Inc.
6  * Copyright 2006-2007 Michele d'Amico
7  * Copyright 1996 Alan Cox <[email protected]>
8  *
9  * Enter bugs at http://blackfin.uclinux.org/
10  *
11  * Licensed under the GPL-2 or later.
12  */
13 
14 #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
15 
16 #include <linux/platform_device.h>
17 #include <linux/module.h>
18 #include <linux/moduleparam.h>
19 #include <linux/types.h>
20 #include <linux/timer.h>
21 #include <linux/miscdevice.h>
22 #include <linux/watchdog.h>
23 #include <linux/fs.h>
24 #include <linux/init.h>
25 #include <linux/interrupt.h>
26 #include <linux/uaccess.h>
27 #include <asm/blackfin.h>
28 #include <asm/bfin_watchdog.h>
29 
30 #define stamp(fmt, args...) \
31  pr_debug("%s:%i: " fmt "\n", __func__, __LINE__, ## args)
32 #define stampit() stamp("here i am")
33 
34 #define WATCHDOG_NAME "bfin-wdt"
35 
36 /* The BF561 has two watchdogs (one per core), but since Linux
37  * only runs on core A, we'll just work with that one.
38  */
39 #ifdef BF561_FAMILY
40 # define bfin_read_WDOG_CTL() bfin_read_WDOGA_CTL()
41 # define bfin_read_WDOG_CNT() bfin_read_WDOGA_CNT()
42 # define bfin_read_WDOG_STAT() bfin_read_WDOGA_STAT()
43 # define bfin_write_WDOG_CTL(x) bfin_write_WDOGA_CTL(x)
44 # define bfin_write_WDOG_CNT(x) bfin_write_WDOGA_CNT(x)
45 # define bfin_write_WDOG_STAT(x) bfin_write_WDOGA_STAT(x)
46 #endif
47 
48 /* some defaults */
49 #define WATCHDOG_TIMEOUT 20
50 
51 static unsigned int timeout = WATCHDOG_TIMEOUT;
52 static bool nowayout = WATCHDOG_NOWAYOUT;
53 static const struct watchdog_info bfin_wdt_info;
54 static unsigned long open_check;
55 static char expect_close;
56 static DEFINE_SPINLOCK(bfin_wdt_spinlock);
57 
63 static int bfin_wdt_keepalive(void)
64 {
65  stampit();
67  return 0;
68 }
69 
75 static int bfin_wdt_stop(void)
76 {
77  stampit();
79  return 0;
80 }
81 
88 static int bfin_wdt_start(void)
89 {
90  stampit();
92  return 0;
93 }
94 
100 static int bfin_wdt_running(void)
101 {
102  stampit();
103  return ((bfin_read_WDOG_CTL() & WDEN_MASK) != WDEN_DISABLE);
104 }
105 
113 static int bfin_wdt_set_timeout(unsigned long t)
114 {
115  u32 cnt, max_t, sclk;
116  unsigned long flags;
117 
118  sclk = get_sclk();
119  max_t = -1 / sclk;
120  cnt = t * sclk;
121  stamp("maxtimeout=%us newtimeout=%lus (cnt=%#x)", max_t, t, cnt);
122 
123  if (t > max_t) {
124  pr_warn("timeout value is too large\n");
125  return -EINVAL;
126  }
127 
128  spin_lock_irqsave(&bfin_wdt_spinlock, flags);
129  {
130  int run = bfin_wdt_running();
131  bfin_wdt_stop();
132  bfin_write_WDOG_CNT(cnt);
133  if (run)
134  bfin_wdt_start();
135  }
136  spin_unlock_irqrestore(&bfin_wdt_spinlock, flags);
137 
138  timeout = t;
139 
140  return 0;
141 }
142 
150 static int bfin_wdt_open(struct inode *inode, struct file *file)
151 {
152  stampit();
153 
154  if (test_and_set_bit(0, &open_check))
155  return -EBUSY;
156 
157  if (nowayout)
158  __module_get(THIS_MODULE);
159 
160  bfin_wdt_keepalive();
161  bfin_wdt_start();
162 
163  return nonseekable_open(inode, file);
164 }
165 
173 static int bfin_wdt_release(struct inode *inode, struct file *file)
174 {
175  stampit();
176 
177  if (expect_close == 42)
178  bfin_wdt_stop();
179  else {
180  pr_crit("Unexpected close, not stopping watchdog!\n");
181  bfin_wdt_keepalive();
182  }
183  expect_close = 0;
184  clear_bit(0, &open_check);
185  return 0;
186 }
187 
197 static ssize_t bfin_wdt_write(struct file *file, const char __user *data,
198  size_t len, loff_t *ppos)
199 {
200  stampit();
201 
202  if (len) {
203  if (!nowayout) {
204  size_t i;
205 
206  /* In case it was set long ago */
207  expect_close = 0;
208 
209  for (i = 0; i != len; i++) {
210  char c;
211  if (get_user(c, data + i))
212  return -EFAULT;
213  if (c == 'V')
214  expect_close = 42;
215  }
216  }
217  bfin_wdt_keepalive();
218  }
219 
220  return len;
221 }
222 
232 static long bfin_wdt_ioctl(struct file *file,
233  unsigned int cmd, unsigned long arg)
234 {
235  void __user *argp = (void __user *)arg;
236  int __user *p = argp;
237 
238  stampit();
239 
240  switch (cmd) {
241  case WDIOC_GETSUPPORT:
242  if (copy_to_user(argp, &bfin_wdt_info, sizeof(bfin_wdt_info)))
243  return -EFAULT;
244  else
245  return 0;
246  case WDIOC_GETSTATUS:
247  case WDIOC_GETBOOTSTATUS:
248  return put_user(!!(_bfin_swrst & SWRST_RESET_WDOG), p);
249  case WDIOC_SETOPTIONS: {
250  unsigned long flags;
251  int options, ret = -EINVAL;
252 
253  if (get_user(options, p))
254  return -EFAULT;
255 
256  spin_lock_irqsave(&bfin_wdt_spinlock, flags);
257  if (options & WDIOS_DISABLECARD) {
258  bfin_wdt_stop();
259  ret = 0;
260  }
261  if (options & WDIOS_ENABLECARD) {
262  bfin_wdt_start();
263  ret = 0;
264  }
265  spin_unlock_irqrestore(&bfin_wdt_spinlock, flags);
266  return ret;
267  }
268  case WDIOC_KEEPALIVE:
269  bfin_wdt_keepalive();
270  return 0;
271  case WDIOC_SETTIMEOUT: {
272  int new_timeout;
273 
274  if (get_user(new_timeout, p))
275  return -EFAULT;
276  if (bfin_wdt_set_timeout(new_timeout))
277  return -EINVAL;
278  }
279  /* Fall */
280  case WDIOC_GETTIMEOUT:
281  return put_user(timeout, p);
282  default:
283  return -ENOTTY;
284  }
285 }
286 
287 #ifdef CONFIG_PM
288 static int state_before_suspend;
289 
299 static int bfin_wdt_suspend(struct platform_device *pdev, pm_message_t state)
300 {
301  stampit();
302 
303  state_before_suspend = bfin_wdt_running();
304  bfin_wdt_stop();
305 
306  return 0;
307 }
308 
315 static int bfin_wdt_resume(struct platform_device *pdev)
316 {
317  stampit();
318 
319  if (state_before_suspend) {
320  bfin_wdt_set_timeout(timeout);
321  bfin_wdt_start();
322  }
323 
324  return 0;
325 }
326 #else
327 # define bfin_wdt_suspend NULL
328 # define bfin_wdt_resume NULL
329 #endif
330 
331 static const struct file_operations bfin_wdt_fops = {
332  .owner = THIS_MODULE,
333  .llseek = no_llseek,
334  .write = bfin_wdt_write,
335  .unlocked_ioctl = bfin_wdt_ioctl,
336  .open = bfin_wdt_open,
337  .release = bfin_wdt_release,
338 };
339 
340 static struct miscdevice bfin_wdt_miscdev = {
341  .minor = WATCHDOG_MINOR,
342  .name = "watchdog",
343  .fops = &bfin_wdt_fops,
344 };
345 
346 static const struct watchdog_info bfin_wdt_info = {
347  .identity = "Blackfin Watchdog",
348  .options = WDIOF_SETTIMEOUT |
351 };
352 
359 static int __devinit bfin_wdt_probe(struct platform_device *pdev)
360 {
361  int ret;
362 
363  ret = misc_register(&bfin_wdt_miscdev);
364  if (ret) {
365  pr_err("cannot register miscdev on minor=%d (err=%d)\n",
366  WATCHDOG_MINOR, ret);
367  return ret;
368  }
369 
370  pr_info("initialized: timeout=%d sec (nowayout=%d)\n",
371  timeout, nowayout);
372 
373  return 0;
374 }
375 
382 static int __devexit bfin_wdt_remove(struct platform_device *pdev)
383 {
384  misc_deregister(&bfin_wdt_miscdev);
385  return 0;
386 }
387 
393 static void bfin_wdt_shutdown(struct platform_device *pdev)
394 {
395  stampit();
396 
397  bfin_wdt_stop();
398 }
399 
400 static struct platform_device *bfin_wdt_device;
401 
402 static struct platform_driver bfin_wdt_driver = {
403  .probe = bfin_wdt_probe,
404  .remove = __devexit_p(bfin_wdt_remove),
405  .shutdown = bfin_wdt_shutdown,
406  .suspend = bfin_wdt_suspend,
407  .resume = bfin_wdt_resume,
408  .driver = {
409  .name = WATCHDOG_NAME,
410  .owner = THIS_MODULE,
411  },
412 };
413 
420 static int __init bfin_wdt_init(void)
421 {
422  int ret;
423 
424  stampit();
425 
426  /* Check that the timeout value is within range */
427  if (bfin_wdt_set_timeout(timeout))
428  return -EINVAL;
429 
430  /* Since this is an on-chip device and needs no board-specific
431  * resources, we'll handle all the platform device stuff here.
432  */
433  ret = platform_driver_register(&bfin_wdt_driver);
434  if (ret) {
435  pr_err("unable to register driver\n");
436  return ret;
437  }
438 
439  bfin_wdt_device = platform_device_register_simple(WATCHDOG_NAME,
440  -1, NULL, 0);
441  if (IS_ERR(bfin_wdt_device)) {
442  pr_err("unable to register device\n");
443  platform_driver_unregister(&bfin_wdt_driver);
444  return PTR_ERR(bfin_wdt_device);
445  }
446 
447  return 0;
448 }
449 
456 static void __exit bfin_wdt_exit(void)
457 {
458  platform_device_unregister(bfin_wdt_device);
459  platform_driver_unregister(&bfin_wdt_driver);
460 }
461 
462 module_init(bfin_wdt_init);
463 module_exit(bfin_wdt_exit);
464 
465 MODULE_AUTHOR("Michele d'Amico, Mike Frysinger <[email protected]>");
466 MODULE_DESCRIPTION("Blackfin Watchdog Device Driver");
467 MODULE_LICENSE("GPL");
469 
470 module_param(timeout, uint, 0);
471 MODULE_PARM_DESC(timeout,
472  "Watchdog timeout in seconds. (1<=timeout<=((2^32)/SCLK), default="
474 
475 module_param(nowayout, bool, 0);
476 MODULE_PARM_DESC(nowayout,
477  "Watchdog cannot be stopped once started (default="