1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34#include <linux/interrupt.h>
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
46#include <asm/io.h>
47#include <asm/uaccess.h>
48#include <asm/system.h>
49#include "wd501p.h"
50
51static unsigned long wdt_is_open;
52static char expect_close;
53
54
55
56
57
58#define WD_TIMO 60
59
60static int heartbeat = WD_TIMO;
61static int wd_heartbeat;
62module_param(heartbeat, int, 0);
63MODULE_PARM_DESC(heartbeat, "Watchdog heartbeat in seconds. (0<heartbeat<65536, default=" __MODULE_STRING(WD_TIMO) ")");
64
65static int nowayout = WATCHDOG_NOWAYOUT;
66module_param(nowayout, int, 0);
67MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default=" __MODULE_STRING(WATCHDOG_NOWAYOUT) ")");
68
69
70static int io=0x240;
71static int irq=11;
72
73module_param(io, int, 0);
74MODULE_PARM_DESC(io, "WDT io port (default=0x240)");
75module_param(irq, int, 0);
76MODULE_PARM_DESC(irq, "WDT irq (default=11)");
77
78#ifdef CONFIG_WDT_501
79
80static int tachometer;
81
82module_param(tachometer, int, 0);
83MODULE_PARM_DESC(tachometer, "WDT501-P Fan Tachometer support (0=disable, default=0)");
84#endif
85
86
87
88
89
90static void wdt_ctr_mode(int ctr, int mode)
91{
92 ctr<<=6;
93 ctr|=0x30;
94 ctr|=(mode<<1);
95 outb_p(ctr, WDT_CR);
96}
97
98static void wdt_ctr_load(int ctr, int val)
99{
100 outb_p(val&0xFF, WDT_COUNT0+ctr);
101 outb_p(val>>8, WDT_COUNT0+ctr);
102}
103
104
105
106
107
108
109
110static int wdt_start(void)
111{
112 inb_p(WDT_DC);
113 wdt_ctr_mode(0,3);
114 wdt_ctr_mode(1,2);
115 wdt_ctr_mode(2,0);
116 wdt_ctr_load(0, 8948);
117 wdt_ctr_load(1,wd_heartbeat);
118 wdt_ctr_load(2,65535);
119 outb_p(0, WDT_DC);
120 return 0;
121}
122
123
124
125
126
127
128
129static int wdt_stop (void)
130{
131
132 inb_p(WDT_DC);
133 wdt_ctr_load(2,0);
134 return 0;
135}
136
137
138
139
140
141
142
143
144static int wdt_ping(void)
145{
146
147 inb_p(WDT_DC);
148 wdt_ctr_mode(1,2);
149 wdt_ctr_load(1,wd_heartbeat);
150 outb_p(0, WDT_DC);
151 return 0;
152}
153
154
155
156
157
158
159
160
161
162static int wdt_set_heartbeat(int t)
163{
164 if ((t < 1) || (t > 65535))
165 return -EINVAL;
166
167 heartbeat = t;
168 wd_heartbeat = t * 100;
169 return 0;
170}
171
172
173
174
175
176
177
178
179
180
181
182
183static int wdt_get_status(int *status)
184{
185 unsigned char new_status=inb_p(WDT_SR);
186
187 *status=0;
188 if (new_status & WDC_SR_ISOI0)
189 *status |= WDIOF_EXTERN1;
190 if (new_status & WDC_SR_ISII1)
191 *status |= WDIOF_EXTERN2;
192#ifdef CONFIG_WDT_501
193 if (!(new_status & WDC_SR_TGOOD))
194 *status |= WDIOF_OVERHEAT;
195 if (!(new_status & WDC_SR_PSUOVER))
196 *status |= WDIOF_POWEROVER;
197 if (!(new_status & WDC_SR_PSUUNDR))
198 *status |= WDIOF_POWERUNDER;
199 if (tachometer) {
200 if (!(new_status & WDC_SR_FANGOOD))
201 *status |= WDIOF_FANFAULT;
202 }
203#endif
204 return 0;
205}
206
207#ifdef CONFIG_WDT_501
208
209
210
211
212
213
214
215static int wdt_get_temperature(int *temperature)
216{
217 unsigned short c=inb_p(WDT_RT);
218
219 *temperature = (c * 11 / 15) + 7;
220 return 0;
221}
222#endif
223
224
225
226
227
228
229
230
231
232
233
234static irqreturn_t wdt_interrupt(int irq, void *dev_id)
235{
236
237
238
239
240 unsigned char status=inb_p(WDT_SR);
241
242 printk(KERN_CRIT "WDT status %d\n", status);
243
244#ifdef CONFIG_WDT_501
245 if (!(status & WDC_SR_TGOOD))
246 printk(KERN_CRIT "Overheat alarm.(%d)\n",inb_p(WDT_RT));
247 if (!(status & WDC_SR_PSUOVER))
248 printk(KERN_CRIT "PSU over voltage.\n");
249 if (!(status & WDC_SR_PSUUNDR))
250 printk(KERN_CRIT "PSU under voltage.\n");
251 if (tachometer) {
252 if (!(status & WDC_SR_FANGOOD))
253 printk(KERN_CRIT "Possible fan fault.\n");
254 }
255#endif
256 if (!(status & WDC_SR_WCCR)) {
257#ifdef SOFTWARE_REBOOT
258#ifdef ONLY_TESTING
259 printk(KERN_CRIT "Would Reboot.\n");
260#else
261 printk(KERN_CRIT "Initiating system reboot.\n");
262 emergency_restart();
263#endif
264#else
265 printk(KERN_CRIT "Reset in 5ms.\n");
266#endif
267 }
268 return IRQ_HANDLED;
269}
270
271
272
273
274
275
276
277
278
279
280
281
282
283static ssize_t wdt_write(struct file *file, const char __user *buf, size_t count, loff_t *ppos)
284{
285 if(count) {
286 if (!nowayout) {
287 size_t i;
288
289
290 expect_close = 0;
291
292 for (i = 0; i != count; i++) {
293 char c;
294 if (get_user(c, buf + i))
295 return -EFAULT;
296 if (c == 'V')
297 expect_close = 42;
298 }
299 }
300 wdt_ping();
301 }
302 return count;
303}
304
305
306
307
308
309
310
311
312
313
314
315
316
317static int wdt_ioctl(struct inode *inode, struct file *file, unsigned int cmd,
318 unsigned long arg)
319{
320 void __user *argp = (void __user *)arg;
321 int __user *p = argp;
322 int new_heartbeat;
323 int status;
324
325 static struct watchdog_info ident = {
326 .options = WDIOF_SETTIMEOUT|
327 WDIOF_MAGICCLOSE|
328 WDIOF_KEEPALIVEPING,
329 .firmware_version = 1,
330 .identity = "WDT500/501",
331 };
332
333
334 ident.options |= (WDIOF_EXTERN1|WDIOF_EXTERN2);
335#ifdef CONFIG_WDT_501
336 ident.options |= (WDIOF_OVERHEAT|WDIOF_POWERUNDER|WDIOF_POWEROVER);
337 if (tachometer)
338 ident.options |= WDIOF_FANFAULT;
339#endif
340
341 switch(cmd)
342 {
343 default:
344 return -ENOTTY;
345 case WDIOC_GETSUPPORT:
346 return copy_to_user(argp, &ident, sizeof(ident))?-EFAULT:0;
347
348 case WDIOC_GETSTATUS:
349 wdt_get_status(&status);
350 return put_user(status, p);
351 case WDIOC_GETBOOTSTATUS:
352 return put_user(0, p);
353 case WDIOC_KEEPALIVE:
354 wdt_ping();
355 return 0;
356 case WDIOC_SETTIMEOUT:
357 if (get_user(new_heartbeat, p))
358 return -EFAULT;
359
360 if (wdt_set_heartbeat(new_heartbeat))
361 return -EINVAL;
362
363 wdt_ping();
364
365 case WDIOC_GETTIMEOUT:
366 return put_user(heartbeat, p);
367 }
368}
369
370
371
372
373
374
375
376
377
378
379
380
381
382static int wdt_open(struct inode *inode, struct file *file)
383{
384 if(test_and_set_bit(0, &wdt_is_open))
385 return -EBUSY;
386
387
388
389 wdt_start();
390 return nonseekable_open(inode, file);
391}
392
393
394
395
396
397
398
399
400
401
402
403
404
405static int wdt_release(struct inode *inode, struct file *file)
406{
407 if (expect_close == 42) {
408 wdt_stop();
409 clear_bit(0, &wdt_is_open);
410 } else {
411 printk(KERN_CRIT "wdt: WDT device closed unexpectedly. WDT will not stop!\n");
412 wdt_ping();
413 }
414 expect_close = 0;
415 return 0;
416}
417
418#ifdef CONFIG_WDT_501
419
420
421
422
423
424
425
426
427
428
429
430static ssize_t wdt_temp_read(struct file *file, char __user *buf, size_t count, loff_t *ptr)
431{
432 int temperature;
433
434 if (wdt_get_temperature(&temperature))
435 return -EFAULT;
436
437 if (copy_to_user (buf, &temperature, 1))
438 return -EFAULT;
439
440 return 1;
441}
442
443
444
445
446
447
448
449
450
451static int wdt_temp_open(struct inode *inode, struct file *file)
452{
453 return nonseekable_open(inode, file);
454}
455
456
457
458
459
460
461
462
463
464static int wdt_temp_release(struct inode *inode, struct file *file)
465{
466 return 0;
467}
468#endif
469
470
471
472
473
474
475
476
477
478
479
480
481
482static int wdt_notify_sys(struct notifier_block *this, unsigned long code,
483 void *unused)
484{
485 if(code==SYS_DOWN || code==SYS_HALT) {
486
487 wdt_stop();
488 }
489 return NOTIFY_DONE;
490}
491
492
493
494
495
496
497static const struct file_operations wdt_fops = {
498 .owner = THIS_MODULE,
499 .llseek = no_llseek,
500 .write = wdt_write,
501 .ioctl = wdt_ioctl,
502 .open = wdt_open,
503 .release = wdt_release,
504};
505
506static struct miscdevice wdt_miscdev = {
507 .minor = WATCHDOG_MINOR,
508 .name = "watchdog",
509 .fops = &wdt_fops,
510};
511
512#ifdef CONFIG_WDT_501
513static const struct file_operations wdt_temp_fops = {
514 .owner = THIS_MODULE,
515 .llseek = no_llseek,
516 .read = wdt_temp_read,
517 .open = wdt_temp_open,
518 .release = wdt_temp_release,
519};
520
521static struct miscdevice temp_miscdev = {
522 .minor = TEMP_MINOR,
523 .name = "temperature",
524 .fops = &wdt_temp_fops,
525};
526#endif
527
528
529
530
531
532
533static struct notifier_block wdt_notifier = {
534 .notifier_call = wdt_notify_sys,
535};
536
537
538
539
540
541
542
543
544
545
546
547static void __exit wdt_exit(void)
548{
549 misc_deregister(&wdt_miscdev);
550#ifdef CONFIG_WDT_501
551 misc_deregister(&temp_miscdev);
552#endif
553 unregister_reboot_notifier(&wdt_notifier);
554 free_irq(irq, NULL);
555 release_region(io,8);
556}
557
558
559
560
561
562
563
564
565
566static int __init wdt_init(void)
567{
568 int ret;
569
570
571 if (wdt_set_heartbeat(heartbeat)) {
572 wdt_set_heartbeat(WD_TIMO);
573 printk(KERN_INFO "wdt: heartbeat value must be 0<heartbeat<65536, using %d\n",
574 WD_TIMO);
575 }
576
577 if (!request_region(io, 8, "wdt501p")) {
578 printk(KERN_ERR "wdt: I/O address 0x%04x already in use\n", io);
579 ret = -EBUSY;
580 goto out;
581 }
582
583 ret = request_irq(irq, wdt_interrupt, IRQF_DISABLED, "wdt501p", NULL);
584 if(ret) {
585 printk(KERN_ERR "wdt: IRQ %d is not free.\n", irq);
586 goto outreg;
587 }
588
589 ret = register_reboot_notifier(&wdt_notifier);
590 if(ret) {
591 printk(KERN_ERR "wdt: cannot register reboot notifier (err=%d)\n", ret);
592 goto outirq;
593 }
594
595#ifdef CONFIG_WDT_501
596 ret = misc_register(&temp_miscdev);
597 if (ret) {
598 printk(KERN_ERR "wdt: cannot register miscdev on minor=%d (err=%d)\n",
599 TEMP_MINOR, ret);
600 goto outrbt;
601 }
602#endif
603
604 ret = misc_register(&wdt_miscdev);
605 if (ret) {
606 printk(KERN_ERR "wdt: cannot register miscdev on minor=%d (err=%d)\n",
607 WATCHDOG_MINOR, ret);
608 goto outmisc;
609 }
610
611 ret = 0;
612 printk(KERN_INFO "WDT500/501-P driver 0.10 at 0x%04x (Interrupt %d). heartbeat=%d sec (nowayout=%d)\n",
613 io, irq, heartbeat, nowayout);
614#ifdef CONFIG_WDT_501
615 printk(KERN_INFO "wdt: Fan Tachometer is %s\n", (tachometer ? "Enabled" : "Disabled"));
616#endif
617
618out:
619 return ret;
620
621outmisc:
622#ifdef CONFIG_WDT_501
623 misc_deregister(&temp_miscdev);
624outrbt:
625#endif
626 unregister_reboot_notifier(&wdt_notifier);
627outirq:
628 free_irq(irq, NULL);
629outreg:
630 release_region(io,8);
631 goto out;
632}
633
634module_init(wdt_init);
635module_exit(wdt_exit);
636
637MODULE_AUTHOR("Alan Cox");
638MODULE_DESCRIPTION("Driver for ISA ICS watchdog cards (WDT500/501)");
639MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR);
640MODULE_ALIAS_MISCDEV(TEMP_MINOR);
641MODULE_LICENSE("GPL");
642