10566170fSBjoern A. Zeeb /*- 24d846d26SWarner Losh * SPDX-License-Identifier: BSD-2-Clause 3718cf2ccSPedro F. Giffuni * 40566170fSBjoern A. Zeeb * Copyright (c) 2011 Sandvine Incorporated ULC. 51adb4ebdSXin LI * Copyright (c) 2012 iXsystems, Inc. 60566170fSBjoern A. Zeeb * All rights reserved. 70566170fSBjoern A. Zeeb * 80566170fSBjoern A. Zeeb * Redistribution and use in source and binary forms, with or without 90566170fSBjoern A. Zeeb * modification, are permitted provided that the following conditions 100566170fSBjoern A. Zeeb * are met: 110566170fSBjoern A. Zeeb * 1. Redistributions of source code must retain the above copyright 120566170fSBjoern A. Zeeb * notice, this list of conditions and the following disclaimer. 130566170fSBjoern A. Zeeb * 2. Redistributions in binary form must reproduce the above copyright 140566170fSBjoern A. Zeeb * notice, this list of conditions and the following disclaimer in the 150566170fSBjoern A. Zeeb * documentation and/or other materials provided with the distribution. 160566170fSBjoern A. Zeeb * 170566170fSBjoern A. Zeeb * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 180566170fSBjoern A. Zeeb * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 190566170fSBjoern A. Zeeb * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 200566170fSBjoern A. Zeeb * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 210566170fSBjoern A. Zeeb * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 220566170fSBjoern A. Zeeb * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 230566170fSBjoern A. Zeeb * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 240566170fSBjoern A. Zeeb * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 250566170fSBjoern A. Zeeb * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 260566170fSBjoern A. Zeeb * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 270566170fSBjoern A. Zeeb * SUCH DAMAGE. 280566170fSBjoern A. Zeeb */ 290566170fSBjoern A. Zeeb /* 300566170fSBjoern A. Zeeb * Support for Winbond watchdog. 310566170fSBjoern A. Zeeb * 320566170fSBjoern A. Zeeb * With minor abstractions it might be possible to add support for other 330566170fSBjoern A. Zeeb * different Winbond Super I/O chips as well. Winbond seems to have four 340566170fSBjoern A. Zeeb * different types of chips, four different ways to get into extended config 350566170fSBjoern A. Zeeb * mode. 360566170fSBjoern A. Zeeb * 370566170fSBjoern A. Zeeb * Note: there is no serialization between the debugging sysctl handlers and 380566170fSBjoern A. Zeeb * the watchdog functions and possibly others poking the registers at the same 390566170fSBjoern A. Zeeb * time. For that at least possibly interfering sysctls are hidden by default. 400566170fSBjoern A. Zeeb */ 410566170fSBjoern A. Zeeb 420566170fSBjoern A. Zeeb #include <sys/cdefs.h> 430566170fSBjoern A. Zeeb __FBSDID("$FreeBSD$"); 440566170fSBjoern A. Zeeb 450566170fSBjoern A. Zeeb #include <sys/param.h> 460566170fSBjoern A. Zeeb #include <sys/kernel.h> 470566170fSBjoern A. Zeeb #include <sys/systm.h> 480566170fSBjoern A. Zeeb #include <sys/bus.h> 490566170fSBjoern A. Zeeb #include <sys/eventhandler.h> 500566170fSBjoern A. Zeeb #include <sys/module.h> 510566170fSBjoern A. Zeeb #include <sys/sbuf.h> 520566170fSBjoern A. Zeeb #include <sys/sysctl.h> 530566170fSBjoern A. Zeeb #include <sys/watchdog.h> 540566170fSBjoern A. Zeeb 55bc37ac7eSAndriy Gapon #include <dev/superio/superio.h> 560566170fSBjoern A. Zeeb 570566170fSBjoern A. Zeeb #include <machine/bus.h> 580566170fSBjoern A. Zeeb #include <machine/resource.h> 590566170fSBjoern A. Zeeb 600566170fSBjoern A. Zeeb /* 610566170fSBjoern A. Zeeb * Global registers. 620566170fSBjoern A. Zeeb */ 630566170fSBjoern A. Zeeb #define WB_DEVICE_ID_REG 0x20 /* Device ID */ 640566170fSBjoern A. Zeeb #define WB_DEVICE_REV_REG 0x21 /* Device revision */ 650566170fSBjoern A. Zeeb #define WB_CR26 0x26 /* Bit6: HEFRAS (base port selector) */ 660566170fSBjoern A. Zeeb 670566170fSBjoern A. Zeeb /* LDN selection. */ 680566170fSBjoern A. Zeeb #define WB_LDN_REG 0x07 690566170fSBjoern A. Zeeb #define WB_LDN_REG_LDN8 0x08 /* GPIO 2, Watchdog */ 700566170fSBjoern A. Zeeb 710566170fSBjoern A. Zeeb /* 720566170fSBjoern A. Zeeb * LDN8 (GPIO 2, Watchdog) specific registers and options. 730566170fSBjoern A. Zeeb */ 740566170fSBjoern A. Zeeb /* CR30: LDN8 activation control. */ 750566170fSBjoern A. Zeeb #define WB_LDN8_CR30 0x30 760566170fSBjoern A. Zeeb #define WB_LDN8_CR30_ACTIVE 0x01 /* 1: LD active */ 770566170fSBjoern A. Zeeb 780566170fSBjoern A. Zeeb /* CRF5: Watchdog scale, P20. Mapped to reg_1. */ 790566170fSBjoern A. Zeeb #define WB_LDN8_CRF5 0xF5 800566170fSBjoern A. Zeeb #define WB_LDN8_CRF5_SCALE 0x08 /* 0: 1s, 1: 60s */ 810566170fSBjoern A. Zeeb #define WB_LDN8_CRF5_KEYB_P20 0x04 /* 1: keyb P20 forces timeout */ 82cf864f03SRobert Noland #define WB_LDN8_CRF5_KBRST 0x02 /* 1: timeout causes pin60 kbd reset */ 830566170fSBjoern A. Zeeb 840566170fSBjoern A. Zeeb /* CRF6: Watchdog Timeout (0 == off). Mapped to reg_timeout. */ 850566170fSBjoern A. Zeeb #define WB_LDN8_CRF6 0xF6 860566170fSBjoern A. Zeeb 870566170fSBjoern A. Zeeb /* CRF7: Watchdog mouse, keyb, force, .. Mapped to reg_2. */ 880566170fSBjoern A. Zeeb #define WB_LDN8_CRF7 0xF7 890566170fSBjoern A. Zeeb #define WB_LDN8_CRF7_MOUSE 0x80 /* 1: mouse irq resets wd timer */ 900566170fSBjoern A. Zeeb #define WB_LDN8_CRF7_KEYB 0x40 /* 1: keyb irq resets wd timer */ 910566170fSBjoern A. Zeeb #define WB_LDN8_CRF7_FORCE 0x20 /* 1: force timeout (self-clear) */ 920566170fSBjoern A. Zeeb #define WB_LDN8_CRF7_TS 0x10 /* 0: counting, 1: fired */ 930566170fSBjoern A. Zeeb #define WB_LDN8_CRF7_IRQS 0x0f /* irq source for watchdog, 2 == SMI */ 94baa7dd65SAlexander Motin 95baa7dd65SAlexander Motin enum chips { w83627hf, w83627s, w83697hf, w83697ug, w83637hf, w83627thf, 96baa7dd65SAlexander Motin w83687thf, w83627ehf, w83627dhg, w83627uhg, w83667hg, 97baa7dd65SAlexander Motin w83627dhg_p, w83667hg_b, nct6775, nct6776, nct6779, nct6791, 98f531e075SAlexander Motin nct6792, nct6793, nct6795, nct6102 }; 990566170fSBjoern A. Zeeb 1000566170fSBjoern A. Zeeb struct wb_softc { 1010566170fSBjoern A. Zeeb device_t dev; 1020566170fSBjoern A. Zeeb eventhandler_tag ev_tag; 103baa7dd65SAlexander Motin enum chips chip; 104baa7dd65SAlexander Motin uint8_t ctl_reg; 105baa7dd65SAlexander Motin uint8_t time_reg; 106baa7dd65SAlexander Motin uint8_t csr_reg; 1070566170fSBjoern A. Zeeb int debug_verbose; 1080566170fSBjoern A. Zeeb 1090566170fSBjoern A. Zeeb /* 1100566170fSBjoern A. Zeeb * Special feature to let the watchdog fire at a different 1110566170fSBjoern A. Zeeb * timeout as set by watchdog(4) but still use that API to 1120566170fSBjoern A. Zeeb * re-load it periodically. 1130566170fSBjoern A. Zeeb */ 1140566170fSBjoern A. Zeeb unsigned int timeout_override; 1150566170fSBjoern A. Zeeb 1160566170fSBjoern A. Zeeb /* 1170566170fSBjoern A. Zeeb * Space to save current state temporary and for sysctls. 1180566170fSBjoern A. Zeeb * We want to know the timeout value and usually need two 1190566170fSBjoern A. Zeeb * additional registers for options. Do not name them by 1200566170fSBjoern A. Zeeb * register as these might be different by chip. 1210566170fSBjoern A. Zeeb */ 1220566170fSBjoern A. Zeeb uint8_t reg_timeout; 1230566170fSBjoern A. Zeeb uint8_t reg_1; 1240566170fSBjoern A. Zeeb uint8_t reg_2; 1250566170fSBjoern A. Zeeb }; 1260566170fSBjoern A. Zeeb 1270566170fSBjoern A. Zeeb struct winbond_vendor_device_id { 1280566170fSBjoern A. Zeeb uint8_t device_id; 129baa7dd65SAlexander Motin enum chips chip; 1300566170fSBjoern A. Zeeb const char * descr; 1310566170fSBjoern A. Zeeb } wb_devs[] = { 1320566170fSBjoern A. Zeeb { 1330566170fSBjoern A. Zeeb .device_id = 0x52, 134baa7dd65SAlexander Motin .chip = w83627hf, 135baa7dd65SAlexander Motin .descr = "Winbond 83627HF/F/HG/G", 1360566170fSBjoern A. Zeeb }, 1370566170fSBjoern A. Zeeb { 138baa7dd65SAlexander Motin .device_id = 0x59, 139baa7dd65SAlexander Motin .chip = w83627s, 140baa7dd65SAlexander Motin .descr = "Winbond 83627S", 1410566170fSBjoern A. Zeeb }, 1420566170fSBjoern A. Zeeb { 143baa7dd65SAlexander Motin .device_id = 0x60, 144baa7dd65SAlexander Motin .chip = w83697hf, 145baa7dd65SAlexander Motin .descr = "Winbond 83697HF", 1460566170fSBjoern A. Zeeb }, 1470566170fSBjoern A. Zeeb { 148baa7dd65SAlexander Motin .device_id = 0x68, 149baa7dd65SAlexander Motin .chip = w83697ug, 150baa7dd65SAlexander Motin .descr = "Winbond 83697UG", 151baa7dd65SAlexander Motin }, 152baa7dd65SAlexander Motin { 153baa7dd65SAlexander Motin .device_id = 0x70, 154baa7dd65SAlexander Motin .chip = w83637hf, 155baa7dd65SAlexander Motin .descr = "Winbond 83637HF", 156baa7dd65SAlexander Motin }, 157baa7dd65SAlexander Motin { 158baa7dd65SAlexander Motin .device_id = 0x82, 159baa7dd65SAlexander Motin .chip = w83627thf, 160baa7dd65SAlexander Motin .descr = "Winbond 83627THF", 161baa7dd65SAlexander Motin }, 162baa7dd65SAlexander Motin { 163baa7dd65SAlexander Motin .device_id = 0x85, 164baa7dd65SAlexander Motin .chip = w83687thf, 165baa7dd65SAlexander Motin .descr = "Winbond 83687THF", 166baa7dd65SAlexander Motin }, 167baa7dd65SAlexander Motin { 168baa7dd65SAlexander Motin .device_id = 0x88, 169baa7dd65SAlexander Motin .chip = w83627ehf, 170baa7dd65SAlexander Motin .descr = "Winbond 83627EHF", 171baa7dd65SAlexander Motin }, 172baa7dd65SAlexander Motin { 1730566170fSBjoern A. Zeeb .device_id = 0xa0, 174baa7dd65SAlexander Motin .chip = w83627dhg, 175baa7dd65SAlexander Motin .descr = "Winbond 83627DHG", 1760566170fSBjoern A. Zeeb }, 177cf864f03SRobert Noland { 178baa7dd65SAlexander Motin .device_id = 0xa2, 179baa7dd65SAlexander Motin .chip = w83627uhg, 180baa7dd65SAlexander Motin .descr = "Winbond 83627UHG", 181baa7dd65SAlexander Motin }, 182baa7dd65SAlexander Motin { 183baa7dd65SAlexander Motin .device_id = 0xa5, 184baa7dd65SAlexander Motin .chip = w83667hg, 185baa7dd65SAlexander Motin .descr = "Winbond 83667HG", 186baa7dd65SAlexander Motin }, 187baa7dd65SAlexander Motin { 188cf864f03SRobert Noland .device_id = 0xb0, 189baa7dd65SAlexander Motin .chip = w83627dhg_p, 190cf864f03SRobert Noland .descr = "Winbond 83627DHG-P", 191cf864f03SRobert Noland }, 1929e8bad81SXin LI { 193baa7dd65SAlexander Motin .device_id = 0xb3, 194baa7dd65SAlexander Motin .chip = w83667hg_b, 195baa7dd65SAlexander Motin .descr = "Winbond 83667HG-B", 196baa7dd65SAlexander Motin }, 197baa7dd65SAlexander Motin { 198baa7dd65SAlexander Motin .device_id = 0xb4, 199baa7dd65SAlexander Motin .chip = nct6775, 200baa7dd65SAlexander Motin .descr = "Nuvoton NCT6775", 201baa7dd65SAlexander Motin }, 202baa7dd65SAlexander Motin { 2039e8bad81SXin LI .device_id = 0xc3, 204baa7dd65SAlexander Motin .chip = nct6776, 205baa7dd65SAlexander Motin .descr = "Nuvoton NCT6776", 206baa7dd65SAlexander Motin }, 207baa7dd65SAlexander Motin { 208baa7dd65SAlexander Motin .device_id = 0xc4, 209baa7dd65SAlexander Motin .chip = nct6102, 210baa7dd65SAlexander Motin .descr = "Nuvoton NCT6102", 211baa7dd65SAlexander Motin }, 212baa7dd65SAlexander Motin { 213baa7dd65SAlexander Motin .device_id = 0xc5, 214baa7dd65SAlexander Motin .chip = nct6779, 215baa7dd65SAlexander Motin .descr = "Nuvoton NCT6779", 216baa7dd65SAlexander Motin }, 217baa7dd65SAlexander Motin { 218baa7dd65SAlexander Motin .device_id = 0xc8, 219baa7dd65SAlexander Motin .chip = nct6791, 220baa7dd65SAlexander Motin .descr = "Nuvoton NCT6791", 221baa7dd65SAlexander Motin }, 222baa7dd65SAlexander Motin { 223baa7dd65SAlexander Motin .device_id = 0xc9, 224baa7dd65SAlexander Motin .chip = nct6792, 225baa7dd65SAlexander Motin .descr = "Nuvoton NCT6792", 2269e8bad81SXin LI }, 227f531e075SAlexander Motin { 228f531e075SAlexander Motin .device_id = 0xd1, 229f531e075SAlexander Motin .chip = nct6793, 230f531e075SAlexander Motin .descr = "Nuvoton NCT6793", 231f531e075SAlexander Motin }, 232f531e075SAlexander Motin { 233f531e075SAlexander Motin .device_id = 0xd3, 234f531e075SAlexander Motin .chip = nct6795, 235f531e075SAlexander Motin .descr = "Nuvoton NCT6795", 236f531e075SAlexander Motin }, 2370566170fSBjoern A. Zeeb }; 2380566170fSBjoern A. Zeeb 2390566170fSBjoern A. Zeeb /* 2400566170fSBjoern A. Zeeb * Return the watchdog related registers as we last read them. This will 2410566170fSBjoern A. Zeeb * usually not give the current timeout or state on whether the watchdog 2420566170fSBjoern A. Zeeb * fired. 2430566170fSBjoern A. Zeeb */ 2440566170fSBjoern A. Zeeb static int 2450566170fSBjoern A. Zeeb sysctl_wb_debug(SYSCTL_HANDLER_ARGS) 2460566170fSBjoern A. Zeeb { 2470566170fSBjoern A. Zeeb struct wb_softc *sc; 2480566170fSBjoern A. Zeeb struct sbuf sb; 2490566170fSBjoern A. Zeeb int error; 2500566170fSBjoern A. Zeeb 2510566170fSBjoern A. Zeeb sc = arg1; 2520566170fSBjoern A. Zeeb 2530566170fSBjoern A. Zeeb sbuf_new_for_sysctl(&sb, NULL, 64, req); 2540566170fSBjoern A. Zeeb 2550566170fSBjoern A. Zeeb sbuf_printf(&sb, "LDN8 (GPIO2, Watchdog): "); 256baa7dd65SAlexander Motin sbuf_printf(&sb, "CR%02X 0x%02x ", sc->ctl_reg, sc->reg_1); 257baa7dd65SAlexander Motin sbuf_printf(&sb, "CR%02X 0x%02x ", sc->time_reg, sc->reg_timeout); 258baa7dd65SAlexander Motin sbuf_printf(&sb, "CR%02X 0x%02x", sc->csr_reg, sc->reg_2); 2590566170fSBjoern A. Zeeb 2600566170fSBjoern A. Zeeb error = sbuf_finish(&sb); 2610566170fSBjoern A. Zeeb sbuf_delete(&sb); 2620566170fSBjoern A. Zeeb return (error); 2630566170fSBjoern A. Zeeb } 2640566170fSBjoern A. Zeeb 2650566170fSBjoern A. Zeeb /* 2660566170fSBjoern A. Zeeb * Read the current values before returning them. Given this might poke 2670566170fSBjoern A. Zeeb * the registers the same time as the watchdog, this sysctl handler should 2680566170fSBjoern A. Zeeb * be marked CTLFLAG_SKIP to not show up by default. 2690566170fSBjoern A. Zeeb */ 2700566170fSBjoern A. Zeeb static int 2710566170fSBjoern A. Zeeb sysctl_wb_debug_current(SYSCTL_HANDLER_ARGS) 2720566170fSBjoern A. Zeeb { 2730566170fSBjoern A. Zeeb struct wb_softc *sc; 2740566170fSBjoern A. Zeeb 2750566170fSBjoern A. Zeeb sc = arg1; 2760566170fSBjoern A. Zeeb 277bc37ac7eSAndriy Gapon sc->reg_1 = superio_read(sc->dev, sc->ctl_reg); 278bc37ac7eSAndriy Gapon sc->reg_timeout = superio_read(sc->dev, sc->time_reg); 279bc37ac7eSAndriy Gapon sc->reg_2 = superio_read(sc->dev, sc->csr_reg); 2800566170fSBjoern A. Zeeb 2810566170fSBjoern A. Zeeb return (sysctl_wb_debug(oidp, arg1, arg2, req)); 2820566170fSBjoern A. Zeeb } 2830566170fSBjoern A. Zeeb 2840566170fSBjoern A. Zeeb /* 2850566170fSBjoern A. Zeeb * Sysctl handlers to force a watchdog timeout or to test the NMI functionality 2860566170fSBjoern A. Zeeb * works as expetced. 2870566170fSBjoern A. Zeeb * For testing we could set a test_nmi flag in the softc that, in case of NMI, a 2880566170fSBjoern A. Zeeb * callback function from trap.c could check whether we fired and not report the 2890566170fSBjoern A. Zeeb * timeout but clear the flag for the sysctl again. This is interesting given a 2900566170fSBjoern A. Zeeb * lot of boards have jumpers to change the action on watchdog timeout or 2910566170fSBjoern A. Zeeb * disable the watchdog completely. 2920566170fSBjoern A. Zeeb * XXX-BZ notyet: currently no general infrastructure exists to do this. 2930566170fSBjoern A. Zeeb */ 2940566170fSBjoern A. Zeeb static int 2950566170fSBjoern A. Zeeb sysctl_wb_force_test_nmi(SYSCTL_HANDLER_ARGS) 2960566170fSBjoern A. Zeeb { 2970566170fSBjoern A. Zeeb struct wb_softc *sc; 2981d85ab6bSWarner Losh int error, val; 2990566170fSBjoern A. Zeeb 3000566170fSBjoern A. Zeeb sc = arg1; 3010566170fSBjoern A. Zeeb 3020566170fSBjoern A. Zeeb #ifdef notyet 3030566170fSBjoern A. Zeeb val = sc->test_nmi; 3040566170fSBjoern A. Zeeb #else 3050566170fSBjoern A. Zeeb val = 0; 3060566170fSBjoern A. Zeeb #endif 3070566170fSBjoern A. Zeeb error = sysctl_handle_int(oidp, &val, 0, req); 3080566170fSBjoern A. Zeeb if (error || !req->newptr) 3090566170fSBjoern A. Zeeb return (error); 3100566170fSBjoern A. Zeeb 3110566170fSBjoern A. Zeeb #ifdef notyet 3121d85ab6bSWarner Losh int test = arg2; 3131d85ab6bSWarner Losh 3140566170fSBjoern A. Zeeb /* Manually clear the test for a value of 0 and do nothing else. */ 3150566170fSBjoern A. Zeeb if (test && val == 0) { 3160566170fSBjoern A. Zeeb sc->test_nmi = 0; 3170566170fSBjoern A. Zeeb return (0); 3180566170fSBjoern A. Zeeb } 3190566170fSBjoern A. Zeeb 3200566170fSBjoern A. Zeeb /* 3210566170fSBjoern A. Zeeb * If we are testing the NMI functionality, set the flag before 3220566170fSBjoern A. Zeeb * forcing the timeout. 3230566170fSBjoern A. Zeeb */ 3240566170fSBjoern A. Zeeb if (test) 3250566170fSBjoern A. Zeeb sc->test_nmi = 1; 3260566170fSBjoern A. Zeeb #endif 3270566170fSBjoern A. Zeeb 3280566170fSBjoern A. Zeeb /* Force watchdog to fire. */ 329bc37ac7eSAndriy Gapon sc->reg_2 = superio_read(sc->dev, sc->csr_reg); 3300566170fSBjoern A. Zeeb sc->reg_2 |= WB_LDN8_CRF7_FORCE; 331bc37ac7eSAndriy Gapon superio_write(sc->dev, sc->csr_reg, sc->reg_2); 3320566170fSBjoern A. Zeeb 3330566170fSBjoern A. Zeeb return (0); 3340566170fSBjoern A. Zeeb } 3350566170fSBjoern A. Zeeb 3360566170fSBjoern A. Zeeb /* 3370566170fSBjoern A. Zeeb * Print current watchdog state. 3380566170fSBjoern A. Zeeb * 3390566170fSBjoern A. Zeeb * Note: it is the responsibility of the caller to update the registers 3400566170fSBjoern A. Zeeb * upfront. 3410566170fSBjoern A. Zeeb */ 3420566170fSBjoern A. Zeeb static void 3430566170fSBjoern A. Zeeb wb_print_state(struct wb_softc *sc, const char *msg) 3440566170fSBjoern A. Zeeb { 3450566170fSBjoern A. Zeeb 3460566170fSBjoern A. Zeeb device_printf(sc->dev, "%s%sWatchdog %sabled. %s" 3470566170fSBjoern A. Zeeb "Scaling by %ds, timer at %d (%s=%ds%s). " 348bc37ac7eSAndriy Gapon "CR%02X 0x%02x CR%02X 0x%02x\n", 3490566170fSBjoern A. Zeeb (msg != NULL) ? msg : "", (msg != NULL) ? ": " : "", 3500566170fSBjoern A. Zeeb (sc->reg_timeout > 0x00) ? "en" : "dis", 3510566170fSBjoern A. Zeeb (sc->reg_2 & WB_LDN8_CRF7_TS) ? "Watchdog fired. " : "", 3520566170fSBjoern A. Zeeb (sc->reg_1 & WB_LDN8_CRF5_SCALE) ? 60 : 1, 3530566170fSBjoern A. Zeeb sc->reg_timeout, 3540566170fSBjoern A. Zeeb (sc->reg_timeout > 0x00) ? "<" : "", 3550566170fSBjoern A. Zeeb sc->reg_timeout * ((sc->reg_1 & WB_LDN8_CRF5_SCALE) ? 60 : 1), 3560566170fSBjoern A. Zeeb (sc->reg_timeout > 0x00) ? " left" : "", 357bc37ac7eSAndriy Gapon sc->ctl_reg, sc->reg_1, sc->csr_reg, sc->reg_2); 3580566170fSBjoern A. Zeeb } 3590566170fSBjoern A. Zeeb 3600566170fSBjoern A. Zeeb /* 3610566170fSBjoern A. Zeeb * (Re)load the watchdog counter depending on timeout. A timeout of 0 will 3620566170fSBjoern A. Zeeb * disable the watchdog. 3630566170fSBjoern A. Zeeb */ 3640566170fSBjoern A. Zeeb static int 3650566170fSBjoern A. Zeeb wb_set_watchdog(struct wb_softc *sc, unsigned int timeout) 3660566170fSBjoern A. Zeeb { 3670566170fSBjoern A. Zeeb 368baa7dd65SAlexander Motin if (timeout != 0) { 3690566170fSBjoern A. Zeeb /* 3700566170fSBjoern A. Zeeb * In case an override is set, let it override. It may lead 3710566170fSBjoern A. Zeeb * to strange results as we do not check the input of the sysctl. 3720566170fSBjoern A. Zeeb */ 3730566170fSBjoern A. Zeeb if (sc->timeout_override > 0) 3740566170fSBjoern A. Zeeb timeout = sc->timeout_override; 3750566170fSBjoern A. Zeeb 3760566170fSBjoern A. Zeeb /* Make sure we support the requested timeout. */ 3770566170fSBjoern A. Zeeb if (timeout > 255 * 60) 3780566170fSBjoern A. Zeeb return (EINVAL); 379baa7dd65SAlexander Motin } 3800566170fSBjoern A. Zeeb 381baa7dd65SAlexander Motin if (sc->debug_verbose) 382baa7dd65SAlexander Motin wb_print_state(sc, "Before watchdog counter (re)load"); 383baa7dd65SAlexander Motin 384baa7dd65SAlexander Motin if (timeout == 0) { 385baa7dd65SAlexander Motin /* Disable watchdog. */ 3864f6dd82aSAlexander Motin sc->reg_timeout = 0; 387bc37ac7eSAndriy Gapon superio_write(sc->dev, sc->time_reg, sc->reg_timeout); 388baa7dd65SAlexander Motin 389baa7dd65SAlexander Motin } else { 3900566170fSBjoern A. Zeeb /* Read current scaling factor. */ 391bc37ac7eSAndriy Gapon sc->reg_1 = superio_read(sc->dev, sc->ctl_reg); 3920566170fSBjoern A. Zeeb 3930566170fSBjoern A. Zeeb if (timeout > 255) { 3940566170fSBjoern A. Zeeb /* Set scaling factor to 60s. */ 3950566170fSBjoern A. Zeeb sc->reg_1 |= WB_LDN8_CRF5_SCALE; 3960566170fSBjoern A. Zeeb sc->reg_timeout = (timeout / 60); 3970566170fSBjoern A. Zeeb if (timeout % 60) 3980566170fSBjoern A. Zeeb sc->reg_timeout++; 3990566170fSBjoern A. Zeeb } else { 4000566170fSBjoern A. Zeeb /* Set scaling factor to 1s. */ 4010566170fSBjoern A. Zeeb sc->reg_1 &= ~WB_LDN8_CRF5_SCALE; 4020566170fSBjoern A. Zeeb sc->reg_timeout = timeout; 4030566170fSBjoern A. Zeeb } 4040566170fSBjoern A. Zeeb 4050566170fSBjoern A. Zeeb /* In case we fired before we need to clear to fire again. */ 406bc37ac7eSAndriy Gapon sc->reg_2 = superio_read(sc->dev, sc->csr_reg); 4070566170fSBjoern A. Zeeb if (sc->reg_2 & WB_LDN8_CRF7_TS) { 4080566170fSBjoern A. Zeeb sc->reg_2 &= ~WB_LDN8_CRF7_TS; 409bc37ac7eSAndriy Gapon superio_write(sc->dev, sc->csr_reg, sc->reg_2); 4100566170fSBjoern A. Zeeb } 4110566170fSBjoern A. Zeeb 4120566170fSBjoern A. Zeeb /* Write back scaling factor. */ 413bc37ac7eSAndriy Gapon superio_write(sc->dev, sc->ctl_reg, sc->reg_1); 4140566170fSBjoern A. Zeeb 4150566170fSBjoern A. Zeeb /* Set timer and arm/reset the watchdog. */ 416bc37ac7eSAndriy Gapon superio_write(sc->dev, sc->time_reg, sc->reg_timeout); 4170566170fSBjoern A. Zeeb } 4180566170fSBjoern A. Zeeb 4190566170fSBjoern A. Zeeb if (sc->debug_verbose) 4200566170fSBjoern A. Zeeb wb_print_state(sc, "After watchdog counter (re)load"); 4210566170fSBjoern A. Zeeb return (0); 4220566170fSBjoern A. Zeeb } 4230566170fSBjoern A. Zeeb 4240566170fSBjoern A. Zeeb /* 4250566170fSBjoern A. Zeeb * watchdog(9) EVENTHANDLER function implementation to (re)load the counter 4260566170fSBjoern A. Zeeb * with the given timeout or disable the watchdog. 4270566170fSBjoern A. Zeeb */ 4280566170fSBjoern A. Zeeb static void 4290566170fSBjoern A. Zeeb wb_watchdog_fn(void *private, u_int cmd, int *error) 4300566170fSBjoern A. Zeeb { 4310566170fSBjoern A. Zeeb struct wb_softc *sc; 4320566170fSBjoern A. Zeeb unsigned int timeout; 4330566170fSBjoern A. Zeeb int e; 4340566170fSBjoern A. Zeeb 4350566170fSBjoern A. Zeeb sc = private; 4360566170fSBjoern A. Zeeb KASSERT(sc != NULL, ("%s: watchdog handler function called without " 4370566170fSBjoern A. Zeeb "softc.", __func__)); 4380566170fSBjoern A. Zeeb 4390566170fSBjoern A. Zeeb cmd &= WD_INTERVAL; 4400566170fSBjoern A. Zeeb if (cmd > 0 && cmd <= 63) { 4410566170fSBjoern A. Zeeb /* Reset (and arm) watchdog. */ 4420566170fSBjoern A. Zeeb timeout = ((uint64_t)1 << cmd) / 1000000000; 4430566170fSBjoern A. Zeeb if (timeout == 0) 4440566170fSBjoern A. Zeeb timeout = 1; 4450566170fSBjoern A. Zeeb e = wb_set_watchdog(sc, timeout); 4460566170fSBjoern A. Zeeb if (e == 0) { 4470566170fSBjoern A. Zeeb if (error != NULL) 4480566170fSBjoern A. Zeeb *error = 0; 4490566170fSBjoern A. Zeeb } else { 4500566170fSBjoern A. Zeeb /* On error, try to make sure the WD is disabled. */ 4510566170fSBjoern A. Zeeb wb_set_watchdog(sc, 0); 4520566170fSBjoern A. Zeeb } 4530566170fSBjoern A. Zeeb 4540566170fSBjoern A. Zeeb } else { 4550566170fSBjoern A. Zeeb /* Disable watchdog. */ 4560566170fSBjoern A. Zeeb e = wb_set_watchdog(sc, 0); 4570566170fSBjoern A. Zeeb if (e != 0 && cmd == 0 && error != NULL) { 4580566170fSBjoern A. Zeeb /* Failed to disable watchdog. */ 4590566170fSBjoern A. Zeeb *error = EOPNOTSUPP; 4600566170fSBjoern A. Zeeb } 4610566170fSBjoern A. Zeeb } 4620566170fSBjoern A. Zeeb } 4630566170fSBjoern A. Zeeb 4640566170fSBjoern A. Zeeb static int 4650566170fSBjoern A. Zeeb wb_probe(device_t dev) 4660566170fSBjoern A. Zeeb { 467bc37ac7eSAndriy Gapon char buf[128]; 468bc37ac7eSAndriy Gapon struct wb_softc *sc; 469fc0b9815SAndriy Gapon int j; 470bc37ac7eSAndriy Gapon uint8_t devid; 471bc37ac7eSAndriy Gapon uint8_t revid; 4720566170fSBjoern A. Zeeb 473bc37ac7eSAndriy Gapon if (superio_vendor(dev) != SUPERIO_VENDOR_NUVOTON) 474bc37ac7eSAndriy Gapon return (ENXIO); 475bc37ac7eSAndriy Gapon if (superio_get_type(dev) != SUPERIO_DEV_WDT) 4760566170fSBjoern A. Zeeb return (ENXIO); 4770566170fSBjoern A. Zeeb 478bc37ac7eSAndriy Gapon sc = device_get_softc(dev); 479bc37ac7eSAndriy Gapon devid = superio_devid(dev) >> 8; 480bc37ac7eSAndriy Gapon revid = superio_revid(dev); 481bc37ac7eSAndriy Gapon for (j = 0; j < nitems(wb_devs); j++) { 482bc37ac7eSAndriy Gapon if (wb_devs[j].device_id == devid) { 483bc37ac7eSAndriy Gapon sc->chip = wb_devs[j].chip; 484bc37ac7eSAndriy Gapon snprintf(buf, sizeof(buf), 485bc37ac7eSAndriy Gapon "%s (0x%02x/0x%02x) Watchdog Timer", 486bc37ac7eSAndriy Gapon wb_devs[j].descr, devid, revid); 487bc37ac7eSAndriy Gapon device_set_desc_copy(dev, buf); 488bc37ac7eSAndriy Gapon return (BUS_PROBE_SPECIFIC); 489bc37ac7eSAndriy Gapon } 490bc37ac7eSAndriy Gapon } 491fc0b9815SAndriy Gapon if (bootverbose) { 492fc0b9815SAndriy Gapon device_printf(dev, 493fc0b9815SAndriy Gapon "unrecognized chip: devid 0x%02x, revid 0x%02x\n", 494fc0b9815SAndriy Gapon devid, revid); 495fc0b9815SAndriy Gapon } 496bc37ac7eSAndriy Gapon return (ENXIO); 4970566170fSBjoern A. Zeeb } 4980566170fSBjoern A. Zeeb 4990566170fSBjoern A. Zeeb static int 5000566170fSBjoern A. Zeeb wb_attach(device_t dev) 5010566170fSBjoern A. Zeeb { 5020566170fSBjoern A. Zeeb struct wb_softc *sc; 5030566170fSBjoern A. Zeeb struct sysctl_ctx_list *sctx; 5040566170fSBjoern A. Zeeb struct sysctl_oid *soid; 5050566170fSBjoern A. Zeeb unsigned long timeout; 506baa7dd65SAlexander Motin uint8_t t; 5070566170fSBjoern A. Zeeb 5080566170fSBjoern A. Zeeb sc = device_get_softc(dev); 509bc37ac7eSAndriy Gapon sc->dev = dev; 5100566170fSBjoern A. Zeeb 511fc0b9815SAndriy Gapon /* Make sure WDT is enabled. */ 512fc0b9815SAndriy Gapon superio_dev_enable(dev, WB_LDN8_CR30_ACTIVE); 513fc0b9815SAndriy Gapon 514fc0b9815SAndriy Gapon switch (sc->chip) { 515fc0b9815SAndriy Gapon case w83697hf: 516fc0b9815SAndriy Gapon case w83697ug: 517bc37ac7eSAndriy Gapon sc->ctl_reg = 0xf3; 518bc37ac7eSAndriy Gapon sc->time_reg = 0xf4; 519fc0b9815SAndriy Gapon sc->csr_reg = 0xf7; 520fc0b9815SAndriy Gapon break; 521fc0b9815SAndriy Gapon case nct6102: 522bc37ac7eSAndriy Gapon sc->ctl_reg = 0xf0; 523bc37ac7eSAndriy Gapon sc->time_reg = 0xf1; 524bc37ac7eSAndriy Gapon sc->csr_reg = 0xf2; 525fc0b9815SAndriy Gapon break; 526fc0b9815SAndriy Gapon default: 527fc0b9815SAndriy Gapon sc->ctl_reg = 0xf5; 528fc0b9815SAndriy Gapon sc->time_reg = 0xf6; 529fc0b9815SAndriy Gapon sc->csr_reg = 0xf7; 530fc0b9815SAndriy Gapon break; 531bc37ac7eSAndriy Gapon } 5320566170fSBjoern A. Zeeb 533baa7dd65SAlexander Motin switch (sc->chip) { 534baa7dd65SAlexander Motin case w83627hf: 535baa7dd65SAlexander Motin case w83627s: 536bc37ac7eSAndriy Gapon t = superio_read(dev, 0x2B) & ~0x10; 537bc37ac7eSAndriy Gapon superio_write(dev, 0x2B, t); /* set GPIO24 to WDT0 */ 538baa7dd65SAlexander Motin break; 539baa7dd65SAlexander Motin case w83697hf: 540baa7dd65SAlexander Motin /* Set pin 119 to WDTO# mode (= CR29, WDT0) */ 541bc37ac7eSAndriy Gapon t = superio_read(dev, 0x29) & ~0x60; 542baa7dd65SAlexander Motin t |= 0x20; 543bc37ac7eSAndriy Gapon superio_write(dev, 0x29, t); 544baa7dd65SAlexander Motin break; 545baa7dd65SAlexander Motin case w83697ug: 546baa7dd65SAlexander Motin /* Set pin 118 to WDTO# mode */ 547bc37ac7eSAndriy Gapon t = superio_read(dev, 0x2b) & ~0x04; 548bc37ac7eSAndriy Gapon superio_write(dev, 0x2b, t); 549baa7dd65SAlexander Motin break; 550baa7dd65SAlexander Motin case w83627thf: 551bc37ac7eSAndriy Gapon t = (superio_read(dev, 0x2B) & ~0x08) | 0x04; 552bc37ac7eSAndriy Gapon superio_write(dev, 0x2B, t); /* set GPIO3 to WDT0 */ 553baa7dd65SAlexander Motin break; 554baa7dd65SAlexander Motin case w83627dhg: 555baa7dd65SAlexander Motin case w83627dhg_p: 556bc37ac7eSAndriy Gapon t = superio_read(dev, 0x2D) & ~0x01; /* PIN77 -> WDT0# */ 557bc37ac7eSAndriy Gapon superio_write(dev, 0x2D, t); /* set GPIO5 to WDT0 */ 558bc37ac7eSAndriy Gapon t = superio_read(dev, sc->ctl_reg); 559baa7dd65SAlexander Motin t |= 0x02; /* enable the WDTO# output low pulse 560baa7dd65SAlexander Motin * to the KBRST# pin */ 561bc37ac7eSAndriy Gapon superio_write(dev, sc->ctl_reg, t); 562baa7dd65SAlexander Motin break; 563baa7dd65SAlexander Motin case w83637hf: 564baa7dd65SAlexander Motin break; 565baa7dd65SAlexander Motin case w83687thf: 566bc37ac7eSAndriy Gapon t = superio_read(dev, 0x2C) & ~0x80; /* PIN47 -> WDT0# */ 567bc37ac7eSAndriy Gapon superio_write(dev, 0x2C, t); 568baa7dd65SAlexander Motin break; 569baa7dd65SAlexander Motin case w83627ehf: 570baa7dd65SAlexander Motin case w83627uhg: 571baa7dd65SAlexander Motin case w83667hg: 572baa7dd65SAlexander Motin case w83667hg_b: 573baa7dd65SAlexander Motin case nct6775: 574baa7dd65SAlexander Motin case nct6776: 575baa7dd65SAlexander Motin case nct6779: 576baa7dd65SAlexander Motin case nct6791: 577baa7dd65SAlexander Motin case nct6792: 578f531e075SAlexander Motin case nct6793: 579f531e075SAlexander Motin case nct6795: 580baa7dd65SAlexander Motin case nct6102: 581baa7dd65SAlexander Motin /* 582baa7dd65SAlexander Motin * These chips have a fixed WDTO# output pin (W83627UHG), 583baa7dd65SAlexander Motin * or support more than one WDTO# output pin. 584baa7dd65SAlexander Motin * Don't touch its configuration, and hope the BIOS 585baa7dd65SAlexander Motin * does the right thing. 586baa7dd65SAlexander Motin */ 587bc37ac7eSAndriy Gapon t = superio_read(dev, sc->ctl_reg); 588baa7dd65SAlexander Motin t |= 0x02; /* enable the WDTO# output low pulse 589baa7dd65SAlexander Motin * to the KBRST# pin */ 590bc37ac7eSAndriy Gapon superio_write(dev, sc->ctl_reg, t); 591baa7dd65SAlexander Motin break; 592baa7dd65SAlexander Motin default: 593baa7dd65SAlexander Motin break; 594baa7dd65SAlexander Motin } 5950566170fSBjoern A. Zeeb 5960566170fSBjoern A. Zeeb /* Read the current watchdog configuration. */ 597bc37ac7eSAndriy Gapon sc->reg_1 = superio_read(dev, sc->ctl_reg); 598bc37ac7eSAndriy Gapon sc->reg_timeout = superio_read(dev, sc->time_reg); 599bc37ac7eSAndriy Gapon sc->reg_2 = superio_read(dev, sc->csr_reg); 6000566170fSBjoern A. Zeeb 6010566170fSBjoern A. Zeeb /* Print current state if bootverbose or watchdog already enabled. */ 6020566170fSBjoern A. Zeeb if (bootverbose || (sc->reg_timeout > 0x00)) 6030566170fSBjoern A. Zeeb wb_print_state(sc, "Before watchdog attach"); 6040566170fSBjoern A. Zeeb 605baa7dd65SAlexander Motin sc->reg_1 &= ~WB_LDN8_CRF5_KEYB_P20; 606cf864f03SRobert Noland sc->reg_1 |= WB_LDN8_CRF5_KBRST; 607bc37ac7eSAndriy Gapon superio_write(dev, sc->ctl_reg, sc->reg_1); 6080566170fSBjoern A. Zeeb 609baa7dd65SAlexander Motin /* 610baa7dd65SAlexander Motin * Clear a previous watchdog timeout event (if still set). 611baa7dd65SAlexander Motin * Disable timer reset on mouse interrupts. Leave reset on keyboard, 612baa7dd65SAlexander Motin * since one of my boards is getting stuck in reboot without it. 613baa7dd65SAlexander Motin */ 614baa7dd65SAlexander Motin sc->reg_2 &= ~(WB_LDN8_CRF7_MOUSE|WB_LDN8_CRF7_TS); 615bc37ac7eSAndriy Gapon superio_write(dev, sc->csr_reg, sc->reg_2); 6160566170fSBjoern A. Zeeb 6170566170fSBjoern A. Zeeb /* Read global timeout override tunable, Add per device sysctls. */ 6180566170fSBjoern A. Zeeb if (TUNABLE_ULONG_FETCH("hw.wbwd.timeout_override", &timeout)) { 6190566170fSBjoern A. Zeeb if (timeout > 0) 6200566170fSBjoern A. Zeeb sc->timeout_override = timeout; 6210566170fSBjoern A. Zeeb } 6220566170fSBjoern A. Zeeb sctx = device_get_sysctl_ctx(dev); 6230566170fSBjoern A. Zeeb soid = device_get_sysctl_tree(dev); 6240566170fSBjoern A. Zeeb SYSCTL_ADD_UINT(sctx, SYSCTL_CHILDREN(soid), OID_AUTO, 6250566170fSBjoern A. Zeeb "timeout_override", CTLFLAG_RW, &sc->timeout_override, 0, 6260566170fSBjoern A. Zeeb "Timeout in seconds overriding default watchdog timeout"); 6270566170fSBjoern A. Zeeb SYSCTL_ADD_INT(sctx, SYSCTL_CHILDREN(soid), OID_AUTO, 6280566170fSBjoern A. Zeeb "debug_verbose", CTLFLAG_RW, &sc->debug_verbose, 0, 6290566170fSBjoern A. Zeeb "Enables extra debugging information"); 6300566170fSBjoern A. Zeeb SYSCTL_ADD_PROC(sctx, SYSCTL_CHILDREN(soid), OID_AUTO, "debug", 631fdec27edSAlexander Motin CTLTYPE_STRING | CTLFLAG_RD | CTLFLAG_MPSAFE, sc, 0, 6327029da5cSPawel Biernacki sysctl_wb_debug, "A", 6330566170fSBjoern A. Zeeb "Selected register information from last change by driver"); 6340566170fSBjoern A. Zeeb SYSCTL_ADD_PROC(sctx, SYSCTL_CHILDREN(soid), OID_AUTO, "debug_current", 635fdec27edSAlexander Motin CTLTYPE_STRING | CTLFLAG_RD | CTLFLAG_SKIP | CTLFLAG_MPSAFE, 6367029da5cSPawel Biernacki sc, 0, sysctl_wb_debug_current, "A", 6370566170fSBjoern A. Zeeb "Selected register information (may interfere)"); 6380566170fSBjoern A. Zeeb SYSCTL_ADD_PROC(sctx, SYSCTL_CHILDREN(soid), OID_AUTO, "force_timeout", 639fdec27edSAlexander Motin CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_SKIP | CTLFLAG_MPSAFE, sc, 0, 6400566170fSBjoern A. Zeeb sysctl_wb_force_test_nmi, "I", "Enable to force watchdog to fire."); 6410566170fSBjoern A. Zeeb 6420566170fSBjoern A. Zeeb /* Register watchdog. */ 6430566170fSBjoern A. Zeeb sc->ev_tag = EVENTHANDLER_REGISTER(watchdog_list, wb_watchdog_fn, sc, 6440566170fSBjoern A. Zeeb 0); 6450566170fSBjoern A. Zeeb 6460566170fSBjoern A. Zeeb if (bootverbose) 6470566170fSBjoern A. Zeeb wb_print_state(sc, "After watchdog attach"); 6480566170fSBjoern A. Zeeb 6490566170fSBjoern A. Zeeb return (0); 6500566170fSBjoern A. Zeeb } 6510566170fSBjoern A. Zeeb 6520566170fSBjoern A. Zeeb static int 6530566170fSBjoern A. Zeeb wb_detach(device_t dev) 6540566170fSBjoern A. Zeeb { 6550566170fSBjoern A. Zeeb struct wb_softc *sc; 6560566170fSBjoern A. Zeeb 6570566170fSBjoern A. Zeeb sc = device_get_softc(dev); 6580566170fSBjoern A. Zeeb 6590566170fSBjoern A. Zeeb /* Unregister and stop the watchdog if running. */ 6600566170fSBjoern A. Zeeb if (sc->ev_tag) 6610566170fSBjoern A. Zeeb EVENTHANDLER_DEREGISTER(watchdog_list, sc->ev_tag); 6620566170fSBjoern A. Zeeb wb_set_watchdog(sc, 0); 6630566170fSBjoern A. Zeeb 6640566170fSBjoern A. Zeeb /* Bus subroutines take care of sysctls already. */ 6650566170fSBjoern A. Zeeb 6660566170fSBjoern A. Zeeb return (0); 6670566170fSBjoern A. Zeeb } 6680566170fSBjoern A. Zeeb 6690566170fSBjoern A. Zeeb static device_method_t wb_methods[] = { 6700566170fSBjoern A. Zeeb /* Device interface */ 6710566170fSBjoern A. Zeeb DEVMETHOD(device_probe, wb_probe), 6720566170fSBjoern A. Zeeb DEVMETHOD(device_attach, wb_attach), 6730566170fSBjoern A. Zeeb DEVMETHOD(device_detach, wb_detach), 6740566170fSBjoern A. Zeeb 675e25fe6cdSXin LI DEVMETHOD_END 6760566170fSBjoern A. Zeeb }; 6770566170fSBjoern A. Zeeb 678bc37ac7eSAndriy Gapon static driver_t wb_driver = { 6790566170fSBjoern A. Zeeb "wbwd", 6800566170fSBjoern A. Zeeb wb_methods, 6810566170fSBjoern A. Zeeb sizeof(struct wb_softc) 6820566170fSBjoern A. Zeeb }; 6830566170fSBjoern A. Zeeb 68486dc8398SJohn Baldwin DRIVER_MODULE(wb, superio, wb_driver, NULL, NULL); 685bc37ac7eSAndriy Gapon MODULE_DEPEND(wb, superio, 1, 1, 1); 686bc37ac7eSAndriy Gapon MODULE_VERSION(wb, 1); 687