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