Linux Kernel  3.7.1
 All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros Groups Pages
wafer5823wdt.c
Go to the documentation of this file.
1 /*
2  * ICP Wafer 5823 Single Board Computer WDT driver
3  * http://www.icpamerica.com/wafer_5823.php
4  * May also work on other similar models
5  *
6  * (c) Copyright 2002 Justin Cormack <[email protected]>
7  *
8  * Release 0.02
9  *
10  * Based on advantechwdt.c which is based on wdt.c.
11  * Original copyright messages:
12  *
13  * (c) Copyright 1996-1997 Alan Cox <[email protected]>,
14  * All Rights Reserved.
15  *
16  * This program is free software; you can redistribute it and/or
17  * modify it under the terms of the GNU General Public License
18  * as published by the Free Software Foundation; either version
19  * 2 of the License, or (at your option) any later version.
20  *
21  * Neither Alan Cox nor CymruNet Ltd. admit liability nor provide
22  * warranty for any of this software. This material is provided
23  * "AS-IS" and at no charge.
24  *
25  * (c) Copyright 1995 Alan Cox <[email protected]>
26  *
27  */
28 
29 #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
30 
31 #include <linux/module.h>
32 #include <linux/moduleparam.h>
33 #include <linux/miscdevice.h>
34 #include <linux/watchdog.h>
35 #include <linux/fs.h>
36 #include <linux/ioport.h>
37 #include <linux/notifier.h>
38 #include <linux/reboot.h>
39 #include <linux/init.h>
40 #include <linux/spinlock.h>
41 #include <linux/io.h>
42 #include <linux/uaccess.h>
43 
44 #define WATCHDOG_NAME "Wafer 5823 WDT"
45 #define PFX WATCHDOG_NAME ": "
46 #define WD_TIMO 60 /* 60 sec default timeout */
47 
48 static unsigned long wafwdt_is_open;
49 static char expect_close;
50 static DEFINE_SPINLOCK(wafwdt_lock);
51 
52 /*
53  * You must set these - there is no sane way to probe for this board.
54  *
55  * To enable, write the timeout value in seconds (1 to 255) to I/O
56  * port WDT_START, then read the port to start the watchdog. To pat
57  * the dog, read port WDT_STOP to stop the timer, then read WDT_START
58  * to restart it again.
59  */
60 
61 static int wdt_stop = 0x843;
62 static int wdt_start = 0x443;
63 
64 static int timeout = WD_TIMO; /* in seconds */
65 module_param(timeout, int, 0);
66 MODULE_PARM_DESC(timeout,
67  "Watchdog timeout in seconds. 1 <= timeout <= 255, default="
69 
70 static bool nowayout = WATCHDOG_NOWAYOUT;
71 module_param(nowayout, bool, 0);
72 MODULE_PARM_DESC(nowayout,
73  "Watchdog cannot be stopped once started (default="
75 
76 static void wafwdt_ping(void)
77 {
78  /* pat watchdog */
79  spin_lock(&wafwdt_lock);
80  inb_p(wdt_stop);
81  inb_p(wdt_start);
82  spin_unlock(&wafwdt_lock);
83 }
84 
85 static void wafwdt_start(void)
86 {
87  /* start up watchdog */
88  outb_p(timeout, wdt_start);
89  inb_p(wdt_start);
90 }
91 
92 static void wafwdt_stop(void)
93 {
94  /* stop watchdog */
95  inb_p(wdt_stop);
96 }
97 
98 static ssize_t wafwdt_write(struct file *file, const char __user *buf,
99  size_t count, loff_t *ppos)
100 {
101  /* See if we got the magic character 'V' and reload the timer */
102  if (count) {
103  if (!nowayout) {
104  size_t i;
105 
106  /* In case it was set long ago */
107  expect_close = 0;
108 
109  /* scan to see whether or not we got the magic
110  character */
111  for (i = 0; i != count; i++) {
112  char c;
113  if (get_user(c, buf + i))
114  return -EFAULT;
115  if (c == 'V')
116  expect_close = 42;
117  }
118  }
119  /* Well, anyhow someone wrote to us, we should
120  return that favour */
121  wafwdt_ping();
122  }
123  return count;
124 }
125 
126 static long wafwdt_ioctl(struct file *file, unsigned int cmd,
127  unsigned long arg)
128 {
129  int new_timeout;
130  void __user *argp = (void __user *)arg;
131  int __user *p = argp;
132  static const struct watchdog_info ident = {
135  .firmware_version = 1,
136  .identity = "Wafer 5823 WDT",
137  };
138 
139  switch (cmd) {
140  case WDIOC_GETSUPPORT:
141  if (copy_to_user(argp, &ident, sizeof(ident)))
142  return -EFAULT;
143  break;
144 
145  case WDIOC_GETSTATUS:
146  case WDIOC_GETBOOTSTATUS:
147  return put_user(0, p);
148 
149  case WDIOC_SETOPTIONS:
150  {
151  int options, retval = -EINVAL;
152 
153  if (get_user(options, p))
154  return -EFAULT;
155 
156  if (options & WDIOS_DISABLECARD) {
157  wafwdt_stop();
158  retval = 0;
159  }
160 
161  if (options & WDIOS_ENABLECARD) {
162  wafwdt_start();
163  retval = 0;
164  }
165 
166  return retval;
167  }
168 
169  case WDIOC_KEEPALIVE:
170  wafwdt_ping();
171  break;
172 
173  case WDIOC_SETTIMEOUT:
174  if (get_user(new_timeout, p))
175  return -EFAULT;
176  if ((new_timeout < 1) || (new_timeout > 255))
177  return -EINVAL;
178  timeout = new_timeout;
179  wafwdt_stop();
180  wafwdt_start();
181  /* Fall */
182  case WDIOC_GETTIMEOUT:
183  return put_user(timeout, p);
184 
185  default:
186  return -ENOTTY;
187  }
188  return 0;
189 }
190 
191 static int wafwdt_open(struct inode *inode, struct file *file)
192 {
193  if (test_and_set_bit(0, &wafwdt_is_open))
194  return -EBUSY;
195 
196  /*
197  * Activate
198  */
199  wafwdt_start();
200  return nonseekable_open(inode, file);
201 }
202 
203 static int wafwdt_close(struct inode *inode, struct file *file)
204 {
205  if (expect_close == 42)
206  wafwdt_stop();
207  else {
208  pr_crit("WDT device closed unexpectedly. WDT will not stop!\n");
209  wafwdt_ping();
210  }
211  clear_bit(0, &wafwdt_is_open);
212  expect_close = 0;
213  return 0;
214 }
215 
216 /*
217  * Notifier for system down
218  */
219 
220 static int wafwdt_notify_sys(struct notifier_block *this, unsigned long code,
221  void *unused)
222 {
223  if (code == SYS_DOWN || code == SYS_HALT)
224  wafwdt_stop();
225  return NOTIFY_DONE;
226 }
227 
228 /*
229  * Kernel Interfaces
230  */
231 
232 static const struct file_operations wafwdt_fops = {
233  .owner = THIS_MODULE,
234  .llseek = no_llseek,
235  .write = wafwdt_write,
236  .unlocked_ioctl = wafwdt_ioctl,
237  .open = wafwdt_open,
238  .release = wafwdt_close,
239 };
240 
241 static struct miscdevice wafwdt_miscdev = {
242  .minor = WATCHDOG_MINOR,
243  .name = "watchdog",
244  .fops = &wafwdt_fops,
245 };
246 
247 /*
248  * The WDT needs to learn about soft shutdowns in order to
249  * turn the timebomb registers off.
250  */
251 
252 static struct notifier_block wafwdt_notifier = {
253  .notifier_call = wafwdt_notify_sys,
254 };
255 
256 static int __init wafwdt_init(void)
257 {
258  int ret;
259 
260  pr_info("WDT driver for Wafer 5823 single board computer initialising\n");
261 
262  if (timeout < 1 || timeout > 255) {
263  timeout = WD_TIMO;
264  pr_info("timeout value must be 1 <= x <= 255, using %d\n",
265  timeout);
266  }
267 
268  if (wdt_stop != wdt_start) {
269  if (!request_region(wdt_stop, 1, "Wafer 5823 WDT")) {
270  pr_err("I/O address 0x%04x already in use\n", wdt_stop);
271  ret = -EIO;
272  goto error;
273  }
274  }
275 
276  if (!request_region(wdt_start, 1, "Wafer 5823 WDT")) {
277  pr_err("I/O address 0x%04x already in use\n", wdt_start);
278  ret = -EIO;
279  goto error2;
280  }
281 
282  ret = register_reboot_notifier(&wafwdt_notifier);
283  if (ret != 0) {
284  pr_err("cannot register reboot notifier (err=%d)\n", ret);
285  goto error3;
286  }
287 
288  ret = misc_register(&wafwdt_miscdev);
289  if (ret != 0) {
290  pr_err("cannot register miscdev on minor=%d (err=%d)\n",
291  WATCHDOG_MINOR, ret);
292  goto error4;
293  }
294 
295  pr_info("initialized. timeout=%d sec (nowayout=%d)\n",
296  timeout, nowayout);
297 
298  return ret;
299 error4:
300  unregister_reboot_notifier(&wafwdt_notifier);
301 error3:
302  release_region(wdt_start, 1);
303 error2:
304  if (wdt_stop != wdt_start)
305  release_region(wdt_stop, 1);
306 error:
307  return ret;
308 }
309 
310 static void __exit wafwdt_exit(void)
311 {
312  misc_deregister(&wafwdt_miscdev);
313  unregister_reboot_notifier(&wafwdt_notifier);
314  if (wdt_stop != wdt_start)
315  release_region(wdt_stop, 1);
316  release_region(wdt_start, 1);
317 }
318 
319 module_init(wafwdt_init);
320 module_exit(wafwdt_exit);
321 
322 MODULE_AUTHOR("Justin Cormack");
323 MODULE_DESCRIPTION("ICP Wafer 5823 Single Board Computer WDT driver");
324 MODULE_LICENSE("GPL");
326 
327 /* end of wafer5823wdt.c */