aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/char/watchdog
diff options
context:
space:
mode:
authorMarcus Junker <junker@anduras.de>2006-08-24 11:11:50 -0400
committerWim Van Sebroeck <wim@iguana.be>2006-10-04 16:41:07 -0400
commitf9a8c8913a95aed91bfa81f7d4043c6430423bf8 (patch)
tree273e9760e548ecb9ceb178d9936d45fc3ae8193c /drivers/char/watchdog
parentaa1fd4d7c3b131026bf156da40fdf94bcbd705aa (diff)
[WATCHDOG] w83697hf WDT driver
New watchdog driver for the Winbond W83697HF chipset. Signed-off-by: Marcus Junker <junker@anduras.de> Signed-off-by: Wim Van Sebroeck <wim@iguana.be>
Diffstat (limited to 'drivers/char/watchdog')
-rw-r--r--drivers/char/watchdog/Kconfig13
-rw-r--r--drivers/char/watchdog/Makefile1
-rw-r--r--drivers/char/watchdog/w83697hf_wdt.c367
3 files changed, 381 insertions, 0 deletions
diff --git a/drivers/char/watchdog/Kconfig b/drivers/char/watchdog/Kconfig
index 8d2ebc73c894..3e67e01b0730 100644
--- a/drivers/char/watchdog/Kconfig
+++ b/drivers/char/watchdog/Kconfig
@@ -430,6 +430,19 @@ config W83627HF_WDT
430 430
431 Most people will say N. 431 Most people will say N.
432 432
433config W83697HF_WDT
434 tristate "W83697HF Watchdog Timer"
435 depends on WATCHDOG && X86
436 ---help---
437 This is the driver for the hardware watchdog on the W83697HF chipset
438 This watchdog simply watches your kernel to make sure it doesn't freeze,
439 and if it does, it reboots your computer after a certain amount of time.
440
441 To compile this driver as a module, choose M here: the
442 module will be called w83697hf_wdt.
443
444 Most people will say N.
445
433config W83877F_WDT 446config W83877F_WDT
434 tristate "W83877F (EMACS) Watchdog Timer" 447 tristate "W83877F (EMACS) Watchdog Timer"
435 depends on WATCHDOG && X86 448 depends on WATCHDOG && X86
diff --git a/drivers/char/watchdog/Makefile b/drivers/char/watchdog/Makefile
index 630526f12376..ee3474190e23 100644
--- a/drivers/char/watchdog/Makefile
+++ b/drivers/char/watchdog/Makefile
@@ -55,6 +55,7 @@ obj-$(CONFIG_SBC8360_WDT) += sbc8360.o
55obj-$(CONFIG_CPU5_WDT) += cpu5wdt.o 55obj-$(CONFIG_CPU5_WDT) += cpu5wdt.o
56obj-$(CONFIG_SMSC37B787_WDT) += smsc37b787_wdt.o 56obj-$(CONFIG_SMSC37B787_WDT) += smsc37b787_wdt.o
57obj-$(CONFIG_W83627HF_WDT) += w83627hf_wdt.o 57obj-$(CONFIG_W83627HF_WDT) += w83627hf_wdt.o
58obj-$(CONFIG_W83697HF_WDT) += w83697hf_wdt.o
58obj-$(CONFIG_W83877F_WDT) += w83877f_wdt.o 59obj-$(CONFIG_W83877F_WDT) += w83877f_wdt.o
59obj-$(CONFIG_W83977F_WDT) += w83977f_wdt.o 60obj-$(CONFIG_W83977F_WDT) += w83977f_wdt.o
60obj-$(CONFIG_MACHZ_WDT) += machzwd.o 61obj-$(CONFIG_MACHZ_WDT) += machzwd.o
diff --git a/drivers/char/watchdog/w83697hf_wdt.c b/drivers/char/watchdog/w83697hf_wdt.c
new file mode 100644
index 000000000000..ef6612e1b91f
--- /dev/null
+++ b/drivers/char/watchdog/w83697hf_wdt.c
@@ -0,0 +1,367 @@
1/*
2 * w83697hf WDT driver
3 *
4 * (c) Copyright 2006 Marcus Junker <junker@anduras.de>
5 *
6 * Based on w83627hf_wdt.c advantechwdt.c which is based on wdt.c.
7 * Original copyright messages:
8 *
9 * (c) Copyright 2003 Pádraig Brady <P@draigBrady.com>
10 *
11 * (c) Copyright 2000-2001 Marek Michalkiewicz <marekm@linux.org.pl>
12 *
13 * (c) Copyright 1996 Alan Cox <alan@redhat.com>, All Rights Reserved.
14 * http://www.redhat.com
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 Marcus Junker nor ANDURAS AG admit liability nor provide
22 * warranty for any of this software. This material is provided
23 * "AS-IS" and at no charge.
24 */
25
26#include <linux/module.h>
27#include <linux/moduleparam.h>
28#include <linux/types.h>
29#include <linux/miscdevice.h>
30#include <linux/watchdog.h>
31#include <linux/fs.h>
32#include <linux/ioport.h>
33#include <linux/notifier.h>
34#include <linux/reboot.h>
35#include <linux/init.h>
36
37#include <asm/io.h>
38#include <asm/uaccess.h>
39#include <asm/system.h>
40
41#define WATCHDOG_NAME "w83697hf WDT"
42#define PFX WATCHDOG_NAME ": "
43#define WATCHDOG_TIMEOUT 60 /* 60 sec default timeout */
44
45static unsigned long wdt_is_open;
46static char expect_close;
47
48/* You must set this - there is no sane way to probe for this board. */
49static int wdt_io = 0x2E;
50module_param(wdt_io, int, 0);
51MODULE_PARM_DESC(wdt_io, "w83697hf WDT io port (default 0x2E)");
52
53static int timeout = WATCHDOG_TIMEOUT; /* in seconds */
54module_param(timeout, int, 0);
55MODULE_PARM_DESC(timeout, "Watchdog timeout in seconds. 1<= timeout <=63, default=" __MODULE_STRING(WATCHDOG_TIMEOUT) ".");
56
57static int nowayout = WATCHDOG_NOWAYOUT;
58module_param(nowayout, int, 0);
59MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default=CONFIG_WATCHDOG_NOWAYOUT)");
60
61/*
62 * Kernel methods.
63 */
64
65#define WDT_EFER (wdt_io+0) /* Extended Function Enable Registers */
66#define WDT_EFIR (wdt_io+0) /* Extended Function Index Register (same as EFER) */
67#define WDT_EFDR (WDT_EFIR+1) /* Extended Function Data Register */
68
69static void
70w83697hf_select_wd_register(void)
71{
72 outb_p(0x87, WDT_EFER); /* Enter extended function mode */
73 outb_p(0x87, WDT_EFER); /* Again according to manual */
74
75 outb_p(0x29, WDT_EFER); /* select CR29 */
76 outb_p(0x20, WDT_EFDR); /* select WDTO */
77
78 outb_p(0x07, WDT_EFER); /* point to logical device number reg */
79 outb_p(0x08, WDT_EFDR); /* select logical device 8 (GPIO2) */
80 outb_p(0x30, WDT_EFER); /* select CR30 */
81 outb_p(0x01, WDT_EFDR); /* set bit 0 to activate GPIO2 */
82}
83
84static void
85w83697hf_unselect_wd_register(void)
86{
87 outb_p(0xAA, WDT_EFER); /* Leave extended function mode */
88}
89
90/* tyan motherboards seem to set F5 to 0x4C ?
91 * So explicitly init to appropriate value. */
92static void
93w83697hf_init(void)
94{
95 unsigned char t;
96
97 w83697hf_select_wd_register();
98
99 outb_p(0xF3, WDT_EFER); /* Select CRF3 */
100
101 t=inb_p(WDT_EFDR); /* read CRF6 */
102 if (t != 0) {
103 printk (KERN_INFO PFX "Watchdog already running. Resetting timeout to %d sec\n", timeout);
104 outb_p(timeout, WDT_EFDR); /* Write back to CRF6 */
105 }
106 outb_p(0xF4, WDT_EFER); /* Select CRF4 */
107 t=inb_p(WDT_EFDR); /* read CRF4 */
108 t&=~0x0C; /* set second mode & disable keyboard turning off watchdog */
109 outb_p(t, WDT_EFDR); /* Write back to CRF5 */
110
111 w83697hf_unselect_wd_register();
112}
113
114static void
115wdt_ctrl(int timeout)
116{
117 w83697hf_select_wd_register();
118
119 outb_p(0xF4, WDT_EFER); /* Select CRF4 */
120 outb_p(timeout, WDT_EFDR); /* Write Timeout counter to CRF4 */
121
122 w83697hf_unselect_wd_register();
123}
124
125static int
126wdt_ping(void)
127{
128 wdt_ctrl(timeout);
129 return 0;
130}
131
132static int
133wdt_disable(void)
134{
135 wdt_ctrl(0);
136 return 0;
137}
138
139static int
140wdt_set_heartbeat(int t)
141{
142 if ((t < 1) || (t > 63))
143 return -EINVAL;
144
145 timeout = t;
146 return 0;
147}
148
149static ssize_t
150wdt_write(struct file *file, const char __user *buf, size_t count, loff_t *ppos)
151{
152 if (count) {
153 if (!nowayout) {
154 size_t i;
155
156 expect_close = 0;
157
158 for (i = 0; i != count; i++) {
159 char c;
160 if (get_user(c, buf+i))
161 return -EFAULT;
162 if (c == 'V')
163 expect_close = 42;
164 }
165 }
166 wdt_ping();
167 }
168 return count;
169}
170
171static int
172wdt_ioctl(struct inode *inode, struct file *file, unsigned int cmd,
173 unsigned long arg)
174{
175 void __user *argp = (void __user *)arg;
176 int __user *p = argp;
177 int new_timeout;
178 static struct watchdog_info ident = {
179 .options = WDIOF_KEEPALIVEPING | WDIOF_SETTIMEOUT | WDIOF_MAGICCLOSE,
180 .firmware_version = 1,
181 .identity = "W83697HF WDT",
182 };
183
184 switch (cmd) {
185 case WDIOC_GETSUPPORT:
186 if (copy_to_user(argp, &ident, sizeof(ident)))
187 return -EFAULT;
188 break;
189
190 case WDIOC_GETSTATUS:
191 case WDIOC_GETBOOTSTATUS:
192 return put_user(0, p);
193
194 case WDIOC_KEEPALIVE:
195 wdt_ping();
196 break;
197
198 case WDIOC_SETTIMEOUT:
199 if (get_user(new_timeout, p))
200 return -EFAULT;
201 if (wdt_set_heartbeat(new_timeout))
202 return -EINVAL;
203 wdt_ping();
204 /* Fall */
205
206 case WDIOC_GETTIMEOUT:
207 return put_user(timeout, p);
208
209 case WDIOC_SETOPTIONS:
210 {
211 int options, retval = -EINVAL;
212
213 if (get_user(options, p))
214 return -EFAULT;
215
216 if (options & WDIOS_DISABLECARD) {
217 wdt_disable();
218 retval = 0;
219 }
220
221 if (options & WDIOS_ENABLECARD) {
222 wdt_ping();
223 retval = 0;
224 }
225
226 return retval;
227 }
228
229 default:
230 return -ENOIOCTLCMD;
231 }
232 return 0;
233}
234
235static int
236wdt_open(struct inode *inode, struct file *file)
237{
238 if (test_and_set_bit(0, &wdt_is_open))
239 return -EBUSY;
240 /*
241 * Activate
242 */
243
244 wdt_ping();
245 return nonseekable_open(inode, file);
246}
247
248static int
249wdt_close(struct inode *inode, struct file *file)
250{
251 if (expect_close == 42) {
252 wdt_disable();
253 } else {
254 printk(KERN_CRIT PFX "Unexpected close, not stopping watchdog!\n");
255 wdt_ping();
256 }
257 expect_close = 0;
258 clear_bit(0, &wdt_is_open);
259 return 0;
260}
261
262/*
263 * Notifier for system down
264 */
265
266static int
267wdt_notify_sys(struct notifier_block *this, unsigned long code,
268 void *unused)
269{
270 if (code == SYS_DOWN || code == SYS_HALT) {
271 /* Turn the WDT off */
272 wdt_disable();
273 }
274 return NOTIFY_DONE;
275}
276
277/*
278 * Kernel Interfaces
279 */
280
281static struct file_operations wdt_fops = {
282 .owner = THIS_MODULE,
283 .llseek = no_llseek,
284 .write = wdt_write,
285 .ioctl = wdt_ioctl,
286 .open = wdt_open,
287 .release = wdt_close,
288};
289
290static struct miscdevice wdt_miscdev = {
291 .minor = WATCHDOG_MINOR,
292 .name = "watchdog",
293 .fops = &wdt_fops,
294};
295
296/*
297 * The WDT needs to learn about soft shutdowns in order to
298 * turn the timebomb registers off.
299 */
300
301static struct notifier_block wdt_notifier = {
302 .notifier_call = wdt_notify_sys,
303};
304
305static int __init
306wdt_init(void)
307{
308 int ret;
309
310 printk(KERN_INFO "WDT driver for the Winbond(TM) W83697HF Super I/O chip initialising.\n");
311
312 if (wdt_set_heartbeat(timeout)) {
313 wdt_set_heartbeat(WATCHDOG_TIMEOUT);
314 printk (KERN_INFO PFX "timeout value must be 1<=timeout<=63, using %d\n",
315 WATCHDOG_TIMEOUT);
316 }
317
318 if (!request_region(wdt_io, 1, WATCHDOG_NAME)) {
319 printk (KERN_ERR PFX "I/O address 0x%04x already in use\n",
320 wdt_io);
321 ret = -EIO;
322 goto out;
323 }
324
325 w83697hf_init();
326
327 ret = register_reboot_notifier(&wdt_notifier);
328 if (ret != 0) {
329 printk (KERN_ERR PFX "cannot register reboot notifier (err=%d)\n",
330 ret);
331 goto unreg_regions;
332 }
333
334 ret = misc_register(&wdt_miscdev);
335 if (ret != 0) {
336 printk (KERN_ERR PFX "cannot register miscdev on minor=%d (err=%d)\n",
337 WATCHDOG_MINOR, ret);
338 goto unreg_reboot;
339 }
340
341 printk (KERN_INFO PFX "initialized. timeout=%d sec (nowayout=%d)\n",
342 timeout, nowayout);
343
344out:
345 return ret;
346unreg_reboot:
347 unregister_reboot_notifier(&wdt_notifier);
348unreg_regions:
349 release_region(wdt_io, 1);
350 goto out;
351}
352
353static void __exit
354wdt_exit(void)
355{
356 misc_deregister(&wdt_miscdev);
357 unregister_reboot_notifier(&wdt_notifier);
358 release_region(wdt_io,1);
359}
360
361module_init(wdt_init);
362module_exit(wdt_exit);
363
364MODULE_LICENSE("GPL");
365MODULE_AUTHOR("Marcus Junker <junker@anduras.de>");
366MODULE_DESCRIPTION("w83697hf WDT driver");
367MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR);