aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorSven Anders <anders@anduras.de>2006-08-24 11:11:50 -0400
committerWim Van Sebroeck <wim@iguana.be>2006-10-04 16:36:02 -0400
commit485ae77dc7f484563707557ccf8c5d228980619f (patch)
treef6b24e7e0b02ea3d33744bfd19671909b197ecc1
parent3bdc9d0b408e01c4e556daba0035ba37f603e920 (diff)
[WATCHDOG] Winbond SMsC37B787 watchdog driver
New watchdog driver for the Winbond SMsC37B787 chipset. Signed-off-by: Sven Anders <anders@anduras.de> Signed-off-by: Wim Van Sebroeck <wim@iguana.be>
-rw-r--r--drivers/char/watchdog/Kconfig20
-rw-r--r--drivers/char/watchdog/Makefile1
-rw-r--r--drivers/char/watchdog/smsc37b787_wdt.c614
3 files changed, 635 insertions, 0 deletions
diff --git a/drivers/char/watchdog/Kconfig b/drivers/char/watchdog/Kconfig
index 89e46d6dfc4e..8d2ebc73c894 100644
--- a/drivers/char/watchdog/Kconfig
+++ b/drivers/char/watchdog/Kconfig
@@ -395,6 +395,26 @@ config CPU5_WDT
395 To compile this driver as a module, choose M here: the 395 To compile this driver as a module, choose M here: the
396 module will be called cpu5wdt. 396 module will be called cpu5wdt.
397 397
398config SMSC37B787_WDT
399 tristate "Winbond SMsC37B787 Watchdog Timer"
400 depends on WATCHDOG && X86
401 ---help---
402 This is the driver for the hardware watchdog component on the
403 Winbond SMsC37B787 chipset as used on the NetRunner Mainboard
404 from Vision Systems and maybe others.
405
406 This watchdog simply watches your kernel to make sure it doesn't
407 freeze, and if it does, it reboots your computer after a certain
408 amount of time.
409
410 Usually a userspace daemon will notify the kernel WDT driver that
411 userspace is still alive, at regular intervals.
412
413 To compile this driver as a module, choose M here: the
414 module will be called smsc37b787_wdt.
415
416 Most people will say N.
417
398config W83627HF_WDT 418config W83627HF_WDT
399 tristate "W83627HF Watchdog Timer" 419 tristate "W83627HF Watchdog Timer"
400 depends on WATCHDOG && X86 420 depends on WATCHDOG && X86
diff --git a/drivers/char/watchdog/Makefile b/drivers/char/watchdog/Makefile
index 7f70abad465a..630526f12376 100644
--- a/drivers/char/watchdog/Makefile
+++ b/drivers/char/watchdog/Makefile
@@ -53,6 +53,7 @@ obj-$(CONFIG_SCx200_WDT) += scx200_wdt.o
53obj-$(CONFIG_60XX_WDT) += sbc60xxwdt.o 53obj-$(CONFIG_60XX_WDT) += sbc60xxwdt.o
54obj-$(CONFIG_SBC8360_WDT) += sbc8360.o 54obj-$(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_W83627HF_WDT) += w83627hf_wdt.o 57obj-$(CONFIG_W83627HF_WDT) += w83627hf_wdt.o
57obj-$(CONFIG_W83877F_WDT) += w83877f_wdt.o 58obj-$(CONFIG_W83877F_WDT) += w83877f_wdt.o
58obj-$(CONFIG_W83977F_WDT) += w83977f_wdt.o 59obj-$(CONFIG_W83977F_WDT) += w83977f_wdt.o
diff --git a/drivers/char/watchdog/smsc37b787_wdt.c b/drivers/char/watchdog/smsc37b787_wdt.c
new file mode 100644
index 000000000000..47141c0b6f25
--- /dev/null
+++ b/drivers/char/watchdog/smsc37b787_wdt.c
@@ -0,0 +1,614 @@
1/*
2 * SMsC 37B787 Watchdog Timer driver for Linux 2.6.x.x
3 *
4 * Based on acquirewdt.c by Alan Cox <alan@redhat.com>
5 * and some other existing drivers
6 *
7 * This program is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU General Public License
9 * as published by the Free Software Foundation; either version
10 * 2 of the License, or (at your option) any later version.
11 *
12 * The authors do NOT admit liability nor provide warranty for
13 * any of this software. This material is provided "AS-IS" in
14 * the hope that it may be useful for others.
15 *
16 * (C) Copyright 2003-2006 Sven Anders <anders@anduras.de>
17 *
18 * History:
19 * 2003 - Created version 1.0 for Linux 2.4.x.
20 * 2006 - Ported to Linux 2.6, added nowayout and MAGICCLOSE
21 * features. Released version 1.1
22 *
23 * Theory of operation:
24 *
25 * A Watchdog Timer (WDT) is a hardware circuit that can
26 * reset the computer system in case of a software fault.
27 * You probably knew that already.
28 *
29 * Usually a userspace daemon will notify the kernel WDT driver
30 * via the /dev/watchdog special device file that userspace is
31 * still alive, at regular intervals. When such a notification
32 * occurs, the driver will usually tell the hardware watchdog
33 * that everything is in order, and that the watchdog should wait
34 * for yet another little while to reset the system.
35 * If userspace fails (RAM error, kernel bug, whatever), the
36 * notifications cease to occur, and the hardware watchdog will
37 * reset the system (causing a reboot) after the timeout occurs.
38 *
39 * Create device with:
40 * mknod /dev/watchdog c 10 130
41 *
42 * For an example userspace keep-alive daemon, see:
43 * Documentation/watchdog/watchdog.txt
44 */
45
46#include <linux/module.h>
47#include <linux/moduleparam.h>
48#include <linux/types.h>
49#include <linux/miscdevice.h>
50#include <linux/watchdog.h>
51#include <linux/delay.h>
52#include <linux/fs.h>
53#include <linux/ioport.h>
54#include <linux/notifier.h>
55#include <linux/reboot.h>
56#include <linux/init.h>
57
58#include <asm/io.h>
59#include <asm/uaccess.h>
60#include <asm/system.h>
61
62/* enable support for minutes as units? */
63/* (does not always work correctly, so disabled by default!) */
64#define SMSC_SUPPORT_MINUTES
65#undef SMSC_SUPPORT_MINUTES
66
67#define MAX_TIMEOUT 255
68
69#define UNIT_SECOND 0
70#define UNIT_MINUTE 1
71
72#define MODNAME "smsc37b787_wdt: "
73#define VERSION "1.1"
74
75#define WATCHDOG_MINOR 130
76
77#define IOPORT 0x3F0
78#define IOPORT_SIZE 2
79#define IODEV_NO 8
80
81static int unit = UNIT_SECOND; /* timer's unit */
82static int timeout = 60; /* timeout value: default is 60 "units" */
83static int timer_enabled = 0; /* is the timer enabled? */
84
85static char expect_close; /* is the close expected? */
86
87static int nowayout = WATCHDOG_NOWAYOUT;
88
89/* -- Low level function ----------------------------------------*/
90
91/* unlock the IO chip */
92
93static inline void open_io_config(void)
94{
95 outb(0x55, IOPORT);
96 mdelay(1);
97 outb(0x55, IOPORT);
98}
99
100/* lock the IO chip */
101static inline void close_io_config(void)
102{
103 outb(0xAA, IOPORT);
104}
105
106/* select the IO device */
107static inline void select_io_device(unsigned char devno)
108{
109 outb(0x07, IOPORT);
110 outb(devno, IOPORT+1);
111}
112
113/* write to the control register */
114static inline void write_io_cr(unsigned char reg, unsigned char data)
115{
116 outb(reg, IOPORT);
117 outb(data, IOPORT+1);
118}
119
120/* read from the control register */
121static inline char read_io_cr(unsigned char reg)
122{
123 outb(reg, IOPORT);
124 return inb(IOPORT+1);
125}
126
127/* -- Medium level functions ------------------------------------*/
128
129static inline void gpio_bit12(unsigned char reg)
130{
131 // -- General Purpose I/O Bit 1.2 --
132 // Bit 0, In/Out: 0 = Output, 1 = Input
133 // Bit 1, Polarity: 0 = No Invert, 1 = Invert
134 // Bit 2, Group Enable Intr.: 0 = Disable, 1 = Enable
135 // Bit 3/4, Function select: 00 = GPI/O, 01 = WDT, 10 = P17,
136 // 11 = Either Edge Triggered Intr. 2
137 // Bit 5/6 (Reserved)
138 // Bit 7, Output Type: 0 = Push Pull Bit, 1 = Open Drain
139 write_io_cr(0xE2, reg);
140}
141
142static inline void gpio_bit13(unsigned char reg)
143{
144 // -- General Purpose I/O Bit 1.3 --
145 // Bit 0, In/Out: 0 = Output, 1 = Input
146 // Bit 1, Polarity: 0 = No Invert, 1 = Invert
147 // Bit 2, Group Enable Intr.: 0 = Disable, 1 = Enable
148 // Bit 3, Function select: 0 = GPI/O, 1 = LED
149 // Bit 4-6 (Reserved)
150 // Bit 7, Output Type: 0 = Push Pull Bit, 1 = Open Drain
151 write_io_cr(0xE3, reg);
152}
153
154static inline void wdt_timer_units(unsigned char new_units)
155{
156 // -- Watchdog timer units --
157 // Bit 0-6 (Reserved)
158 // Bit 7, WDT Time-out Value Units Select
159 // (0 = Minutes, 1 = Seconds)
160 write_io_cr(0xF1, new_units);
161}
162
163static inline void wdt_timeout_value(unsigned char new_timeout)
164{
165 // -- Watchdog Timer Time-out Value --
166 // Bit 0-7 Binary coded units (0=Disabled, 1..255)
167 write_io_cr(0xF2, new_timeout);
168}
169
170static inline void wdt_timer_conf(unsigned char conf)
171{
172 // -- Watchdog timer configuration --
173 // Bit 0 Joystick enable: 0* = No Reset, 1 = Reset WDT upon Gameport I/O
174 // Bit 1 Keyboard enable: 0* = No Reset, 1 = Reset WDT upon KBD Intr.
175 // Bit 2 Mouse enable: 0* = No Reset, 1 = Reset WDT upon Mouse Intr.
176 // Bit 3 Reset the timer
177 // (Wrong in SMsC documentation? Given as: PowerLED Timout Enabled)
178 // Bit 4-7 WDT Interrupt Mapping: (0000* = Disabled,
179 // 0001=IRQ1, 0010=(Invalid), 0011=IRQ3 to 1111=IRQ15)
180 write_io_cr(0xF3, conf);
181}
182
183static inline void wdt_timer_ctrl(unsigned char reg)
184{
185 // -- Watchdog timer control --
186 // Bit 0 Status Bit: 0 = Timer counting, 1 = Timeout occured
187 // Bit 1 Power LED Toggle: 0 = Disable Toggle, 1 = Toggle at 1 Hz
188 // Bit 2 Force Timeout: 1 = Forces WD timeout event (self-cleaning)
189 // Bit 3 P20 Force Timeout enabled:
190 // 0 = P20 activity does not generate the WD timeout event
191 // 1 = P20 Allows rising edge of P20, from the keyboard
192 // controller, to force the WD timeout event.
193 // Bit 4 (Reserved)
194 // -- Soft power management --
195 // Bit 5 Stop Counter: 1 = Stop software power down counter
196 // set via register 0xB8, (self-cleaning)
197 // (Upon read: 0 = Counter running, 1 = Counter stopped)
198 // Bit 6 Restart Counter: 1 = Restart software power down counter
199 // set via register 0xB8, (self-cleaning)
200 // Bit 7 SPOFF: 1 = Force software power down (self-cleaning)
201
202 write_io_cr(0xF4, reg);
203}
204
205/* -- Higher level functions ------------------------------------*/
206
207/* initialize watchdog */
208
209static void wb_smsc_wdt_initialize(void)
210{
211 unsigned char old;
212
213 open_io_config();
214 select_io_device(IODEV_NO);
215
216 // enable the watchdog
217 gpio_bit13(0x08); // Select pin 80 = LED not GPIO
218 gpio_bit12(0x0A); // Set pin 79 = WDT not GPIO/Output/Polarity=Invert
219
220 // disable the timeout
221 wdt_timeout_value(0);
222
223 // reset control register
224 wdt_timer_ctrl(0x00);
225
226 // reset configuration register
227 wdt_timer_conf(0x00);
228
229 // read old (timer units) register
230 old = read_io_cr(0xF1) & 0x7F;
231 if (unit == UNIT_SECOND) old |= 0x80; // set to seconds
232
233 // set the watchdog timer units
234 wdt_timer_units(old);
235
236 close_io_config();
237}
238
239/* shutdown the watchdog */
240
241static void wb_smsc_wdt_shutdown(void)
242{
243 open_io_config();
244 select_io_device(IODEV_NO);
245
246 // disable the watchdog
247 gpio_bit13(0x09);
248 gpio_bit12(0x09);
249
250 // reset watchdog config register
251 wdt_timer_conf(0x00);
252
253 // reset watchdog control register
254 wdt_timer_ctrl(0x00);
255
256 // disable timeout
257 wdt_timeout_value(0x00);
258
259 close_io_config();
260}
261
262/* set timeout => enable watchdog */
263
264static void wb_smsc_wdt_set_timeout(unsigned char new_timeout)
265{
266 open_io_config();
267 select_io_device(IODEV_NO);
268
269 // set Power LED to blink, if we enable the timeout
270 wdt_timer_ctrl((new_timeout == 0) ? 0x00 : 0x02);
271
272 // set timeout value
273 wdt_timeout_value(new_timeout);
274
275 close_io_config();
276}
277
278/* get timeout */
279
280static unsigned char wb_smsc_wdt_get_timeout(void)
281{
282 unsigned char set_timeout;
283
284 open_io_config();
285 select_io_device(IODEV_NO);
286 set_timeout = read_io_cr(0xF2);
287 close_io_config();
288
289 return set_timeout;
290}
291
292/* disable watchdog */
293
294static void wb_smsc_wdt_disable(void)
295{
296 // set the timeout to 0 to disable the watchdog
297 wb_smsc_wdt_set_timeout(0);
298}
299
300/* enable watchdog by setting the current timeout */
301
302static void wb_smsc_wdt_enable(void)
303{
304 // set the current timeout...
305 wb_smsc_wdt_set_timeout(timeout);
306}
307
308/* reset the timer */
309
310static void wb_smsc_wdt_reset_timer(void)
311{
312 open_io_config();
313 select_io_device(IODEV_NO);
314
315 // reset the timer
316 wdt_timeout_value(timeout);
317 wdt_timer_conf(0x08);
318
319 close_io_config();
320}
321
322/* return, if the watchdog is enabled (timeout is set...) */
323
324static int wb_smsc_wdt_status(void)
325{
326 return (wb_smsc_wdt_get_timeout() == 0) ? 0 : WDIOF_KEEPALIVEPING;
327}
328
329
330/* -- File operations -------------------------------------------*/
331
332/* open => enable watchdog and set initial timeout */
333
334static int wb_smsc_wdt_open(struct inode *inode, struct file *file)
335{
336 /* /dev/watchdog can only be opened once */
337
338 if (timer_enabled)
339 return -EBUSY;
340
341 if (nowayout)
342 __module_get(THIS_MODULE);
343
344 /* Reload and activate timer */
345 timer_enabled = 1;
346 wb_smsc_wdt_enable();
347
348 printk(KERN_INFO MODNAME "Watchdog enabled. Timeout set to %d %s.\n", timeout, (unit == UNIT_SECOND) ? "second(s)" : "minute(s)");
349
350 return nonseekable_open(inode, file);
351}
352
353/* close => shut off the timer */
354
355static int wb_smsc_wdt_release(struct inode *inode, struct file *file)
356{
357 /* Shut off the timer. */
358
359 if (expect_close == 42) {
360 wb_smsc_wdt_disable();
361 printk(KERN_INFO MODNAME "Watchdog disabled, sleeping again...\n");
362 } else {
363 printk(KERN_CRIT MODNAME "Unexpected close, not stopping watchdog!\n");
364 wb_smsc_wdt_reset_timer();
365 }
366
367 timer_enabled = 0;
368 expect_close = 0;
369 return 0;
370}
371
372/* write => update the timer to keep the machine alive */
373
374static ssize_t wb_smsc_wdt_write(struct file *file, const char __user *data,
375 size_t len, loff_t *ppos)
376{
377 /* See if we got the magic character 'V' and reload the timer */
378 if (len) {
379 if (!nowayout) {
380 size_t i;
381
382 /* reset expect flag */
383 expect_close = 0;
384
385 /* scan to see whether or not we got the magic character */
386 for (i = 0; i != len; i++) {
387 char c;
388 if (get_user(c, data+i))
389 return -EFAULT;
390 if (c == 'V')
391 expect_close = 42;
392 }
393 }
394
395 /* someone wrote to us, we should reload the timer */
396 wb_smsc_wdt_reset_timer();
397 }
398 return len;
399}
400
401/* ioctl => control interface */
402
403static int wb_smsc_wdt_ioctl(struct inode *inode, struct file *file,
404 unsigned int cmd, unsigned long arg)
405{
406 int new_timeout;
407
408 union {
409 struct watchdog_info __user *ident;
410 int __user *i;
411 } uarg;
412
413 static struct watchdog_info ident = {
414 .options = WDIOF_KEEPALIVEPING |
415 WDIOF_SETTIMEOUT |
416 WDIOF_MAGICCLOSE,
417 .firmware_version = 0,
418 .identity = "SMsC 37B787 Watchdog"
419 };
420
421 uarg.i = (int __user *)arg;
422
423 switch (cmd) {
424 default:
425 return -ENOTTY;
426
427 case WDIOC_GETSUPPORT:
428 return copy_to_user(uarg.ident, &ident, sizeof(ident));
429
430 case WDIOC_GETSTATUS:
431 return put_user(wb_smsc_wdt_status(), uarg.i);
432
433 case WDIOC_GETBOOTSTATUS:
434 return put_user(0, uarg.i);
435
436 case WDIOC_KEEPALIVE:
437 wb_smsc_wdt_reset_timer();
438 return 0;
439
440 case WDIOC_SETTIMEOUT:
441 if (get_user(new_timeout, uarg.i))
442 return -EFAULT;
443
444 // the API states this is given in secs
445 if (unit == UNIT_MINUTE)
446 new_timeout /= 60;
447
448 if (new_timeout < 0 || new_timeout > MAX_TIMEOUT)
449 return -EINVAL;
450
451 timeout = new_timeout;
452 wb_smsc_wdt_set_timeout(timeout);
453
454 // fall through and return the new timeout...
455
456 case WDIOC_GETTIMEOUT:
457
458 new_timeout = timeout;
459
460 if (unit == UNIT_MINUTE)
461 new_timeout *= 60;
462
463 return put_user(new_timeout, uarg.i);
464
465 case WDIOC_SETOPTIONS:
466 {
467 int options, retval = -EINVAL;
468
469 if (get_user(options, uarg.i))
470 return -EFAULT;
471
472 if (options & WDIOS_DISABLECARD) {
473 wb_smsc_wdt_disable();
474 retval = 0;
475 }
476
477 if (options & WDIOS_ENABLECARD) {
478 wb_smsc_wdt_enable();
479 retval = 0;
480 }
481
482 return retval;
483 }
484 }
485}
486
487/* -- Notifier funtions -----------------------------------------*/
488
489static int wb_smsc_wdt_notify_sys(struct notifier_block *this, unsigned long code, void *unused)
490{
491 if (code == SYS_DOWN || code == SYS_HALT)
492 {
493 // set timeout to 0, to avoid possible race-condition
494 timeout = 0;
495 wb_smsc_wdt_disable();
496 }
497 return NOTIFY_DONE;
498}
499
500/* -- Module's structures ---------------------------------------*/
501
502static struct file_operations wb_smsc_wdt_fops =
503{
504 .owner = THIS_MODULE,
505 .llseek = no_llseek,
506 .write = wb_smsc_wdt_write,
507 .ioctl = wb_smsc_wdt_ioctl,
508 .open = wb_smsc_wdt_open,
509 .release = wb_smsc_wdt_release
510};
511
512static struct notifier_block wb_smsc_wdt_notifier =
513{
514 .notifier_call = wb_smsc_wdt_notify_sys
515};
516
517static struct miscdevice wb_smsc_wdt_miscdev =
518{
519 .minor = WATCHDOG_MINOR,
520 .name = "watchdog",
521 .fops = &wb_smsc_wdt_fops,
522};
523
524/* -- Module init functions -------------------------------------*/
525
526/* module's "constructor" */
527
528static int __init wb_smsc_wdt_init(void)
529{
530 int ret;
531
532 printk("SMsC 37B787 watchdog component driver " VERSION " initialising...\n");
533
534 if (!request_region(IOPORT, IOPORT_SIZE, "SMsC 37B787 watchdog")) {
535 printk(KERN_ERR MODNAME "Unable to register IO port %#x\n", IOPORT);
536 ret = -EBUSY;
537 goto out_pnp;
538 }
539
540 ret = register_reboot_notifier(&wb_smsc_wdt_notifier);
541 if (ret) {
542 printk(KERN_ERR MODNAME "Unable to register reboot notifier err = %d\n", ret);
543 goto out_io;
544 }
545
546 ret = misc_register(&wb_smsc_wdt_miscdev);
547 if (ret) {
548 printk(KERN_ERR MODNAME "Unable to register miscdev on minor %d\n", WATCHDOG_MINOR);
549 goto out_rbt;
550 }
551
552 // init the watchdog timer
553 wb_smsc_wdt_initialize();
554
555 // set new maximum, if it's too big
556 if (timeout > MAX_TIMEOUT)
557 timeout = MAX_TIMEOUT;
558
559 // output info
560 printk(KERN_INFO MODNAME "Timeout set to %d %s.\n", timeout, (unit == UNIT_SECOND) ? "second(s)" : "minute(s)");
561 printk(KERN_INFO MODNAME "Watchdog initialized and sleeping (nowayout=%d)...\n", nowayout);
562
563 // ret = 0
564
565out_clean:
566 return ret;
567
568out_rbt:
569 unregister_reboot_notifier(&wb_smsc_wdt_notifier);
570
571out_io:
572 release_region(IOPORT, IOPORT_SIZE);
573
574out_pnp:
575 goto out_clean;
576}
577
578/* module's "destructor" */
579
580static void __exit wb_smsc_wdt_exit(void)
581{
582 /* Stop the timer before we leave */
583 if (!nowayout)
584 {
585 wb_smsc_wdt_shutdown();
586 printk(KERN_INFO MODNAME "Watchdog disabled.\n");
587 }
588
589 misc_deregister(&wb_smsc_wdt_miscdev);
590 unregister_reboot_notifier(&wb_smsc_wdt_notifier);
591 release_region(IOPORT, IOPORT_SIZE);
592
593 printk("SMsC 37B787 watchdog component driver removed.\n");
594}
595
596module_init(wb_smsc_wdt_init);
597module_exit(wb_smsc_wdt_exit);
598
599MODULE_AUTHOR("Sven Anders <anders@anduras.de>");
600MODULE_DESCRIPTION("Driver for SMsC 37B787 watchdog component (Version " VERSION ")");
601MODULE_LICENSE("GPL");
602
603MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR);
604
605#ifdef SMSC_SUPPORT_MINUTES
606module_param(unit, int, 0);
607MODULE_PARM_DESC(unit, "set unit to use, 0=seconds or 1=minutes, default is 0");
608#endif
609
610module_param(timeout, int, 60);
611MODULE_PARM_DESC(timeout, "range is 1-255 units, default is 60");
612
613module_param(nowayout, int, 0);
614MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default=CONFIG_WATCHDOG_NOWAYOUT)");