aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorFlemming Frandsen <ff@nrvissing.net>2008-04-17 05:29:54 -0400
committerWim Van Sebroeck <wim@iguana.be>2008-10-10 09:09:28 -0400
commit7d8b09066117e3023e55964ae4626c107f437b60 (patch)
treeeda88e7a4cb04bc19361e5354d7b2fcb677ab288
parente1fee94f346387739e683b31815ab54dc0a30bd6 (diff)
[WATCHDOG] Add driver for winbond w83697ug/uf watchdog feature
Adapted the w83627hf_wdt.c driver to work with the w83697ug/uf chip, found on MSI Fuzzy CX700 boards. The method used is taken directly from the winbond datasheet and surprisingly it differs slightly from all the other winbond watchdogs. So far it has only been tested on the CX700 board that I have, but it seems to work nicely. Signed-off-by: Flemming Frandsen <ff@nrvissing.net> Signed-off-by: Wim Van Sebroeck <wim@iguana.be>
-rw-r--r--drivers/watchdog/Kconfig15
-rw-r--r--drivers/watchdog/Makefile1
-rw-r--r--drivers/watchdog/w83697ug_wdt.c392
3 files changed, 408 insertions, 0 deletions
diff --git a/drivers/watchdog/Kconfig b/drivers/watchdog/Kconfig
index 82e65154e75c..37dc27c4ecdc 100644
--- a/drivers/watchdog/Kconfig
+++ b/drivers/watchdog/Kconfig
@@ -585,6 +585,21 @@ config W83697HF_WDT
585 585
586 Most people will say N. 586 Most people will say N.
587 587
588config W83697UG_WDT
589 tristate "W83697UG/W83697UF Watchdog Timer"
590 depends on X86
591 ---help---
592 This is the driver for the hardware watchdog on the W83697UG/UF
593 chipset as used in MSI Fuzzy CX700 VIA motherboards (and likely others).
594 This watchdog simply watches your kernel to make sure it doesn't
595 freeze, and if it does, it reboots your computer after a certain
596 amount of time.
597
598 To compile this driver as a module, choose M here: the
599 module will be called w83697ug_wdt.
600
601 Most people will say N.
602
588config W83877F_WDT 603config W83877F_WDT
589 tristate "W83877F (EMACS) Watchdog Timer" 604 tristate "W83877F (EMACS) Watchdog Timer"
590 depends on X86 605 depends on X86
diff --git a/drivers/watchdog/Makefile b/drivers/watchdog/Makefile
index e2e4bcf0a56e..6f018e5b3dd3 100644
--- a/drivers/watchdog/Makefile
+++ b/drivers/watchdog/Makefile
@@ -84,6 +84,7 @@ obj-$(CONFIG_CPU5_WDT) += cpu5wdt.o
84obj-$(CONFIG_SMSC37B787_WDT) += smsc37b787_wdt.o 84obj-$(CONFIG_SMSC37B787_WDT) += smsc37b787_wdt.o
85obj-$(CONFIG_W83627HF_WDT) += w83627hf_wdt.o 85obj-$(CONFIG_W83627HF_WDT) += w83627hf_wdt.o
86obj-$(CONFIG_W83697HF_WDT) += w83697hf_wdt.o 86obj-$(CONFIG_W83697HF_WDT) += w83697hf_wdt.o
87obj-$(CONFIG_W83697UG_WDT) += w83697ug_wdt.o
87obj-$(CONFIG_W83877F_WDT) += w83877f_wdt.o 88obj-$(CONFIG_W83877F_WDT) += w83877f_wdt.o
88obj-$(CONFIG_W83977F_WDT) += w83977f_wdt.o 89obj-$(CONFIG_W83977F_WDT) += w83977f_wdt.o
89obj-$(CONFIG_MACHZ_WDT) += machzwd.o 90obj-$(CONFIG_MACHZ_WDT) += machzwd.o
diff --git a/drivers/watchdog/w83697ug_wdt.c b/drivers/watchdog/w83697ug_wdt.c
new file mode 100644
index 000000000000..c73b5e2919c6
--- /dev/null
+++ b/drivers/watchdog/w83697ug_wdt.c
@@ -0,0 +1,392 @@
1/*
2 * w83697ug/uf WDT driver
3 *
4 * (c) Copyright 2008 Flemming Fransen <ff@nrvissing.net>
5 * reused original code to supoprt 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 <vlad@storewiz.com>
12 * added support for W83627THF.
13 *
14 * (c) Copyright 2003 Pádraig Brady <P@draigBrady.com>
15 *
16 * (c) Copyright 2000-2001 Marek Michalkiewicz <marekm@linux.org.pl>
17 *
18 * (c) Copyright 1996 Alan Cox <alan@redhat.com>, 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 <alan@redhat.com>
31 */
32
33#include <linux/module.h>
34#include <linux/moduleparam.h>
35#include <linux/types.h>
36#include <linux/miscdevice.h>
37#include <linux/watchdog.h>
38#include <linux/fs.h>
39#include <linux/ioport.h>
40#include <linux/notifier.h>
41#include <linux/reboot.h>
42#include <linux/init.h>
43#include <linux/spinlock.h>
44#include <linux/io.h>
45#include <linux/uaccess.h>
46
47#include <asm/system.h>
48
49#define WATCHDOG_NAME "w83697ug/uf WDT"
50#define PFX WATCHDOG_NAME ": "
51#define WATCHDOG_TIMEOUT 60 /* 60 sec default timeout */
52
53static unsigned long wdt_is_open;
54static char expect_close;
55static DEFINE_SPINLOCK(io_lock);
56
57static int wdt_io = 0x2e;
58module_param(wdt_io, int, 0);
59MODULE_PARM_DESC(wdt_io, "w83697ug/uf WDT io port (default 0x2e)");
60
61static int timeout = WATCHDOG_TIMEOUT; /* in seconds */
62module_param(timeout, int, 0);
63MODULE_PARM_DESC(timeout,
64 "Watchdog timeout in seconds. 1<= timeout <=255 (default="
65 __MODULE_STRING(WATCHDOG_TIMEOUT) ")");
66
67static int nowayout = WATCHDOG_NOWAYOUT;
68module_param(nowayout, int, 0);
69MODULE_PARM_DESC(nowayout,
70 "Watchdog cannot be stopped once started (default="
71 __MODULE_STRING(WATCHDOG_NOWAYOUT) ")");
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
82static void 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 printk(KERN_INFO PFX "Watchdog chip version 0x%02x = "
95 "W83697UG/UF found at 0x%04x\n", 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 printk(KERN_ERR PFX "No W83697UG/UF could be found\n");
105 return -EIO;
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
115static void w83697ug_unselect_wd_register(void)
116{
117 outb_p(0xAA, WDT_EFER); /* Leave extended function mode */
118}
119
120static void w83697ug_init(void)
121{
122 unsigned char t;
123
124 w83697ug_select_wd_register();
125
126 outb_p(0xF6, WDT_EFER); /* Select CRF6 */
127 t = inb_p(WDT_EFDR); /* read CRF6 */
128 if (t != 0) {
129 printk(KERN_INFO PFX "Watchdog already running."
130 " Resetting timeout to %d sec\n", timeout);
131 outb_p(timeout, WDT_EFDR); /* Write back to CRF6 */
132 }
133 outb_p(0xF5, WDT_EFER); /* Select CRF5 */
134 t = inb_p(WDT_EFDR); /* read CRF5 */
135 t &= ~0x0C; /* set second mode &
136 disable keyboard turning off watchdog */
137 outb_p(t, WDT_EFDR); /* Write back to CRF5 */
138
139 w83697ug_unselect_wd_register();
140}
141
142static void wdt_ctrl(int timeout)
143{
144 spin_lock(&io_lock);
145
146 w83697ug_select_wd_register();
147
148 outb_p(0xF4, WDT_EFER); /* Select CRF4 */
149 outb_p(timeout, WDT_EFDR); /* Write Timeout counter to CRF4 */
150
151 w83697ug_unselect_wd_register();
152
153 spin_unlock(&io_lock);
154}
155
156static int wdt_ping(void)
157{
158 wdt_ctrl(timeout);
159 return 0;
160}
161
162static int wdt_disable(void)
163{
164 wdt_ctrl(0);
165 return 0;
166}
167
168static int wdt_set_heartbeat(int t)
169{
170 if (t < 1 || t > 255)
171 return -EINVAL;
172
173 timeout = t;
174 return 0;
175}
176
177static ssize_t wdt_write(struct file *file, const char __user *buf,
178 size_t count, loff_t *ppos)
179{
180 if (count) {
181 if (!nowayout) {
182 size_t i;
183
184 expect_close = 0;
185
186 for (i = 0; i != count; i++) {
187 char c;
188 if (get_user(c, buf + i))
189 return -EFAULT;
190 if (c == 'V')
191 expect_close = 42;
192 }
193 }
194 wdt_ping();
195 }
196 return count;
197}
198
199static long wdt_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
200{
201 void __user *argp = (void __user *)arg;
202 int __user *p = argp;
203 int new_timeout;
204 static const struct watchdog_info ident = {
205 .options = WDIOF_KEEPALIVEPING |
206 WDIOF_SETTIMEOUT |
207 WDIOF_MAGICCLOSE,
208 .firmware_version = 1,
209 .identity = "W83697UG WDT",
210 };
211
212 switch (cmd) {
213 case WDIOC_GETSUPPORT:
214 if (copy_to_user(argp, &ident, sizeof(ident)))
215 return -EFAULT;
216 break;
217
218 case WDIOC_GETSTATUS:
219 case WDIOC_GETBOOTSTATUS:
220 return put_user(0, p);
221
222 case WDIOC_SETOPTIONS:
223 {
224 int options, retval = -EINVAL;
225
226 if (get_user(options, p))
227 return -EFAULT;
228
229 if (options & WDIOS_DISABLECARD) {
230 wdt_disable();
231 retval = 0;
232 }
233
234 if (options & WDIOS_ENABLECARD) {
235 wdt_ping();
236 retval = 0;
237 }
238
239 return retval;
240 }
241
242 case WDIOC_KEEPALIVE:
243 wdt_ping();
244 break;
245
246 case WDIOC_SETTIMEOUT:
247 if (get_user(new_timeout, p))
248 return -EFAULT;
249 if (wdt_set_heartbeat(new_timeout))
250 return -EINVAL;
251 wdt_ping();
252 /* Fall */
253
254 case WDIOC_GETTIMEOUT:
255 return put_user(timeout, p);
256
257 default:
258 return -ENOTTY;
259 }
260 return 0;
261}
262
263static int wdt_open(struct inode *inode, struct file *file)
264{
265 if (test_and_set_bit(0, &wdt_is_open))
266 return -EBUSY;
267 /*
268 * Activate
269 */
270
271 wdt_ping();
272 return nonseekable_open(inode, file);
273}
274
275static int wdt_close(struct inode *inode, struct file *file)
276{
277 if (expect_close == 42)
278 wdt_disable();
279 else {
280 printk(KERN_CRIT PFX
281 "Unexpected close, not stopping watchdog!\n");
282 wdt_ping();
283 }
284 expect_close = 0;
285 clear_bit(0, &wdt_is_open);
286 return 0;
287}
288
289/*
290 * Notifier for system down
291 */
292
293static int wdt_notify_sys(struct notifier_block *this, unsigned long code,
294 void *unused)
295{
296 if (code == SYS_DOWN || code == SYS_HALT)
297 wdt_disable(); /* Turn the WDT off */
298
299 return NOTIFY_DONE;
300}
301
302/*
303 * Kernel Interfaces
304 */
305
306static const struct file_operations wdt_fops = {
307 .owner = THIS_MODULE,
308 .llseek = no_llseek,
309 .write = wdt_write,
310 .unlocked_ioctl = wdt_ioctl,
311 .open = wdt_open,
312 .release = wdt_close,
313};
314
315static struct miscdevice wdt_miscdev = {
316 .minor = WATCHDOG_MINOR,
317 .name = "watchdog",
318 .fops = &wdt_fops,
319};
320
321/*
322 * The WDT needs to learn about soft shutdowns in order to
323 * turn the timebomb registers off.
324 */
325
326static struct notifier_block wdt_notifier = {
327 .notifier_call = wdt_notify_sys,
328};
329
330static int __init wdt_init(void)
331{
332 int ret;
333
334 printk(KERN_INFO "WDT driver for the Winbond(TM) W83697UG/UF Super I/O chip initialising.\n");
335
336 if (wdt_set_heartbeat(timeout)) {
337 wdt_set_heartbeat(WATCHDOG_TIMEOUT);
338 printk(KERN_INFO PFX
339 "timeout value must be 1<=timeout<=255, using %d\n",
340 WATCHDOG_TIMEOUT);
341 }
342
343 if (!request_region(wdt_io, 1, WATCHDOG_NAME)) {
344 printk(KERN_ERR PFX "I/O address 0x%04x already in use\n",
345 wdt_io);
346 ret = -EIO;
347 goto out;
348 }
349
350 w83697ug_init();
351
352 ret = register_reboot_notifier(&wdt_notifier);
353 if (ret != 0) {
354 printk(KERN_ERR PFX
355 "cannot register reboot notifier (err=%d)\n", ret);
356 goto unreg_regions;
357 }
358
359 ret = misc_register(&wdt_miscdev);
360 if (ret != 0) {
361 printk(KERN_ERR PFX
362 "cannot register miscdev on minor=%d (err=%d)\n",
363 WATCHDOG_MINOR, ret);
364 goto unreg_reboot;
365 }
366
367 printk(KERN_INFO PFX "initialized. timeout=%d sec (nowayout=%d)\n",
368 timeout, nowayout);
369
370out:
371 return ret;
372unreg_reboot:
373 unregister_reboot_notifier(&wdt_notifier);
374unreg_regions:
375 release_region(wdt_io, 1);
376 goto out;
377}
378
379static void __exit wdt_exit(void)
380{
381 misc_deregister(&wdt_miscdev);
382 unregister_reboot_notifier(&wdt_notifier);
383 release_region(wdt_io, 1);
384}
385
386module_init(wdt_init);
387module_exit(wdt_exit);
388
389MODULE_LICENSE("GPL");
390MODULE_AUTHOR("Flemming Frandsen <ff@nrvissing.net>");
391MODULE_DESCRIPTION("w83697ug/uf WDT driver");
392MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR);