Linux Kernel  3.7.1
 All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros Groups Pages
w83697ug_wdt.c
Go to the documentation of this file.
1 /*
2  * w83697ug/uf WDT driver
3  *
4  * (c) Copyright 2008 Flemming Fransen <[email protected]>
5  * reused original code to support w83697ug/uf.
6  *
7  * Based on w83627hf_wdt.c which is based on advantechwdt.c
8  * which is based on wdt.c.
9  * Original copyright messages:
10  *
11  * (c) Copyright 2007 Vlad Drukker <[email protected]>
12  * added support for W83627THF.
13  *
14  * (c) Copyright 2003 Pádraig Brady <[email protected]>
15  *
16  * (c) Copyright 2000-2001 Marek Michalkiewicz <[email protected]>
17  *
18  * (c) Copyright 1996 Alan Cox <[email protected]>, All Rights Reserved.
19  * http://www.redhat.com
20  *
21  * This program is free software; you can redistribute it and/or
22  * modify it under the terms of the GNU General Public License
23  * as published by the Free Software Foundation; either version
24  * 2 of the License, or (at your option) any later version.
25  *
26  * Neither Alan Cox nor CymruNet Ltd. admit liability nor provide
27  * warranty for any of this software. This material is provided
28  * "AS-IS" and at no charge.
29  *
30  * (c) Copyright 1995 Alan Cox <[email protected]>
31  */
32 
33 #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
34 
35 #include <linux/module.h>
36 #include <linux/moduleparam.h>
37 #include <linux/types.h>
38 #include <linux/miscdevice.h>
39 #include <linux/watchdog.h>
40 #include <linux/fs.h>
41 #include <linux/ioport.h>
42 #include <linux/notifier.h>
43 #include <linux/reboot.h>
44 #include <linux/init.h>
45 #include <linux/spinlock.h>
46 #include <linux/io.h>
47 #include <linux/uaccess.h>
48 
49 
50 #define WATCHDOG_NAME "w83697ug/uf WDT"
51 #define WATCHDOG_TIMEOUT 60 /* 60 sec default timeout */
52 
53 static unsigned long wdt_is_open;
54 static char expect_close;
55 static DEFINE_SPINLOCK(io_lock);
56 
57 static int wdt_io = 0x2e;
58 module_param(wdt_io, int, 0);
59 MODULE_PARM_DESC(wdt_io, "w83697ug/uf WDT io port (default 0x2e)");
60 
61 static int timeout = WATCHDOG_TIMEOUT; /* in seconds */
62 module_param(timeout, int, 0);
63 MODULE_PARM_DESC(timeout,
64  "Watchdog timeout in seconds. 1<= timeout <=255 (default="
66 
67 static bool nowayout = WATCHDOG_NOWAYOUT;
68 module_param(nowayout, bool, 0);
69 MODULE_PARM_DESC(nowayout,
70  "Watchdog cannot be stopped once started (default="
72 
73 /*
74  * Kernel methods.
75  */
76 
77 #define WDT_EFER (wdt_io+0) /* Extended Function Enable Registers */
78 #define WDT_EFIR (wdt_io+0) /* Extended Function Index Register
79  (same as EFER) */
80 #define WDT_EFDR (WDT_EFIR+1) /* Extended Function Data Register */
81 
82 static int w83697ug_select_wd_register(void)
83 {
84  unsigned char c;
85  unsigned char version;
86 
87  outb_p(0x87, WDT_EFER); /* Enter extended function mode */
88  outb_p(0x87, WDT_EFER); /* Again according to manual */
89 
90  outb(0x20, WDT_EFER); /* check chip version */
91  version = inb(WDT_EFDR);
92 
93  if (version == 0x68) { /* W83697UG */
94  pr_info("Watchdog chip version 0x%02x = W83697UG/UF found at 0x%04x\n",
95  version, wdt_io);
96 
97  outb_p(0x2b, WDT_EFER);
98  c = inb_p(WDT_EFDR); /* select WDT0 */
99  c &= ~0x04;
100  outb_p(0x2b, WDT_EFER);
101  outb_p(c, WDT_EFDR); /* set pin118 to WDT0 */
102 
103  } else {
104  pr_err("No W83697UG/UF could be found\n");
105  return -ENODEV;
106  }
107 
108  outb_p(0x07, WDT_EFER); /* point to logical device number reg */
109  outb_p(0x08, WDT_EFDR); /* select logical device 8 (GPIO2) */
110  outb_p(0x30, WDT_EFER); /* select CR30 */
111  c = inb_p(WDT_EFDR);
112  outb_p(c | 0x01, WDT_EFDR); /* set bit 0 to activate GPIO2 */
113 
114  return 0;
115 }
116 
117 static void w83697ug_unselect_wd_register(void)
118 {
119  outb_p(0xAA, WDT_EFER); /* Leave extended function mode */
120 }
121 
122 static int w83697ug_init(void)
123 {
124  int ret;
125  unsigned char t;
126 
127  ret = w83697ug_select_wd_register();
128  if (ret != 0)
129  return ret;
130 
131  outb_p(0xF6, WDT_EFER); /* Select CRF6 */
132  t = inb_p(WDT_EFDR); /* read CRF6 */
133  if (t != 0) {
134  pr_info("Watchdog already running. Resetting timeout to %d sec\n",
135  timeout);
136  outb_p(timeout, WDT_EFDR); /* Write back to CRF6 */
137  }
138  outb_p(0xF5, WDT_EFER); /* Select CRF5 */
139  t = inb_p(WDT_EFDR); /* read CRF5 */
140  t &= ~0x0C; /* set second mode &
141  disable keyboard turning off watchdog */
142  outb_p(t, WDT_EFDR); /* Write back to CRF5 */
143 
144  w83697ug_unselect_wd_register();
145  return 0;
146 }
147 
148 static void wdt_ctrl(int timeout)
149 {
150  spin_lock(&io_lock);
151 
152  if (w83697ug_select_wd_register() < 0) {
153  spin_unlock(&io_lock);
154  return;
155  }
156 
157  outb_p(0xF4, WDT_EFER); /* Select CRF4 */
158  outb_p(timeout, WDT_EFDR); /* Write Timeout counter to CRF4 */
159 
160  w83697ug_unselect_wd_register();
161 
162  spin_unlock(&io_lock);
163 }
164 
165 static int wdt_ping(void)
166 {
167  wdt_ctrl(timeout);
168  return 0;
169 }
170 
171 static int wdt_disable(void)
172 {
173  wdt_ctrl(0);
174  return 0;
175 }
176 
177 static int wdt_set_heartbeat(int t)
178 {
179  if (t < 1 || t > 255)
180  return -EINVAL;
181 
182  timeout = t;
183  return 0;
184 }
185 
186 static ssize_t wdt_write(struct file *file, const char __user *buf,
187  size_t count, loff_t *ppos)
188 {
189  if (count) {
190  if (!nowayout) {
191  size_t i;
192 
193  expect_close = 0;
194 
195  for (i = 0; i != count; i++) {
196  char c;
197  if (get_user(c, buf + i))
198  return -EFAULT;
199  if (c == 'V')
200  expect_close = 42;
201  }
202  }
203  wdt_ping();
204  }
205  return count;
206 }
207 
208 static long wdt_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
209 {
210  void __user *argp = (void __user *)arg;
211  int __user *p = argp;
212  int new_timeout;
213  static const struct watchdog_info ident = {
214  .options = WDIOF_KEEPALIVEPING |
217  .firmware_version = 1,
218  .identity = "W83697UG WDT",
219  };
220 
221  switch (cmd) {
222  case WDIOC_GETSUPPORT:
223  if (copy_to_user(argp, &ident, sizeof(ident)))
224  return -EFAULT;
225  break;
226 
227  case WDIOC_GETSTATUS:
228  case WDIOC_GETBOOTSTATUS:
229  return put_user(0, p);
230 
231  case WDIOC_SETOPTIONS:
232  {
233  int options, retval = -EINVAL;
234 
235  if (get_user(options, p))
236  return -EFAULT;
237 
238  if (options & WDIOS_DISABLECARD) {
239  wdt_disable();
240  retval = 0;
241  }
242 
243  if (options & WDIOS_ENABLECARD) {
244  wdt_ping();
245  retval = 0;
246  }
247 
248  return retval;
249  }
250 
251  case WDIOC_KEEPALIVE:
252  wdt_ping();
253  break;
254 
255  case WDIOC_SETTIMEOUT:
256  if (get_user(new_timeout, p))
257  return -EFAULT;
258  if (wdt_set_heartbeat(new_timeout))
259  return -EINVAL;
260  wdt_ping();
261  /* Fall */
262 
263  case WDIOC_GETTIMEOUT:
264  return put_user(timeout, p);
265 
266  default:
267  return -ENOTTY;
268  }
269  return 0;
270 }
271 
272 static int wdt_open(struct inode *inode, struct file *file)
273 {
274  if (test_and_set_bit(0, &wdt_is_open))
275  return -EBUSY;
276  /*
277  * Activate
278  */
279 
280  wdt_ping();
281  return nonseekable_open(inode, file);
282 }
283 
284 static int wdt_close(struct inode *inode, struct file *file)
285 {
286  if (expect_close == 42)
287  wdt_disable();
288  else {
289  pr_crit("Unexpected close, not stopping watchdog!\n");
290  wdt_ping();
291  }
292  expect_close = 0;
293  clear_bit(0, &wdt_is_open);
294  return 0;
295 }
296 
297 /*
298  * Notifier for system down
299  */
300 
301 static int wdt_notify_sys(struct notifier_block *this, unsigned long code,
302  void *unused)
303 {
304  if (code == SYS_DOWN || code == SYS_HALT)
305  wdt_disable(); /* Turn the WDT off */
306 
307  return NOTIFY_DONE;
308 }
309 
310 /*
311  * Kernel Interfaces
312  */
313 
314 static const struct file_operations wdt_fops = {
315  .owner = THIS_MODULE,
316  .llseek = no_llseek,
317  .write = wdt_write,
318  .unlocked_ioctl = wdt_ioctl,
319  .open = wdt_open,
320  .release = wdt_close,
321 };
322 
323 static struct miscdevice wdt_miscdev = {
324  .minor = WATCHDOG_MINOR,
325  .name = "watchdog",
326  .fops = &wdt_fops,
327 };
328 
329 /*
330  * The WDT needs to learn about soft shutdowns in order to
331  * turn the timebomb registers off.
332  */
333 
334 static struct notifier_block wdt_notifier = {
335  .notifier_call = wdt_notify_sys,
336 };
337 
338 static int __init wdt_init(void)
339 {
340  int ret;
341 
342  pr_info("WDT driver for the Winbond(TM) W83697UG/UF Super I/O chip initialising\n");
343 
344  if (wdt_set_heartbeat(timeout)) {
345  wdt_set_heartbeat(WATCHDOG_TIMEOUT);
346  pr_info("timeout value must be 1<=timeout<=255, using %d\n",
348  }
349 
350  if (!request_region(wdt_io, 1, WATCHDOG_NAME)) {
351  pr_err("I/O address 0x%04x already in use\n", wdt_io);
352  ret = -EIO;
353  goto out;
354  }
355 
356  ret = w83697ug_init();
357  if (ret != 0)
358  goto unreg_regions;
359 
360  ret = register_reboot_notifier(&wdt_notifier);
361  if (ret != 0) {
362  pr_err("cannot register reboot notifier (err=%d)\n", ret);
363  goto unreg_regions;
364  }
365 
366  ret = misc_register(&wdt_miscdev);
367  if (ret != 0) {
368  pr_err("cannot register miscdev on minor=%d (err=%d)\n",
369  WATCHDOG_MINOR, ret);
370  goto unreg_reboot;
371  }
372 
373  pr_info("initialized. timeout=%d sec (nowayout=%d)\n",
374  timeout, nowayout);
375 
376 out:
377  return ret;
378 unreg_reboot:
379  unregister_reboot_notifier(&wdt_notifier);
380 unreg_regions:
381  release_region(wdt_io, 1);
382  goto out;
383 }
384 
385 static void __exit wdt_exit(void)
386 {
387  misc_deregister(&wdt_miscdev);
388  unregister_reboot_notifier(&wdt_notifier);
389  release_region(wdt_io, 1);
390 }
391 
393 module_exit(wdt_exit);
394 
395 MODULE_LICENSE("GPL");
396 MODULE_AUTHOR("Flemming Frandsen <[email protected]>");
397 MODULE_DESCRIPTION("w83697ug/uf WDT driver");