10566170fSBjoern A. Zeeb /*- 20566170fSBjoern A. Zeeb * Copyright (c) 2011 Sandvine Incorporated ULC. 31adb4ebdSXin LI * Copyright (c) 2012 iXsystems, Inc. 40566170fSBjoern A. Zeeb * All rights reserved. 50566170fSBjoern A. Zeeb * 60566170fSBjoern A. Zeeb * Redistribution and use in source and binary forms, with or without 70566170fSBjoern A. Zeeb * modification, are permitted provided that the following conditions 80566170fSBjoern A. Zeeb * are met: 90566170fSBjoern A. Zeeb * 1. Redistributions of source code must retain the above copyright 100566170fSBjoern A. Zeeb * notice, this list of conditions and the following disclaimer. 110566170fSBjoern A. Zeeb * 2. Redistributions in binary form must reproduce the above copyright 120566170fSBjoern A. Zeeb * notice, this list of conditions and the following disclaimer in the 130566170fSBjoern A. Zeeb * documentation and/or other materials provided with the distribution. 140566170fSBjoern A. Zeeb * 150566170fSBjoern A. Zeeb * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 160566170fSBjoern A. Zeeb * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 170566170fSBjoern A. Zeeb * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 180566170fSBjoern A. Zeeb * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 190566170fSBjoern A. Zeeb * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 200566170fSBjoern A. Zeeb * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 210566170fSBjoern A. Zeeb * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 220566170fSBjoern A. Zeeb * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 230566170fSBjoern A. Zeeb * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 240566170fSBjoern A. Zeeb * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 250566170fSBjoern A. Zeeb * SUCH DAMAGE. 260566170fSBjoern A. Zeeb */ 270566170fSBjoern A. Zeeb /* 280566170fSBjoern A. Zeeb * Support for Winbond watchdog. 290566170fSBjoern A. Zeeb * 300566170fSBjoern A. Zeeb * With minor abstractions it might be possible to add support for other 310566170fSBjoern A. Zeeb * different Winbond Super I/O chips as well. Winbond seems to have four 320566170fSBjoern A. Zeeb * different types of chips, four different ways to get into extended config 330566170fSBjoern A. Zeeb * mode. 340566170fSBjoern A. Zeeb * 350566170fSBjoern A. Zeeb * Note: there is no serialization between the debugging sysctl handlers and 360566170fSBjoern A. Zeeb * the watchdog functions and possibly others poking the registers at the same 370566170fSBjoern A. Zeeb * time. For that at least possibly interfering sysctls are hidden by default. 380566170fSBjoern A. Zeeb */ 390566170fSBjoern A. Zeeb 400566170fSBjoern A. Zeeb #include <sys/cdefs.h> 410566170fSBjoern A. Zeeb __FBSDID("$FreeBSD$"); 420566170fSBjoern A. Zeeb 430566170fSBjoern A. Zeeb #include <sys/param.h> 440566170fSBjoern A. Zeeb #include <sys/kernel.h> 450566170fSBjoern A. Zeeb #include <sys/systm.h> 460566170fSBjoern A. Zeeb #include <sys/bus.h> 470566170fSBjoern A. Zeeb #include <sys/eventhandler.h> 480566170fSBjoern A. Zeeb #include <sys/lock.h> 490566170fSBjoern A. Zeeb #include <sys/module.h> 500566170fSBjoern A. Zeeb #include <sys/rman.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 550566170fSBjoern A. Zeeb #include <isa/isavar.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 */ 940566170fSBjoern A. Zeeb #define WB_LDN8_CRF7_CLEAR_MASK \ 950566170fSBjoern A. Zeeb (WB_LDN8_CRF7_MOUSE|WB_LDN8_CRF7_KEYB|WB_LDN8_CRF7_TS|WB_LDN8_CRF7_IRQS) 960566170fSBjoern A. Zeeb 970566170fSBjoern A. Zeeb struct wb_softc { 980566170fSBjoern A. Zeeb device_t dev; 990566170fSBjoern A. Zeeb struct resource *portres; 1000566170fSBjoern A. Zeeb bus_space_tag_t bst; 1010566170fSBjoern A. Zeeb bus_space_handle_t bsh; 1020566170fSBjoern A. Zeeb int rid; 1030566170fSBjoern A. Zeeb eventhandler_tag ev_tag; 1041adb4ebdSXin LI int (*ext_cfg_enter_f)(struct wb_softc *, u_short); 1051adb4ebdSXin LI void (*ext_cfg_exit_f)(struct wb_softc *, u_short); 1060566170fSBjoern A. Zeeb int debug_verbose; 1070566170fSBjoern A. Zeeb 1080566170fSBjoern A. Zeeb /* 1090566170fSBjoern A. Zeeb * Special feature to let the watchdog fire at a different 1100566170fSBjoern A. Zeeb * timeout as set by watchdog(4) but still use that API to 1110566170fSBjoern A. Zeeb * re-load it periodically. 1120566170fSBjoern A. Zeeb */ 1130566170fSBjoern A. Zeeb unsigned int timeout_override; 1140566170fSBjoern A. Zeeb 1150566170fSBjoern A. Zeeb /* 1160566170fSBjoern A. Zeeb * Space to save current state temporary and for sysctls. 1170566170fSBjoern A. Zeeb * We want to know the timeout value and usually need two 1180566170fSBjoern A. Zeeb * additional registers for options. Do not name them by 1190566170fSBjoern A. Zeeb * register as these might be different by chip. 1200566170fSBjoern A. Zeeb */ 1210566170fSBjoern A. Zeeb uint8_t reg_timeout; 1220566170fSBjoern A. Zeeb uint8_t reg_1; 1230566170fSBjoern A. Zeeb uint8_t reg_2; 1240566170fSBjoern A. Zeeb }; 1250566170fSBjoern A. Zeeb 1261adb4ebdSXin LI static int ext_cfg_enter_0x87_0x87(struct wb_softc *, u_short); 1271adb4ebdSXin LI static void ext_cfg_exit_0xaa(struct wb_softc *, u_short); 1280566170fSBjoern A. Zeeb 1290566170fSBjoern A. Zeeb struct winbond_superio_cfg { 1300566170fSBjoern A. Zeeb uint8_t efer; /* and efir */ 1311adb4ebdSXin LI int (*ext_cfg_enter_f)(struct wb_softc *, u_short); 1321adb4ebdSXin LI void (*ext_cfg_exit_f)(struct wb_softc *, u_short); 1330566170fSBjoern A. Zeeb } probe_addrs[] = { 1340566170fSBjoern A. Zeeb { 1350566170fSBjoern A. Zeeb .efer = 0x2e, 1360566170fSBjoern A. Zeeb .ext_cfg_enter_f = ext_cfg_enter_0x87_0x87, 1370566170fSBjoern A. Zeeb .ext_cfg_exit_f = ext_cfg_exit_0xaa, 1380566170fSBjoern A. Zeeb }, 1390566170fSBjoern A. Zeeb { 1400566170fSBjoern A. Zeeb .efer = 0x4e, 1410566170fSBjoern A. Zeeb .ext_cfg_enter_f = ext_cfg_enter_0x87_0x87, 1420566170fSBjoern A. Zeeb .ext_cfg_exit_f = ext_cfg_exit_0xaa, 1430566170fSBjoern A. Zeeb }, 1440566170fSBjoern A. Zeeb }; 1450566170fSBjoern A. Zeeb 1460566170fSBjoern A. Zeeb struct winbond_vendor_device_id { 1470566170fSBjoern A. Zeeb uint16_t vendor_id; 1480566170fSBjoern A. Zeeb uint8_t device_id; 1490566170fSBjoern A. Zeeb uint8_t device_rev; 1500566170fSBjoern A. Zeeb const char * descr; 1510566170fSBjoern A. Zeeb } wb_devs[] = { 1520566170fSBjoern A. Zeeb { 1530566170fSBjoern A. Zeeb .vendor_id = 0x5ca3, 1540566170fSBjoern A. Zeeb .device_id = 0x52, 1550566170fSBjoern A. Zeeb .device_rev = 0x17, 1560566170fSBjoern A. Zeeb .descr = "Winbond 83627HF/F/HG/G Rev. G", 1570566170fSBjoern A. Zeeb }, 1580566170fSBjoern A. Zeeb { 1590566170fSBjoern A. Zeeb .vendor_id = 0x5ca3, 1600566170fSBjoern A. Zeeb .device_id = 0x52, 1610566170fSBjoern A. Zeeb .device_rev = 0x3a, 1620566170fSBjoern A. Zeeb .descr = "Winbond 83627HF/F/HG/G Rev. J", 1630566170fSBjoern A. Zeeb }, 1640566170fSBjoern A. Zeeb { 1650566170fSBjoern A. Zeeb .vendor_id = 0x5ca3, 1660566170fSBjoern A. Zeeb .device_id = 0x52, 1670566170fSBjoern A. Zeeb .device_rev = 0x41, 1680566170fSBjoern A. Zeeb .descr = "Winbond 83627HF/F/HG/G Rev. UD-A", 1690566170fSBjoern A. Zeeb }, 1700566170fSBjoern A. Zeeb { 1710566170fSBjoern A. Zeeb .vendor_id = 0x5ca3, 1720566170fSBjoern A. Zeeb .device_id = 0xa0, 1730566170fSBjoern A. Zeeb .device_rev = 0x25, 1740566170fSBjoern A. Zeeb .descr = "Winbond 83627DHG IC ver. 5", 1750566170fSBjoern A. Zeeb }, 176cf864f03SRobert Noland { 177cf864f03SRobert Noland .vendor_id = 0x5ca3, 178cf864f03SRobert Noland .device_id = 0xb0, 179cf864f03SRobert Noland .device_rev = 0x73, 180cf864f03SRobert Noland .descr = "Winbond 83627DHG-P", 181cf864f03SRobert Noland }, 1820566170fSBjoern A. Zeeb }; 1830566170fSBjoern A. Zeeb 1841adb4ebdSXin LI static void 1851adb4ebdSXin LI write_efir_1(struct wb_softc *sc, u_short baseport, uint8_t value) 1861adb4ebdSXin LI { 1871adb4ebdSXin LI 1881adb4ebdSXin LI MPASS(sc != NULL || baseport != 0); 1891adb4ebdSXin LI if (sc != NULL) 1901adb4ebdSXin LI bus_space_write_1((sc)->bst, (sc)->bsh, 0, (value)); 1911adb4ebdSXin LI else 1921adb4ebdSXin LI outb(baseport, value); 1931adb4ebdSXin LI } 1941adb4ebdSXin LI 1951adb4ebdSXin LI static uint8_t __unused 1961adb4ebdSXin LI read_efir_1(struct wb_softc *sc, u_short baseport) 1971adb4ebdSXin LI { 1981adb4ebdSXin LI 1991adb4ebdSXin LI MPASS(sc != NULL || baseport != 0); 2001adb4ebdSXin LI if (sc != NULL) 2011adb4ebdSXin LI return (bus_space_read_1((sc)->bst, (sc)->bsh, 0)); 2021adb4ebdSXin LI else 2031adb4ebdSXin LI return (inb(baseport)); 2041adb4ebdSXin LI } 2051adb4ebdSXin LI 2061adb4ebdSXin LI static void 2071adb4ebdSXin LI write_efdr_1(struct wb_softc *sc, u_short baseport, uint8_t value) 2081adb4ebdSXin LI { 2091adb4ebdSXin LI 2101adb4ebdSXin LI MPASS(sc != NULL || baseport != 0); 2111adb4ebdSXin LI if (sc != NULL) 2121adb4ebdSXin LI bus_space_write_1((sc)->bst, (sc)->bsh, 1, (value)); 2131adb4ebdSXin LI else 2141adb4ebdSXin LI outb(baseport + 1, value); 2151adb4ebdSXin LI } 2161adb4ebdSXin LI 2171adb4ebdSXin LI static uint8_t 2181adb4ebdSXin LI read_efdr_1(struct wb_softc *sc, u_short baseport) 2191adb4ebdSXin LI { 2201adb4ebdSXin LI 2211adb4ebdSXin LI MPASS(sc != NULL || baseport != 0); 2221adb4ebdSXin LI if (sc != NULL) 2231adb4ebdSXin LI return (bus_space_read_1((sc)->bst, (sc)->bsh, 1)); 2241adb4ebdSXin LI else 2251adb4ebdSXin LI return (inb(baseport + 1)); 2261adb4ebdSXin LI } 2271adb4ebdSXin LI 2280566170fSBjoern A. Zeeb /* 2290566170fSBjoern A. Zeeb * Return the watchdog related registers as we last read them. This will 2300566170fSBjoern A. Zeeb * usually not give the current timeout or state on whether the watchdog 2310566170fSBjoern A. Zeeb * fired. 2320566170fSBjoern A. Zeeb */ 2330566170fSBjoern A. Zeeb static int 2340566170fSBjoern A. Zeeb sysctl_wb_debug(SYSCTL_HANDLER_ARGS) 2350566170fSBjoern A. Zeeb { 2360566170fSBjoern A. Zeeb struct wb_softc *sc; 2370566170fSBjoern A. Zeeb struct sbuf sb; 2380566170fSBjoern A. Zeeb int error; 2390566170fSBjoern A. Zeeb 2400566170fSBjoern A. Zeeb sc = arg1; 2410566170fSBjoern A. Zeeb 2420566170fSBjoern A. Zeeb sbuf_new_for_sysctl(&sb, NULL, 64, req); 2430566170fSBjoern A. Zeeb 2440566170fSBjoern A. Zeeb sbuf_printf(&sb, "LDN8 (GPIO2, Watchdog): "); 2450566170fSBjoern A. Zeeb sbuf_printf(&sb, "CRF5 0x%02x ", sc->reg_1); 2460566170fSBjoern A. Zeeb sbuf_printf(&sb, "CRF6 0x%02x ", sc->reg_timeout); 2470566170fSBjoern A. Zeeb sbuf_printf(&sb, "CRF7 0x%02x", sc->reg_2); 2480566170fSBjoern A. Zeeb 2490566170fSBjoern A. Zeeb error = sbuf_finish(&sb); 2500566170fSBjoern A. Zeeb sbuf_delete(&sb); 2510566170fSBjoern A. Zeeb return (error); 2520566170fSBjoern A. Zeeb } 2530566170fSBjoern A. Zeeb 2540566170fSBjoern A. Zeeb /* 2550566170fSBjoern A. Zeeb * Read the current values before returning them. Given this might poke 2560566170fSBjoern A. Zeeb * the registers the same time as the watchdog, this sysctl handler should 2570566170fSBjoern A. Zeeb * be marked CTLFLAG_SKIP to not show up by default. 2580566170fSBjoern A. Zeeb */ 2590566170fSBjoern A. Zeeb static int 2600566170fSBjoern A. Zeeb sysctl_wb_debug_current(SYSCTL_HANDLER_ARGS) 2610566170fSBjoern A. Zeeb { 2620566170fSBjoern A. Zeeb struct wb_softc *sc; 2630566170fSBjoern A. Zeeb 2640566170fSBjoern A. Zeeb sc = arg1; 2650566170fSBjoern A. Zeeb 2660566170fSBjoern A. Zeeb /* 2670566170fSBjoern A. Zeeb * Enter extended function mode in case someone else has been 2680566170fSBjoern A. Zeeb * poking on the registers. We will not leave it though. 2690566170fSBjoern A. Zeeb */ 2701adb4ebdSXin LI if ((*sc->ext_cfg_enter_f)(sc, 0) != 0) 2710566170fSBjoern A. Zeeb return (ENXIO); 2720566170fSBjoern A. Zeeb 2730566170fSBjoern A. Zeeb /* Watchdog is configured as part of LDN 8 (GPIO Port2, Watchdog). */ 2741adb4ebdSXin LI write_efir_1(sc, 0, WB_LDN_REG); 2751adb4ebdSXin LI write_efdr_1(sc, 0, WB_LDN_REG_LDN8); 2760566170fSBjoern A. Zeeb 2771adb4ebdSXin LI write_efir_1(sc, 0, WB_LDN8_CRF5); 2781adb4ebdSXin LI sc->reg_1 = read_efdr_1(sc, 0); 2791adb4ebdSXin LI write_efir_1(sc, 0, WB_LDN8_CRF6); 2801adb4ebdSXin LI sc->reg_timeout = read_efdr_1(sc, 0); 2811adb4ebdSXin LI write_efir_1(sc, 0, WB_LDN8_CRF7); 2821adb4ebdSXin LI sc->reg_2 = read_efdr_1(sc, 0); 2830566170fSBjoern A. Zeeb 2840566170fSBjoern A. Zeeb return (sysctl_wb_debug(oidp, arg1, arg2, req)); 2850566170fSBjoern A. Zeeb } 2860566170fSBjoern A. Zeeb 2870566170fSBjoern A. Zeeb /* 2880566170fSBjoern A. Zeeb * Sysctl handlers to force a watchdog timeout or to test the NMI functionality 2890566170fSBjoern A. Zeeb * works as expetced. 2900566170fSBjoern A. Zeeb * For testing we could set a test_nmi flag in the softc that, in case of NMI, a 2910566170fSBjoern A. Zeeb * callback function from trap.c could check whether we fired and not report the 2920566170fSBjoern A. Zeeb * timeout but clear the flag for the sysctl again. This is interesting given a 2930566170fSBjoern A. Zeeb * lot of boards have jumpers to change the action on watchdog timeout or 2940566170fSBjoern A. Zeeb * disable the watchdog completely. 2950566170fSBjoern A. Zeeb * XXX-BZ notyet: currently no general infrastructure exists to do this. 2960566170fSBjoern A. Zeeb */ 2970566170fSBjoern A. Zeeb static int 2980566170fSBjoern A. Zeeb sysctl_wb_force_test_nmi(SYSCTL_HANDLER_ARGS) 2990566170fSBjoern A. Zeeb { 3000566170fSBjoern A. Zeeb struct wb_softc *sc; 3010566170fSBjoern A. Zeeb int error, test, val; 3020566170fSBjoern A. Zeeb 3030566170fSBjoern A. Zeeb sc = arg1; 3040566170fSBjoern A. Zeeb test = arg2; 3050566170fSBjoern A. Zeeb 3060566170fSBjoern A. Zeeb #ifdef notyet 3070566170fSBjoern A. Zeeb val = sc->test_nmi; 3080566170fSBjoern A. Zeeb #else 3090566170fSBjoern A. Zeeb val = 0; 3100566170fSBjoern A. Zeeb #endif 3110566170fSBjoern A. Zeeb error = sysctl_handle_int(oidp, &val, 0, req); 3120566170fSBjoern A. Zeeb if (error || !req->newptr) 3130566170fSBjoern A. Zeeb return (error); 3140566170fSBjoern A. Zeeb 3150566170fSBjoern A. Zeeb #ifdef notyet 3160566170fSBjoern A. Zeeb /* Manually clear the test for a value of 0 and do nothing else. */ 3170566170fSBjoern A. Zeeb if (test && val == 0) { 3180566170fSBjoern A. Zeeb sc->test_nmi = 0; 3190566170fSBjoern A. Zeeb return (0); 3200566170fSBjoern A. Zeeb } 3210566170fSBjoern A. Zeeb #endif 3220566170fSBjoern A. Zeeb 3230566170fSBjoern A. Zeeb /* 3240566170fSBjoern A. Zeeb * Enter extended function mode in case someone else has been 3250566170fSBjoern A. Zeeb * poking on the registers. We will not leave it though. 3260566170fSBjoern A. Zeeb */ 3271adb4ebdSXin LI if ((*sc->ext_cfg_enter_f)(sc, 0) != 0) 3280566170fSBjoern A. Zeeb return (ENXIO); 3290566170fSBjoern A. Zeeb 3300566170fSBjoern A. Zeeb #ifdef notyet 3310566170fSBjoern A. Zeeb /* 3320566170fSBjoern A. Zeeb * If we are testing the NMI functionality, set the flag before 3330566170fSBjoern A. Zeeb * forcing the timeout. 3340566170fSBjoern A. Zeeb */ 3350566170fSBjoern A. Zeeb if (test) 3360566170fSBjoern A. Zeeb sc->test_nmi = 1; 3370566170fSBjoern A. Zeeb #endif 3380566170fSBjoern A. Zeeb 3390566170fSBjoern A. Zeeb /* Watchdog is configured as part of LDN 8 (GPIO Port2, Watchdog). */ 3401adb4ebdSXin LI write_efir_1(sc, 0, WB_LDN_REG); 3411adb4ebdSXin LI write_efdr_1(sc, 0, WB_LDN_REG_LDN8); 3420566170fSBjoern A. Zeeb 3430566170fSBjoern A. Zeeb /* Force watchdog to fire. */ 3441adb4ebdSXin LI write_efir_1(sc, 0, WB_LDN8_CRF7); 3451adb4ebdSXin LI sc->reg_2 = read_efdr_1(sc, 0); 3460566170fSBjoern A. Zeeb sc->reg_2 |= WB_LDN8_CRF7_FORCE; 3470566170fSBjoern A. Zeeb 3481adb4ebdSXin LI write_efir_1(sc, 0, WB_LDN8_CRF7); 3491adb4ebdSXin LI write_efdr_1(sc, 0, sc->reg_2); 3500566170fSBjoern A. Zeeb 3510566170fSBjoern A. Zeeb return (0); 3520566170fSBjoern A. Zeeb } 3530566170fSBjoern A. Zeeb 3540566170fSBjoern A. Zeeb /* 3550566170fSBjoern A. Zeeb * Print current watchdog state. 3560566170fSBjoern A. Zeeb * 3570566170fSBjoern A. Zeeb * Note: it is the responsibility of the caller to update the registers 3580566170fSBjoern A. Zeeb * upfront. 3590566170fSBjoern A. Zeeb */ 3600566170fSBjoern A. Zeeb static void 3610566170fSBjoern A. Zeeb wb_print_state(struct wb_softc *sc, const char *msg) 3620566170fSBjoern A. Zeeb { 3630566170fSBjoern A. Zeeb 3640566170fSBjoern A. Zeeb device_printf(sc->dev, "%s%sWatchdog %sabled. %s" 3650566170fSBjoern A. Zeeb "Scaling by %ds, timer at %d (%s=%ds%s). " 3660566170fSBjoern A. Zeeb "CRF5 0x%02x CRF7 0x%02x\n", 3670566170fSBjoern A. Zeeb (msg != NULL) ? msg : "", (msg != NULL) ? ": " : "", 3680566170fSBjoern A. Zeeb (sc->reg_timeout > 0x00) ? "en" : "dis", 3690566170fSBjoern A. Zeeb (sc->reg_2 & WB_LDN8_CRF7_TS) ? "Watchdog fired. " : "", 3700566170fSBjoern A. Zeeb (sc->reg_1 & WB_LDN8_CRF5_SCALE) ? 60 : 1, 3710566170fSBjoern A. Zeeb sc->reg_timeout, 3720566170fSBjoern A. Zeeb (sc->reg_timeout > 0x00) ? "<" : "", 3730566170fSBjoern A. Zeeb sc->reg_timeout * ((sc->reg_1 & WB_LDN8_CRF5_SCALE) ? 60 : 1), 3740566170fSBjoern A. Zeeb (sc->reg_timeout > 0x00) ? " left" : "", 3750566170fSBjoern A. Zeeb sc->reg_1, sc->reg_2); 3760566170fSBjoern A. Zeeb } 3770566170fSBjoern A. Zeeb 3780566170fSBjoern A. Zeeb /* 3790566170fSBjoern A. Zeeb * Functions to enter and exit extended function mode. Possibly shared 3800566170fSBjoern A. Zeeb * between different chips. 3810566170fSBjoern A. Zeeb */ 3820566170fSBjoern A. Zeeb static int 3831adb4ebdSXin LI ext_cfg_enter_0x87_0x87(struct wb_softc *sc, u_short baseport) 3840566170fSBjoern A. Zeeb { 3850566170fSBjoern A. Zeeb 3860566170fSBjoern A. Zeeb /* 3870566170fSBjoern A. Zeeb * Enable extended function mode. 3880566170fSBjoern A. Zeeb * Winbond does not allow us to validate so always return success. 3890566170fSBjoern A. Zeeb */ 3901adb4ebdSXin LI write_efir_1(sc, baseport, 0x87); 3911adb4ebdSXin LI write_efir_1(sc, baseport, 0x87); 3920566170fSBjoern A. Zeeb 3930566170fSBjoern A. Zeeb return (0); 3940566170fSBjoern A. Zeeb } 3950566170fSBjoern A. Zeeb 3960566170fSBjoern A. Zeeb static void 3971adb4ebdSXin LI ext_cfg_exit_0xaa(struct wb_softc *sc, u_short baseport) 3980566170fSBjoern A. Zeeb { 3990566170fSBjoern A. Zeeb 4001adb4ebdSXin LI write_efir_1(sc, baseport, 0xaa); 4010566170fSBjoern A. Zeeb } 4020566170fSBjoern A. Zeeb 4030566170fSBjoern A. Zeeb /* 4040566170fSBjoern A. Zeeb * (Re)load the watchdog counter depending on timeout. A timeout of 0 will 4050566170fSBjoern A. Zeeb * disable the watchdog. 4060566170fSBjoern A. Zeeb */ 4070566170fSBjoern A. Zeeb static int 4080566170fSBjoern A. Zeeb wb_set_watchdog(struct wb_softc *sc, unsigned int timeout) 4090566170fSBjoern A. Zeeb { 4100566170fSBjoern A. Zeeb 4110566170fSBjoern A. Zeeb if (sc->debug_verbose) 4120566170fSBjoern A. Zeeb wb_print_state(sc, "Before watchdog counter (re)load"); 4130566170fSBjoern A. Zeeb 4140566170fSBjoern A. Zeeb /* 4150566170fSBjoern A. Zeeb * Enter extended function mode in case someone else has been 4160566170fSBjoern A. Zeeb * poking on the registers. We will not leave it though. 4170566170fSBjoern A. Zeeb */ 4181adb4ebdSXin LI if ((*sc->ext_cfg_enter_f)(sc, 0) != 0) 4190566170fSBjoern A. Zeeb return (ENXIO); 4200566170fSBjoern A. Zeeb 4210566170fSBjoern A. Zeeb /* Watchdog is configured as part of LDN 8 (GPIO Port2, Watchdog) */ 4221adb4ebdSXin LI write_efir_1(sc, 0, WB_LDN_REG); 4231adb4ebdSXin LI write_efdr_1(sc, 0, WB_LDN_REG_LDN8); 4240566170fSBjoern A. Zeeb 4250566170fSBjoern A. Zeeb /* Disable and validate or arm/reset watchdog. */ 4260566170fSBjoern A. Zeeb if (timeout == 0) { 4270566170fSBjoern A. Zeeb /* Disable watchdog. */ 4281adb4ebdSXin LI write_efir_1(sc, 0, WB_LDN8_CRF6); 4291adb4ebdSXin LI write_efdr_1(sc, 0, 0x00); 4300566170fSBjoern A. Zeeb 4310566170fSBjoern A. Zeeb /* Re-check. */ 4321adb4ebdSXin LI write_efir_1(sc, 0, WB_LDN8_CRF6); 4331adb4ebdSXin LI sc->reg_timeout = read_efdr_1(sc, 0); 4340566170fSBjoern A. Zeeb 4350566170fSBjoern A. Zeeb if (sc->reg_timeout != 0x00) { 4360566170fSBjoern A. Zeeb device_printf(sc->dev, "Failed to disable watchdog: " 4370566170fSBjoern A. Zeeb "0x%02x.\n", sc->reg_timeout); 4380566170fSBjoern A. Zeeb return (EIO); 4390566170fSBjoern A. Zeeb } 4400566170fSBjoern A. Zeeb 4410566170fSBjoern A. Zeeb } else { 4420566170fSBjoern A. Zeeb /* 4430566170fSBjoern A. Zeeb * In case an override is set, let it override. It may lead 4440566170fSBjoern A. Zeeb * to strange results as we do not check the input of the sysctl. 4450566170fSBjoern A. Zeeb */ 4460566170fSBjoern A. Zeeb if (sc->timeout_override > 0) 4470566170fSBjoern A. Zeeb timeout = sc->timeout_override; 4480566170fSBjoern A. Zeeb 4490566170fSBjoern A. Zeeb /* Make sure we support the requested timeout. */ 4500566170fSBjoern A. Zeeb if (timeout > 255 * 60) 4510566170fSBjoern A. Zeeb return (EINVAL); 4520566170fSBjoern A. Zeeb 4530566170fSBjoern A. Zeeb /* Read current scaling factor. */ 4541adb4ebdSXin LI write_efir_1(sc, 0, WB_LDN8_CRF5); 4551adb4ebdSXin LI sc->reg_1 = read_efdr_1(sc, 0); 4560566170fSBjoern A. Zeeb 4570566170fSBjoern A. Zeeb if (timeout > 255) { 4580566170fSBjoern A. Zeeb /* Set scaling factor to 60s. */ 4590566170fSBjoern A. Zeeb sc->reg_1 |= WB_LDN8_CRF5_SCALE; 4600566170fSBjoern A. Zeeb sc->reg_timeout = (timeout / 60); 4610566170fSBjoern A. Zeeb if (timeout % 60) 4620566170fSBjoern A. Zeeb sc->reg_timeout++; 4630566170fSBjoern A. Zeeb } else { 4640566170fSBjoern A. Zeeb /* Set scaling factor to 1s. */ 4650566170fSBjoern A. Zeeb sc->reg_1 &= ~WB_LDN8_CRF5_SCALE; 4660566170fSBjoern A. Zeeb sc->reg_timeout = timeout; 4670566170fSBjoern A. Zeeb } 4680566170fSBjoern A. Zeeb 4690566170fSBjoern A. Zeeb /* In case we fired before we need to clear to fire again. */ 4701adb4ebdSXin LI write_efir_1(sc, 0, WB_LDN8_CRF7); 4711adb4ebdSXin LI sc->reg_2 = read_efdr_1(sc, 0); 4720566170fSBjoern A. Zeeb if (sc->reg_2 & WB_LDN8_CRF7_TS) { 4730566170fSBjoern A. Zeeb sc->reg_2 &= ~WB_LDN8_CRF7_TS; 4741adb4ebdSXin LI write_efir_1(sc, 0, WB_LDN8_CRF7); 4751adb4ebdSXin LI write_efdr_1(sc, 0, sc->reg_2); 4760566170fSBjoern A. Zeeb } 4770566170fSBjoern A. Zeeb 4780566170fSBjoern A. Zeeb /* Write back scaling factor. */ 4791adb4ebdSXin LI write_efir_1(sc, 0, WB_LDN8_CRF5); 4801adb4ebdSXin LI write_efdr_1(sc, 0, sc->reg_1); 4810566170fSBjoern A. Zeeb 4820566170fSBjoern A. Zeeb /* Set timer and arm/reset the watchdog. */ 4831adb4ebdSXin LI write_efir_1(sc, 0, WB_LDN8_CRF6); 4841adb4ebdSXin LI write_efdr_1(sc, 0, sc->reg_timeout); 4850566170fSBjoern A. Zeeb } 4860566170fSBjoern A. Zeeb 4870566170fSBjoern A. Zeeb if (sc->debug_verbose) 4880566170fSBjoern A. Zeeb wb_print_state(sc, "After watchdog counter (re)load"); 4890566170fSBjoern A. Zeeb 4900566170fSBjoern A. Zeeb return (0); 4910566170fSBjoern A. Zeeb } 4920566170fSBjoern A. Zeeb 4930566170fSBjoern A. Zeeb /* 4940566170fSBjoern A. Zeeb * watchdog(9) EVENTHANDLER function implementation to (re)load the counter 4950566170fSBjoern A. Zeeb * with the given timeout or disable the watchdog. 4960566170fSBjoern A. Zeeb */ 4970566170fSBjoern A. Zeeb static void 4980566170fSBjoern A. Zeeb wb_watchdog_fn(void *private, u_int cmd, int *error) 4990566170fSBjoern A. Zeeb { 5000566170fSBjoern A. Zeeb struct wb_softc *sc; 5010566170fSBjoern A. Zeeb unsigned int timeout; 5020566170fSBjoern A. Zeeb int e; 5030566170fSBjoern A. Zeeb 5040566170fSBjoern A. Zeeb sc = private; 5050566170fSBjoern A. Zeeb KASSERT(sc != NULL, ("%s: watchdog handler function called without " 5060566170fSBjoern A. Zeeb "softc.", __func__)); 5070566170fSBjoern A. Zeeb 5080566170fSBjoern A. Zeeb cmd &= WD_INTERVAL; 5090566170fSBjoern A. Zeeb if (cmd > 0 && cmd <= 63) { 5100566170fSBjoern A. Zeeb /* Reset (and arm) watchdog. */ 5110566170fSBjoern A. Zeeb timeout = ((uint64_t)1 << cmd) / 1000000000; 5120566170fSBjoern A. Zeeb if (timeout == 0) 5130566170fSBjoern A. Zeeb timeout = 1; 5140566170fSBjoern A. Zeeb e = wb_set_watchdog(sc, timeout); 5150566170fSBjoern A. Zeeb if (e == 0) { 5160566170fSBjoern A. Zeeb if (error != NULL) 5170566170fSBjoern A. Zeeb *error = 0; 5180566170fSBjoern A. Zeeb } else { 5190566170fSBjoern A. Zeeb /* On error, try to make sure the WD is disabled. */ 5200566170fSBjoern A. Zeeb wb_set_watchdog(sc, 0); 5210566170fSBjoern A. Zeeb } 5220566170fSBjoern A. Zeeb 5230566170fSBjoern A. Zeeb } else { 5240566170fSBjoern A. Zeeb /* Disable watchdog. */ 5250566170fSBjoern A. Zeeb e = wb_set_watchdog(sc, 0); 5260566170fSBjoern A. Zeeb if (e != 0 && cmd == 0 && error != NULL) { 5270566170fSBjoern A. Zeeb /* Failed to disable watchdog. */ 5280566170fSBjoern A. Zeeb *error = EOPNOTSUPP; 5290566170fSBjoern A. Zeeb } 5300566170fSBjoern A. Zeeb } 5310566170fSBjoern A. Zeeb } 5320566170fSBjoern A. Zeeb 5330566170fSBjoern A. Zeeb /* 5340566170fSBjoern A. Zeeb * Probe/attach the Winbond Super I/O chip. 5350566170fSBjoern A. Zeeb * 5360566170fSBjoern A. Zeeb * Initial abstraction to possibly support more chips: 5370566170fSBjoern A. Zeeb * - Iterate over the well known base ports, try to enable extended function 5380566170fSBjoern A. Zeeb * mode and read and match the device ID and device revision. Unfortunately 5390566170fSBjoern A. Zeeb * the Vendor ID is in the hardware monitoring section accessible by different 5400566170fSBjoern A. Zeeb * base ports only. 5410566170fSBjoern A. Zeeb * - Also HEFRAS, which would tell use the base port, is only accessible after 5420566170fSBjoern A. Zeeb * entering extended function mode, for which the base port is needed. 5430566170fSBjoern A. Zeeb * At least check HEFRAS to match the current base port we are probing. 5440566170fSBjoern A. Zeeb * - On match set the description, remember functions to enter/exit extended 5450566170fSBjoern A. Zeeb * function mode as well as the base port. 5460566170fSBjoern A. Zeeb */ 5470566170fSBjoern A. Zeeb static int 5480566170fSBjoern A. Zeeb wb_probe_enable(device_t dev, int probe) 5490566170fSBjoern A. Zeeb { 5500566170fSBjoern A. Zeeb struct wb_softc *sc; 5510566170fSBjoern A. Zeeb int error, found, i, j; 5520566170fSBjoern A. Zeeb uint8_t dev_id, dev_rev, cr26; 5530566170fSBjoern A. Zeeb 5541adb4ebdSXin LI if (dev == NULL) 5551adb4ebdSXin LI sc = NULL; 5561adb4ebdSXin LI else { 5570566170fSBjoern A. Zeeb sc = device_get_softc(dev); 5580566170fSBjoern A. Zeeb bzero(sc, sizeof(*sc)); 5590566170fSBjoern A. Zeeb sc->dev = dev; 5601adb4ebdSXin LI } 5610566170fSBjoern A. Zeeb 5620566170fSBjoern A. Zeeb error = ENXIO; 5630566170fSBjoern A. Zeeb for (i = 0; i < sizeof(probe_addrs) / sizeof(*probe_addrs); i++) { 5640566170fSBjoern A. Zeeb 5651adb4ebdSXin LI if (sc != NULL) { 5660566170fSBjoern A. Zeeb /* Allocate bus resources for IO index/data register access. */ 5670566170fSBjoern A. Zeeb sc->portres = bus_alloc_resource(dev, SYS_RES_IOPORT, &sc->rid, 5680566170fSBjoern A. Zeeb probe_addrs[i].efer, probe_addrs[i].efer + 1, 2, RF_ACTIVE); 5690566170fSBjoern A. Zeeb if (sc->portres == NULL) 5700566170fSBjoern A. Zeeb continue; 5710566170fSBjoern A. Zeeb sc->bst = rman_get_bustag(sc->portres); 5720566170fSBjoern A. Zeeb sc->bsh = rman_get_bushandle(sc->portres); 5731adb4ebdSXin LI } 5740566170fSBjoern A. Zeeb 5750566170fSBjoern A. Zeeb found = 0; 5761adb4ebdSXin LI error = (*probe_addrs[i].ext_cfg_enter_f)(sc, probe_addrs[i].efer); 5770566170fSBjoern A. Zeeb if (error != 0) 5780566170fSBjoern A. Zeeb goto cleanup; 5790566170fSBjoern A. Zeeb 5800566170fSBjoern A. Zeeb /* Identify the SuperIO chip. */ 5811adb4ebdSXin LI write_efir_1(sc, probe_addrs[i].efer, WB_DEVICE_ID_REG); 5821adb4ebdSXin LI dev_id = read_efdr_1(sc, probe_addrs[i].efer); 5831adb4ebdSXin LI write_efir_1(sc, probe_addrs[i].efer, WB_DEVICE_REV_REG); 5841adb4ebdSXin LI dev_rev = read_efdr_1(sc, probe_addrs[i].efer); 5851adb4ebdSXin LI write_efir_1(sc, probe_addrs[i].efer, WB_CR26); 5861adb4ebdSXin LI cr26 = read_efdr_1(sc, probe_addrs[i].efer); 5870566170fSBjoern A. Zeeb 5880566170fSBjoern A. Zeeb /* HEFRAS of 0 means EFER at 0x2e, 1 means EFER at 0x4e. */ 5890566170fSBjoern A. Zeeb if (((cr26 & 0x40) == 0x00 && probe_addrs[i].efer != 0x2e) || 5900566170fSBjoern A. Zeeb ((cr26 & 0x40) == 0x40 && probe_addrs[i].efer != 0x4e)) { 5911adb4ebdSXin LI if (dev != NULL) 5921adb4ebdSXin LI device_printf(dev, "HEFRAS and EFER do not " 5931adb4ebdSXin LI "align: EFER 0x%02x DevID 0x%02x DevRev " 5941adb4ebdSXin LI "0x%02x CR26 0x%02x\n", 5950566170fSBjoern A. Zeeb probe_addrs[i].efer, dev_id, dev_rev, cr26); 5960566170fSBjoern A. Zeeb goto cleanup; 5970566170fSBjoern A. Zeeb } 5980566170fSBjoern A. Zeeb 5990566170fSBjoern A. Zeeb for (j = 0; j < sizeof(wb_devs) / sizeof(*wb_devs); j++) { 6000566170fSBjoern A. Zeeb if (wb_devs[j].device_id == dev_id && 6010566170fSBjoern A. Zeeb wb_devs[j].device_rev == dev_rev) { 6021adb4ebdSXin LI if (probe && dev != NULL) 6030566170fSBjoern A. Zeeb device_set_desc(dev, wb_devs[j].descr); 6040566170fSBjoern A. Zeeb found++; 6050566170fSBjoern A. Zeeb break; 6060566170fSBjoern A. Zeeb } 6070566170fSBjoern A. Zeeb } 6081adb4ebdSXin LI if (probe && found && bootverbose && dev != NULL) 6090566170fSBjoern A. Zeeb device_printf(dev, "%s EFER 0x%02x ID 0x%02x Rev 0x%02x" 6100566170fSBjoern A. Zeeb " CR26 0x%02x (probing)\n", device_get_desc(dev), 6110566170fSBjoern A. Zeeb probe_addrs[i].efer, dev_id, dev_rev, cr26); 6120566170fSBjoern A. Zeeb cleanup: 6130566170fSBjoern A. Zeeb if (probe || !found) { 6141adb4ebdSXin LI (*probe_addrs[i].ext_cfg_exit_f)(sc, probe_addrs[i].efer); 6150566170fSBjoern A. Zeeb 6161adb4ebdSXin LI if (sc != NULL) 6171adb4ebdSXin LI (void) bus_release_resource(dev, SYS_RES_IOPORT, 6181adb4ebdSXin LI sc->rid, sc->portres); 6190566170fSBjoern A. Zeeb } 6200566170fSBjoern A. Zeeb 6210566170fSBjoern A. Zeeb /* 6220566170fSBjoern A. Zeeb * Stop probing if have successfully identified the SuperIO. 6230566170fSBjoern A. Zeeb * Remember the extended function mode enter/exit functions 6240566170fSBjoern A. Zeeb * for operations. 6250566170fSBjoern A. Zeeb */ 6260566170fSBjoern A. Zeeb if (found) { 6271adb4ebdSXin LI if (sc != NULL) { 6280566170fSBjoern A. Zeeb sc->ext_cfg_enter_f = probe_addrs[i].ext_cfg_enter_f; 6290566170fSBjoern A. Zeeb sc->ext_cfg_exit_f = probe_addrs[i].ext_cfg_exit_f; 6301adb4ebdSXin LI } 6310566170fSBjoern A. Zeeb error = BUS_PROBE_DEFAULT; 6320566170fSBjoern A. Zeeb break; 6330566170fSBjoern A. Zeeb } else 6340566170fSBjoern A. Zeeb error = ENXIO; 6350566170fSBjoern A. Zeeb } 6360566170fSBjoern A. Zeeb 6370566170fSBjoern A. Zeeb return (error); 6380566170fSBjoern A. Zeeb } 6390566170fSBjoern A. Zeeb 6401adb4ebdSXin LI static void 6411adb4ebdSXin LI wb_identify(driver_t *driver, device_t parent) 6421adb4ebdSXin LI { 6431adb4ebdSXin LI device_t dev; 6441adb4ebdSXin LI 6451adb4ebdSXin LI if ((dev = device_find_child(parent, driver->name, 0)) == NULL) { 6461adb4ebdSXin LI if (wb_probe_enable(dev, 1) != BUS_PROBE_DEFAULT) { 6471adb4ebdSXin LI if (bootverbose) 6481adb4ebdSXin LI device_printf(dev, "can not find compatible Winbond chip.\n"); 6491adb4ebdSXin LI } else 6501adb4ebdSXin LI dev = BUS_ADD_CHILD(parent, 0, driver->name, 0); 6511adb4ebdSXin LI return; 6521adb4ebdSXin LI } 6531adb4ebdSXin LI } 6541adb4ebdSXin LI 6550566170fSBjoern A. Zeeb static int 6560566170fSBjoern A. Zeeb wb_probe(device_t dev) 6570566170fSBjoern A. Zeeb { 6580566170fSBjoern A. Zeeb 6590566170fSBjoern A. Zeeb /* Make sure we do not claim some ISA PNP device. */ 6600566170fSBjoern A. Zeeb if (isa_get_logicalid(dev) != 0) 6610566170fSBjoern A. Zeeb return (ENXIO); 6620566170fSBjoern A. Zeeb 6630566170fSBjoern A. Zeeb return (wb_probe_enable(dev, 1)); 6640566170fSBjoern A. Zeeb } 6650566170fSBjoern A. Zeeb 6660566170fSBjoern A. Zeeb static int 6670566170fSBjoern A. Zeeb wb_attach(device_t dev) 6680566170fSBjoern A. Zeeb { 6690566170fSBjoern A. Zeeb struct wb_softc *sc; 6700566170fSBjoern A. Zeeb struct sysctl_ctx_list *sctx; 6710566170fSBjoern A. Zeeb struct sysctl_oid *soid; 6720566170fSBjoern A. Zeeb unsigned long timeout; 6730566170fSBjoern A. Zeeb int error; 6740566170fSBjoern A. Zeeb 6750566170fSBjoern A. Zeeb error = wb_probe_enable(dev, 0); 6760566170fSBjoern A. Zeeb if (error > 0) 6770566170fSBjoern A. Zeeb return (ENXIO); 6780566170fSBjoern A. Zeeb 6790566170fSBjoern A. Zeeb sc = device_get_softc(dev); 6800566170fSBjoern A. Zeeb KASSERT(sc->ext_cfg_enter_f != NULL && sc->ext_cfg_exit_f != NULL, 6810566170fSBjoern A. Zeeb ("%s: successfull probe result but not setup correctly", __func__)); 6820566170fSBjoern A. Zeeb 6830566170fSBjoern A. Zeeb /* Watchdog is configured as part of LDN 8 (GPIO Port2, Watchdog). */ 6841adb4ebdSXin LI write_efir_1(sc, 0, WB_LDN_REG); 6851adb4ebdSXin LI write_efdr_1(sc, 0, WB_LDN_REG_LDN8); 6860566170fSBjoern A. Zeeb 6870566170fSBjoern A. Zeeb /* Make sure LDN8 is enabled (Do we need to? Also affects GPIO). */ 6881adb4ebdSXin LI write_efir_1(sc, 0, WB_LDN8_CR30); 6891adb4ebdSXin LI write_efdr_1(sc, 0, WB_LDN8_CR30_ACTIVE); 6900566170fSBjoern A. Zeeb 6910566170fSBjoern A. Zeeb /* Read the current watchdog configuration. */ 6921adb4ebdSXin LI write_efir_1(sc, 0, WB_LDN8_CRF5); 6931adb4ebdSXin LI sc->reg_1 = read_efdr_1(sc, 0); 6941adb4ebdSXin LI write_efir_1(sc, 0, WB_LDN8_CRF6); 6951adb4ebdSXin LI sc->reg_timeout = read_efdr_1(sc, 0); 6961adb4ebdSXin LI write_efir_1(sc, 0, WB_LDN8_CRF7); 6971adb4ebdSXin LI sc->reg_2 = read_efdr_1(sc, 0); 6980566170fSBjoern A. Zeeb 6990566170fSBjoern A. Zeeb /* Print current state if bootverbose or watchdog already enabled. */ 7000566170fSBjoern A. Zeeb if (bootverbose || (sc->reg_timeout > 0x00)) 7010566170fSBjoern A. Zeeb wb_print_state(sc, "Before watchdog attach"); 7020566170fSBjoern A. Zeeb 7030566170fSBjoern A. Zeeb /* 7040566170fSBjoern A. Zeeb * Clear a previous watchdog timeout event (if (still) set). 7050566170fSBjoern A. Zeeb * Disable all all interrupt reset sources (defaults). 7060566170fSBjoern A. Zeeb */ 7070566170fSBjoern A. Zeeb sc->reg_1 &= ~(WB_LDN8_CRF5_KEYB_P20); 708cf864f03SRobert Noland sc->reg_1 |= WB_LDN8_CRF5_KBRST; 7091adb4ebdSXin LI write_efir_1(sc, 0, WB_LDN8_CRF5); 7101adb4ebdSXin LI write_efdr_1(sc, 0, sc->reg_1); 7110566170fSBjoern A. Zeeb 7120566170fSBjoern A. Zeeb sc->reg_2 &= ~WB_LDN8_CRF7_CLEAR_MASK; 7131adb4ebdSXin LI write_efir_1(sc, 0, WB_LDN8_CRF7); 7141adb4ebdSXin LI write_efdr_1(sc, 0, sc->reg_2); 7150566170fSBjoern A. Zeeb 7160566170fSBjoern A. Zeeb /* Read global timeout override tunable, Add per device sysctls. */ 7170566170fSBjoern A. Zeeb if (TUNABLE_ULONG_FETCH("hw.wbwd.timeout_override", &timeout)) { 7180566170fSBjoern A. Zeeb if (timeout > 0) 7190566170fSBjoern A. Zeeb sc->timeout_override = timeout; 7200566170fSBjoern A. Zeeb } 7210566170fSBjoern A. Zeeb sctx = device_get_sysctl_ctx(dev); 7220566170fSBjoern A. Zeeb soid = device_get_sysctl_tree(dev); 7230566170fSBjoern A. Zeeb SYSCTL_ADD_UINT(sctx, SYSCTL_CHILDREN(soid), OID_AUTO, 7240566170fSBjoern A. Zeeb "timeout_override", CTLFLAG_RW, &sc->timeout_override, 0, 7250566170fSBjoern A. Zeeb "Timeout in seconds overriding default watchdog timeout"); 7260566170fSBjoern A. Zeeb SYSCTL_ADD_INT(sctx, SYSCTL_CHILDREN(soid), OID_AUTO, 7270566170fSBjoern A. Zeeb "debug_verbose", CTLFLAG_RW, &sc->debug_verbose, 0, 7280566170fSBjoern A. Zeeb "Enables extra debugging information"); 7290566170fSBjoern A. Zeeb SYSCTL_ADD_PROC(sctx, SYSCTL_CHILDREN(soid), OID_AUTO, "debug", 7300566170fSBjoern A. Zeeb CTLTYPE_STRING|CTLFLAG_RD, sc, 0, sysctl_wb_debug, "A", 7310566170fSBjoern A. Zeeb "Selected register information from last change by driver"); 7320566170fSBjoern A. Zeeb SYSCTL_ADD_PROC(sctx, SYSCTL_CHILDREN(soid), OID_AUTO, "debug_current", 7330566170fSBjoern A. Zeeb CTLTYPE_STRING|CTLFLAG_RD|CTLFLAG_SKIP, sc, 0, 7340566170fSBjoern A. Zeeb sysctl_wb_debug_current, "A", 7350566170fSBjoern A. Zeeb "Selected register information (may interfere)"); 7360566170fSBjoern A. Zeeb SYSCTL_ADD_PROC(sctx, SYSCTL_CHILDREN(soid), OID_AUTO, "force_timeout", 7370566170fSBjoern A. Zeeb CTLTYPE_INT|CTLFLAG_RW|CTLFLAG_SKIP, sc, 0, 7380566170fSBjoern A. Zeeb sysctl_wb_force_test_nmi, "I", "Enable to force watchdog to fire."); 7390566170fSBjoern A. Zeeb 7400566170fSBjoern A. Zeeb /* Register watchdog. */ 7410566170fSBjoern A. Zeeb sc->ev_tag = EVENTHANDLER_REGISTER(watchdog_list, wb_watchdog_fn, sc, 7420566170fSBjoern A. Zeeb 0); 7430566170fSBjoern A. Zeeb 7440566170fSBjoern A. Zeeb if (bootverbose) 7450566170fSBjoern A. Zeeb wb_print_state(sc, "After watchdog attach"); 7460566170fSBjoern A. Zeeb 7470566170fSBjoern A. Zeeb return (0); 7480566170fSBjoern A. Zeeb } 7490566170fSBjoern A. Zeeb 7500566170fSBjoern A. Zeeb static int 7510566170fSBjoern A. Zeeb wb_detach(device_t dev) 7520566170fSBjoern A. Zeeb { 7530566170fSBjoern A. Zeeb struct wb_softc *sc; 7540566170fSBjoern A. Zeeb 7550566170fSBjoern A. Zeeb sc = device_get_softc(dev); 7560566170fSBjoern A. Zeeb 7570566170fSBjoern A. Zeeb /* Unregister and stop the watchdog if running. */ 7580566170fSBjoern A. Zeeb if (sc->ev_tag) 7590566170fSBjoern A. Zeeb EVENTHANDLER_DEREGISTER(watchdog_list, sc->ev_tag); 7600566170fSBjoern A. Zeeb wb_set_watchdog(sc, 0); 7610566170fSBjoern A. Zeeb 7620566170fSBjoern A. Zeeb /* Disable extended function mode. */ 7631adb4ebdSXin LI (*sc->ext_cfg_exit_f)(sc, 0); 7640566170fSBjoern A. Zeeb 7650566170fSBjoern A. Zeeb /* Cleanup resources. */ 7660566170fSBjoern A. Zeeb (void) bus_release_resource(dev, SYS_RES_IOPORT, sc->rid, sc->portres); 7670566170fSBjoern A. Zeeb 7680566170fSBjoern A. Zeeb /* Bus subroutines take care of sysctls already. */ 7690566170fSBjoern A. Zeeb 7700566170fSBjoern A. Zeeb return (0); 7710566170fSBjoern A. Zeeb } 7720566170fSBjoern A. Zeeb 7730566170fSBjoern A. Zeeb static device_method_t wb_methods[] = { 7740566170fSBjoern A. Zeeb /* Device interface */ 7751adb4ebdSXin LI DEVMETHOD(device_identify, wb_identify), 7760566170fSBjoern A. Zeeb DEVMETHOD(device_probe, wb_probe), 7770566170fSBjoern A. Zeeb DEVMETHOD(device_attach, wb_attach), 7780566170fSBjoern A. Zeeb DEVMETHOD(device_detach, wb_detach), 7790566170fSBjoern A. Zeeb 780e25fe6cdSXin LI DEVMETHOD_END 7810566170fSBjoern A. Zeeb }; 7820566170fSBjoern A. Zeeb 7830566170fSBjoern A. Zeeb static driver_t wb_isa_driver = { 7840566170fSBjoern A. Zeeb "wbwd", 7850566170fSBjoern A. Zeeb wb_methods, 7860566170fSBjoern A. Zeeb sizeof(struct wb_softc) 7870566170fSBjoern A. Zeeb }; 7880566170fSBjoern A. Zeeb 7890566170fSBjoern A. Zeeb static devclass_t wb_devclass; 7900566170fSBjoern A. Zeeb 7910566170fSBjoern A. Zeeb DRIVER_MODULE(wb, isa, wb_isa_driver, wb_devclass, NULL, NULL); 792