1 /*-
2 * SPDX-License-Identifier: BSD-2-Clause
3 *
4 * Copyright (c) 2011 Sandvine Incorporated ULC.
5 * Copyright (c) 2012 iXsystems, Inc.
6 * All rights reserved.
7 *
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
10 * are met:
11 * 1. Redistributions of source code must retain the above copyright
12 * notice, this list of conditions and the following disclaimer.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 * notice, this list of conditions and the following disclaimer in the
15 * documentation and/or other materials provided with the distribution.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
18 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
21 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27 * SUCH DAMAGE.
28 */
29 /*
30 * Support for Winbond watchdog.
31 *
32 * With minor abstractions it might be possible to add support for other
33 * different Winbond Super I/O chips as well. Winbond seems to have four
34 * different types of chips, four different ways to get into extended config
35 * mode.
36 *
37 * Note: there is no serialization between the debugging sysctl handlers and
38 * the watchdog functions and possibly others poking the registers at the same
39 * time. For that at least possibly interfering sysctls are hidden by default.
40 */
41
42 #include <sys/cdefs.h>
43 #include <sys/param.h>
44 #include <sys/kernel.h>
45 #include <sys/systm.h>
46 #include <sys/bus.h>
47 #include <sys/eventhandler.h>
48 #include <sys/module.h>
49 #include <sys/sbuf.h>
50 #include <sys/sysctl.h>
51 #include <sys/watchdog.h>
52
53 #include <dev/superio/superio.h>
54
55 #include <machine/bus.h>
56 #include <machine/resource.h>
57
58 /*
59 * Global registers.
60 */
61 #define WB_DEVICE_ID_REG 0x20 /* Device ID */
62 #define WB_DEVICE_REV_REG 0x21 /* Device revision */
63 #define WB_CR26 0x26 /* Bit6: HEFRAS (base port selector) */
64
65 /* LDN selection. */
66 #define WB_LDN_REG 0x07
67 #define WB_LDN_REG_LDN8 0x08 /* GPIO 2, Watchdog */
68
69 /*
70 * LDN8 (GPIO 2, Watchdog) specific registers and options.
71 */
72 /* CR30: LDN8 activation control. */
73 #define WB_LDN8_CR30 0x30
74 #define WB_LDN8_CR30_ACTIVE 0x01 /* 1: LD active */
75
76 /* CRF5: Watchdog scale, P20. Mapped to reg_1. */
77 #define WB_LDN8_CRF5 0xF5
78 #define WB_LDN8_CRF5_SCALE 0x08 /* 0: 1s, 1: 60s */
79 #define WB_LDN8_CRF5_KEYB_P20 0x04 /* 1: keyb P20 forces timeout */
80 #define WB_LDN8_CRF5_KBRST 0x02 /* 1: timeout causes pin60 kbd reset */
81
82 /* CRF6: Watchdog Timeout (0 == off). Mapped to reg_timeout. */
83 #define WB_LDN8_CRF6 0xF6
84
85 /* CRF7: Watchdog mouse, keyb, force, .. Mapped to reg_2. */
86 #define WB_LDN8_CRF7 0xF7
87 #define WB_LDN8_CRF7_MOUSE 0x80 /* 1: mouse irq resets wd timer */
88 #define WB_LDN8_CRF7_KEYB 0x40 /* 1: keyb irq resets wd timer */
89 #define WB_LDN8_CRF7_FORCE 0x20 /* 1: force timeout (self-clear) */
90 #define WB_LDN8_CRF7_TS 0x10 /* 0: counting, 1: fired */
91 #define WB_LDN8_CRF7_IRQS 0x0f /* irq source for watchdog, 2 == SMI */
92
93 enum chips { w83627hf, w83627s, w83697hf, w83697ug, w83637hf, w83627thf,
94 w83687thf, w83627ehf, w83627dhg, w83627uhg, w83667hg,
95 w83627dhg_p, w83667hg_b, nct6775, nct6776, nct6779, nct6791,
96 nct6792, nct6793, nct6795, nct6102 };
97
98 struct wb_softc {
99 device_t dev;
100 eventhandler_tag ev_tag;
101 enum chips chip;
102 uint8_t ctl_reg;
103 uint8_t time_reg;
104 uint8_t csr_reg;
105 int debug_verbose;
106
107 /*
108 * Special feature to let the watchdog fire at a different
109 * timeout as set by watchdog(4) but still use that API to
110 * re-load it periodically.
111 */
112 unsigned int timeout_override;
113
114 /*
115 * Space to save current state temporary and for sysctls.
116 * We want to know the timeout value and usually need two
117 * additional registers for options. Do not name them by
118 * register as these might be different by chip.
119 */
120 uint8_t reg_timeout;
121 uint8_t reg_1;
122 uint8_t reg_2;
123 };
124
125 struct winbond_vendor_device_id {
126 uint8_t device_id;
127 enum chips chip;
128 const char * descr;
129 } wb_devs[] = {
130 {
131 .device_id = 0x52,
132 .chip = w83627hf,
133 .descr = "Winbond 83627HF/F/HG/G",
134 },
135 {
136 .device_id = 0x59,
137 .chip = w83627s,
138 .descr = "Winbond 83627S",
139 },
140 {
141 .device_id = 0x60,
142 .chip = w83697hf,
143 .descr = "Winbond 83697HF",
144 },
145 {
146 .device_id = 0x68,
147 .chip = w83697ug,
148 .descr = "Winbond 83697UG",
149 },
150 {
151 .device_id = 0x70,
152 .chip = w83637hf,
153 .descr = "Winbond 83637HF",
154 },
155 {
156 .device_id = 0x82,
157 .chip = w83627thf,
158 .descr = "Winbond 83627THF",
159 },
160 {
161 .device_id = 0x85,
162 .chip = w83687thf,
163 .descr = "Winbond 83687THF",
164 },
165 {
166 .device_id = 0x88,
167 .chip = w83627ehf,
168 .descr = "Winbond 83627EHF",
169 },
170 {
171 .device_id = 0xa0,
172 .chip = w83627dhg,
173 .descr = "Winbond 83627DHG",
174 },
175 {
176 .device_id = 0xa2,
177 .chip = w83627uhg,
178 .descr = "Winbond 83627UHG",
179 },
180 {
181 .device_id = 0xa5,
182 .chip = w83667hg,
183 .descr = "Winbond 83667HG",
184 },
185 {
186 .device_id = 0xb0,
187 .chip = w83627dhg_p,
188 .descr = "Winbond 83627DHG-P",
189 },
190 {
191 .device_id = 0xb3,
192 .chip = w83667hg_b,
193 .descr = "Winbond 83667HG-B",
194 },
195 {
196 .device_id = 0xb4,
197 .chip = nct6775,
198 .descr = "Nuvoton NCT6775",
199 },
200 {
201 .device_id = 0xc3,
202 .chip = nct6776,
203 .descr = "Nuvoton NCT6776",
204 },
205 {
206 .device_id = 0xc4,
207 .chip = nct6102,
208 .descr = "Nuvoton NCT6102",
209 },
210 {
211 .device_id = 0xc5,
212 .chip = nct6779,
213 .descr = "Nuvoton NCT6779",
214 },
215 {
216 .device_id = 0xc8,
217 .chip = nct6791,
218 .descr = "Nuvoton NCT6791",
219 },
220 {
221 .device_id = 0xc9,
222 .chip = nct6792,
223 .descr = "Nuvoton NCT6792",
224 },
225 {
226 .device_id = 0xd1,
227 .chip = nct6793,
228 .descr = "Nuvoton NCT6793",
229 },
230 {
231 .device_id = 0xd3,
232 .chip = nct6795,
233 .descr = "Nuvoton NCT6795",
234 },
235 };
236
237 /*
238 * Return the watchdog related registers as we last read them. This will
239 * usually not give the current timeout or state on whether the watchdog
240 * fired.
241 */
242 static int
sysctl_wb_debug(SYSCTL_HANDLER_ARGS)243 sysctl_wb_debug(SYSCTL_HANDLER_ARGS)
244 {
245 struct wb_softc *sc;
246 struct sbuf sb;
247 int error;
248
249 sc = arg1;
250
251 sbuf_new_for_sysctl(&sb, NULL, 64, req);
252
253 sbuf_printf(&sb, "LDN8 (GPIO2, Watchdog): ");
254 sbuf_printf(&sb, "CR%02X 0x%02x ", sc->ctl_reg, sc->reg_1);
255 sbuf_printf(&sb, "CR%02X 0x%02x ", sc->time_reg, sc->reg_timeout);
256 sbuf_printf(&sb, "CR%02X 0x%02x", sc->csr_reg, sc->reg_2);
257
258 error = sbuf_finish(&sb);
259 sbuf_delete(&sb);
260 return (error);
261 }
262
263 /*
264 * Read the current values before returning them. Given this might poke
265 * the registers the same time as the watchdog, this sysctl handler should
266 * be marked CTLFLAG_SKIP to not show up by default.
267 */
268 static int
sysctl_wb_debug_current(SYSCTL_HANDLER_ARGS)269 sysctl_wb_debug_current(SYSCTL_HANDLER_ARGS)
270 {
271 struct wb_softc *sc;
272
273 sc = arg1;
274
275 sc->reg_1 = superio_read(sc->dev, sc->ctl_reg);
276 sc->reg_timeout = superio_read(sc->dev, sc->time_reg);
277 sc->reg_2 = superio_read(sc->dev, sc->csr_reg);
278
279 return (sysctl_wb_debug(oidp, arg1, arg2, req));
280 }
281
282 /*
283 * Sysctl handlers to force a watchdog timeout or to test the NMI functionality
284 * works as expetced.
285 * For testing we could set a test_nmi flag in the softc that, in case of NMI, a
286 * callback function from trap.c could check whether we fired and not report the
287 * timeout but clear the flag for the sysctl again. This is interesting given a
288 * lot of boards have jumpers to change the action on watchdog timeout or
289 * disable the watchdog completely.
290 * XXX-BZ notyet: currently no general infrastructure exists to do this.
291 */
292 static int
sysctl_wb_force_test_nmi(SYSCTL_HANDLER_ARGS)293 sysctl_wb_force_test_nmi(SYSCTL_HANDLER_ARGS)
294 {
295 struct wb_softc *sc;
296 int error, val;
297
298 sc = arg1;
299
300 #ifdef notyet
301 val = sc->test_nmi;
302 #else
303 val = 0;
304 #endif
305 error = sysctl_handle_int(oidp, &val, 0, req);
306 if (error || !req->newptr)
307 return (error);
308
309 #ifdef notyet
310 int test = arg2;
311
312 /* Manually clear the test for a value of 0 and do nothing else. */
313 if (test && val == 0) {
314 sc->test_nmi = 0;
315 return (0);
316 }
317
318 /*
319 * If we are testing the NMI functionality, set the flag before
320 * forcing the timeout.
321 */
322 if (test)
323 sc->test_nmi = 1;
324 #endif
325
326 /* Force watchdog to fire. */
327 sc->reg_2 = superio_read(sc->dev, sc->csr_reg);
328 sc->reg_2 |= WB_LDN8_CRF7_FORCE;
329 superio_write(sc->dev, sc->csr_reg, sc->reg_2);
330
331 return (0);
332 }
333
334 /*
335 * Print current watchdog state.
336 *
337 * Note: it is the responsibility of the caller to update the registers
338 * upfront.
339 */
340 static void
wb_print_state(struct wb_softc * sc,const char * msg)341 wb_print_state(struct wb_softc *sc, const char *msg)
342 {
343
344 device_printf(sc->dev, "%s%sWatchdog %sabled. %s"
345 "Scaling by %ds, timer at %d (%s=%ds%s). "
346 "CR%02X 0x%02x CR%02X 0x%02x\n",
347 (msg != NULL) ? msg : "", (msg != NULL) ? ": " : "",
348 (sc->reg_timeout > 0x00) ? "en" : "dis",
349 (sc->reg_2 & WB_LDN8_CRF7_TS) ? "Watchdog fired. " : "",
350 (sc->reg_1 & WB_LDN8_CRF5_SCALE) ? 60 : 1,
351 sc->reg_timeout,
352 (sc->reg_timeout > 0x00) ? "<" : "",
353 sc->reg_timeout * ((sc->reg_1 & WB_LDN8_CRF5_SCALE) ? 60 : 1),
354 (sc->reg_timeout > 0x00) ? " left" : "",
355 sc->ctl_reg, sc->reg_1, sc->csr_reg, sc->reg_2);
356 }
357
358 /*
359 * (Re)load the watchdog counter depending on timeout. A timeout of 0 will
360 * disable the watchdog.
361 */
362 static int
wb_set_watchdog(struct wb_softc * sc,unsigned int timeout)363 wb_set_watchdog(struct wb_softc *sc, unsigned int timeout)
364 {
365
366 if (timeout != 0) {
367 /*
368 * In case an override is set, let it override. It may lead
369 * to strange results as we do not check the input of the sysctl.
370 */
371 if (sc->timeout_override > 0)
372 timeout = sc->timeout_override;
373
374 /* Make sure we support the requested timeout. */
375 if (timeout > 255 * 60)
376 return (EINVAL);
377 }
378
379 if (sc->debug_verbose)
380 wb_print_state(sc, "Before watchdog counter (re)load");
381
382 if (timeout == 0) {
383 /* Disable watchdog. */
384 sc->reg_timeout = 0;
385 superio_write(sc->dev, sc->time_reg, sc->reg_timeout);
386
387 } else {
388 /* Read current scaling factor. */
389 sc->reg_1 = superio_read(sc->dev, sc->ctl_reg);
390
391 if (timeout > 255) {
392 /* Set scaling factor to 60s. */
393 sc->reg_1 |= WB_LDN8_CRF5_SCALE;
394 sc->reg_timeout = (timeout / 60);
395 if (timeout % 60)
396 sc->reg_timeout++;
397 } else {
398 /* Set scaling factor to 1s. */
399 sc->reg_1 &= ~WB_LDN8_CRF5_SCALE;
400 sc->reg_timeout = timeout;
401 }
402
403 /* In case we fired before we need to clear to fire again. */
404 sc->reg_2 = superio_read(sc->dev, sc->csr_reg);
405 if (sc->reg_2 & WB_LDN8_CRF7_TS) {
406 sc->reg_2 &= ~WB_LDN8_CRF7_TS;
407 superio_write(sc->dev, sc->csr_reg, sc->reg_2);
408 }
409
410 /* Write back scaling factor. */
411 superio_write(sc->dev, sc->ctl_reg, sc->reg_1);
412
413 /* Set timer and arm/reset the watchdog. */
414 superio_write(sc->dev, sc->time_reg, sc->reg_timeout);
415 }
416
417 if (sc->debug_verbose)
418 wb_print_state(sc, "After watchdog counter (re)load");
419 return (0);
420 }
421
422 /*
423 * watchdog(9) EVENTHANDLER function implementation to (re)load the counter
424 * with the given timeout or disable the watchdog.
425 */
426 static void
wb_watchdog_fn(void * private,u_int cmd,int * error)427 wb_watchdog_fn(void *private, u_int cmd, int *error)
428 {
429 struct wb_softc *sc;
430 unsigned int timeout;
431 int e;
432
433 sc = private;
434 KASSERT(sc != NULL, ("%s: watchdog handler function called without "
435 "softc.", __func__));
436
437 cmd &= WD_INTERVAL;
438 if (cmd > 0 && cmd <= 63) {
439 /* Reset (and arm) watchdog. */
440 timeout = ((uint64_t)1 << cmd) / 1000000000;
441 if (timeout == 0)
442 timeout = 1;
443 e = wb_set_watchdog(sc, timeout);
444 if (e == 0) {
445 if (error != NULL)
446 *error = 0;
447 } else {
448 /* On error, try to make sure the WD is disabled. */
449 wb_set_watchdog(sc, 0);
450 }
451
452 } else {
453 /* Disable watchdog. */
454 e = wb_set_watchdog(sc, 0);
455 if (e != 0 && cmd == 0 && error != NULL) {
456 /* Failed to disable watchdog. */
457 *error = EOPNOTSUPP;
458 }
459 }
460 }
461
462 static int
wb_probe(device_t dev)463 wb_probe(device_t dev)
464 {
465 struct wb_softc *sc;
466 int j;
467 uint8_t devid;
468 uint8_t revid;
469
470 if (superio_vendor(dev) != SUPERIO_VENDOR_NUVOTON)
471 return (ENXIO);
472 if (superio_get_type(dev) != SUPERIO_DEV_WDT)
473 return (ENXIO);
474
475 sc = device_get_softc(dev);
476 devid = superio_devid(dev) >> 8;
477 revid = superio_revid(dev);
478 for (j = 0; j < nitems(wb_devs); j++) {
479 if (wb_devs[j].device_id == devid) {
480 sc->chip = wb_devs[j].chip;
481 device_set_descf(dev,
482 "%s (0x%02x/0x%02x) Watchdog Timer",
483 wb_devs[j].descr, devid, revid);
484 return (BUS_PROBE_SPECIFIC);
485 }
486 }
487 if (bootverbose) {
488 device_printf(dev,
489 "unrecognized chip: devid 0x%02x, revid 0x%02x\n",
490 devid, revid);
491 }
492 return (ENXIO);
493 }
494
495 static int
wb_attach(device_t dev)496 wb_attach(device_t dev)
497 {
498 struct wb_softc *sc;
499 struct sysctl_ctx_list *sctx;
500 struct sysctl_oid *soid;
501 unsigned long timeout;
502 uint8_t t;
503
504 sc = device_get_softc(dev);
505 sc->dev = dev;
506
507 /* Make sure WDT is enabled. */
508 superio_dev_enable(dev, WB_LDN8_CR30_ACTIVE);
509
510 switch (sc->chip) {
511 case w83697hf:
512 case w83697ug:
513 sc->ctl_reg = 0xf3;
514 sc->time_reg = 0xf4;
515 sc->csr_reg = 0xf7;
516 break;
517 case nct6102:
518 sc->ctl_reg = 0xf0;
519 sc->time_reg = 0xf1;
520 sc->csr_reg = 0xf2;
521 break;
522 default:
523 sc->ctl_reg = 0xf5;
524 sc->time_reg = 0xf6;
525 sc->csr_reg = 0xf7;
526 break;
527 }
528
529 switch (sc->chip) {
530 case w83627hf:
531 case w83627s:
532 t = superio_read(dev, 0x2B) & ~0x10;
533 superio_write(dev, 0x2B, t); /* set GPIO24 to WDT0 */
534 break;
535 case w83697hf:
536 /* Set pin 119 to WDTO# mode (= CR29, WDT0) */
537 t = superio_read(dev, 0x29) & ~0x60;
538 t |= 0x20;
539 superio_write(dev, 0x29, t);
540 break;
541 case w83697ug:
542 /* Set pin 118 to WDTO# mode */
543 t = superio_read(dev, 0x2b) & ~0x04;
544 superio_write(dev, 0x2b, t);
545 break;
546 case w83627thf:
547 t = (superio_read(dev, 0x2B) & ~0x08) | 0x04;
548 superio_write(dev, 0x2B, t); /* set GPIO3 to WDT0 */
549 break;
550 case w83627dhg:
551 case w83627dhg_p:
552 t = superio_read(dev, 0x2D) & ~0x01; /* PIN77 -> WDT0# */
553 superio_write(dev, 0x2D, t); /* set GPIO5 to WDT0 */
554 t = superio_read(dev, sc->ctl_reg);
555 t |= 0x02; /* enable the WDTO# output low pulse
556 * to the KBRST# pin */
557 superio_write(dev, sc->ctl_reg, t);
558 break;
559 case w83637hf:
560 break;
561 case w83687thf:
562 t = superio_read(dev, 0x2C) & ~0x80; /* PIN47 -> WDT0# */
563 superio_write(dev, 0x2C, t);
564 break;
565 case w83627ehf:
566 case w83627uhg:
567 case w83667hg:
568 case w83667hg_b:
569 case nct6775:
570 case nct6776:
571 case nct6779:
572 case nct6791:
573 case nct6792:
574 case nct6793:
575 case nct6795:
576 case nct6102:
577 /*
578 * These chips have a fixed WDTO# output pin (W83627UHG),
579 * or support more than one WDTO# output pin.
580 * Don't touch its configuration, and hope the BIOS
581 * does the right thing.
582 */
583 t = superio_read(dev, sc->ctl_reg);
584 t |= 0x02; /* enable the WDTO# output low pulse
585 * to the KBRST# pin */
586 superio_write(dev, sc->ctl_reg, t);
587 break;
588 default:
589 break;
590 }
591
592 /* Read the current watchdog configuration. */
593 sc->reg_1 = superio_read(dev, sc->ctl_reg);
594 sc->reg_timeout = superio_read(dev, sc->time_reg);
595 sc->reg_2 = superio_read(dev, sc->csr_reg);
596
597 /* Print current state if bootverbose or watchdog already enabled. */
598 if (bootverbose || (sc->reg_timeout > 0x00))
599 wb_print_state(sc, "Before watchdog attach");
600
601 sc->reg_1 &= ~WB_LDN8_CRF5_KEYB_P20;
602 sc->reg_1 |= WB_LDN8_CRF5_KBRST;
603 superio_write(dev, sc->ctl_reg, sc->reg_1);
604
605 /*
606 * Clear a previous watchdog timeout event (if still set).
607 * Disable timer reset on mouse interrupts. Leave reset on keyboard,
608 * since one of my boards is getting stuck in reboot without it.
609 */
610 sc->reg_2 &= ~(WB_LDN8_CRF7_MOUSE|WB_LDN8_CRF7_TS);
611 superio_write(dev, sc->csr_reg, sc->reg_2);
612
613 /* Read global timeout override tunable, Add per device sysctls. */
614 if (TUNABLE_ULONG_FETCH("hw.wbwd.timeout_override", &timeout)) {
615 if (timeout > 0)
616 sc->timeout_override = timeout;
617 }
618 sctx = device_get_sysctl_ctx(dev);
619 soid = device_get_sysctl_tree(dev);
620 SYSCTL_ADD_UINT(sctx, SYSCTL_CHILDREN(soid), OID_AUTO,
621 "timeout_override", CTLFLAG_RW, &sc->timeout_override, 0,
622 "Timeout in seconds overriding default watchdog timeout");
623 SYSCTL_ADD_INT(sctx, SYSCTL_CHILDREN(soid), OID_AUTO,
624 "debug_verbose", CTLFLAG_RW, &sc->debug_verbose, 0,
625 "Enables extra debugging information");
626 SYSCTL_ADD_PROC(sctx, SYSCTL_CHILDREN(soid), OID_AUTO, "debug",
627 CTLTYPE_STRING | CTLFLAG_RD | CTLFLAG_MPSAFE, sc, 0,
628 sysctl_wb_debug, "A",
629 "Selected register information from last change by driver");
630 SYSCTL_ADD_PROC(sctx, SYSCTL_CHILDREN(soid), OID_AUTO, "debug_current",
631 CTLTYPE_STRING | CTLFLAG_RD | CTLFLAG_SKIP | CTLFLAG_MPSAFE,
632 sc, 0, sysctl_wb_debug_current, "A",
633 "Selected register information (may interfere)");
634 SYSCTL_ADD_PROC(sctx, SYSCTL_CHILDREN(soid), OID_AUTO, "force_timeout",
635 CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_SKIP | CTLFLAG_MPSAFE, sc, 0,
636 sysctl_wb_force_test_nmi, "I", "Enable to force watchdog to fire.");
637
638 /* Register watchdog. */
639 sc->ev_tag = EVENTHANDLER_REGISTER(watchdog_list, wb_watchdog_fn, sc,
640 0);
641
642 if (bootverbose)
643 wb_print_state(sc, "After watchdog attach");
644
645 return (0);
646 }
647
648 static int
wb_detach(device_t dev)649 wb_detach(device_t dev)
650 {
651 struct wb_softc *sc;
652
653 sc = device_get_softc(dev);
654
655 /* Unregister and stop the watchdog if running. */
656 if (sc->ev_tag)
657 EVENTHANDLER_DEREGISTER(watchdog_list, sc->ev_tag);
658 wb_set_watchdog(sc, 0);
659
660 /* Bus subroutines take care of sysctls already. */
661
662 return (0);
663 }
664
665 static device_method_t wb_methods[] = {
666 /* Device interface */
667 DEVMETHOD(device_probe, wb_probe),
668 DEVMETHOD(device_attach, wb_attach),
669 DEVMETHOD(device_detach, wb_detach),
670
671 DEVMETHOD_END
672 };
673
674 static driver_t wb_driver = {
675 "wbwd",
676 wb_methods,
677 sizeof(struct wb_softc)
678 };
679
680 DRIVER_MODULE(wb, superio, wb_driver, NULL, NULL);
681 MODULE_DEPEND(wb, superio, 1, 1, 1);
682 MODULE_VERSION(wb, 1);
683