FreeBSD/Linux Kernel Cross Reference
sys/dev/wbwd/wbwd.c
1 /*-
2 * Copyright (c) 2011 Sandvine Incorporated ULC.
3 * Copyright (c) 2012 iXsystems, Inc.
4 * All rights reserved.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 * 1. Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * 2. Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
14 *
15 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
16 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
19 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25 * SUCH DAMAGE.
26 */
27 /*
28 * Support for Winbond watchdog.
29 *
30 * With minor abstractions it might be possible to add support for other
31 * different Winbond Super I/O chips as well. Winbond seems to have four
32 * different types of chips, four different ways to get into extended config
33 * mode.
34 *
35 * Note: there is no serialization between the debugging sysctl handlers and
36 * the watchdog functions and possibly others poking the registers at the same
37 * time. For that at least possibly interfering sysctls are hidden by default.
38 */
39
40 #include <sys/cdefs.h>
41 __FBSDID("$FreeBSD: releng/11.2/sys/dev/wbwd/wbwd.c 331722 2018-03-29 02:50:57Z eadler $");
42
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/lock.h>
49 #include <sys/module.h>
50 #include <sys/rman.h>
51 #include <sys/sbuf.h>
52 #include <sys/sysctl.h>
53 #include <sys/watchdog.h>
54
55 #include <isa/isavar.h>
56
57 #include <machine/bus.h>
58 #include <machine/resource.h>
59
60 /*
61 * Global registers.
62 */
63 #define WB_DEVICE_ID_REG 0x20 /* Device ID */
64 #define WB_DEVICE_REV_REG 0x21 /* Device revision */
65 #define WB_CR26 0x26 /* Bit6: HEFRAS (base port selector) */
66
67 /* LDN selection. */
68 #define WB_LDN_REG 0x07
69 #define WB_LDN_REG_LDN8 0x08 /* GPIO 2, Watchdog */
70
71 /*
72 * LDN8 (GPIO 2, Watchdog) specific registers and options.
73 */
74 /* CR30: LDN8 activation control. */
75 #define WB_LDN8_CR30 0x30
76 #define WB_LDN8_CR30_ACTIVE 0x01 /* 1: LD active */
77
78 /* CRF5: Watchdog scale, P20. Mapped to reg_1. */
79 #define WB_LDN8_CRF5 0xF5
80 #define WB_LDN8_CRF5_SCALE 0x08 /* 0: 1s, 1: 60s */
81 #define WB_LDN8_CRF5_KEYB_P20 0x04 /* 1: keyb P20 forces timeout */
82 #define WB_LDN8_CRF5_KBRST 0x02 /* 1: timeout causes pin60 kbd reset */
83
84 /* CRF6: Watchdog Timeout (0 == off). Mapped to reg_timeout. */
85 #define WB_LDN8_CRF6 0xF6
86
87 /* CRF7: Watchdog mouse, keyb, force, .. Mapped to reg_2. */
88 #define WB_LDN8_CRF7 0xF7
89 #define WB_LDN8_CRF7_MOUSE 0x80 /* 1: mouse irq resets wd timer */
90 #define WB_LDN8_CRF7_KEYB 0x40 /* 1: keyb irq resets wd timer */
91 #define WB_LDN8_CRF7_FORCE 0x20 /* 1: force timeout (self-clear) */
92 #define WB_LDN8_CRF7_TS 0x10 /* 0: counting, 1: fired */
93 #define WB_LDN8_CRF7_IRQS 0x0f /* irq source for watchdog, 2 == SMI */
94
95 enum chips { w83627hf, w83627s, w83697hf, w83697ug, w83637hf, w83627thf,
96 w83687thf, w83627ehf, w83627dhg, w83627uhg, w83667hg,
97 w83627dhg_p, w83667hg_b, nct6775, nct6776, nct6779, nct6791,
98 nct6792, nct6793, nct6795, nct6102 };
99
100 struct wb_softc {
101 device_t dev;
102 struct resource *portres;
103 bus_space_tag_t bst;
104 bus_space_handle_t bsh;
105 int rid;
106 eventhandler_tag ev_tag;
107 int (*ext_cfg_enter_f)(struct wb_softc *, u_short);
108 void (*ext_cfg_exit_f)(struct wb_softc *, u_short);
109 enum chips chip;
110 uint8_t ctl_reg;
111 uint8_t time_reg;
112 uint8_t csr_reg;
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 *, u_short);
134 static void ext_cfg_exit_0xaa(struct wb_softc *, u_short);
135
136 struct winbond_superio_cfg {
137 uint8_t efer; /* and efir */
138 int (*ext_cfg_enter_f)(struct wb_softc *, u_short);
139 void (*ext_cfg_exit_f)(struct wb_softc *, u_short);
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 uint8_t device_id;
155 enum chips chip;
156 const char * descr;
157 } wb_devs[] = {
158 {
159 .device_id = 0x52,
160 .chip = w83627hf,
161 .descr = "Winbond 83627HF/F/HG/G",
162 },
163 {
164 .device_id = 0x59,
165 .chip = w83627s,
166 .descr = "Winbond 83627S",
167 },
168 {
169 .device_id = 0x60,
170 .chip = w83697hf,
171 .descr = "Winbond 83697HF",
172 },
173 {
174 .device_id = 0x68,
175 .chip = w83697ug,
176 .descr = "Winbond 83697UG",
177 },
178 {
179 .device_id = 0x70,
180 .chip = w83637hf,
181 .descr = "Winbond 83637HF",
182 },
183 {
184 .device_id = 0x82,
185 .chip = w83627thf,
186 .descr = "Winbond 83627THF",
187 },
188 {
189 .device_id = 0x85,
190 .chip = w83687thf,
191 .descr = "Winbond 83687THF",
192 },
193 {
194 .device_id = 0x88,
195 .chip = w83627ehf,
196 .descr = "Winbond 83627EHF",
197 },
198 {
199 .device_id = 0xa0,
200 .chip = w83627dhg,
201 .descr = "Winbond 83627DHG",
202 },
203 {
204 .device_id = 0xa2,
205 .chip = w83627uhg,
206 .descr = "Winbond 83627UHG",
207 },
208 {
209 .device_id = 0xa5,
210 .chip = w83667hg,
211 .descr = "Winbond 83667HG",
212 },
213 {
214 .device_id = 0xb0,
215 .chip = w83627dhg_p,
216 .descr = "Winbond 83627DHG-P",
217 },
218 {
219 .device_id = 0xb3,
220 .chip = w83667hg_b,
221 .descr = "Winbond 83667HG-B",
222 },
223 {
224 .device_id = 0xb4,
225 .chip = nct6775,
226 .descr = "Nuvoton NCT6775",
227 },
228 {
229 .device_id = 0xc3,
230 .chip = nct6776,
231 .descr = "Nuvoton NCT6776",
232 },
233 {
234 .device_id = 0xc4,
235 .chip = nct6102,
236 .descr = "Nuvoton NCT6102",
237 },
238 {
239 .device_id = 0xc5,
240 .chip = nct6779,
241 .descr = "Nuvoton NCT6779",
242 },
243 {
244 .device_id = 0xc8,
245 .chip = nct6791,
246 .descr = "Nuvoton NCT6791",
247 },
248 {
249 .device_id = 0xc9,
250 .chip = nct6792,
251 .descr = "Nuvoton NCT6792",
252 },
253 {
254 .device_id = 0xd1,
255 .chip = nct6793,
256 .descr = "Nuvoton NCT6793",
257 },
258 {
259 .device_id = 0xd3,
260 .chip = nct6795,
261 .descr = "Nuvoton NCT6795",
262 },
263 };
264
265 static void
266 write_efir_1(struct wb_softc *sc, u_short baseport, uint8_t value)
267 {
268
269 MPASS(sc != NULL || baseport != 0);
270 if (sc != NULL)
271 bus_space_write_1((sc)->bst, (sc)->bsh, 0, (value));
272 else
273 outb(baseport, value);
274 }
275
276 static uint8_t __unused
277 read_efir_1(struct wb_softc *sc, u_short baseport)
278 {
279
280 MPASS(sc != NULL || baseport != 0);
281 if (sc != NULL)
282 return (bus_space_read_1((sc)->bst, (sc)->bsh, 0));
283 else
284 return (inb(baseport));
285 }
286
287 static void
288 write_efdr_1(struct wb_softc *sc, u_short baseport, uint8_t value)
289 {
290
291 MPASS(sc != NULL || baseport != 0);
292 if (sc != NULL)
293 bus_space_write_1((sc)->bst, (sc)->bsh, 1, (value));
294 else
295 outb(baseport + 1, value);
296 }
297
298 static uint8_t
299 read_efdr_1(struct wb_softc *sc, u_short baseport)
300 {
301
302 MPASS(sc != NULL || baseport != 0);
303 if (sc != NULL)
304 return (bus_space_read_1((sc)->bst, (sc)->bsh, 1));
305 else
306 return (inb(baseport + 1));
307 }
308
309 static void
310 write_reg(struct wb_softc *sc, uint8_t reg, uint8_t value)
311 {
312
313 write_efir_1(sc, 0, reg);
314 write_efdr_1(sc, 0, value);
315 }
316
317 static uint8_t
318 read_reg(struct wb_softc *sc, uint8_t reg)
319 {
320
321 write_efir_1(sc, 0, reg);
322 return (read_efdr_1(sc, 0));
323 }
324
325 /*
326 * Return the watchdog related registers as we last read them. This will
327 * usually not give the current timeout or state on whether the watchdog
328 * fired.
329 */
330 static int
331 sysctl_wb_debug(SYSCTL_HANDLER_ARGS)
332 {
333 struct wb_softc *sc;
334 struct sbuf sb;
335 int error;
336
337 sc = arg1;
338
339 sbuf_new_for_sysctl(&sb, NULL, 64, req);
340
341 sbuf_printf(&sb, "LDN8 (GPIO2, Watchdog): ");
342 sbuf_printf(&sb, "CR%02X 0x%02x ", sc->ctl_reg, sc->reg_1);
343 sbuf_printf(&sb, "CR%02X 0x%02x ", sc->time_reg, sc->reg_timeout);
344 sbuf_printf(&sb, "CR%02X 0x%02x", sc->csr_reg, sc->reg_2);
345
346 error = sbuf_finish(&sb);
347 sbuf_delete(&sb);
348 return (error);
349 }
350
351 /*
352 * Read the current values before returning them. Given this might poke
353 * the registers the same time as the watchdog, this sysctl handler should
354 * be marked CTLFLAG_SKIP to not show up by default.
355 */
356 static int
357 sysctl_wb_debug_current(SYSCTL_HANDLER_ARGS)
358 {
359 struct wb_softc *sc;
360
361 sc = arg1;
362
363 if ((*sc->ext_cfg_enter_f)(sc, 0) != 0)
364 return (ENXIO);
365
366 /* Watchdog is configured as part of LDN 8 (GPIO Port2, Watchdog). */
367 write_reg(sc, WB_LDN_REG, WB_LDN_REG_LDN8);
368
369 sc->reg_1 = read_reg(sc, sc->ctl_reg);
370 sc->reg_timeout = read_reg(sc, sc->time_reg);
371 sc->reg_2 = read_reg(sc, sc->csr_reg);
372
373 (*sc->ext_cfg_exit_f)(sc, 0);
374
375 return (sysctl_wb_debug(oidp, arg1, arg2, req));
376 }
377
378 /*
379 * Sysctl handlers to force a watchdog timeout or to test the NMI functionality
380 * works as expetced.
381 * For testing we could set a test_nmi flag in the softc that, in case of NMI, a
382 * callback function from trap.c could check whether we fired and not report the
383 * timeout but clear the flag for the sysctl again. This is interesting given a
384 * lot of boards have jumpers to change the action on watchdog timeout or
385 * disable the watchdog completely.
386 * XXX-BZ notyet: currently no general infrastructure exists to do this.
387 */
388 static int
389 sysctl_wb_force_test_nmi(SYSCTL_HANDLER_ARGS)
390 {
391 struct wb_softc *sc;
392 int error, test, val;
393
394 sc = arg1;
395 test = arg2;
396
397 #ifdef notyet
398 val = sc->test_nmi;
399 #else
400 val = 0;
401 #endif
402 error = sysctl_handle_int(oidp, &val, 0, req);
403 if (error || !req->newptr)
404 return (error);
405
406 #ifdef notyet
407 /* Manually clear the test for a value of 0 and do nothing else. */
408 if (test && val == 0) {
409 sc->test_nmi = 0;
410 return (0);
411 }
412 #endif
413
414 if ((*sc->ext_cfg_enter_f)(sc, 0) != 0)
415 return (ENXIO);
416
417 #ifdef notyet
418 /*
419 * If we are testing the NMI functionality, set the flag before
420 * forcing the timeout.
421 */
422 if (test)
423 sc->test_nmi = 1;
424 #endif
425
426 /* Watchdog is configured as part of LDN 8 (GPIO Port2, Watchdog). */
427 write_reg(sc, WB_LDN_REG, WB_LDN_REG_LDN8);
428
429 /* Force watchdog to fire. */
430 sc->reg_2 = read_reg(sc, sc->csr_reg);
431 sc->reg_2 |= WB_LDN8_CRF7_FORCE;
432 write_reg(sc, sc->csr_reg, sc->reg_2);
433
434 (*sc->ext_cfg_exit_f)(sc, 0);
435
436 return (0);
437 }
438
439 /*
440 * Print current watchdog state.
441 *
442 * Note: it is the responsibility of the caller to update the registers
443 * upfront.
444 */
445 static void
446 wb_print_state(struct wb_softc *sc, const char *msg)
447 {
448
449 device_printf(sc->dev, "%s%sWatchdog %sabled. %s"
450 "Scaling by %ds, timer at %d (%s=%ds%s). "
451 "CRF5 0x%02x CRF7 0x%02x\n",
452 (msg != NULL) ? msg : "", (msg != NULL) ? ": " : "",
453 (sc->reg_timeout > 0x00) ? "en" : "dis",
454 (sc->reg_2 & WB_LDN8_CRF7_TS) ? "Watchdog fired. " : "",
455 (sc->reg_1 & WB_LDN8_CRF5_SCALE) ? 60 : 1,
456 sc->reg_timeout,
457 (sc->reg_timeout > 0x00) ? "<" : "",
458 sc->reg_timeout * ((sc->reg_1 & WB_LDN8_CRF5_SCALE) ? 60 : 1),
459 (sc->reg_timeout > 0x00) ? " left" : "",
460 sc->reg_1, sc->reg_2);
461 }
462
463 /*
464 * Functions to enter and exit extended function mode. Possibly shared
465 * between different chips.
466 */
467 static int
468 ext_cfg_enter_0x87_0x87(struct wb_softc *sc, u_short baseport)
469 {
470
471 /*
472 * Enable extended function mode.
473 * Winbond does not allow us to validate so always return success.
474 */
475 write_efir_1(sc, baseport, 0x87);
476 write_efir_1(sc, baseport, 0x87);
477
478 return (0);
479 }
480
481 static void
482 ext_cfg_exit_0xaa(struct wb_softc *sc, u_short baseport)
483 {
484
485 write_efir_1(sc, baseport, 0xaa);
486 }
487
488 /*
489 * (Re)load the watchdog counter depending on timeout. A timeout of 0 will
490 * disable the watchdog.
491 */
492 static int
493 wb_set_watchdog(struct wb_softc *sc, unsigned int timeout)
494 {
495
496 if (timeout != 0) {
497 /*
498 * In case an override is set, let it override. It may lead
499 * to strange results as we do not check the input of the sysctl.
500 */
501 if (sc->timeout_override > 0)
502 timeout = sc->timeout_override;
503
504 /* Make sure we support the requested timeout. */
505 if (timeout > 255 * 60)
506 return (EINVAL);
507 }
508
509 if (sc->debug_verbose)
510 wb_print_state(sc, "Before watchdog counter (re)load");
511
512 if ((*sc->ext_cfg_enter_f)(sc, 0) != 0)
513 return (ENXIO);
514
515 /* Watchdog is configured as part of LDN 8 (GPIO Port2, Watchdog) */
516 write_reg(sc, WB_LDN_REG, WB_LDN_REG_LDN8);
517
518 if (timeout == 0) {
519 /* Disable watchdog. */
520 sc->reg_timeout = 0;
521 write_reg(sc, sc->time_reg, sc->reg_timeout);
522
523 } else {
524 /* Read current scaling factor. */
525 sc->reg_1 = read_reg(sc, sc->ctl_reg);
526
527 if (timeout > 255) {
528 /* Set scaling factor to 60s. */
529 sc->reg_1 |= WB_LDN8_CRF5_SCALE;
530 sc->reg_timeout = (timeout / 60);
531 if (timeout % 60)
532 sc->reg_timeout++;
533 } else {
534 /* Set scaling factor to 1s. */
535 sc->reg_1 &= ~WB_LDN8_CRF5_SCALE;
536 sc->reg_timeout = timeout;
537 }
538
539 /* In case we fired before we need to clear to fire again. */
540 sc->reg_2 = read_reg(sc, sc->csr_reg);
541 if (sc->reg_2 & WB_LDN8_CRF7_TS) {
542 sc->reg_2 &= ~WB_LDN8_CRF7_TS;
543 write_reg(sc, sc->csr_reg, sc->reg_2);
544 }
545
546 /* Write back scaling factor. */
547 write_reg(sc, sc->ctl_reg, sc->reg_1);
548
549 /* Set timer and arm/reset the watchdog. */
550 write_reg(sc, sc->time_reg, sc->reg_timeout);
551 }
552
553 (*sc->ext_cfg_exit_f)(sc, 0);
554
555 if (sc->debug_verbose)
556 wb_print_state(sc, "After watchdog counter (re)load");
557 return (0);
558 }
559
560 /*
561 * watchdog(9) EVENTHANDLER function implementation to (re)load the counter
562 * with the given timeout or disable the watchdog.
563 */
564 static void
565 wb_watchdog_fn(void *private, u_int cmd, int *error)
566 {
567 struct wb_softc *sc;
568 unsigned int timeout;
569 int e;
570
571 sc = private;
572 KASSERT(sc != NULL, ("%s: watchdog handler function called without "
573 "softc.", __func__));
574
575 cmd &= WD_INTERVAL;
576 if (cmd > 0 && cmd <= 63) {
577 /* Reset (and arm) watchdog. */
578 timeout = ((uint64_t)1 << cmd) / 1000000000;
579 if (timeout == 0)
580 timeout = 1;
581 e = wb_set_watchdog(sc, timeout);
582 if (e == 0) {
583 if (error != NULL)
584 *error = 0;
585 } else {
586 /* On error, try to make sure the WD is disabled. */
587 wb_set_watchdog(sc, 0);
588 }
589
590 } else {
591 /* Disable watchdog. */
592 e = wb_set_watchdog(sc, 0);
593 if (e != 0 && cmd == 0 && error != NULL) {
594 /* Failed to disable watchdog. */
595 *error = EOPNOTSUPP;
596 }
597 }
598 }
599
600 /*
601 * Probe/attach the Winbond Super I/O chip.
602 *
603 * Initial abstraction to possibly support more chips:
604 * - Iterate over the well known base ports, try to enable extended function
605 * mode and read and match the device ID and device revision. Unfortunately
606 * the Vendor ID is in the hardware monitoring section accessible by different
607 * base ports only.
608 * - Also HEFRAS, which would tell use the base port, is only accessible after
609 * entering extended function mode, for which the base port is needed.
610 * At least check HEFRAS to match the current base port we are probing.
611 * - On match set the description, remember functions to enter/exit extended
612 * function mode as well as the base port.
613 */
614 static int
615 wb_probe_enable(device_t dev, int probe)
616 {
617 struct wb_softc *sc;
618 int error, found, i, j;
619 uint8_t dev_id, dev_rev, cr26;
620 char buf[128];
621
622 if (dev == NULL)
623 sc = NULL;
624 else {
625 sc = device_get_softc(dev);
626 bzero(sc, sizeof(*sc));
627 sc->dev = dev;
628 }
629
630 error = ENXIO;
631 found = 0;
632 for (i = 0; i < nitems(probe_addrs); i++) {
633
634 if (sc != NULL) {
635 /* Allocate bus resources for IO index/data register access. */
636 sc->portres = bus_alloc_resource(dev, SYS_RES_IOPORT, &sc->rid,
637 probe_addrs[i].efer, probe_addrs[i].efer + 1, 2, RF_ACTIVE);
638 if (sc->portres == NULL)
639 continue;
640 sc->bst = rman_get_bustag(sc->portres);
641 sc->bsh = rman_get_bushandle(sc->portres);
642 }
643
644 error = (*probe_addrs[i].ext_cfg_enter_f)(sc, probe_addrs[i].efer);
645 if (error != 0)
646 goto cleanup;
647
648 /* Identify the SuperIO chip. */
649 write_efir_1(sc, probe_addrs[i].efer, WB_DEVICE_ID_REG);
650 dev_id = read_efdr_1(sc, probe_addrs[i].efer);
651 write_efir_1(sc, probe_addrs[i].efer, WB_DEVICE_REV_REG);
652 dev_rev = read_efdr_1(sc, probe_addrs[i].efer);
653 write_efir_1(sc, probe_addrs[i].efer, WB_CR26);
654 cr26 = read_efdr_1(sc, probe_addrs[i].efer);
655
656 if (dev_id == 0xff && dev_rev == 0xff)
657 goto cleanup;
658
659 /* HEFRAS of 0 means EFER at 0x2e, 1 means EFER at 0x4e. */
660 if (((cr26 & 0x40) == 0x00 && probe_addrs[i].efer != 0x2e) ||
661 ((cr26 & 0x40) == 0x40 && probe_addrs[i].efer != 0x4e)) {
662 if (dev != NULL)
663 device_printf(dev, "HEFRAS and EFER do not "
664 "align: EFER 0x%02x DevID 0x%02x DevRev "
665 "0x%02x CR26 0x%02x\n",
666 probe_addrs[i].efer, dev_id, dev_rev, cr26);
667 goto cleanup;
668 }
669
670 for (j = 0; j < nitems(wb_devs); j++) {
671 if (wb_devs[j].device_id == dev_id) {
672 found = 1;
673 break;
674 }
675 }
676
677 if (probe && dev != NULL) {
678 snprintf(buf, sizeof(buf),
679 "%s (0x%02x/0x%02x) Watchdog Timer",
680 found ? wb_devs[j].descr :
681 "Unknown Winbond/Nuvoton", dev_id, dev_rev);
682 device_set_desc_copy(dev, buf);
683 }
684
685 /* If this is hinted attach, try to guess the model. */
686 if (dev != NULL && !found) {
687 found = 1;
688 j = 0;
689 }
690
691 cleanup:
692 if (probe || !found) {
693 (*probe_addrs[i].ext_cfg_exit_f)(sc, probe_addrs[i].efer);
694 if (sc != NULL)
695 (void) bus_release_resource(dev, SYS_RES_IOPORT,
696 sc->rid, sc->portres);
697 }
698
699 /*
700 * Stop probing if have successfully identified the SuperIO.
701 * Remember the extended function mode enter/exit functions
702 * for operations.
703 */
704 if (found) {
705 if (sc != NULL) {
706 sc->ext_cfg_enter_f = probe_addrs[i].ext_cfg_enter_f;
707 sc->ext_cfg_exit_f = probe_addrs[i].ext_cfg_exit_f;
708 sc->chip = wb_devs[j].chip;
709 sc->ctl_reg = 0xf5;
710 sc->time_reg = 0xf6;
711 sc->csr_reg = 0xf7;
712 if (sc->chip == w83697hf ||
713 sc->chip == w83697ug) {
714 sc->ctl_reg = 0xf3;
715 sc->time_reg = 0xf4;
716 } else if (sc->chip == nct6102) {
717 sc->ctl_reg = 0xf0;
718 sc->time_reg = 0xf1;
719 sc->csr_reg = 0xf2;
720 }
721 }
722 return (BUS_PROBE_SPECIFIC);
723 } else
724 error = ENXIO;
725 }
726
727 return (error);
728 }
729
730 static void
731 wb_identify(driver_t *driver, device_t parent)
732 {
733
734 if (device_find_child(parent, driver->name, 0) == NULL) {
735 if (wb_probe_enable(NULL, 1) <= 0)
736 BUS_ADD_CHILD(parent, 0, driver->name, 0);
737 }
738 }
739
740 static int
741 wb_probe(device_t dev)
742 {
743
744 /* Make sure we do not claim some ISA PNP device. */
745 if (isa_get_logicalid(dev) != 0)
746 return (ENXIO);
747
748 return (wb_probe_enable(dev, 1));
749 }
750
751 static int
752 wb_attach(device_t dev)
753 {
754 struct wb_softc *sc;
755 struct sysctl_ctx_list *sctx;
756 struct sysctl_oid *soid;
757 unsigned long timeout;
758 int error;
759 uint8_t t;
760
761 error = wb_probe_enable(dev, 0);
762 if (error > 0)
763 return (ENXIO);
764
765 sc = device_get_softc(dev);
766 KASSERT(sc->ext_cfg_enter_f != NULL && sc->ext_cfg_exit_f != NULL,
767 ("%s: successful probe result but not setup correctly", __func__));
768
769 /* Watchdog is configured as part of LDN 8 (GPIO Port2, Watchdog). */
770 write_reg(sc, WB_LDN_REG, WB_LDN_REG_LDN8);
771
772 /* Make sure WDT is enabled. */
773 write_reg(sc, WB_LDN8_CR30,
774 read_reg(sc, WB_LDN8_CR30) | WB_LDN8_CR30_ACTIVE);
775
776 switch (sc->chip) {
777 case w83627hf:
778 case w83627s:
779 t = read_reg(sc, 0x2B) & ~0x10;
780 write_reg(sc, 0x2B, t); /* set GPIO24 to WDT0 */
781 break;
782 case w83697hf:
783 /* Set pin 119 to WDTO# mode (= CR29, WDT0) */
784 t = read_reg(sc, 0x29) & ~0x60;
785 t |= 0x20;
786 write_reg(sc, 0x29, t);
787 break;
788 case w83697ug:
789 /* Set pin 118 to WDTO# mode */
790 t = read_reg(sc, 0x2b) & ~0x04;
791 write_reg(sc, 0x2b, t);
792 break;
793 case w83627thf:
794 t = (read_reg(sc, 0x2B) & ~0x08) | 0x04;
795 write_reg(sc, 0x2B, t); /* set GPIO3 to WDT0 */
796 break;
797 case w83627dhg:
798 case w83627dhg_p:
799 t = read_reg(sc, 0x2D) & ~0x01; /* PIN77 -> WDT0# */
800 write_reg(sc, 0x2D, t); /* set GPIO5 to WDT0 */
801 t = read_reg(sc, sc->ctl_reg);
802 t |= 0x02; /* enable the WDTO# output low pulse
803 * to the KBRST# pin */
804 write_reg(sc, sc->ctl_reg, t);
805 break;
806 case w83637hf:
807 break;
808 case w83687thf:
809 t = read_reg(sc, 0x2C) & ~0x80; /* PIN47 -> WDT0# */
810 write_reg(sc, 0x2C, t);
811 break;
812 case w83627ehf:
813 case w83627uhg:
814 case w83667hg:
815 case w83667hg_b:
816 case nct6775:
817 case nct6776:
818 case nct6779:
819 case nct6791:
820 case nct6792:
821 case nct6793:
822 case nct6795:
823 case nct6102:
824 /*
825 * These chips have a fixed WDTO# output pin (W83627UHG),
826 * or support more than one WDTO# output pin.
827 * Don't touch its configuration, and hope the BIOS
828 * does the right thing.
829 */
830 t = read_reg(sc, sc->ctl_reg);
831 t |= 0x02; /* enable the WDTO# output low pulse
832 * to the KBRST# pin */
833 write_reg(sc, sc->ctl_reg, t);
834 break;
835 default:
836 break;
837 }
838
839 /* Read the current watchdog configuration. */
840 sc->reg_1 = read_reg(sc, sc->ctl_reg);
841 sc->reg_timeout = read_reg(sc, sc->time_reg);
842 sc->reg_2 = read_reg(sc, sc->csr_reg);
843
844 /* Print current state if bootverbose or watchdog already enabled. */
845 if (bootverbose || (sc->reg_timeout > 0x00))
846 wb_print_state(sc, "Before watchdog attach");
847
848 sc->reg_1 &= ~WB_LDN8_CRF5_KEYB_P20;
849 sc->reg_1 |= WB_LDN8_CRF5_KBRST;
850 write_reg(sc, sc->ctl_reg, sc->reg_1);
851
852 /*
853 * Clear a previous watchdog timeout event (if still set).
854 * Disable timer reset on mouse interrupts. Leave reset on keyboard,
855 * since one of my boards is getting stuck in reboot without it.
856 */
857 sc->reg_2 &= ~(WB_LDN8_CRF7_MOUSE|WB_LDN8_CRF7_TS);
858 write_reg(sc, sc->csr_reg, sc->reg_2);
859
860 (*sc->ext_cfg_exit_f)(sc, 0);
861
862 /* Read global timeout override tunable, Add per device sysctls. */
863 if (TUNABLE_ULONG_FETCH("hw.wbwd.timeout_override", &timeout)) {
864 if (timeout > 0)
865 sc->timeout_override = timeout;
866 }
867 sctx = device_get_sysctl_ctx(dev);
868 soid = device_get_sysctl_tree(dev);
869 SYSCTL_ADD_UINT(sctx, SYSCTL_CHILDREN(soid), OID_AUTO,
870 "timeout_override", CTLFLAG_RW, &sc->timeout_override, 0,
871 "Timeout in seconds overriding default watchdog timeout");
872 SYSCTL_ADD_INT(sctx, SYSCTL_CHILDREN(soid), OID_AUTO,
873 "debug_verbose", CTLFLAG_RW, &sc->debug_verbose, 0,
874 "Enables extra debugging information");
875 SYSCTL_ADD_PROC(sctx, SYSCTL_CHILDREN(soid), OID_AUTO, "debug",
876 CTLTYPE_STRING|CTLFLAG_RD, sc, 0, sysctl_wb_debug, "A",
877 "Selected register information from last change by driver");
878 SYSCTL_ADD_PROC(sctx, SYSCTL_CHILDREN(soid), OID_AUTO, "debug_current",
879 CTLTYPE_STRING|CTLFLAG_RD|CTLFLAG_SKIP, sc, 0,
880 sysctl_wb_debug_current, "A",
881 "Selected register information (may interfere)");
882 SYSCTL_ADD_PROC(sctx, SYSCTL_CHILDREN(soid), OID_AUTO, "force_timeout",
883 CTLTYPE_INT|CTLFLAG_RW|CTLFLAG_SKIP, sc, 0,
884 sysctl_wb_force_test_nmi, "I", "Enable to force watchdog to fire.");
885
886 /* Register watchdog. */
887 sc->ev_tag = EVENTHANDLER_REGISTER(watchdog_list, wb_watchdog_fn, sc,
888 0);
889
890 if (bootverbose)
891 wb_print_state(sc, "After watchdog attach");
892
893 return (0);
894 }
895
896 static int
897 wb_detach(device_t dev)
898 {
899 struct wb_softc *sc;
900
901 sc = device_get_softc(dev);
902
903 /* Unregister and stop the watchdog if running. */
904 if (sc->ev_tag)
905 EVENTHANDLER_DEREGISTER(watchdog_list, sc->ev_tag);
906 wb_set_watchdog(sc, 0);
907
908 /* Disable extended function mode. */
909 (*sc->ext_cfg_exit_f)(sc, 0);
910
911 /* Cleanup resources. */
912 (void) bus_release_resource(dev, SYS_RES_IOPORT, sc->rid, sc->portres);
913
914 /* Bus subroutines take care of sysctls already. */
915
916 return (0);
917 }
918
919 static device_method_t wb_methods[] = {
920 /* Device interface */
921 DEVMETHOD(device_identify, wb_identify),
922 DEVMETHOD(device_probe, wb_probe),
923 DEVMETHOD(device_attach, wb_attach),
924 DEVMETHOD(device_detach, wb_detach),
925
926 DEVMETHOD_END
927 };
928
929 static driver_t wb_isa_driver = {
930 "wbwd",
931 wb_methods,
932 sizeof(struct wb_softc)
933 };
934
935 static devclass_t wb_devclass;
936
937 DRIVER_MODULE(wb, isa, wb_isa_driver, wb_devclass, NULL, NULL);
Cache object: ca3663cf9132fbabc4f26f53074cedee
|