aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/watchdog
diff options
context:
space:
mode:
authorGuenter Roeck <linux@roeck-us.net>2014-04-05 14:27:37 -0400
committerWim Van Sebroeck <wim@iguana.be>2014-06-10 15:46:26 -0400
commit7285fae9345e030c6e215a9a4e5fe69b8db92bb5 (patch)
treeee8c6770578a485e8a4e2514a6d23c2a6d89b7f8 /drivers/watchdog
parentbe281588d0bb21ccb07b1747939e0c919c2203fc (diff)
watchdog: Remove drivers for W83697HF and W83697UG
Since both chips are now supported by the w83627hf watchdog driver, the chip specific drivers are no longer needed and can be removed. Signed-off-by: Guenter Roeck <linux@roeck-us.net> Signed-off-by: Wim Van Sebroeck <wim@iguana.be>
Diffstat (limited to 'drivers/watchdog')
-rw-r--r--drivers/watchdog/Kconfig30
-rw-r--r--drivers/watchdog/Makefile2
-rw-r--r--drivers/watchdog/w83697hf_wdt.c460
-rw-r--r--drivers/watchdog/w83697ug_wdt.c397
4 files changed, 0 insertions, 889 deletions
diff --git a/drivers/watchdog/Kconfig b/drivers/watchdog/Kconfig
index 9360c5525fa9..36fca2cb086b 100644
--- a/drivers/watchdog/Kconfig
+++ b/drivers/watchdog/Kconfig
@@ -940,36 +940,6 @@ config W83627HF_WDT
940 940
941 Most people will say N. 941 Most people will say N.
942 942
943config W83697HF_WDT
944 tristate "W83697HF/W83697HG Watchdog Timer"
945 depends on X86
946 ---help---
947 This is the driver for the hardware watchdog on the W83697HF/HG
948 chipset as used in Dedibox/VIA motherboards (and likely others).
949 This watchdog simply watches your kernel to make sure it doesn't
950 freeze, and if it does, it reboots your computer after a certain
951 amount of time.
952
953 To compile this driver as a module, choose M here: the
954 module will be called w83697hf_wdt.
955
956 Most people will say N.
957
958config W83697UG_WDT
959 tristate "W83697UG/W83697UF Watchdog Timer"
960 depends on X86
961 ---help---
962 This is the driver for the hardware watchdog on the W83697UG/UF
963 chipset as used in MSI Fuzzy CX700 VIA motherboards (and likely others).
964 This watchdog simply watches your kernel to make sure it doesn't
965 freeze, and if it does, it reboots your computer after a certain
966 amount of time.
967
968 To compile this driver as a module, choose M here: the
969 module will be called w83697ug_wdt.
970
971 Most people will say N.
972
973config W83877F_WDT 943config W83877F_WDT
974 tristate "W83877F (EMACS) Watchdog Timer" 944 tristate "W83877F (EMACS) Watchdog Timer"
975 depends on X86 945 depends on X86
diff --git a/drivers/watchdog/Makefile b/drivers/watchdog/Makefile
index 1b5f3d5efad5..1384531eaa45 100644
--- a/drivers/watchdog/Makefile
+++ b/drivers/watchdog/Makefile
@@ -107,8 +107,6 @@ obj-$(CONFIG_SMSC_SCH311X_WDT) += sch311x_wdt.o
107obj-$(CONFIG_SMSC37B787_WDT) += smsc37b787_wdt.o 107obj-$(CONFIG_SMSC37B787_WDT) += smsc37b787_wdt.o
108obj-$(CONFIG_VIA_WDT) += via_wdt.o 108obj-$(CONFIG_VIA_WDT) += via_wdt.o
109obj-$(CONFIG_W83627HF_WDT) += w83627hf_wdt.o 109obj-$(CONFIG_W83627HF_WDT) += w83627hf_wdt.o
110obj-$(CONFIG_W83697HF_WDT) += w83697hf_wdt.o
111obj-$(CONFIG_W83697UG_WDT) += w83697ug_wdt.o
112obj-$(CONFIG_W83877F_WDT) += w83877f_wdt.o 110obj-$(CONFIG_W83877F_WDT) += w83877f_wdt.o
113obj-$(CONFIG_W83977F_WDT) += w83977f_wdt.o 111obj-$(CONFIG_W83977F_WDT) += w83977f_wdt.o
114obj-$(CONFIG_MACHZ_WDT) += machzwd.o 112obj-$(CONFIG_MACHZ_WDT) += machzwd.o
diff --git a/drivers/watchdog/w83697hf_wdt.c b/drivers/watchdog/w83697hf_wdt.c
deleted file mode 100644
index e9ea856b8ff2..000000000000
--- a/drivers/watchdog/w83697hf_wdt.c
+++ /dev/null
@@ -1,460 +0,0 @@
1/*
2 * w83697hf/hg WDT driver
3 *
4 * (c) Copyright 2006 Samuel Tardieu <sam@rfc1149.net>
5 * (c) Copyright 2006 Marcus Junker <junker@anduras.de>
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 2003 Pádraig Brady <P@draigBrady.com>
12 *
13 * (c) Copyright 2000-2001 Marek Michalkiewicz <marekm@linux.org.pl>
14 *
15 * (c) Copyright 1996 Alan Cox <alan@lxorguk.ukuu.org.uk>,
16 * All Rights Reserved.
17 *
18 * This program is free software; you can redistribute it and/or
19 * modify it under the terms of the GNU General Public License
20 * as published by the Free Software Foundation; either version
21 * 2 of the License, or (at your option) any later version.
22 *
23 * Neither Marcus Junker nor ANDURAS AG admit liability nor provide
24 * warranty for any of this software. This material is provided
25 * "AS-IS" and at no charge.
26 */
27
28#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
29
30#include <linux/module.h>
31#include <linux/moduleparam.h>
32#include <linux/types.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
45#define WATCHDOG_NAME "w83697hf/hg WDT"
46#define WATCHDOG_TIMEOUT 60 /* 60 sec default timeout */
47#define WATCHDOG_EARLY_DISABLE 1 /* Disable until userland kicks in */
48
49static unsigned long wdt_is_open;
50static char expect_close;
51static DEFINE_SPINLOCK(io_lock);
52
53/* You must set this - there is no sane way to probe for this board. */
54static int wdt_io = 0x2e;
55module_param(wdt_io, int, 0);
56MODULE_PARM_DESC(wdt_io,
57 "w83697hf/hg WDT io port (default 0x2e, 0 = autodetect)");
58
59static int timeout = WATCHDOG_TIMEOUT; /* in seconds */
60module_param(timeout, int, 0);
61MODULE_PARM_DESC(timeout,
62 "Watchdog timeout in seconds. 1<= timeout <=255 (default="
63 __MODULE_STRING(WATCHDOG_TIMEOUT) ")");
64
65static bool nowayout = WATCHDOG_NOWAYOUT;
66module_param(nowayout, bool, 0);
67MODULE_PARM_DESC(nowayout,
68 "Watchdog cannot be stopped once started (default="
69 __MODULE_STRING(WATCHDOG_NOWAYOUT) ")");
70
71static int early_disable = WATCHDOG_EARLY_DISABLE;
72module_param(early_disable, int, 0);
73MODULE_PARM_DESC(early_disable,
74 "Watchdog gets disabled at boot time (default="
75 __MODULE_STRING(WATCHDOG_EARLY_DISABLE) ")");
76
77/*
78 * Kernel methods.
79 */
80
81#define W83697HF_EFER (wdt_io + 0) /* Extended Function Enable Register */
82#define W83697HF_EFIR (wdt_io + 0) /* Extended Function Index Register
83 (same as EFER) */
84#define W83697HF_EFDR (wdt_io + 1) /* Extended Function Data Register */
85
86static inline void w83697hf_unlock(void)
87{
88 outb_p(0x87, W83697HF_EFER); /* Enter extended function mode */
89 outb_p(0x87, W83697HF_EFER); /* Again according to manual */
90}
91
92static inline void w83697hf_lock(void)
93{
94 outb_p(0xAA, W83697HF_EFER); /* Leave extended function mode */
95}
96
97/*
98 * The three functions w83697hf_get_reg(), w83697hf_set_reg() and
99 * w83697hf_write_timeout() must be called with the device unlocked.
100 */
101
102static unsigned char w83697hf_get_reg(unsigned char reg)
103{
104 outb_p(reg, W83697HF_EFIR);
105 return inb_p(W83697HF_EFDR);
106}
107
108static void w83697hf_set_reg(unsigned char reg, unsigned char data)
109{
110 outb_p(reg, W83697HF_EFIR);
111 outb_p(data, W83697HF_EFDR);
112}
113
114static void w83697hf_write_timeout(int timeout)
115{
116 /* Write Timeout counter to CRF4 */
117 w83697hf_set_reg(0xF4, timeout);
118}
119
120static void w83697hf_select_wdt(void)
121{
122 w83697hf_unlock();
123 w83697hf_set_reg(0x07, 0x08); /* Switch to logic device 8 (GPIO2) */
124}
125
126static inline void w83697hf_deselect_wdt(void)
127{
128 w83697hf_lock();
129}
130
131static void w83697hf_init(void)
132{
133 unsigned char bbuf;
134
135 w83697hf_select_wdt();
136
137 bbuf = w83697hf_get_reg(0x29);
138 bbuf &= ~0x60;
139 bbuf |= 0x20;
140
141 /* Set pin 119 to WDTO# mode (= CR29, WDT0) */
142 w83697hf_set_reg(0x29, bbuf);
143
144 bbuf = w83697hf_get_reg(0xF3);
145 bbuf &= ~0x04;
146 w83697hf_set_reg(0xF3, bbuf); /* Count mode is seconds */
147
148 w83697hf_deselect_wdt();
149}
150
151static void wdt_ping(void)
152{
153 spin_lock(&io_lock);
154 w83697hf_select_wdt();
155
156 w83697hf_write_timeout(timeout);
157
158 w83697hf_deselect_wdt();
159 spin_unlock(&io_lock);
160}
161
162static void wdt_enable(void)
163{
164 spin_lock(&io_lock);
165 w83697hf_select_wdt();
166
167 w83697hf_write_timeout(timeout);
168 w83697hf_set_reg(0x30, 1); /* Enable timer */
169
170 w83697hf_deselect_wdt();
171 spin_unlock(&io_lock);
172}
173
174static void wdt_disable(void)
175{
176 spin_lock(&io_lock);
177 w83697hf_select_wdt();
178
179 w83697hf_set_reg(0x30, 0); /* Disable timer */
180 w83697hf_write_timeout(0);
181
182 w83697hf_deselect_wdt();
183 spin_unlock(&io_lock);
184}
185
186static unsigned char wdt_running(void)
187{
188 unsigned char t;
189
190 spin_lock(&io_lock);
191 w83697hf_select_wdt();
192
193 t = w83697hf_get_reg(0xF4); /* Read timer */
194
195 w83697hf_deselect_wdt();
196 spin_unlock(&io_lock);
197
198 return t;
199}
200
201static int wdt_set_heartbeat(int t)
202{
203 if (t < 1 || t > 255)
204 return -EINVAL;
205
206 timeout = t;
207 return 0;
208}
209
210static ssize_t wdt_write(struct file *file, const char __user *buf,
211 size_t count, loff_t *ppos)
212{
213 if (count) {
214 if (!nowayout) {
215 size_t i;
216
217 expect_close = 0;
218
219 for (i = 0; i != count; i++) {
220 char c;
221 if (get_user(c, buf + i))
222 return -EFAULT;
223 if (c == 'V')
224 expect_close = 42;
225 }
226 }
227 wdt_ping();
228 }
229 return count;
230}
231
232static long wdt_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
233{
234 void __user *argp = (void __user *)arg;
235 int __user *p = argp;
236 int new_timeout;
237 static const struct watchdog_info ident = {
238 .options = WDIOF_KEEPALIVEPING | WDIOF_SETTIMEOUT
239 | WDIOF_MAGICCLOSE,
240 .firmware_version = 1,
241 .identity = "W83697HF WDT",
242 };
243
244 switch (cmd) {
245 case WDIOC_GETSUPPORT:
246 if (copy_to_user(argp, &ident, sizeof(ident)))
247 return -EFAULT;
248 break;
249
250 case WDIOC_GETSTATUS:
251 case WDIOC_GETBOOTSTATUS:
252 return put_user(0, p);
253
254 case WDIOC_SETOPTIONS:
255 {
256 int options, retval = -EINVAL;
257
258 if (get_user(options, p))
259 return -EFAULT;
260
261 if (options & WDIOS_DISABLECARD) {
262 wdt_disable();
263 retval = 0;
264 }
265
266 if (options & WDIOS_ENABLECARD) {
267 wdt_enable();
268 retval = 0;
269 }
270
271 return retval;
272 }
273
274 case WDIOC_KEEPALIVE:
275 wdt_ping();
276 break;
277
278 case WDIOC_SETTIMEOUT:
279 if (get_user(new_timeout, p))
280 return -EFAULT;
281 if (wdt_set_heartbeat(new_timeout))
282 return -EINVAL;
283 wdt_ping();
284 /* Fall */
285
286 case WDIOC_GETTIMEOUT:
287 return put_user(timeout, p);
288
289 default:
290 return -ENOTTY;
291 }
292 return 0;
293}
294
295static int wdt_open(struct inode *inode, struct file *file)
296{
297 if (test_and_set_bit(0, &wdt_is_open))
298 return -EBUSY;
299 /*
300 * Activate
301 */
302
303 wdt_enable();
304 return nonseekable_open(inode, file);
305}
306
307static int wdt_close(struct inode *inode, struct file *file)
308{
309 if (expect_close == 42)
310 wdt_disable();
311 else {
312 pr_crit("Unexpected close, not stopping watchdog!\n");
313 wdt_ping();
314 }
315 expect_close = 0;
316 clear_bit(0, &wdt_is_open);
317 return 0;
318}
319
320/*
321 * Notifier for system down
322 */
323
324static int wdt_notify_sys(struct notifier_block *this, unsigned long code,
325 void *unused)
326{
327 if (code == SYS_DOWN || code == SYS_HALT)
328 wdt_disable(); /* Turn the WDT off */
329
330 return NOTIFY_DONE;
331}
332
333/*
334 * Kernel Interfaces
335 */
336
337static const struct file_operations wdt_fops = {
338 .owner = THIS_MODULE,
339 .llseek = no_llseek,
340 .write = wdt_write,
341 .unlocked_ioctl = wdt_ioctl,
342 .open = wdt_open,
343 .release = wdt_close,
344};
345
346static struct miscdevice wdt_miscdev = {
347 .minor = WATCHDOG_MINOR,
348 .name = "watchdog",
349 .fops = &wdt_fops,
350};
351
352/*
353 * The WDT needs to learn about soft shutdowns in order to
354 * turn the timebomb registers off.
355 */
356
357static struct notifier_block wdt_notifier = {
358 .notifier_call = wdt_notify_sys,
359};
360
361static int w83697hf_check_wdt(void)
362{
363 if (!request_region(wdt_io, 2, WATCHDOG_NAME)) {
364 pr_err("I/O address 0x%x already in use\n", wdt_io);
365 return -EIO;
366 }
367
368 pr_debug("Looking for watchdog at address 0x%x\n", wdt_io);
369 w83697hf_unlock();
370 if (w83697hf_get_reg(0x20) == 0x60) {
371 pr_info("watchdog found at address 0x%x\n", wdt_io);
372 w83697hf_lock();
373 return 0;
374 }
375 /* Reprotect in case it was a compatible device */
376 w83697hf_lock();
377
378 pr_info("watchdog not found at address 0x%x\n", wdt_io);
379 release_region(wdt_io, 2);
380 return -EIO;
381}
382
383static int w83697hf_ioports[] = { 0x2e, 0x4e, 0x00 };
384
385static int __init wdt_init(void)
386{
387 int ret, i, found = 0;
388
389 pr_info("WDT driver for W83697HF/HG initializing\n");
390
391 if (wdt_io == 0) {
392 /* we will autodetect the W83697HF/HG watchdog */
393 for (i = 0; ((!found) && (w83697hf_ioports[i] != 0)); i++) {
394 wdt_io = w83697hf_ioports[i];
395 if (!w83697hf_check_wdt())
396 found++;
397 }
398 } else {
399 if (!w83697hf_check_wdt())
400 found++;
401 }
402
403 if (!found) {
404 pr_err("No W83697HF/HG could be found\n");
405 ret = -ENODEV;
406 goto out;
407 }
408
409 w83697hf_init();
410 if (early_disable) {
411 if (wdt_running())
412 pr_warn("Stopping previously enabled watchdog until userland kicks in\n");
413 wdt_disable();
414 }
415
416 if (wdt_set_heartbeat(timeout)) {
417 wdt_set_heartbeat(WATCHDOG_TIMEOUT);
418 pr_info("timeout value must be 1 <= timeout <= 255, using %d\n",
419 WATCHDOG_TIMEOUT);
420 }
421
422 ret = register_reboot_notifier(&wdt_notifier);
423 if (ret != 0) {
424 pr_err("cannot register reboot notifier (err=%d)\n", ret);
425 goto unreg_regions;
426 }
427
428 ret = misc_register(&wdt_miscdev);
429 if (ret != 0) {
430 pr_err("cannot register miscdev on minor=%d (err=%d)\n",
431 WATCHDOG_MINOR, ret);
432 goto unreg_reboot;
433 }
434
435 pr_info("initialized. timeout=%d sec (nowayout=%d)\n",
436 timeout, nowayout);
437
438out:
439 return ret;
440unreg_reboot:
441 unregister_reboot_notifier(&wdt_notifier);
442unreg_regions:
443 release_region(wdt_io, 2);
444 goto out;
445}
446
447static void __exit wdt_exit(void)
448{
449 misc_deregister(&wdt_miscdev);
450 unregister_reboot_notifier(&wdt_notifier);
451 release_region(wdt_io, 2);
452}
453
454module_init(wdt_init);
455module_exit(wdt_exit);
456
457MODULE_LICENSE("GPL");
458MODULE_AUTHOR("Marcus Junker <junker@anduras.de>");
459MODULE_AUTHOR("Samuel Tardieu <sam@rfc1149.net>");
460MODULE_DESCRIPTION("w83697hf/hg WDT driver");
diff --git a/drivers/watchdog/w83697ug_wdt.c b/drivers/watchdog/w83697ug_wdt.c
deleted file mode 100644
index ff58cb74671f..000000000000
--- a/drivers/watchdog/w83697ug_wdt.c
+++ /dev/null
@@ -1,397 +0,0 @@
1/*
2 * w83697ug/uf WDT driver
3 *
4 * (c) Copyright 2008 Flemming Fransen <ff@nrvissing.net>
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 <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#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
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 bool nowayout = WATCHDOG_NOWAYOUT;
68module_param(nowayout, bool, 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 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
117static void w83697ug_unselect_wd_register(void)
118{
119 outb_p(0xAA, WDT_EFER); /* Leave extended function mode */
120}
121
122static 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
148static 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
165static int wdt_ping(void)
166{
167 wdt_ctrl(timeout);
168 return 0;
169}
170
171static int wdt_disable(void)
172{
173 wdt_ctrl(0);
174 return 0;
175}
176
177static 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
186static 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
208static 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 |
215 WDIOF_SETTIMEOUT |
216 WDIOF_MAGICCLOSE,
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
272static 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
284static 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
301static 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
314static 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
323static 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
334static struct notifier_block wdt_notifier = {
335 .notifier_call = wdt_notify_sys,
336};
337
338static 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",
347 WATCHDOG_TIMEOUT);
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
376out:
377 return ret;
378unreg_reboot:
379 unregister_reboot_notifier(&wdt_notifier);
380unreg_regions:
381 release_region(wdt_io, 1);
382 goto out;
383}
384
385static 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
392module_init(wdt_init);
393module_exit(wdt_exit);
394
395MODULE_LICENSE("GPL");
396MODULE_AUTHOR("Flemming Frandsen <ff@nrvissing.net>");
397MODULE_DESCRIPTION("w83697ug/uf WDT driver");