]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - sys/dev/wbwd/wbwd.c
Merge bmake-20121111
[FreeBSD/FreeBSD.git] / sys / dev / wbwd / wbwd.c
1 /*-
2  * Copyright (c) 2011 Sandvine Incorporated ULC.
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  *
14  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24  * SUCH DAMAGE.
25  */
26 /*
27  * Support for Winbond watchdog.
28  *
29  * With minor abstractions it might be possible to add support for other
30  * different Winbond Super I/O chips as well.  Winbond seems to have four
31  * different types of chips, four different ways to get into extended config
32  * mode.
33  *
34  * Note: there is no serialization between the debugging sysctl handlers and
35  * the watchdog functions and possibly others poking the registers at the same
36  * time.  For that at least possibly interfering sysctls are hidden by default.
37  */
38
39 #include <sys/cdefs.h>
40 __FBSDID("$FreeBSD$");
41
42 #include <sys/param.h>
43 #include <sys/kernel.h>
44 #include <sys/systm.h>
45 #include <sys/bus.h>
46 #include <sys/eventhandler.h>
47 #include <sys/lock.h>
48 #include <sys/module.h>
49 #include <sys/rman.h>
50 #include <sys/sbuf.h>
51 #include <sys/sysctl.h>
52 #include <sys/watchdog.h>
53
54 #include <isa/isavar.h>
55
56 #include <machine/bus.h>
57 #include <machine/resource.h>
58
59 /*
60  * Global registers.
61  */
62 #define WB_DEVICE_ID_REG        0x20    /* Device ID */
63 #define WB_DEVICE_REV_REG       0x21    /* Device revision */
64 #define WB_CR26                 0x26    /* Bit6: HEFRAS (base port selector) */
65
66 /* LDN selection. */
67 #define WB_LDN_REG              0x07
68 #define WB_LDN_REG_LDN8         0x08    /* GPIO 2, Watchdog */
69
70 /*
71  * LDN8 (GPIO 2, Watchdog) specific registers and options.
72  */
73 /* CR30: LDN8 activation control. */
74 #define WB_LDN8_CR30            0x30
75 #define WB_LDN8_CR30_ACTIVE     0x01    /* 1: LD active */
76
77 /* CRF5: Watchdog scale, P20. Mapped to reg_1. */
78 #define WB_LDN8_CRF5            0xF5
79 #define WB_LDN8_CRF5_SCALE      0x08    /* 0: 1s, 1: 60s */
80 #define WB_LDN8_CRF5_KEYB_P20   0x04    /* 1: keyb P20 forces timeout */
81 #define WB_LDN8_CRF5_KBRST      0x02    /* 1: timeout causes pin60 kbd reset */
82
83 /* CRF6: Watchdog Timeout (0 == off). Mapped to reg_timeout. */
84 #define WB_LDN8_CRF6            0xF6
85
86 /* CRF7: Watchdog mouse, keyb, force, .. Mapped to reg_2. */
87 #define WB_LDN8_CRF7            0xF7
88 #define WB_LDN8_CRF7_MOUSE      0x80    /* 1: mouse irq resets wd timer */
89 #define WB_LDN8_CRF7_KEYB       0x40    /* 1: keyb irq resets wd timer */
90 #define WB_LDN8_CRF7_FORCE      0x20    /* 1: force timeout (self-clear) */
91 #define WB_LDN8_CRF7_TS         0x10    /* 0: counting, 1: fired */
92 #define WB_LDN8_CRF7_IRQS       0x0f    /* irq source for watchdog, 2 == SMI */
93 #define WB_LDN8_CRF7_CLEAR_MASK \
94     (WB_LDN8_CRF7_MOUSE|WB_LDN8_CRF7_KEYB|WB_LDN8_CRF7_TS|WB_LDN8_CRF7_IRQS)
95
96 #define write_efir_1(sc, value)                                         \
97         bus_space_write_1((sc)->bst, (sc)->bsh, 0, (value))
98 #define read_efir_1(sc)                                                 \
99         bus_space_read_1((sc)->bst, (sc)->bsh, 0)
100 #define write_efdr_1(sc, value)                                         \
101         bus_space_write_1((sc)->bst, (sc)->bsh, 1, (value))
102 #define read_efdr_1(sc)                                                 \
103         bus_space_read_1((sc)->bst, (sc)->bsh, 1)
104
105 struct wb_softc {
106         device_t                dev;
107         struct resource         *portres;
108         bus_space_tag_t         bst;
109         bus_space_handle_t      bsh;
110         int                     rid;
111         eventhandler_tag        ev_tag;
112         int                     (*ext_cfg_enter_f)(struct wb_softc *);
113         void                    (*ext_cfg_exit_f)(struct wb_softc *);
114         int                     debug_verbose;
115
116         /*
117          * Special feature to let the watchdog fire at a different
118          * timeout as set by watchdog(4) but still use that API to
119          * re-load it periodically.
120          */
121         unsigned int            timeout_override;
122
123         /*
124          * Space to save current state temporary and for sysctls.
125          * We want to know the timeout value and usually need two
126          * additional registers for options. Do not name them by
127          * register as these might be different by chip.
128          */
129         uint8_t                 reg_timeout;
130         uint8_t                 reg_1;
131         uint8_t                 reg_2;
132 };
133
134 static int      ext_cfg_enter_0x87_0x87(struct wb_softc *);
135 static void     ext_cfg_exit_0xaa(struct wb_softc *);
136
137 struct winbond_superio_cfg {
138         uint8_t                 efer;   /* and efir */
139         int                     (*ext_cfg_enter_f)(struct wb_softc *);
140         void                    (*ext_cfg_exit_f)(struct wb_softc *);
141 } probe_addrs[] = {
142         {
143                 .efer                   = 0x2e,
144                 .ext_cfg_enter_f        = ext_cfg_enter_0x87_0x87,
145                 .ext_cfg_exit_f         = ext_cfg_exit_0xaa,
146         },
147         {
148                 .efer                   = 0x4e,
149                 .ext_cfg_enter_f        = ext_cfg_enter_0x87_0x87,
150                 .ext_cfg_exit_f         = ext_cfg_exit_0xaa,
151         },
152 };
153
154 struct winbond_vendor_device_id {
155         uint16_t                vendor_id;
156         uint8_t                 device_id;
157         uint8_t                 device_rev;
158         const char *            descr;
159 } wb_devs[] = {
160         {
161                 .vendor_id      = 0x5ca3,
162                 .device_id      = 0x52,
163                 .device_rev     = 0x17,
164                 .descr          = "Winbond 83627HF/F/HG/G Rev. G",
165         },
166         {
167                 .vendor_id      = 0x5ca3,
168                 .device_id      = 0x52,
169                 .device_rev     = 0x3a,
170                 .descr          = "Winbond 83627HF/F/HG/G Rev. J",
171         },
172         {
173                 .vendor_id      = 0x5ca3,
174                 .device_id      = 0x52,
175                 .device_rev     = 0x41,
176                 .descr          = "Winbond 83627HF/F/HG/G Rev. UD-A",
177         },
178         {
179                 .vendor_id      = 0x5ca3,
180                 .device_id      = 0xa0,
181                 .device_rev     = 0x25,
182                 .descr          = "Winbond 83627DHG IC ver. 5",   
183         },
184         {
185                 .vendor_id      = 0x5ca3,
186                 .device_id      = 0xb0,
187                 .device_rev     = 0x73,
188                 .descr          = "Winbond 83627DHG-P",   
189         },
190 };
191
192 /*
193  * Return the watchdog related registers as we last read them.  This will
194  * usually not give the current timeout or state on whether the watchdog
195  * fired.
196  */
197 static int
198 sysctl_wb_debug(SYSCTL_HANDLER_ARGS)
199 {
200         struct wb_softc *sc;
201         struct sbuf sb;
202         int error;
203
204         sc = arg1;
205
206         sbuf_new_for_sysctl(&sb, NULL, 64, req);
207
208         sbuf_printf(&sb, "LDN8 (GPIO2, Watchdog): ");
209         sbuf_printf(&sb, "CRF5 0x%02x ", sc->reg_1);
210         sbuf_printf(&sb, "CRF6 0x%02x ", sc->reg_timeout);
211         sbuf_printf(&sb, "CRF7 0x%02x ", sc->reg_2);
212
213         sbuf_trim(&sb);
214         error = sbuf_finish(&sb);
215         sbuf_delete(&sb);
216         return (error);
217 }
218
219 /*
220  * Read the current values before returning them.  Given this might poke
221  * the registers the same time as the watchdog, this sysctl handler should
222  * be marked CTLFLAG_SKIP to not show up by default.
223  */
224 static int
225 sysctl_wb_debug_current(SYSCTL_HANDLER_ARGS)
226 {
227         struct wb_softc *sc;
228
229         sc = arg1;
230
231         /*
232          * Enter extended function mode in case someone else has been
233          * poking on the registers.  We will not leave it though.
234          */
235         if ((*sc->ext_cfg_enter_f)(sc) != 0)
236                 return (ENXIO);
237
238         /* Watchdog is configured as part of LDN 8 (GPIO Port2, Watchdog). */
239         write_efir_1(sc, WB_LDN_REG);
240         write_efdr_1(sc, WB_LDN_REG_LDN8);
241
242         write_efir_1(sc, WB_LDN8_CRF5);
243         sc->reg_1 = read_efdr_1(sc);
244         write_efir_1(sc, WB_LDN8_CRF6);
245         sc->reg_timeout = read_efdr_1(sc);
246         write_efir_1(sc, WB_LDN8_CRF7);
247         sc->reg_2 = read_efdr_1(sc);
248
249         return (sysctl_wb_debug(oidp, arg1, arg2, req));
250 }
251
252 /*
253  * Sysctl handlers to force a watchdog timeout or to test the NMI functionality
254  * works as expetced.
255  * For testing we could set a test_nmi flag in the softc that, in case of NMI, a
256  * callback function from trap.c could check whether we fired and not report the
257  * timeout but clear the flag for the sysctl again.  This is interesting given a
258  * lot of boards have jumpers to change the action on watchdog timeout or
259  * disable the watchdog completely.
260  * XXX-BZ notyet: currently no general infrastructure exists to do this.
261  */
262 static int
263 sysctl_wb_force_test_nmi(SYSCTL_HANDLER_ARGS)
264 {
265         struct wb_softc *sc;
266         int error, test, val;
267
268         sc = arg1;
269         test = arg2;
270
271 #ifdef notyet
272         val = sc->test_nmi;
273 #else
274         val = 0;
275 #endif
276         error = sysctl_handle_int(oidp, &val, 0, req);
277         if (error || !req->newptr)
278                 return (error);
279
280 #ifdef notyet
281         /* Manually clear the test for a value of 0 and do nothing else. */
282         if (test && val == 0) {
283                 sc->test_nmi = 0;
284                 return (0);
285         }
286 #endif
287
288         /*
289          * Enter extended function mode in case someone else has been
290          * poking on the registers.  We will not leave it though.
291          */
292         if ((*sc->ext_cfg_enter_f)(sc) != 0)
293                 return (ENXIO);
294
295 #ifdef notyet
296         /*
297          * If we are testing the NMI functionality, set the flag before
298          * forcing the timeout.
299          */
300         if (test)
301                 sc->test_nmi = 1;
302 #endif
303
304         /* Watchdog is configured as part of LDN 8 (GPIO Port2, Watchdog). */
305         write_efir_1(sc, WB_LDN_REG);
306         write_efdr_1(sc, WB_LDN_REG_LDN8);
307
308         /* Force watchdog to fire. */
309         write_efir_1(sc, WB_LDN8_CRF7);
310         sc->reg_2 = read_efdr_1(sc);
311         sc->reg_2 |= WB_LDN8_CRF7_FORCE;
312
313         write_efir_1(sc, WB_LDN8_CRF7);
314         write_efdr_1(sc, sc->reg_2);
315
316         return (0);
317 }
318
319 /*
320  * Print current watchdog state.
321  *
322  * Note: it is the responsibility of the caller to update the registers
323  * upfront.
324  */
325 static void
326 wb_print_state(struct wb_softc *sc, const char *msg)
327 {
328
329         device_printf(sc->dev, "%s%sWatchdog %sabled. %s"
330             "Scaling by %ds, timer at %d (%s=%ds%s). "
331             "CRF5 0x%02x CRF7 0x%02x\n",
332             (msg != NULL) ? msg : "", (msg != NULL) ? ": " : "",
333             (sc->reg_timeout > 0x00) ? "en" : "dis",
334             (sc->reg_2 & WB_LDN8_CRF7_TS) ? "Watchdog fired. " : "",
335             (sc->reg_1 & WB_LDN8_CRF5_SCALE) ? 60 : 1,
336             sc->reg_timeout,
337             (sc->reg_timeout > 0x00) ? "<" : "",
338             sc->reg_timeout * ((sc->reg_1 & WB_LDN8_CRF5_SCALE) ? 60 : 1),
339             (sc->reg_timeout > 0x00) ? " left" : "",
340             sc->reg_1, sc->reg_2);
341 }
342
343 /*
344  * Functions to enter and exit extended function mode.  Possibly shared
345  * between different chips.
346  */
347 static int
348 ext_cfg_enter_0x87_0x87(struct wb_softc *sc)
349 {
350
351         /*
352          * Enable extended function mode.
353          * Winbond does not allow us to validate so always return success.
354          */
355         write_efir_1(sc, 0x87);
356         write_efir_1(sc, 0x87);
357
358         return (0);
359 }
360
361 static void
362 ext_cfg_exit_0xaa(struct wb_softc *sc)
363 {
364
365         write_efir_1(sc, 0xaa);
366 }
367
368 /*
369  * (Re)load the watchdog counter depending on timeout.  A timeout of 0 will
370  * disable the watchdog.
371  */
372 static int
373 wb_set_watchdog(struct wb_softc *sc, unsigned int timeout)
374 {
375
376         if (sc->debug_verbose)
377                 wb_print_state(sc, "Before watchdog counter (re)load");
378
379         /*
380          * Enter extended function mode in case someone else has been
381          * poking on the registers.  We will not leave it though.
382          */
383         if ((*sc->ext_cfg_enter_f)(sc) != 0)
384                 return (ENXIO);
385
386         /* Watchdog is configured as part of LDN 8 (GPIO Port2, Watchdog) */
387         write_efir_1(sc, WB_LDN_REG);
388         write_efdr_1(sc, WB_LDN_REG_LDN8);
389
390         /* Disable and validate or arm/reset watchdog. */
391         if (timeout == 0) {
392                 /* Disable watchdog. */
393                 write_efir_1(sc, WB_LDN8_CRF6);
394                 write_efdr_1(sc, 0x00);
395
396                 /* Re-check. */
397                 write_efir_1(sc, WB_LDN8_CRF6);
398                 sc->reg_timeout = read_efdr_1(sc);
399                 
400                 if (sc->reg_timeout != 0x00) {
401                         device_printf(sc->dev, "Failed to disable watchdog: "
402                             "0x%02x.\n", sc->reg_timeout);
403                         return (EIO);
404                 }
405
406         } else {
407                 /*
408                  * In case an override is set, let it override.  It may lead
409                  * to strange results as we do not check the input of the sysctl.
410                  */
411                 if (sc->timeout_override > 0)
412                         timeout = sc->timeout_override;
413
414                 /* Make sure we support the requested timeout. */
415                 if (timeout > 255 * 60)
416                         return (EINVAL);
417
418                 /* Read current scaling factor. */
419                 write_efir_1(sc, WB_LDN8_CRF5);
420                 sc->reg_1 = read_efdr_1(sc);
421
422                 if (timeout > 255) {
423                         /* Set scaling factor to 60s. */
424                         sc->reg_1 |= WB_LDN8_CRF5_SCALE;
425                         sc->reg_timeout = (timeout / 60);
426                         if (timeout % 60)
427                                 sc->reg_timeout++;
428                 } else {
429                         /* Set scaling factor to 1s. */
430                         sc->reg_1 &= ~WB_LDN8_CRF5_SCALE;
431                         sc->reg_timeout = timeout;
432                 }
433
434                 /* In case we fired before we need to clear to fire again. */
435                 write_efir_1(sc, WB_LDN8_CRF7);
436                 sc->reg_2 = read_efdr_1(sc);
437                 if (sc->reg_2 & WB_LDN8_CRF7_TS) {
438                         sc->reg_2 &= ~WB_LDN8_CRF7_TS;
439                         write_efir_1(sc, WB_LDN8_CRF7);
440                         write_efdr_1(sc, sc->reg_2);
441                 }
442
443                 /* Write back scaling factor. */
444                 write_efir_1(sc, WB_LDN8_CRF5);
445                 write_efdr_1(sc, sc->reg_1);
446
447                 /* Set timer and arm/reset the watchdog. */
448                 write_efir_1(sc, WB_LDN8_CRF6);
449                 write_efdr_1(sc, sc->reg_timeout);
450         }
451
452         if (sc->debug_verbose)
453                 wb_print_state(sc, "After watchdog counter (re)load");
454
455         return (0);
456 }
457
458 /*
459  * watchdog(9) EVENTHANDLER function implementation to (re)load the counter
460  * with the given timeout or disable the watchdog.
461  */
462 static void
463 wb_watchdog_fn(void *private, u_int cmd, int *error)
464 {
465         struct wb_softc *sc;
466         unsigned int timeout;
467         int e;
468
469         sc = private;
470         KASSERT(sc != NULL, ("%s: watchdog handler function called without "
471             "softc.", __func__));
472
473         cmd &= WD_INTERVAL;
474         if (cmd > 0 && cmd <= 63) {
475                 /* Reset (and arm) watchdog. */
476                 timeout = ((uint64_t)1 << cmd) / 1000000000;
477                 if (timeout == 0)
478                         timeout = 1;
479                 e = wb_set_watchdog(sc, timeout);
480                 if (e == 0) {
481                         if (error != NULL)
482                                 *error = 0;
483                 } else {
484                         /* On error, try to make sure the WD is disabled. */
485                         wb_set_watchdog(sc, 0);
486                 }
487
488         } else {
489                 /* Disable watchdog. */
490                 e = wb_set_watchdog(sc, 0);
491                 if (e != 0 && cmd == 0 && error != NULL) {
492                         /* Failed to disable watchdog. */
493                         *error = EOPNOTSUPP;
494                 }
495         }
496 }
497
498 /*
499  * Probe/attach the Winbond Super I/O chip.
500  *
501  * Initial abstraction to possibly support more chips:
502  * - Iterate over the well known base ports, try to enable extended function
503  *   mode and read and match the device ID and device revision.  Unfortunately
504  *   the Vendor ID is in the hardware monitoring section accessible by different
505  *   base ports only.
506  * - Also HEFRAS, which would tell use the base port, is only accessible after
507  *   entering extended function mode, for which the base port is needed.
508  *   At least check HEFRAS to match the current base port we are probing.
509  * - On match set the description, remember functions to enter/exit extended
510  *   function mode as well as the base port.
511  */
512 static int
513 wb_probe_enable(device_t dev, int probe)
514 {
515         struct wb_softc *sc;
516         int error, found, i, j;
517         uint8_t dev_id, dev_rev, cr26;
518
519         sc = device_get_softc(dev);
520         bzero(sc, sizeof(*sc));
521         sc->dev = dev;
522
523         error = ENXIO;
524         for (i = 0; i < sizeof(probe_addrs) / sizeof(*probe_addrs); i++) {
525
526                 /* Allocate bus resources for IO index/data register access. */
527                 sc->portres = bus_alloc_resource(dev, SYS_RES_IOPORT, &sc->rid,
528                     probe_addrs[i].efer, probe_addrs[i].efer + 1, 2, RF_ACTIVE);
529                 if (sc->portres == NULL)
530                         continue;
531                 sc->bst = rman_get_bustag(sc->portres);
532                 sc->bsh = rman_get_bushandle(sc->portres);
533
534                 found = 0;
535                 error = (*probe_addrs[i].ext_cfg_enter_f)(sc);
536                 if (error != 0)
537                         goto cleanup;
538
539                 /* Identify the SuperIO chip. */
540                 write_efir_1(sc, WB_DEVICE_ID_REG);
541                 dev_id = read_efdr_1(sc);
542                 write_efir_1(sc, WB_DEVICE_REV_REG);
543                 dev_rev = read_efdr_1(sc);
544                 write_efir_1(sc, WB_CR26);
545                 cr26 = read_efdr_1(sc);
546
547                 /* HEFRAS of 0 means EFER at 0x2e, 1 means EFER at 0x4e. */
548                 if (((cr26 & 0x40) == 0x00 && probe_addrs[i].efer != 0x2e) ||
549                     ((cr26 & 0x40) == 0x40 && probe_addrs[i].efer != 0x4e)) {
550                         device_printf(dev, "HEFRAS and EFER do not align: EFER "
551                             "0x%02x DevID 0x%02x DevRev 0x%02x CR26 0x%02x\n",
552                              probe_addrs[i].efer, dev_id, dev_rev, cr26);
553                         goto cleanup;
554                 }
555
556                 for (j = 0; j < sizeof(wb_devs) / sizeof(*wb_devs); j++) {
557                         if (wb_devs[j].device_id == dev_id &&
558                             wb_devs[j].device_rev == dev_rev) {
559                                 if (probe)
560                                         device_set_desc(dev, wb_devs[j].descr);
561                                 found++;
562                                 break;
563                         }
564                 }
565                 if (probe && found && bootverbose)
566                         device_printf(dev, "%s EFER 0x%02x ID 0x%02x Rev 0x%02x"
567                              " CR26 0x%02x (probing)\n", device_get_desc(dev),
568                              probe_addrs[i].efer, dev_id, dev_rev, cr26);
569 cleanup:
570                 if (probe || !found) {
571                         (*probe_addrs[i].ext_cfg_exit_f)(sc);
572
573                         (void) bus_release_resource(dev, SYS_RES_IOPORT, sc->rid,
574                             sc->portres);
575                 }
576
577                 /*
578                  * Stop probing if have successfully identified the SuperIO.
579                  * Remember the extended function mode enter/exit functions
580                  * for operations.
581                  */
582                 if (found) {
583                         sc->ext_cfg_enter_f = probe_addrs[i].ext_cfg_enter_f;
584                         sc->ext_cfg_exit_f = probe_addrs[i].ext_cfg_exit_f;
585                         error = BUS_PROBE_DEFAULT;
586                         break;
587                 } else
588                         error = ENXIO;
589         }
590
591         return (error);
592 }
593
594 static int
595 wb_probe(device_t dev)
596 {
597
598         /* Make sure we do not claim some ISA PNP device. */
599         if (isa_get_logicalid(dev) != 0)
600                 return (ENXIO);
601
602         return (wb_probe_enable(dev, 1));
603 }
604
605 static int
606 wb_attach(device_t dev)
607 {
608         struct wb_softc *sc;
609         struct sysctl_ctx_list *sctx;
610         struct sysctl_oid *soid;
611         unsigned long timeout;
612         int error;
613
614         error = wb_probe_enable(dev, 0);
615         if (error > 0)
616                 return (ENXIO);
617
618         sc = device_get_softc(dev);
619         KASSERT(sc->ext_cfg_enter_f != NULL && sc->ext_cfg_exit_f != NULL,
620             ("%s: successfull probe result but not setup correctly", __func__));
621
622         /* Watchdog is configured as part of LDN 8 (GPIO Port2, Watchdog). */
623         write_efir_1(sc, WB_LDN_REG);
624         write_efdr_1(sc, WB_LDN_REG_LDN8);
625
626         /* Make sure LDN8 is enabled (Do we need to? Also affects GPIO). */
627         write_efir_1(sc, WB_LDN8_CR30);
628         write_efdr_1(sc, WB_LDN8_CR30_ACTIVE);
629
630         /* Read the current watchdog configuration. */
631         write_efir_1(sc, WB_LDN8_CRF5);
632         sc->reg_1 = read_efdr_1(sc);
633         write_efir_1(sc, WB_LDN8_CRF6);
634         sc->reg_timeout = read_efdr_1(sc);
635         write_efir_1(sc, WB_LDN8_CRF7);
636         sc->reg_2 = read_efdr_1(sc);
637
638         /* Print current state if bootverbose or watchdog already enabled. */
639         if (bootverbose || (sc->reg_timeout > 0x00))
640                 wb_print_state(sc, "Before watchdog attach");
641
642         /*
643          * Clear a previous watchdog timeout event (if (still) set).
644          * Disable all all interrupt reset sources (defaults).
645          */
646         sc->reg_1 &= ~(WB_LDN8_CRF5_KEYB_P20);
647         sc->reg_1 |= WB_LDN8_CRF5_KBRST;
648         write_efir_1(sc, WB_LDN8_CRF5);
649         write_efdr_1(sc, sc->reg_1);
650
651         sc->reg_2 &= ~WB_LDN8_CRF7_CLEAR_MASK;
652         write_efir_1(sc, WB_LDN8_CRF7);
653         write_efdr_1(sc, sc->reg_2);
654
655         /* Read global timeout override tunable, Add per device sysctls. */
656         if (TUNABLE_ULONG_FETCH("hw.wbwd.timeout_override", &timeout)) {
657                 if (timeout > 0)
658                         sc->timeout_override = timeout;
659         }
660         sctx = device_get_sysctl_ctx(dev);
661         soid = device_get_sysctl_tree(dev);
662         SYSCTL_ADD_UINT(sctx, SYSCTL_CHILDREN(soid), OID_AUTO,
663             "timeout_override", CTLFLAG_RW, &sc->timeout_override, 0,
664             "Timeout in seconds overriding default watchdog timeout");
665         SYSCTL_ADD_INT(sctx, SYSCTL_CHILDREN(soid), OID_AUTO,
666             "debug_verbose", CTLFLAG_RW, &sc->debug_verbose, 0,
667             "Enables extra debugging information");
668         SYSCTL_ADD_PROC(sctx, SYSCTL_CHILDREN(soid), OID_AUTO, "debug",
669             CTLTYPE_STRING|CTLFLAG_RD, sc, 0, sysctl_wb_debug, "A",
670             "Selected register information from last change by driver");
671         SYSCTL_ADD_PROC(sctx, SYSCTL_CHILDREN(soid), OID_AUTO, "debug_current",
672             CTLTYPE_STRING|CTLFLAG_RD|CTLFLAG_SKIP, sc, 0,
673              sysctl_wb_debug_current, "A",
674              "Selected register information (may interfere)");
675         SYSCTL_ADD_PROC(sctx, SYSCTL_CHILDREN(soid), OID_AUTO, "force_timeout",
676             CTLTYPE_INT|CTLFLAG_RW|CTLFLAG_SKIP, sc, 0,
677             sysctl_wb_force_test_nmi, "I", "Enable to force watchdog to fire.");
678
679         /* Register watchdog. */
680         sc->ev_tag = EVENTHANDLER_REGISTER(watchdog_list, wb_watchdog_fn, sc,
681             0);
682
683         if (bootverbose)
684                 wb_print_state(sc, "After watchdog attach");
685
686         return (0);
687 }
688
689 static int
690 wb_detach(device_t dev)
691 {
692         struct wb_softc *sc;
693
694         sc = device_get_softc(dev);
695
696         /* Unregister and stop the watchdog if running. */
697         if (sc->ev_tag)
698                 EVENTHANDLER_DEREGISTER(watchdog_list, sc->ev_tag);
699         wb_set_watchdog(sc, 0);
700
701         /* Disable extended function mode. */
702         (*sc->ext_cfg_exit_f)(sc);
703
704         /* Cleanup resources. */
705         (void) bus_release_resource(dev, SYS_RES_IOPORT, sc->rid, sc->portres);
706
707         /* Bus subroutines take care of sysctls already. */
708
709         return (0);
710 }
711
712 static device_method_t wb_methods[] = {
713         /* Device interface */
714         DEVMETHOD(device_probe,         wb_probe),
715         DEVMETHOD(device_attach,        wb_attach),
716         DEVMETHOD(device_detach,        wb_detach),
717
718         { 0, 0 }
719 };
720
721 static driver_t wb_isa_driver = {
722         "wbwd",
723         wb_methods,
724         sizeof(struct wb_softc)
725 };
726
727 static devclass_t wb_devclass;
728
729 DRIVER_MODULE(wb, isa, wb_isa_driver, wb_devclass, NULL, NULL);