cleanup
[linux-2.4.21-pre4.git] / drivers / char / pcwd.c
1 /*
2  * PC Watchdog Driver
3  * by Ken Hollis (khollis@bitgate.com)
4  *
5  * Permission granted from Simon Machell (73244.1270@compuserve.com)
6  * Written for the Linux Kernel, and GPLed by Ken Hollis
7  *
8  * 960107       Added request_region routines, modulized the whole thing.
9  * 960108       Fixed end-of-file pointer (Thanks to Dan Hollis), added
10  *              WD_TIMEOUT define.
11  * 960216       Added eof marker on the file, and changed verbose messages.
12  * 960716       Made functional and cosmetic changes to the source for
13  *              inclusion in Linux 2.0.x kernels, thanks to Alan Cox.
14  * 960717       Removed read/seek routines, replaced with ioctl.  Also, added
15  *              check_region command due to Alan's suggestion.
16  * 960821       Made changes to compile in newer 2.0.x kernels.  Added
17  *              "cold reboot sense" entry.
18  * 960825       Made a few changes to code, deleted some defines and made
19  *              typedefs to replace them.  Made heartbeat reset only available
20  *              via ioctl, and removed the write routine.
21  * 960828       Added new items for PC Watchdog Rev.C card.
22  * 960829       Changed around all of the IOCTLs, added new features,
23  *              added watchdog disable/re-enable routines.  Added firmware
24  *              version reporting.  Added read routine for temperature.
25  *              Removed some extra defines, added an autodetect Revision
26  *              routine.
27  * 961006       Revised some documentation, fixed some cosmetic bugs.  Made
28  *              drivers to panic the system if it's overheating at bootup.
29  * 961118       Changed some verbiage on some of the output, tidied up
30  *              code bits, and added compatibility to 2.1.x.
31  * 970912       Enabled board on open and disable on close.
32  * 971107       Took account of recent VFS changes (broke read).
33  * 971210       Disable board on initialisation in case board already ticking.
34  * 971222       Changed open/close for temperature handling
35  *              Michael Meskes <meskes@debian.org>.
36  * 980112       Used minor numbers from include/linux/miscdevice.h
37  * 990403       Clear reset status after reading control status register in 
38  *              pcwd_showprevstate(). [Marc Boucher <marc@mbsi.ca>]
39  * 990605       Made changes to code to support Firmware 1.22a, added
40  *              fairly useless proc entry.
41  * 990610       removed said useless proc code for the merge <alan>
42  * 000403       Removed last traces of proc code. <davej>
43  * 011214       Added nowayout module option to override CONFIG_WATCHDOG_NOWAYOUT <Matt_Domsch@dell.com>
44  * 020210       Backported 2.5 open_allowed changes, and got rid of a useless
45  *              variable <rob@osinvestor.com>
46  *              Added timeout module option to override default
47  * 020306       Support the PCI version [Lindsay Harris <lindsay@bluegum.com>]
48  */
49
50 /*
51  *  A bells and whistles driver is available from http://www.pcwd.de/
52  */
53 #include <linux/module.h>
54
55 #include <linux/types.h>
56 #include <linux/delay.h>
57 #include <linux/miscdevice.h>
58 #include <linux/watchdog.h>
59 #include <linux/init.h>
60
61 #include <asm/uaccess.h>
62 #include <asm/io.h>
63
64 #include <linux/notifier.h>
65 #include <linux/reboot.h>
66 #include <linux/pci.h>
67
68 #define WD_VER                  "1.13 (03/06/2002)"
69
70 /*  Stuff for the PCI version  */
71 #ifndef PCI_VENDOR_ID_QUICKLOGIC
72 #define PCI_VENDOR_ID_QUICKLOGIC        0x11e3
73 #endif
74 #ifndef PCI_DEVICE_ID_BERKSHIRE
75 #define PCI_DEVICE_ID_BERKSHIRE 0x5030
76 #endif
77
78 /*
79  * It should be noted that PCWD_REV_B was removed because A and B
80  * are essentially the same types of card, with the exception that B
81  * has temperature reporting.  Since I didn't receive a Rev.B card,
82  * the Rev.B card is not supported.  (It's a good thing too, as they
83  * are no longer in production.)
84  */
85 #define PCWD_REV_A      0
86 #define PCWD_REV_C      1
87 #define PCWD_REV_PCI    2
88
89 static int timeout_val;
90 static int timeout = 2;
91 static int expect_close = 0;
92
93 MODULE_PARM (timeout, "i");
94 MODULE_PARM_DESC (timeout, "Watchdog timeout in seconds (default=2)");
95
96 #ifdef CONFIG_WATCHDOG_NOWAYOUT
97 static int nowayout = 1;
98 #else
99 static int nowayout = 0;
100 #endif
101
102 MODULE_PARM (nowayout, "i");
103 MODULE_PARM_DESC (nowayout,
104                   "Watchdog cannot be stopped once started (default=CONFIG_WATCHDOG_NOWAYOUT)");
105
106 /*
107  * These are the defines for the PC Watchdog card, revision A.
108  */
109 #define WD_WDRST                0x01    /* Previously reset state */
110 #define WD_T110                 0x02    /* Temperature overheat sense */
111 #define WD_HRTBT                0x04    /* Heartbeat sense */
112 #define WD_RLY2                 0x08    /* External relay triggered */
113 #define WD_SRLY2                0x80    /* Software external relay triggered */
114
115 /*
116  *  Differences between cards regarding how they perform some operations
117  *  are handled by an array of structs, with per card functions for the
118  *  incompatible operations.  It's all defined here.
119  */
120
121 /*  ENABLE/DISABLE the card  */
122 typedef int (*fn_enable) (int); /* Enable/disable card */
123
124 static int pcwd_enable_card (int enable);       /* Actually works */
125 static int pcwd_enable_nop (int enable);        /* NOP - REV A cannot */
126
127 /* Obtain firmware version, if possible */
128 #define PCWD_FIRMWARE_BSZ       16      /* Version buffer size */
129 typedef void (*fn_firmware) (char *bp);
130
131 static void pcwd_firmware_ver_none (char *bp);  /* REV A can't do it */
132 static void pcwd_firmware_ver_revc (char *bp);  /* REV C boards can */
133 static void pcwd_firmware_ver_pci (char *bp);   /* PCI boards can too */
134
135 /*  Tickle the watchdog timer */
136 typedef void (*fn_tickle) (void);
137
138 static void pcwd_tickle_reva (void);    /* Rev A only */
139 static void pcwd_tickle (void); /* Rev C, PCI */
140
141 /*  Determine reboot and temperature status */
142 typedef int (*fn_status) (int reset_boot);
143
144 static int pcwd_get_stat_reva (int reset_boot);
145 static int pcwd_get_stat (int reset_boot);
146
147 /*  Per card type specifications */
148 typedef struct {
149         fn_tickle wd_tickle;    /* Reset the watchdog */
150         fn_enable enable_card;  /* Enable/disable card, if possible */
151         fn_firmware firmware_ver;       /* Get firmware version, if possible */
152         fn_status wd_status;    /* Card reset and/or over temp */
153         int io_size;            /* I/O space used */
154         const char *name;       /* Nice name to display */
155 } PCWD_CARD_INFO;
156
157 /* Per card information, indexed by card version ID */
158 static PCWD_CARD_INFO pcwd_card_info[] = {
159         {
160          pcwd_tickle_reva,
161          pcwd_enable_nop,
162          pcwd_firmware_ver_none,
163          pcwd_get_stat_reva,
164          2,
165          "Berkshire Products PC Watchdog (REV A)",
166          },
167         {
168          pcwd_tickle,
169          pcwd_enable_card,
170          pcwd_firmware_ver_revc,
171          pcwd_get_stat,
172          4,
173          "Berkshire Products PC Watchdog (REV C)",
174          },
175         {
176          pcwd_tickle,
177          pcwd_enable_card,
178          pcwd_firmware_ver_pci,
179          pcwd_get_stat,
180          8,
181          "Berkshire Products PC Watchdog (PCI)",
182          },
183 };
184
185 /*  Overall driver information, including per card pointer */
186 static struct {
187         PCWD_CARD_INFO *card_info;      /* Points to one of the above */
188         atomic_t open_allowed;  /* Watchdog is single open */
189         int flags;              /* Defined below */
190         int boot_status;        /* Card status at boot time */
191         int io_addr;            /* Card's base address */
192 } pcwd_info = {
193 NULL, ATOMIC_INIT (1), 0, 0, 0};
194
195 /*  Bits allocated in flags above. */
196 #define PCWD_HAS_TEMP   0x0001  /* Set when thermometer available */
197 #define PCWD_PCI_REG    0x0002  /* Set if PCI register code worked */
198 #define PCWD_TEMP_PANIC 0x0004  /* Panic when over temperature */
199
200 static spinlock_t io_lock;
201
202 /* D E T E R M I N E   C A R D   S T A T U S   F U N C T I O N S  */
203 /*   Rev A cards return status information from the base register,
204  * which is used for the temperature in other cards.  */
205
206 static int
207 pcwd_get_stat_reva (int reset_boot)
208 {
209         int retval;
210         int status;
211
212         spin_lock (&io_lock);
213         status = inb_p (pcwd_info.io_addr);
214         spin_unlock (&io_lock);
215
216         /* Transform the card register to the ioctl bits we use internally */
217         retval = WDIOF_MAGICCLOSE;
218         if (status & WD_WDRST)
219                 retval |= WDIOF_CARDRESET;
220         if (status & WD_T110)
221                 retval |= WDIOF_OVERHEAT;
222
223         return retval;
224 }
225
226 /*
227  *  Rev C and PCI cards return card status in the base address + 1 register.
228  *  And use different bits to indicate a card initiated reset, and
229  *  an over-temperature condition.  And the reboot status can be reset.
230  */
231
232 static int
233 pcwd_get_stat (int reset_boot)
234 {
235         int retval;
236         int status;
237
238         spin_lock (&io_lock);
239         status = inb_p (pcwd_info.io_addr + 1);
240         if (reset_boot) {
241                 /*  NOTE:  the REV C card clears the "card caused reboot"
242                  * flag when writing ANY value to this port.  However,
243                  * the PCI card requires writing a 1 to bit 0.  */
244                 outb_p (0x01, pcwd_info.io_addr + 1);
245         }
246         spin_unlock (&io_lock);
247
248         retval = 0;
249         if (status & 0x01)
250                 retval |= WDIOF_CARDRESET;
251         if (status & 0x04)
252                 retval |= WDIOF_OVERHEAT;
253
254         return retval;
255 }
256
257 /*  W A T C H D O G   T I M E R   R E S E T   F U N C T I O N S   */
258 /*  Rev A cards are reset by setting a specific bit in register 1.  */
259
260 static void
261 pcwd_tickle_reva (void)
262 {
263         int wdrst_stat;
264
265         spin_lock (&io_lock);
266         wdrst_stat = inb_p (pcwd_info.io_addr);
267         wdrst_stat = (wdrst_stat & 0x0F) | WD_WDRST;
268
269         outb_p (wdrst_stat, pcwd_info.io_addr + 1);
270         spin_unlock (&io_lock);
271
272         return;
273 }
274
275 /*  Other cards are reset by writing anything to the base register.  */
276
277 static void
278 pcwd_tickle (void)
279 {
280         spin_lock (&io_lock);
281         outb_p (0x42, pcwd_info.io_addr);
282         spin_unlock (&io_lock);
283
284         return;
285 }
286
287 static int
288 pcwd_ioctl (struct inode *inode, struct file *file,
289             unsigned int cmd, unsigned long arg)
290 {
291         int rv;
292         int retval;
293
294         static struct watchdog_info ident = {
295                 WDIOF_OVERHEAT | WDIOF_CARDRESET,
296                 1,
297                 "PCWD"
298         };
299
300         switch (cmd) {
301         case WDIOC_GETSUPPORT:
302                 rv = copy_to_user ((void *) arg, &ident, sizeof (ident));
303                 return rv ? -EFAULT : 0;
304
305         case WDIOC_GETSTATUS:
306                 rv = pcwd_info.card_info->wd_status (0);
307
308                 if (rv & WDIOF_OVERHEAT) {
309                         if (pcwd_info.flags & PCWD_TEMP_PANIC)
310                                 panic ("pcwd: Temperature overheat trip!\n");
311                 }
312
313                 if (put_user (rv, (int *) arg))
314                         return -EFAULT;
315                 return 0;
316
317         case WDIOC_GETBOOTSTATUS:
318                 rv = pcwd_info.boot_status;
319
320                 if (put_user (rv, (int *) arg))
321                         return -EFAULT;
322                 return 0;
323
324         case WDIOC_GETTEMP:
325                 rv = 0;
326                 if (pcwd_info.flags & PCWD_HAS_TEMP) {
327                         spin_lock (&io_lock);
328                         rv = inb_p (pcwd_info.io_addr);
329                         spin_unlock (&io_lock);
330                 }
331                 if (put_user (rv, (int *) arg))
332                         return -EFAULT;
333                 return 0;
334
335         case WDIOC_SETOPTIONS:
336                 if (copy_from_user (&rv, (int *) arg, sizeof (int)))
337                         return -EFAULT;
338
339                 retval = -EINVAL;
340
341                 if (rv & WDIOS_DISABLECARD) {
342                         if (!pcwd_info.card_info->enable_card (0)) {
343                                 printk (KERN_EMERG
344                                         "pcwd: Could not disable card\n");
345                                 return -EIO;
346                         }
347
348                         retval = 0;
349                 }
350
351                 if (rv & WDIOS_ENABLECARD) {
352                         if (!pcwd_info.card_info->enable_card (1)) {
353                                 printk (KERN_EMERG
354                                         "pcwd: Could not enable card\n");
355                                 return -EIO;
356                         }
357                         retval = 0;
358                 }
359
360                 if (rv & WDIOS_TEMPPANIC) {
361                         pcwd_info.flags |= PCWD_TEMP_PANIC;
362
363                         retval = 0;
364                 }
365
366                 return retval;
367
368         case WDIOC_KEEPALIVE:
369                 pcwd_info.card_info->wd_tickle ();
370                 return 0;
371
372         default:
373                 return -ENOTTY;
374         }
375
376         return 0;
377 }
378
379 /*   Write:  only for the watchdog device (thermometer is read-only).  */
380
381 static ssize_t
382 pcwd_write (struct file *file, const char *buf, size_t len, loff_t * ppos)
383 {
384         /*  Can't seek (pwrite) on this device  */
385         if (ppos != &file->f_pos)
386                 return -ESPIPE;
387
388         if (len) {
389                 if (!nowayout) {
390                         size_t i;
391
392                         /* In case it was set long ago */
393                         expect_close = 0;
394
395                         for (i = 0; i != len; i++) {
396                                 char c;
397                                 if (get_user(c, buf + i))
398                                         return -EFAULT;
399                                 if (c == 'V')
400                                         expect_close = 1;
401                         }
402                 }
403                 pcwd_info.card_info->wd_tickle ();
404                 return 1;
405         }
406         return 0;
407 }
408
409 static int
410 pcwd_open (struct inode *ino, struct file *filep)
411 {
412         switch (MINOR (ino->i_rdev)) {
413         case WATCHDOG_MINOR:
414                 if (!atomic_dec_and_test (&pcwd_info.open_allowed)) {
415                         atomic_inc (&pcwd_info.open_allowed);
416                         return -EBUSY;
417                 }
418
419                 /*  Enable the card  */
420                 pcwd_info.card_info->enable_card (1);
421                 pcwd_info.card_info->wd_tickle ();
422
423                 return 0;
424
425         case TEMP_MINOR:
426                 if (pcwd_info.flags & PCWD_HAS_TEMP) {
427                         return 0;
428                 }
429                 return -ENODEV;
430
431         default:
432                 return -ENODEV;
433         }
434 }
435
436 /*      Read:  applies only to the thermometer (watchdog is write only).  */
437 static ssize_t
438 pcwd_read (struct file *file, char *buf, size_t count, loff_t * ppos)
439 {
440         unsigned short c;
441         unsigned char cp;
442
443         /*  Can't seek (pread) on this device  */
444         if (ppos != &file->f_pos)
445                 return -ESPIPE;
446
447         /*
448          * Convert celsius to fahrenheit, since this was
449          * the decided 'standard' for this return value.
450          */
451
452         spin_lock (&io_lock);
453         c = inb_p (pcwd_info.io_addr);
454         spin_unlock (&io_lock);
455
456         cp = (c * 9 / 5) + 32;
457         if (copy_to_user (buf, &cp, 1))
458                 return -EFAULT;
459
460         return 1;
461 }
462
463 static int
464 pcwd_close (struct inode *ino, struct file *filep)
465 {
466         switch (MINOR (ino->i_rdev)) {
467         case WATCHDOG_MINOR:
468                 if (expect_close)
469                         pcwd_info.card_info->enable_card (0);
470
471                 atomic_inc (&pcwd_info.open_allowed);
472                 break;
473
474         case TEMP_MINOR:
475                 break;
476         }
477         return 0;
478 }
479
480 /*
481  *  System is shutting down, so disable the card.  Otherwise the timeout
482  * may expire during shutdown.  Of course, this means a hang during
483  * shutdown will not be reset, but somebody is probably nearby and will
484  * notice.  The alternative is to have the shutdown aborted when the
485  * watchdog expires and hits reset.
486  */
487
488 static int
489 pcwd_notify_sys (struct notifier_block *this, unsigned long code, void *unused)
490 {
491         if (code == SYS_DOWN || code == SYS_HALT) {
492                 /*
493                  *  If initialisation is still in progress, the device pointer
494                  * may not be valid, so check, just to make sure.
495                  */
496
497                 if (pcwd_info.card_info)
498                         pcwd_info.card_info->enable_card (0);
499         }
500
501         return NOTIFY_DONE;
502 }
503
504 /*  C A R D   E N A B L E / D I S A B L E   F U N C T I O N S  */
505 /*  Enable/disable the card, not REV A.  The two writes are required by card */
506
507 static int
508 pcwd_enable_card (int enable)
509 {
510         int stat_reg;
511
512         spin_lock (&io_lock);
513         if (enable) {
514                 outb_p (0x00, pcwd_info.io_addr + 3);
515         } else {
516                 outb_p (0xA5, pcwd_info.io_addr + 3);
517                 outb_p (0xA5, pcwd_info.io_addr + 3);
518         }
519         stat_reg = inb_p (pcwd_info.io_addr + 2);
520         spin_unlock (&io_lock);
521
522         stat_reg &= 0x10;       /* "disabled when set" bit */
523         if (enable) {
524                 stat_reg ^= 0x10;
525         }
526
527         return stat_reg;
528 }
529
530 static int
531 pcwd_enable_nop (int enable)
532 {
533         return 0;
534 }
535
536 static void __init
537 set_card_type (int is_pci)
538 {
539
540         if (is_pci) {
541                 pcwd_info.card_info = &pcwd_card_info[PCWD_REV_PCI];
542                 pcwd_info.flags |= PCWD_PCI_REG;
543         } else {
544                 pcwd_info.card_info = &pcwd_card_info[PCWD_REV_C];
545
546                 /* REV A cards use only 2 io ports; test
547                  * presumes a floating bus reads as 0xff.  */
548                 if ((inb (pcwd_info.io_addr + 2) == 0xFF) ||
549                     (inb (pcwd_info.io_addr + 3) == 0xFF)) {
550                         pcwd_info.card_info = &pcwd_card_info[PCWD_REV_A];
551                 }
552         }
553
554         return;
555 }
556
557 /* G E T    F I R M W A R E   V E R S I O N   F U N C T I O N S    */
558 /* REV A can't do it */
559 static void __init
560 pcwd_firmware_ver_none (char *bp)
561 {
562         strncpy (bp, "<unavailable>", PCWD_FIRMWARE_BSZ);
563
564         return;
565 }
566
567 /* PCI boards can too */
568 static void __init
569 pcwd_firmware_ver_pci (char *bp)
570 {
571         int count;
572
573         /* Write the 'Get Firmware Version' command to port 6 and wait */
574         outb (0x08, pcwd_info.io_addr + 6);
575
576         /* Card sets bit 0x40 (WRSP) bit in port 2.  Can take 10ms! */
577         for (count = 0; count < 15; ++count) {
578                 mdelay (1);     /* Board responds slowly */
579
580                 if (inb (pcwd_info.io_addr + 2) & 0x40) {
581                         /* Board says data now valid */
582
583                         snprintf (bp, PCWD_FIRMWARE_BSZ, "%u.%u",
584                                   inb (pcwd_info.io_addr + 5),
585                                   inb (pcwd_info.io_addr + 4));
586
587                         return;
588                 }
589         }
590         strncpy (bp, "<card no answer>", PCWD_FIRMWARE_BSZ);
591
592         return;
593 }
594
595 /*
596  *   REV C boards read diagnostic (including firmware version) data
597  * from the register 0.  To do this, the card is put into diagnostic
598  * mode, then the command is submitted and data read from register 0.
599  * NOTE:  the onboard processor writes 4 bits at a time to the register,
600  * so it's necessary to wait for the data to stabilise before
601  * accepting it.
602  */
603
604 static int __init
605 send_command (int cmd)
606 {
607         int ii;
608
609         int reg0, last_reg0;    /* Double read for stabilising */
610
611         outb (cmd, pcwd_info.io_addr + 2);
612         /*
613          *    The following delay need only be 200 microseconds according
614          *  to the spec I have.  But my card seems slower, as waiting
615          *  250 microseconds returns valid data, but NOT from this
616          *  command.  The 1000 value may be excessive, but is reliable.
617          */
618         mdelay (1);
619
620         reg0 = inb (pcwd_info.io_addr);
621         for (ii = 0; ii < 25; ++ii) {
622                 last_reg0 = reg0;
623                 reg0 = inb (pcwd_info.io_addr);
624
625                 if (reg0 == last_reg0)
626                         break;  /* Data is stable */
627
628                 udelay (250);
629         }
630
631         return reg0;
632 }
633
634 /*  REV C board function to retrieve firmware version */
635 static void __init
636 pcwd_firmware_ver_revc (char *bp)
637 {
638         int i, found = 0, count = 0;
639
640         /*  Set the card into debug mode to find firmware version */
641         outb_p (0x00, pcwd_info.io_addr + 2);   /* Spec says to do this */
642         udelay (500);
643
644         while ((count < 3) && (!found)) {
645                 i = send_command (0x80);
646
647                 if (i == 0x00) {
648                         found = 1;
649                         break;
650                 } else if (i == 0xF3) {
651                         /* Card does not like what we've done to it */
652                         outb_p (0x00, pcwd_info.io_addr + 2);
653                         udelay (1200);  /* Spec says wait 1ms */
654                         outb_p (0x00, pcwd_info.io_addr + 2);
655                         udelay (500);
656                 }
657                 count++;
658         }
659
660         if (found) {
661
662                 *bp++ = send_command (0x81);
663                 *bp++ = '.';
664                 *bp++ = send_command (0x82);
665                 *bp++ = send_command (0x83);
666                 *bp++ = send_command (0x84);
667                 *bp++ = '\0';
668
669                 /* Out of debug mode */
670                 outb (0x00, pcwd_info.io_addr + 2);
671         } else
672                 strncpy (bp, "<err - no go>", PCWD_FIRMWARE_BSZ);
673
674         return;
675 }
676
677 /*   Initialisation function called ONLY from the PCI layer.  */
678
679 static int __init
680 pcwd_init_one (struct pci_dev *dev, const struct pci_device_id *ent)
681 {
682         static int devices = 0;
683
684         ++devices;
685         if (devices > 1) {
686                 printk (KERN_ERR "pcwd: Driver supports only ONE device\n");
687
688                 return -ENODEV;
689         }
690
691         pcwd_info.io_addr = pci_resource_start (dev, 0);
692
693         if (pcwd_info.io_addr == 0 || pci_enable_device (dev))
694                 return -ENODEV;
695
696         return 0;
697 }
698
699 static struct pci_device_id pcwd_pci_tbl[] __initdata = {
700         {PCI_VENDOR_ID_QUICKLOGIC, PCI_DEVICE_ID_BERKSHIRE,
701          PCI_ANY_ID, PCI_ANY_ID,},
702         {0},                    /* End of list */
703 };
704
705 MODULE_DEVICE_TABLE (pci, pcwd_pci_tbl);
706
707 static struct pci_driver pcwd_driver = {
708         name:"pcwd",
709         id_table:pcwd_pci_tbl,
710         probe:pcwd_init_one,
711 };
712
713 static struct file_operations pcwd_fops = {
714         owner:THIS_MODULE,
715         write:pcwd_write,
716         ioctl:pcwd_ioctl,
717         open:pcwd_open,
718         release:pcwd_close,
719 };
720
721 static struct miscdevice pcwd_miscdev = {
722         WATCHDOG_MINOR,
723         "watchdog",
724         &pcwd_fops
725 };
726
727 static struct file_operations pcwd_temp_fops = {
728         owner:THIS_MODULE,
729         read:pcwd_read,
730         open:pcwd_open,
731         release:pcwd_close,
732 };
733
734 static struct miscdevice temp_miscdev = {
735         TEMP_MINOR,
736         "temperature",
737         &pcwd_temp_fops
738 };
739
740 /* Need to know about shutdown to kill the timer - may reset during shutdown! */
741     static struct notifier_block pcwd_notifier =
742 {
743         pcwd_notify_sys,
744         NULL,
745         0,
746 };
747
748 /*
749  *   The ISA cards have a heartbeat bit in one of the registers, which
750  *  register is card dependent.  The heartbeat bit is monitored, and if
751  *  found, is considered proof that a Berkshire card has been found.
752  *  The initial rate is once per second at board start up, then twice
753  *  per second for normal operation.
754  */
755 static int __init
756 check_isa_card (int base_addr)
757 {
758         int reg0, last_reg0;    /* Reg 0, in case it's REV A */
759         int reg1, last_reg1;    /* Register 1 for REV C cards */
760         int ii;
761         int retval;
762
763         /* As suggested by Alan Cox - this is a safety measure. */
764         if (!request_region (base_addr, 4, "pcwd-isa")) {
765                 printk (KERN_INFO "pcwd: Port 0x%x unavailable\n", base_addr);
766                 return 0;
767         }
768
769         retval = 0;
770
771         reg0 = inb_p (base_addr);       /* For REV A boards */
772         reg1 = inb (base_addr + 1);     /* For REV C boards */
773         if (reg0 != 0xff || reg1 != 0xff) {
774                 /* Not an 'ff' from a floating bus, so must be a card! */
775                 for (ii = 0; ii < timeout_val; ++ii) {
776
777                         set_current_state (TASK_INTERRUPTIBLE);
778                         schedule_timeout (HZ / 2);
779
780                         last_reg0 = reg0;
781                         last_reg1 = reg1;
782
783                         reg0 = inb_p (base_addr);
784                         reg1 = inb (base_addr + 1);
785
786                         /* Has either hearbeat bit changed?  */
787                         if ((reg0 ^ last_reg0) & WD_HRTBT ||
788                             (reg1 ^ last_reg1) & 0x02) {
789
790                                 retval = 1;
791                                 break;
792                         }
793                 }
794         }
795         release_region (base_addr, 4);
796
797         return retval;
798 }
799
800 static int __init
801 pcwd_card_init (void)
802 {
803         int retval;
804         char fvbuf[PCWD_FIRMWARE_BSZ];
805
806         pcwd_info.card_info->firmware_ver (fvbuf);
807
808         printk (KERN_INFO "pcwd: %s at port 0x%03x (Firmware: %s)\n",
809                 pcwd_info.card_info->name, pcwd_info.io_addr, fvbuf);
810
811         /* Returns 0xf0 in temperature register if no thermometer */
812         if (inb (pcwd_info.io_addr) != 0xF0) {
813                 pcwd_info.flags |= PCWD_HAS_TEMP;
814                 printk (KERN_INFO "pcwd: Temperature option detected\n");
815         }
816
817         if (nowayout)
818                 printk (KERN_INFO
819                         "pcwd: Watchdog cannot be stopped once started\n");
820
821         /* Record the power up status of "card did reset" and/or temp trip */
822         pcwd_info.boot_status = pcwd_info.card_info->wd_status (1);
823
824         if (pcwd_info.boot_status & WDIOF_CARDRESET)
825                 printk (KERN_INFO
826                         "pcwd: Previous reboot was caused by the card\n");
827
828         if (pcwd_info.boot_status & WDIOF_OVERHEAT) {
829                 printk (KERN_EMERG
830                         "pcwd: Card senses a CPU Overheat.  Panicking!\n");
831                 panic ("pcwd: CPU Overheat\n");
832         }
833
834         if (pcwd_info.boot_status == 0)
835                 printk (KERN_INFO "pcwd: Cold boot sense\n");
836
837         pcwd_info.card_info->enable_card (0);
838
839         retval = 0;
840         if (!request_region (pcwd_info.io_addr,
841                              pcwd_info.card_info->io_size,
842                              pcwd_info.card_info->name)) {
843                 printk (KERN_ERR "pcwd: I/0 %d is not free\n",
844                         pcwd_info.io_addr);
845
846                 return retval;
847         }
848
849         retval = misc_register (&pcwd_miscdev);
850         if (retval) {
851                 release_region (pcwd_info.io_addr,
852                                 pcwd_info.card_info->io_size);
853                 printk (KERN_ERR "pcwd: can't misc_register on minor %d\n",
854                         WATCHDOG_MINOR);
855                 return retval;
856         }
857
858         if (pcwd_info.flags & PCWD_HAS_TEMP) {
859                 if (misc_register (&temp_miscdev)) {
860                         printk (KERN_ERR
861                                 "pwcd: can't misc_register thermometer - disabling it\n");
862                         pcwd_info.flags &= ~PCWD_HAS_TEMP;
863                 }
864         }
865
866         retval = register_reboot_notifier (&pcwd_notifier);
867         if (retval) {
868                 if (pcwd_info.flags & PCWD_HAS_TEMP)
869                         misc_deregister (&temp_miscdev);
870                 misc_deregister (&pcwd_miscdev);
871                 release_region (pcwd_info.io_addr,
872                                 pcwd_info.card_info->io_size);
873         }
874
875         return retval;
876 }
877
878 static int __init
879 pcwatchdog_init (void)
880 {
881         int i, found = 0;
882         /*
883          * ISA card auto-probe addresses available.  Last one is only
884          * available on REV C cards.
885          */
886         static int pcwd_ioports[] = { 0x270, 0x350, 0x370 };
887 #define PCWD_NUM_ADDR   (sizeof(pcwd_ioports)/sizeof(pcwd_ioports[0]))
888
889         timeout_val = timeout * 2;
890
891         spin_lock_init (&io_lock);
892
893         printk (KERN_INFO "pcwd: v%s Ken Hollis (kenji@bitgate.com)\n", WD_VER);
894
895         if (pci_register_driver (&pcwd_driver) > 0) {
896                 found = 1;
897                 set_card_type (1);      /* Set to PCI card model */
898         } else {
899                 /* No PCI entry, try the ISA addresses.  */
900                 for (i = 0; i < PCWD_NUM_ADDR; i++) {
901
902                         if (check_isa_card (pcwd_ioports[i])) {
903                                 found = 1;
904
905                                 pcwd_info.io_addr = pcwd_ioports[i];
906
907                                 set_card_type (0);
908                                 break;
909                         }
910                 }
911         }
912
913         if (!found) {
914                 printk (KERN_INFO
915                         "pcwd: No card detected, or port not available\n");
916                 return -EIO;
917         }
918
919         return pcwd_card_init ();
920 }
921
922 static void __exit
923 pcwatchdog_exit (void)
924 {
925         unregister_reboot_notifier (&pcwd_notifier);
926         misc_deregister (&pcwd_miscdev);
927
928         if (!nowayout)
929                 pcwd_info.card_info->enable_card (0);
930
931         if (pcwd_info.flags & PCWD_HAS_TEMP)
932                 misc_deregister (&temp_miscdev);
933
934         release_region (pcwd_info.io_addr, pcwd_info.card_info->io_size);
935
936         if (pcwd_info.flags & PCWD_PCI_REG)
937                 pci_unregister_driver (&pcwd_driver);
938
939         return;
940 }
941
942 module_init (pcwatchdog_init);
943 module_exit (pcwatchdog_exit);
944
945 MODULE_LICENSE ("GPL");
946
947 EXPORT_NO_SYMBOLS;