1 /* cpwatchdog.c - driver implementation for hardware watchdog
2 * timers found on Sun Microsystems CP1400 and CP1500 boards.
4 * This device supports both the generic Linux watchdog
5 * interface and Solaris-compatible ioctls as best it is
8 * NOTE: CP1400 systems appear to have a defective intr_mask
9 * register on the PLD, preventing the disabling of
10 * timer interrupts. We use a timer to periodically
11 * reset 'stopped' watchdogs on affected platforms.
13 * Copyright (c) 2000 Eric Brower (ebrower@usa.net)
16 #include <linux/kernel.h>
17 #include <linux/module.h>
19 #include <linux/errno.h>
20 #include <linux/major.h>
21 #include <linux/init.h>
22 #include <linux/miscdevice.h>
23 #include <linux/sched.h>
24 #include <linux/interrupt.h>
25 #include <linux/ioport.h>
26 #include <linux/timer.h>
27 #include <linux/smp_lock.h>
30 #include <asm/oplib.h>
31 #include <asm/uaccess.h>
33 #include <asm/watchdog.h>
35 #define WD_OBPNAME "watchdog"
36 #define WD_BADMODEL "SUNW,501-5336"
37 #define WD_BTIMEOUT (jiffies + (HZ * 1000))
38 #define WD_BLIMIT 0xFFFF
40 #define WD0_DEVNAME "watchdog0"
41 #define WD1_DEVNAME "watchdog1"
42 #define WD2_DEVNAME "watchdog2"
49 /* Internal driver definitions
51 #define WD0_ID 0 /* Watchdog0 */
52 #define WD1_ID 1 /* Watchdog1 */
53 #define WD2_ID 2 /* Watchdog2 */
54 #define WD_NUMDEVS 3 /* Device contains 3 timers */
56 #define WD_INTR_OFF 0 /* Interrupt disable value */
57 #define WD_INTR_ON 1 /* Interrupt enable value */
59 #define WD_STAT_INIT 0x01 /* Watchdog timer is initialized */
60 #define WD_STAT_BSTOP 0x02 /* Watchdog timer is brokenstopped */
61 #define WD_STAT_SVCD 0x04 /* Watchdog interrupt occurred */
63 /* Register value definitions
65 #define WD0_INTR_MASK 0x01 /* Watchdog device interrupt masks */
66 #define WD1_INTR_MASK 0x02
67 #define WD2_INTR_MASK 0x04
69 #define WD_S_RUNNING 0x01 /* Watchdog device status running */
70 #define WD_S_EXPIRED 0x02 /* Watchdog device status expired */
72 /* Sun uses Altera PLD EPF8820ATC144-4
73 * providing three hardware watchdogs:
75 * 1) RIC - sends an interrupt when triggered
76 * 2) XIR - asserts XIR_B_RESET when triggered, resets CPU
77 * 3) POR - asserts POR_B_RESET when triggered, resets CPU, backplane, board
79 *** Timer register block definition (struct wd_timer_regblk)
81 * dcntr and limit registers (halfword access):
87 * dcntr - Current 16-bit downcounter value.
88 * When downcounter reaches '0' watchdog expires.
89 * Reading this register resets downcounter with 'limit' value.
90 * limit - 16-bit countdown value in 1/10th second increments.
91 * Writing this register begins countdown with input value.
92 * Reading from this register does not affect counter.
93 * NOTES: After watchdog reset, dcntr and limit contain '1'
95 * status register (byte access):
96 * ---------------------------
97 * | 7 | ... | 2 | 1 | 0 |
98 * --------------+------------
99 * |- UNUSED -| EXP | RUN |
100 * ---------------------------
101 * status- Bit 0 - Watchdog is running
102 * Bit 1 - Watchdog has expired
104 *** PLD register block definition (struct wd_pld_regblk)
106 * intr_mask register (byte access):
107 * ---------------------------------
108 * | 7 | ... | 3 | 2 | 1 | 0 |
109 * +-------------+------------------
110 * |- UNUSED -| WD3 | WD2 | WD1 |
111 * ---------------------------------
112 * WD3 - 1 == Interrupt disabled for watchdog 3
113 * WD2 - 1 == Interrupt disabled for watchdog 2
114 * WD1 - 1 == Interrupt disabled for watchdog 1
116 * pld_status register (byte access):
117 * UNKNOWN, MAGICAL MYSTERY REGISTER
120 #define WD_TIMER_REGSZ 16
122 #define WD1_OFF (WD_TIMER_REGSZ * 1)
123 #define WD2_OFF (WD_TIMER_REGSZ * 2)
124 #define PLD_OFF (WD_TIMER_REGSZ * 3)
126 #define WD_DCNTR 0x00
127 #define WD_LIMIT 0x04
128 #define WD_STATUS 0x08
130 #define PLD_IMASK (PLD_OFF + 0x00)
131 #define PLD_STATUS (PLD_OFF + 0x04)
133 /* Individual timer structure
138 unsigned char runstatus
;
147 unsigned char isbaddoggie
; /* defective PLD */
148 unsigned char opt_enable
;
149 unsigned char opt_reboot
;
150 unsigned short opt_timeout
;
151 unsigned char initialized
;
152 struct wd_timer watchdog
[WD_NUMDEVS
];
156 static struct wd_device wd_dev
= {
157 0, SPIN_LOCK_UNLOCKED
, 0, 0, 0, 0,
160 static struct timer_list wd_timer
;
162 static int wd0_timeout
= 0;
163 static int wd1_timeout
= 0;
164 static int wd2_timeout
= 0;
167 module_param (wd0_timeout
, int, 0);
168 MODULE_PARM_DESC(wd0_timeout
, "Default watchdog0 timeout in 1/10secs");
169 module_param (wd1_timeout
, int, 0);
170 MODULE_PARM_DESC(wd1_timeout
, "Default watchdog1 timeout in 1/10secs");
171 module_param (wd2_timeout
, int, 0);
172 MODULE_PARM_DESC(wd2_timeout
, "Default watchdog2 timeout in 1/10secs");
175 ("Eric Brower <ebrower@usa.net>");
177 ("Hardware watchdog driver for Sun Microsystems CP1400/1500");
178 MODULE_LICENSE("GPL");
179 MODULE_SUPPORTED_DEVICE
181 #endif /* ifdef MODULE */
183 /* Forward declarations of internal methods
186 static void wd_dumpregs(void);
188 static irqreturn_t
wd_interrupt(int irq
, void *dev_id
, struct pt_regs
*regs
);
189 static void wd_toggleintr(struct wd_timer
* pTimer
, int enable
);
190 static void wd_pingtimer(struct wd_timer
* pTimer
);
191 static void wd_starttimer(struct wd_timer
* pTimer
);
192 static void wd_resetbrokentimer(struct wd_timer
* pTimer
);
193 static void wd_stoptimer(struct wd_timer
* pTimer
);
194 static void wd_brokentimer(unsigned long data
);
195 static int wd_getstatus(struct wd_timer
* pTimer
);
197 /* PLD expects words to be written in LSB format,
198 * so we must flip all words prior to writing them to regs
200 static inline unsigned short flip_word(unsigned short word
)
202 return ((word
& 0xff) << 8) | ((word
>> 8) & 0xff);
205 #define wd_writew(val, addr) (writew(flip_word(val), addr))
206 #define wd_readw(addr) (flip_word(readw(addr)))
207 #define wd_writeb(val, addr) (writeb(val, addr))
208 #define wd_readb(addr) (readb(addr))
211 /* CP1400s seem to have broken PLD implementations--
212 * the interrupt_mask register cannot be written, so
213 * no timer interrupts can be masked within the PLD.
215 static inline int wd_isbroken(void)
217 /* we could test this by read/write/read/restore
218 * on the interrupt mask register only if OBP
219 * 'watchdog-enable?' == FALSE, but it seems
220 * ubiquitous on CP1400s
223 prom_getproperty(prom_root_node
, "model", val
, sizeof(val
));
224 return((!strcmp(val
, WD_BADMODEL
)) ? 1 : 0);
227 /* Retrieve watchdog-enable? option from OBP
228 * Returns 0 if false, 1 if true
230 static inline int wd_opt_enable(void)
234 opt_node
= prom_getchild(prom_root_node
);
235 opt_node
= prom_searchsiblings(opt_node
, "options");
236 return((-1 == prom_getint(opt_node
, "watchdog-enable?")) ? 0 : 1);
239 /* Retrieve watchdog-reboot? option from OBP
240 * Returns 0 if false, 1 if true
242 static inline int wd_opt_reboot(void)
246 opt_node
= prom_getchild(prom_root_node
);
247 opt_node
= prom_searchsiblings(opt_node
, "options");
248 return((-1 == prom_getint(opt_node
, "watchdog-reboot?")) ? 0 : 1);
251 /* Retrieve watchdog-timeout option from OBP
252 * Returns OBP value, or 0 if not located
254 static inline int wd_opt_timeout(void)
260 opt_node
= prom_getchild(prom_root_node
);
261 opt_node
= prom_searchsiblings(opt_node
, "options");
262 opt_node
= prom_getproperty(opt_node
,
267 /* atoi implementation */
268 for(opt_node
= 0; /* nop */; p
++) {
269 if(*p
>= '0' && *p
<= '9') {
270 opt_node
= (10*opt_node
)+(*p
-'0');
277 return((-1 == opt_node
) ? (0) : (opt_node
));
280 static int wd_open(struct inode
*inode
, struct file
*f
)
282 switch(iminor(inode
))
285 f
->private_data
= &wd_dev
.watchdog
[WD0_ID
];
288 f
->private_data
= &wd_dev
.watchdog
[WD1_ID
];
291 f
->private_data
= &wd_dev
.watchdog
[WD2_ID
];
297 /* Register IRQ on first open of device */
298 if(0 == wd_dev
.initialized
)
300 if (request_irq(wd_dev
.irq
,
304 (void *)wd_dev
.regs
)) {
305 printk("%s: Cannot register IRQ %d\n",
306 WD_OBPNAME
, wd_dev
.irq
);
309 wd_dev
.initialized
= 1;
312 return(nonseekable_open(inode
, f
));
315 static int wd_release(struct inode
*inode
, struct file
*file
)
320 static int wd_ioctl(struct inode
*inode
, struct file
*file
,
321 unsigned int cmd
, unsigned long arg
)
324 struct wd_timer
* pTimer
= (struct wd_timer
*)file
->private_data
;
325 void __user
*argp
= (void __user
*)arg
;
326 struct watchdog_info info
= {
329 "Altera EPF8820ATC144-4"
338 /* Generic Linux IOCTLs */
339 case WDIOC_GETSUPPORT
:
340 if(copy_to_user(argp
, &info
, sizeof(struct watchdog_info
))) {
344 case WDIOC_GETSTATUS
:
345 case WDIOC_GETBOOTSTATUS
:
346 if (put_user(0, (int __user
*)argp
))
349 case WDIOC_KEEPALIVE
:
350 wd_pingtimer(pTimer
);
352 case WDIOC_SETOPTIONS
:
353 if(copy_from_user(&setopt
, argp
, sizeof(unsigned int))) {
356 if(setopt
& WDIOS_DISABLECARD
) {
357 if(wd_dev
.opt_enable
) {
359 "%s: cannot disable watchdog in ENABLED mode\n",
363 wd_stoptimer(pTimer
);
365 else if(setopt
& WDIOS_ENABLECARD
) {
366 wd_starttimer(pTimer
);
372 /* Solaris-compatible IOCTLs */
374 setopt
= wd_getstatus(pTimer
);
375 if(copy_to_user(argp
, &setopt
, sizeof(unsigned int))) {
380 wd_starttimer(pTimer
);
383 if(wd_dev
.opt_enable
) {
384 printk("%s: cannot disable watchdog in ENABLED mode\n",
388 wd_stoptimer(pTimer
);
396 static long wd_compat_ioctl(struct file
*file
, unsigned int cmd
,
399 int rval
= -ENOIOCTLCMD
;
402 /* solaris ioctls are specific to this driver */
407 rval
= wd_ioctl(file
->f_dentry
->d_inode
, file
, cmd
, arg
);
410 /* everything else is handled by the generic compat layer */
418 static ssize_t
wd_write(struct file
*file
,
419 const char __user
*buf
,
423 struct wd_timer
* pTimer
= (struct wd_timer
*)file
->private_data
;
430 wd_pingtimer(pTimer
);
436 static ssize_t
wd_read(struct file
* file
, char __user
*buffer
,
437 size_t count
, loff_t
*ppos
)
444 #endif /* ifdef WD_DEBUG */
447 static irqreturn_t
wd_interrupt(int irq
, void *dev_id
, struct pt_regs
*regs
)
449 /* Only WD0 will interrupt-- others are NMI and we won't
452 spin_lock_irq(&wd_dev
.lock
);
453 if((unsigned long)wd_dev
.regs
== (unsigned long)dev_id
)
455 wd_stoptimer(&wd_dev
.watchdog
[WD0_ID
]);
456 wd_dev
.watchdog
[WD0_ID
].runstatus
|= WD_STAT_SVCD
;
458 spin_unlock_irq(&wd_dev
.lock
);
462 static struct file_operations wd_fops
= {
463 .owner
= THIS_MODULE
,
465 .compat_ioctl
= wd_compat_ioctl
,
469 .release
= wd_release
,
472 static struct miscdevice wd0_miscdev
= { WD0_MINOR
, WD0_DEVNAME
, &wd_fops
};
473 static struct miscdevice wd1_miscdev
= { WD1_MINOR
, WD1_DEVNAME
, &wd_fops
};
474 static struct miscdevice wd2_miscdev
= { WD2_MINOR
, WD2_DEVNAME
, &wd_fops
};
477 static void wd_dumpregs(void)
479 /* Reading from downcounters initiates watchdog countdown--
480 * Example is included below for illustration purposes.
483 printk("%s: dumping register values\n", WD_OBPNAME
);
484 for(i
= WD0_ID
; i
< WD_NUMDEVS
; ++i
) {
485 /* printk("\t%s%i: dcntr at 0x%lx: 0x%x\n",
488 * (unsigned long)(&wd_dev.watchdog[i].regs->dcntr),
489 * readw(&wd_dev.watchdog[i].regs->dcntr));
491 printk("\t%s%i: limit at 0x%lx: 0x%x\n",
494 (unsigned long)(&wd_dev
.watchdog
[i
].regs
->limit
),
495 readw(&wd_dev
.watchdog
[i
].regs
->limit
));
496 printk("\t%s%i: status at 0x%lx: 0x%x\n",
499 (unsigned long)(&wd_dev
.watchdog
[i
].regs
->status
),
500 readb(&wd_dev
.watchdog
[i
].regs
->status
));
501 printk("\t%s%i: driver status: 0x%x\n",
504 wd_getstatus(&wd_dev
.watchdog
[i
]));
506 printk("\tintr_mask at %p: 0x%x\n",
507 wd_dev
.regs
+ PLD_IMASK
,
508 readb(wd_dev
.regs
+ PLD_IMASK
));
509 printk("\tpld_status at %p: 0x%x\n",
510 wd_dev
.regs
+ PLD_STATUS
,
511 readb(wd_dev
.regs
+ PLD_STATUS
));
515 /* Enable or disable watchdog interrupts
516 * Because of the CP1400 defect this should only be
517 * called during initialzation or by wd_[start|stop]timer()
519 * pTimer - pointer to timer device, or NULL to indicate all timers
520 * enable - non-zero to enable interrupts, zero to disable
522 static void wd_toggleintr(struct wd_timer
* pTimer
, int enable
)
524 unsigned char curregs
= wd_readb(wd_dev
.regs
+ PLD_IMASK
);
525 unsigned char setregs
=
527 (WD0_INTR_MASK
| WD1_INTR_MASK
| WD2_INTR_MASK
) :
530 (WD_INTR_ON
== enable
) ?
531 (curregs
&= ~setregs
):
532 (curregs
|= setregs
);
534 wd_writeb(curregs
, wd_dev
.regs
+ PLD_IMASK
);
538 /* Reset countdown timer with 'limit' value and continue countdown.
539 * This will not start a stopped timer.
541 * pTimer - pointer to timer device
543 static void wd_pingtimer(struct wd_timer
* pTimer
)
545 if (wd_readb(pTimer
->regs
+ WD_STATUS
) & WD_S_RUNNING
) {
546 wd_readw(pTimer
->regs
+ WD_DCNTR
);
550 /* Stop a running watchdog timer-- the timer actually keeps
551 * running, but the interrupt is masked so that no action is
552 * taken upon expiration.
554 * pTimer - pointer to timer device
556 static void wd_stoptimer(struct wd_timer
* pTimer
)
558 if(wd_readb(pTimer
->regs
+ WD_STATUS
) & WD_S_RUNNING
) {
559 wd_toggleintr(pTimer
, WD_INTR_OFF
);
561 if(wd_dev
.isbaddoggie
) {
562 pTimer
->runstatus
|= WD_STAT_BSTOP
;
563 wd_brokentimer((unsigned long)&wd_dev
);
568 /* Start a watchdog timer with the specified limit value
569 * If the watchdog is running, it will be restarted with
570 * the provided limit value.
572 * This function will enable interrupts on the specified
575 * pTimer - pointer to timer device
576 * limit - limit (countdown) value in 1/10th seconds
578 static void wd_starttimer(struct wd_timer
* pTimer
)
580 if(wd_dev
.isbaddoggie
) {
581 pTimer
->runstatus
&= ~WD_STAT_BSTOP
;
583 pTimer
->runstatus
&= ~WD_STAT_SVCD
;
585 wd_writew(pTimer
->timeout
, pTimer
->regs
+ WD_LIMIT
);
586 wd_toggleintr(pTimer
, WD_INTR_ON
);
589 /* Restarts timer with maximum limit value and
590 * does not unset 'brokenstop' value.
592 static void wd_resetbrokentimer(struct wd_timer
* pTimer
)
594 wd_toggleintr(pTimer
, WD_INTR_ON
);
595 wd_writew(WD_BLIMIT
, pTimer
->regs
+ WD_LIMIT
);
598 /* Timer device initialization helper.
599 * Returns 0 on success, other on failure
601 static int wd_inittimer(int whichdog
)
603 struct miscdevice
*whichmisc
;
604 void __iomem
*whichregs
;
612 whichmisc
= &wd0_miscdev
;
613 strcpy(whichident
, "RIC");
614 whichregs
= wd_dev
.regs
+ WD0_OFF
;
615 whichmask
= WD0_INTR_MASK
;
616 whichlimit
= (0 == wd0_timeout
) ?
617 (wd_dev
.opt_timeout
):
621 whichmisc
= &wd1_miscdev
;
622 strcpy(whichident
, "XIR");
623 whichregs
= wd_dev
.regs
+ WD1_OFF
;
624 whichmask
= WD1_INTR_MASK
;
625 whichlimit
= (0 == wd1_timeout
) ?
626 (wd_dev
.opt_timeout
):
630 whichmisc
= &wd2_miscdev
;
631 strcpy(whichident
, "POR");
632 whichregs
= wd_dev
.regs
+ WD2_OFF
;
633 whichmask
= WD2_INTR_MASK
;
634 whichlimit
= (0 == wd2_timeout
) ?
635 (wd_dev
.opt_timeout
):
639 printk("%s: %s: invalid watchdog id: %i\n",
640 WD_OBPNAME
, __FUNCTION__
, whichdog
);
643 if(0 != misc_register(whichmisc
))
647 wd_dev
.watchdog
[whichdog
].regs
= whichregs
;
648 wd_dev
.watchdog
[whichdog
].timeout
= whichlimit
;
649 wd_dev
.watchdog
[whichdog
].intr_mask
= whichmask
;
650 wd_dev
.watchdog
[whichdog
].runstatus
&= ~WD_STAT_BSTOP
;
651 wd_dev
.watchdog
[whichdog
].runstatus
|= WD_STAT_INIT
;
653 printk("%s%i: %s hardware watchdog [%01i.%i sec] %s\n",
657 wd_dev
.watchdog
[whichdog
].timeout
/ 10,
658 wd_dev
.watchdog
[whichdog
].timeout
% 10,
659 (0 != wd_dev
.opt_enable
) ? "in ENABLED mode" : "");
663 /* Timer method called to reset stopped watchdogs--
664 * because of the PLD bug on CP1400, we cannot mask
665 * interrupts within the PLD so me must continually
666 * reset the timers ad infinitum.
668 static void wd_brokentimer(unsigned long data
)
670 struct wd_device
* pDev
= (struct wd_device
*)data
;
673 /* kill a running timer instance, in case we
674 * were called directly instead of by kernel timer
676 if(timer_pending(&wd_timer
)) {
677 del_timer(&wd_timer
);
680 for(id
= WD0_ID
; id
< WD_NUMDEVS
; ++id
) {
681 if(pDev
->watchdog
[id
].runstatus
& WD_STAT_BSTOP
) {
683 wd_resetbrokentimer(&pDev
->watchdog
[id
]);
688 /* there is at least one timer brokenstopped-- reschedule */
689 init_timer(&wd_timer
);
690 wd_timer
.expires
= WD_BTIMEOUT
;
691 add_timer(&wd_timer
);
695 static int wd_getstatus(struct wd_timer
* pTimer
)
697 unsigned char stat
= wd_readb(pTimer
->regs
+ WD_STATUS
);
698 unsigned char intr
= wd_readb(wd_dev
.regs
+ PLD_IMASK
);
699 unsigned char ret
= WD_STOPPED
;
701 /* determine STOPPED */
705 /* determine EXPIRED vs FREERUN vs RUNNING */
706 else if(WD_S_EXPIRED
& stat
) {
709 else if(WD_S_RUNNING
& stat
) {
710 if(intr
& pTimer
->intr_mask
) {
714 /* Fudge WD_EXPIRED status for defective CP1400--
715 * IF timer is running
716 * AND brokenstop is set
717 * AND an interrupt has been serviced
720 * IF timer is running
721 * AND brokenstop is set
722 * AND no interrupt has been serviced
725 if(wd_dev
.isbaddoggie
&& (pTimer
->runstatus
& WD_STAT_BSTOP
)) {
726 if(pTimer
->runstatus
& WD_STAT_SVCD
) {
730 /* we could as well pretend we are expired */
740 /* determine SERVICED */
741 if(pTimer
->runstatus
& WD_STAT_SVCD
) {
748 static int __init
wd_init(void)
751 struct linux_ebus
*ebus
= NULL
;
752 struct linux_ebus_device
*edev
= NULL
;
754 for_each_ebus(ebus
) {
755 for_each_ebusdev(edev
, ebus
) {
756 if (!strcmp(edev
->ofdev
.node
->name
, WD_OBPNAME
))
763 printk("%s: unable to locate device\n", WD_OBPNAME
);
768 ioremap(edev
->resource
[0].start
, 4 * WD_TIMER_REGSZ
); /* ? */
770 if(NULL
== wd_dev
.regs
) {
771 printk("%s: unable to map registers\n", WD_OBPNAME
);
775 /* initialize device structure from OBP parameters */
776 wd_dev
.irq
= edev
->irqs
[0];
777 wd_dev
.opt_enable
= wd_opt_enable();
778 wd_dev
.opt_reboot
= wd_opt_reboot();
779 wd_dev
.opt_timeout
= wd_opt_timeout();
780 wd_dev
.isbaddoggie
= wd_isbroken();
782 /* disable all interrupts unless watchdog-enabled? == true */
783 if(! wd_dev
.opt_enable
) {
784 wd_toggleintr(NULL
, WD_INTR_OFF
);
787 /* register miscellaneous devices */
788 for(id
= WD0_ID
; id
< WD_NUMDEVS
; ++id
) {
789 if(0 != wd_inittimer(id
)) {
790 printk("%s%i: unable to initialize\n", WD_OBPNAME
, id
);
794 /* warn about possible defective PLD */
795 if(wd_dev
.isbaddoggie
) {
796 init_timer(&wd_timer
);
797 wd_timer
.function
= wd_brokentimer
;
798 wd_timer
.data
= (unsigned long)&wd_dev
;
799 wd_timer
.expires
= WD_BTIMEOUT
;
801 printk("%s: PLD defect workaround enabled for model %s\n",
802 WD_OBPNAME
, WD_BADMODEL
);
807 static void __exit
wd_cleanup(void)
811 /* if 'watchdog-enable?' == TRUE, timers are not stopped
812 * when module is unloaded. All brokenstopped timers will
813 * also now eventually trip.
815 for(id
= WD0_ID
; id
< WD_NUMDEVS
; ++id
) {
816 if(WD_S_RUNNING
== wd_readb(wd_dev
.watchdog
[id
].regs
+ WD_STATUS
)) {
817 if(wd_dev
.opt_enable
) {
818 printk(KERN_WARNING
"%s%i: timer not stopped at release\n",
822 wd_stoptimer(&wd_dev
.watchdog
[id
]);
823 if(wd_dev
.watchdog
[id
].runstatus
& WD_STAT_BSTOP
) {
824 wd_resetbrokentimer(&wd_dev
.watchdog
[id
]);
826 "%s%i: defect workaround disabled at release, "\
827 "timer expires in ~%01i sec\n",
829 wd_readw(wd_dev
.watchdog
[id
].regs
+ WD_LIMIT
) / 10);
835 if(wd_dev
.isbaddoggie
&& timer_pending(&wd_timer
)) {
836 del_timer(&wd_timer
);
838 if(0 != (wd_dev
.watchdog
[WD0_ID
].runstatus
& WD_STAT_INIT
)) {
839 misc_deregister(&wd0_miscdev
);
841 if(0 != (wd_dev
.watchdog
[WD1_ID
].runstatus
& WD_STAT_INIT
)) {
842 misc_deregister(&wd1_miscdev
);
844 if(0 != (wd_dev
.watchdog
[WD2_ID
].runstatus
& WD_STAT_INIT
)) {
845 misc_deregister(&wd2_miscdev
);
847 if(0 != wd_dev
.initialized
) {
848 free_irq(wd_dev
.irq
, (void *)wd_dev
.regs
);
850 iounmap(wd_dev
.regs
);
853 module_init(wd_init
);
854 module_exit(wd_cleanup
);