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 */ 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, 98baa7dd65SAlexander Motin nct6792, nct6102 }; 990566170fSBjoern A. Zeeb 1000566170fSBjoern A. Zeeb struct wb_softc { 1010566170fSBjoern A. Zeeb device_t dev; 1020566170fSBjoern A. Zeeb struct resource *portres; 1030566170fSBjoern A. Zeeb bus_space_tag_t bst; 1040566170fSBjoern A. Zeeb bus_space_handle_t bsh; 1050566170fSBjoern A. Zeeb int rid; 1060566170fSBjoern A. Zeeb eventhandler_tag ev_tag; 1071adb4ebdSXin LI int (*ext_cfg_enter_f)(struct wb_softc *, u_short); 1081adb4ebdSXin LI void (*ext_cfg_exit_f)(struct wb_softc *, u_short); 109baa7dd65SAlexander Motin enum chips chip; 110baa7dd65SAlexander Motin uint8_t ctl_reg; 111baa7dd65SAlexander Motin uint8_t time_reg; 112baa7dd65SAlexander Motin uint8_t csr_reg; 1130566170fSBjoern A. Zeeb int debug_verbose; 1140566170fSBjoern A. Zeeb 1150566170fSBjoern A. Zeeb /* 1160566170fSBjoern A. Zeeb * Special feature to let the watchdog fire at a different 1170566170fSBjoern A. Zeeb * timeout as set by watchdog(4) but still use that API to 1180566170fSBjoern A. Zeeb * re-load it periodically. 1190566170fSBjoern A. Zeeb */ 1200566170fSBjoern A. Zeeb unsigned int timeout_override; 1210566170fSBjoern A. Zeeb 1220566170fSBjoern A. Zeeb /* 1230566170fSBjoern A. Zeeb * Space to save current state temporary and for sysctls. 1240566170fSBjoern A. Zeeb * We want to know the timeout value and usually need two 1250566170fSBjoern A. Zeeb * additional registers for options. Do not name them by 1260566170fSBjoern A. Zeeb * register as these might be different by chip. 1270566170fSBjoern A. Zeeb */ 1280566170fSBjoern A. Zeeb uint8_t reg_timeout; 1290566170fSBjoern A. Zeeb uint8_t reg_1; 1300566170fSBjoern A. Zeeb uint8_t reg_2; 1310566170fSBjoern A. Zeeb }; 1320566170fSBjoern A. Zeeb 1331adb4ebdSXin LI static int ext_cfg_enter_0x87_0x87(struct wb_softc *, u_short); 1341adb4ebdSXin LI static void ext_cfg_exit_0xaa(struct wb_softc *, u_short); 1350566170fSBjoern A. Zeeb 1360566170fSBjoern A. Zeeb struct winbond_superio_cfg { 1370566170fSBjoern A. Zeeb uint8_t efer; /* and efir */ 1381adb4ebdSXin LI int (*ext_cfg_enter_f)(struct wb_softc *, u_short); 1391adb4ebdSXin LI void (*ext_cfg_exit_f)(struct wb_softc *, u_short); 1400566170fSBjoern A. Zeeb } probe_addrs[] = { 1410566170fSBjoern A. Zeeb { 1420566170fSBjoern A. Zeeb .efer = 0x2e, 1430566170fSBjoern A. Zeeb .ext_cfg_enter_f = ext_cfg_enter_0x87_0x87, 1440566170fSBjoern A. Zeeb .ext_cfg_exit_f = ext_cfg_exit_0xaa, 1450566170fSBjoern A. Zeeb }, 1460566170fSBjoern A. Zeeb { 1470566170fSBjoern A. Zeeb .efer = 0x4e, 1480566170fSBjoern A. Zeeb .ext_cfg_enter_f = ext_cfg_enter_0x87_0x87, 1490566170fSBjoern A. Zeeb .ext_cfg_exit_f = ext_cfg_exit_0xaa, 1500566170fSBjoern A. Zeeb }, 1510566170fSBjoern A. Zeeb }; 1520566170fSBjoern A. Zeeb 1530566170fSBjoern A. Zeeb struct winbond_vendor_device_id { 1540566170fSBjoern A. Zeeb uint8_t device_id; 155baa7dd65SAlexander Motin enum chips chip; 1560566170fSBjoern A. Zeeb const char * descr; 1570566170fSBjoern A. Zeeb } wb_devs[] = { 1580566170fSBjoern A. Zeeb { 1590566170fSBjoern A. Zeeb .device_id = 0x52, 160baa7dd65SAlexander Motin .chip = w83627hf, 161baa7dd65SAlexander Motin .descr = "Winbond 83627HF/F/HG/G", 1620566170fSBjoern A. Zeeb }, 1630566170fSBjoern A. Zeeb { 164baa7dd65SAlexander Motin .device_id = 0x59, 165baa7dd65SAlexander Motin .chip = w83627s, 166baa7dd65SAlexander Motin .descr = "Winbond 83627S", 1670566170fSBjoern A. Zeeb }, 1680566170fSBjoern A. Zeeb { 169baa7dd65SAlexander Motin .device_id = 0x60, 170baa7dd65SAlexander Motin .chip = w83697hf, 171baa7dd65SAlexander Motin .descr = "Winbond 83697HF", 1720566170fSBjoern A. Zeeb }, 1730566170fSBjoern A. Zeeb { 174baa7dd65SAlexander Motin .device_id = 0x68, 175baa7dd65SAlexander Motin .chip = w83697ug, 176baa7dd65SAlexander Motin .descr = "Winbond 83697UG", 177baa7dd65SAlexander Motin }, 178baa7dd65SAlexander Motin { 179baa7dd65SAlexander Motin .device_id = 0x70, 180baa7dd65SAlexander Motin .chip = w83637hf, 181baa7dd65SAlexander Motin .descr = "Winbond 83637HF", 182baa7dd65SAlexander Motin }, 183baa7dd65SAlexander Motin { 184baa7dd65SAlexander Motin .device_id = 0x82, 185baa7dd65SAlexander Motin .chip = w83627thf, 186baa7dd65SAlexander Motin .descr = "Winbond 83627THF", 187baa7dd65SAlexander Motin }, 188baa7dd65SAlexander Motin { 189baa7dd65SAlexander Motin .device_id = 0x85, 190baa7dd65SAlexander Motin .chip = w83687thf, 191baa7dd65SAlexander Motin .descr = "Winbond 83687THF", 192baa7dd65SAlexander Motin }, 193baa7dd65SAlexander Motin { 194baa7dd65SAlexander Motin .device_id = 0x88, 195baa7dd65SAlexander Motin .chip = w83627ehf, 196baa7dd65SAlexander Motin .descr = "Winbond 83627EHF", 197baa7dd65SAlexander Motin }, 198baa7dd65SAlexander Motin { 1990566170fSBjoern A. Zeeb .device_id = 0xa0, 200baa7dd65SAlexander Motin .chip = w83627dhg, 201baa7dd65SAlexander Motin .descr = "Winbond 83627DHG", 2020566170fSBjoern A. Zeeb }, 203cf864f03SRobert Noland { 204baa7dd65SAlexander Motin .device_id = 0xa2, 205baa7dd65SAlexander Motin .chip = w83627uhg, 206baa7dd65SAlexander Motin .descr = "Winbond 83627UHG", 207baa7dd65SAlexander Motin }, 208baa7dd65SAlexander Motin { 209baa7dd65SAlexander Motin .device_id = 0xa5, 210baa7dd65SAlexander Motin .chip = w83667hg, 211baa7dd65SAlexander Motin .descr = "Winbond 83667HG", 212baa7dd65SAlexander Motin }, 213baa7dd65SAlexander Motin { 214cf864f03SRobert Noland .device_id = 0xb0, 215baa7dd65SAlexander Motin .chip = w83627dhg_p, 216cf864f03SRobert Noland .descr = "Winbond 83627DHG-P", 217cf864f03SRobert Noland }, 2189e8bad81SXin LI { 219baa7dd65SAlexander Motin .device_id = 0xb3, 220baa7dd65SAlexander Motin .chip = w83667hg_b, 221baa7dd65SAlexander Motin .descr = "Winbond 83667HG-B", 222baa7dd65SAlexander Motin }, 223baa7dd65SAlexander Motin { 224baa7dd65SAlexander Motin .device_id = 0xb4, 225baa7dd65SAlexander Motin .chip = nct6775, 226baa7dd65SAlexander Motin .descr = "Nuvoton NCT6775", 227baa7dd65SAlexander Motin }, 228baa7dd65SAlexander Motin { 2299e8bad81SXin LI .device_id = 0xc3, 230baa7dd65SAlexander Motin .chip = nct6776, 231baa7dd65SAlexander Motin .descr = "Nuvoton NCT6776", 232baa7dd65SAlexander Motin }, 233baa7dd65SAlexander Motin { 234baa7dd65SAlexander Motin .device_id = 0xc4, 235baa7dd65SAlexander Motin .chip = nct6102, 236baa7dd65SAlexander Motin .descr = "Nuvoton NCT6102", 237baa7dd65SAlexander Motin }, 238baa7dd65SAlexander Motin { 239baa7dd65SAlexander Motin .device_id = 0xc5, 240baa7dd65SAlexander Motin .chip = nct6779, 241baa7dd65SAlexander Motin .descr = "Nuvoton NCT6779", 242baa7dd65SAlexander Motin }, 243baa7dd65SAlexander Motin { 244baa7dd65SAlexander Motin .device_id = 0xc8, 245baa7dd65SAlexander Motin .chip = nct6791, 246baa7dd65SAlexander Motin .descr = "Nuvoton NCT6791", 247baa7dd65SAlexander Motin }, 248baa7dd65SAlexander Motin { 249baa7dd65SAlexander Motin .device_id = 0xc9, 250baa7dd65SAlexander Motin .chip = nct6792, 251baa7dd65SAlexander Motin .descr = "Nuvoton NCT6792", 2529e8bad81SXin LI }, 2530566170fSBjoern A. Zeeb }; 2540566170fSBjoern A. Zeeb 2551adb4ebdSXin LI static void 2561adb4ebdSXin LI write_efir_1(struct wb_softc *sc, u_short baseport, uint8_t value) 2571adb4ebdSXin LI { 2581adb4ebdSXin LI 2591adb4ebdSXin LI MPASS(sc != NULL || baseport != 0); 2601adb4ebdSXin LI if (sc != NULL) 2611adb4ebdSXin LI bus_space_write_1((sc)->bst, (sc)->bsh, 0, (value)); 2621adb4ebdSXin LI else 2631adb4ebdSXin LI outb(baseport, value); 2641adb4ebdSXin LI } 2651adb4ebdSXin LI 2661adb4ebdSXin LI static uint8_t __unused 2671adb4ebdSXin LI read_efir_1(struct wb_softc *sc, u_short baseport) 2681adb4ebdSXin LI { 2691adb4ebdSXin LI 2701adb4ebdSXin LI MPASS(sc != NULL || baseport != 0); 2711adb4ebdSXin LI if (sc != NULL) 2721adb4ebdSXin LI return (bus_space_read_1((sc)->bst, (sc)->bsh, 0)); 2731adb4ebdSXin LI else 2741adb4ebdSXin LI return (inb(baseport)); 2751adb4ebdSXin LI } 2761adb4ebdSXin LI 2771adb4ebdSXin LI static void 2781adb4ebdSXin LI write_efdr_1(struct wb_softc *sc, u_short baseport, uint8_t value) 2791adb4ebdSXin LI { 2801adb4ebdSXin LI 2811adb4ebdSXin LI MPASS(sc != NULL || baseport != 0); 2821adb4ebdSXin LI if (sc != NULL) 2831adb4ebdSXin LI bus_space_write_1((sc)->bst, (sc)->bsh, 1, (value)); 2841adb4ebdSXin LI else 2851adb4ebdSXin LI outb(baseport + 1, value); 2861adb4ebdSXin LI } 2871adb4ebdSXin LI 2881adb4ebdSXin LI static uint8_t 2891adb4ebdSXin LI read_efdr_1(struct wb_softc *sc, u_short baseport) 2901adb4ebdSXin LI { 2911adb4ebdSXin LI 2921adb4ebdSXin LI MPASS(sc != NULL || baseport != 0); 2931adb4ebdSXin LI if (sc != NULL) 2941adb4ebdSXin LI return (bus_space_read_1((sc)->bst, (sc)->bsh, 1)); 2951adb4ebdSXin LI else 2961adb4ebdSXin LI return (inb(baseport + 1)); 2971adb4ebdSXin LI } 2981adb4ebdSXin LI 299baa7dd65SAlexander Motin static void 300baa7dd65SAlexander Motin write_reg(struct wb_softc *sc, uint8_t reg, uint8_t value) 301baa7dd65SAlexander Motin { 302baa7dd65SAlexander Motin 303baa7dd65SAlexander Motin write_efir_1(sc, 0, reg); 304baa7dd65SAlexander Motin write_efdr_1(sc, 0, value); 305baa7dd65SAlexander Motin } 306baa7dd65SAlexander Motin 307baa7dd65SAlexander Motin static uint8_t 308baa7dd65SAlexander Motin read_reg(struct wb_softc *sc, uint8_t reg) 309baa7dd65SAlexander Motin { 310baa7dd65SAlexander Motin 311baa7dd65SAlexander Motin write_efir_1(sc, 0, reg); 312baa7dd65SAlexander Motin return (read_efdr_1(sc, 0)); 313baa7dd65SAlexander Motin } 314baa7dd65SAlexander Motin 3150566170fSBjoern A. Zeeb /* 3160566170fSBjoern A. Zeeb * Return the watchdog related registers as we last read them. This will 3170566170fSBjoern A. Zeeb * usually not give the current timeout or state on whether the watchdog 3180566170fSBjoern A. Zeeb * fired. 3190566170fSBjoern A. Zeeb */ 3200566170fSBjoern A. Zeeb static int 3210566170fSBjoern A. Zeeb sysctl_wb_debug(SYSCTL_HANDLER_ARGS) 3220566170fSBjoern A. Zeeb { 3230566170fSBjoern A. Zeeb struct wb_softc *sc; 3240566170fSBjoern A. Zeeb struct sbuf sb; 3250566170fSBjoern A. Zeeb int error; 3260566170fSBjoern A. Zeeb 3270566170fSBjoern A. Zeeb sc = arg1; 3280566170fSBjoern A. Zeeb 3290566170fSBjoern A. Zeeb sbuf_new_for_sysctl(&sb, NULL, 64, req); 3300566170fSBjoern A. Zeeb 3310566170fSBjoern A. Zeeb sbuf_printf(&sb, "LDN8 (GPIO2, Watchdog): "); 332baa7dd65SAlexander Motin sbuf_printf(&sb, "CR%02X 0x%02x ", sc->ctl_reg, sc->reg_1); 333baa7dd65SAlexander Motin sbuf_printf(&sb, "CR%02X 0x%02x ", sc->time_reg, sc->reg_timeout); 334baa7dd65SAlexander Motin sbuf_printf(&sb, "CR%02X 0x%02x", sc->csr_reg, sc->reg_2); 3350566170fSBjoern A. Zeeb 3360566170fSBjoern A. Zeeb error = sbuf_finish(&sb); 3370566170fSBjoern A. Zeeb sbuf_delete(&sb); 3380566170fSBjoern A. Zeeb return (error); 3390566170fSBjoern A. Zeeb } 3400566170fSBjoern A. Zeeb 3410566170fSBjoern A. Zeeb /* 3420566170fSBjoern A. Zeeb * Read the current values before returning them. Given this might poke 3430566170fSBjoern A. Zeeb * the registers the same time as the watchdog, this sysctl handler should 3440566170fSBjoern A. Zeeb * be marked CTLFLAG_SKIP to not show up by default. 3450566170fSBjoern A. Zeeb */ 3460566170fSBjoern A. Zeeb static int 3470566170fSBjoern A. Zeeb sysctl_wb_debug_current(SYSCTL_HANDLER_ARGS) 3480566170fSBjoern A. Zeeb { 3490566170fSBjoern A. Zeeb struct wb_softc *sc; 3500566170fSBjoern A. Zeeb 3510566170fSBjoern A. Zeeb sc = arg1; 3520566170fSBjoern A. Zeeb 3531adb4ebdSXin LI if ((*sc->ext_cfg_enter_f)(sc, 0) != 0) 3540566170fSBjoern A. Zeeb return (ENXIO); 3550566170fSBjoern A. Zeeb 3560566170fSBjoern A. Zeeb /* Watchdog is configured as part of LDN 8 (GPIO Port2, Watchdog). */ 357baa7dd65SAlexander Motin write_reg(sc, WB_LDN_REG, WB_LDN_REG_LDN8); 3580566170fSBjoern A. Zeeb 359baa7dd65SAlexander Motin sc->reg_1 = read_reg(sc, sc->ctl_reg); 360baa7dd65SAlexander Motin sc->reg_timeout = read_reg(sc, sc->time_reg); 361baa7dd65SAlexander Motin sc->reg_2 = read_reg(sc, sc->csr_reg); 362baa7dd65SAlexander Motin 363baa7dd65SAlexander Motin (*sc->ext_cfg_exit_f)(sc, 0); 3640566170fSBjoern A. Zeeb 3650566170fSBjoern A. Zeeb return (sysctl_wb_debug(oidp, arg1, arg2, req)); 3660566170fSBjoern A. Zeeb } 3670566170fSBjoern A. Zeeb 3680566170fSBjoern A. Zeeb /* 3690566170fSBjoern A. Zeeb * Sysctl handlers to force a watchdog timeout or to test the NMI functionality 3700566170fSBjoern A. Zeeb * works as expetced. 3710566170fSBjoern A. Zeeb * For testing we could set a test_nmi flag in the softc that, in case of NMI, a 3720566170fSBjoern A. Zeeb * callback function from trap.c could check whether we fired and not report the 3730566170fSBjoern A. Zeeb * timeout but clear the flag for the sysctl again. This is interesting given a 3740566170fSBjoern A. Zeeb * lot of boards have jumpers to change the action on watchdog timeout or 3750566170fSBjoern A. Zeeb * disable the watchdog completely. 3760566170fSBjoern A. Zeeb * XXX-BZ notyet: currently no general infrastructure exists to do this. 3770566170fSBjoern A. Zeeb */ 3780566170fSBjoern A. Zeeb static int 3790566170fSBjoern A. Zeeb sysctl_wb_force_test_nmi(SYSCTL_HANDLER_ARGS) 3800566170fSBjoern A. Zeeb { 3810566170fSBjoern A. Zeeb struct wb_softc *sc; 3820566170fSBjoern A. Zeeb int error, test, val; 3830566170fSBjoern A. Zeeb 3840566170fSBjoern A. Zeeb sc = arg1; 3850566170fSBjoern A. Zeeb test = arg2; 3860566170fSBjoern A. Zeeb 3870566170fSBjoern A. Zeeb #ifdef notyet 3880566170fSBjoern A. Zeeb val = sc->test_nmi; 3890566170fSBjoern A. Zeeb #else 3900566170fSBjoern A. Zeeb val = 0; 3910566170fSBjoern A. Zeeb #endif 3920566170fSBjoern A. Zeeb error = sysctl_handle_int(oidp, &val, 0, req); 3930566170fSBjoern A. Zeeb if (error || !req->newptr) 3940566170fSBjoern A. Zeeb return (error); 3950566170fSBjoern A. Zeeb 3960566170fSBjoern A. Zeeb #ifdef notyet 3970566170fSBjoern A. Zeeb /* Manually clear the test for a value of 0 and do nothing else. */ 3980566170fSBjoern A. Zeeb if (test && val == 0) { 3990566170fSBjoern A. Zeeb sc->test_nmi = 0; 4000566170fSBjoern A. Zeeb return (0); 4010566170fSBjoern A. Zeeb } 4020566170fSBjoern A. Zeeb #endif 4030566170fSBjoern A. Zeeb 4041adb4ebdSXin LI if ((*sc->ext_cfg_enter_f)(sc, 0) != 0) 4050566170fSBjoern A. Zeeb return (ENXIO); 4060566170fSBjoern A. Zeeb 4070566170fSBjoern A. Zeeb #ifdef notyet 4080566170fSBjoern A. Zeeb /* 4090566170fSBjoern A. Zeeb * If we are testing the NMI functionality, set the flag before 4100566170fSBjoern A. Zeeb * forcing the timeout. 4110566170fSBjoern A. Zeeb */ 4120566170fSBjoern A. Zeeb if (test) 4130566170fSBjoern A. Zeeb sc->test_nmi = 1; 4140566170fSBjoern A. Zeeb #endif 4150566170fSBjoern A. Zeeb 4160566170fSBjoern A. Zeeb /* Watchdog is configured as part of LDN 8 (GPIO Port2, Watchdog). */ 417baa7dd65SAlexander Motin write_reg(sc, WB_LDN_REG, WB_LDN_REG_LDN8); 4180566170fSBjoern A. Zeeb 4190566170fSBjoern A. Zeeb /* Force watchdog to fire. */ 420baa7dd65SAlexander Motin sc->reg_2 = read_reg(sc, sc->csr_reg); 4210566170fSBjoern A. Zeeb sc->reg_2 |= WB_LDN8_CRF7_FORCE; 422baa7dd65SAlexander Motin write_reg(sc, sc->csr_reg, sc->reg_2); 4230566170fSBjoern A. Zeeb 424baa7dd65SAlexander Motin (*sc->ext_cfg_exit_f)(sc, 0); 4250566170fSBjoern A. Zeeb 4260566170fSBjoern A. Zeeb return (0); 4270566170fSBjoern A. Zeeb } 4280566170fSBjoern A. Zeeb 4290566170fSBjoern A. Zeeb /* 4300566170fSBjoern A. Zeeb * Print current watchdog state. 4310566170fSBjoern A. Zeeb * 4320566170fSBjoern A. Zeeb * Note: it is the responsibility of the caller to update the registers 4330566170fSBjoern A. Zeeb * upfront. 4340566170fSBjoern A. Zeeb */ 4350566170fSBjoern A. Zeeb static void 4360566170fSBjoern A. Zeeb wb_print_state(struct wb_softc *sc, const char *msg) 4370566170fSBjoern A. Zeeb { 4380566170fSBjoern A. Zeeb 4390566170fSBjoern A. Zeeb device_printf(sc->dev, "%s%sWatchdog %sabled. %s" 4400566170fSBjoern A. Zeeb "Scaling by %ds, timer at %d (%s=%ds%s). " 4410566170fSBjoern A. Zeeb "CRF5 0x%02x CRF7 0x%02x\n", 4420566170fSBjoern A. Zeeb (msg != NULL) ? msg : "", (msg != NULL) ? ": " : "", 4430566170fSBjoern A. Zeeb (sc->reg_timeout > 0x00) ? "en" : "dis", 4440566170fSBjoern A. Zeeb (sc->reg_2 & WB_LDN8_CRF7_TS) ? "Watchdog fired. " : "", 4450566170fSBjoern A. Zeeb (sc->reg_1 & WB_LDN8_CRF5_SCALE) ? 60 : 1, 4460566170fSBjoern A. Zeeb sc->reg_timeout, 4470566170fSBjoern A. Zeeb (sc->reg_timeout > 0x00) ? "<" : "", 4480566170fSBjoern A. Zeeb sc->reg_timeout * ((sc->reg_1 & WB_LDN8_CRF5_SCALE) ? 60 : 1), 4490566170fSBjoern A. Zeeb (sc->reg_timeout > 0x00) ? " left" : "", 4500566170fSBjoern A. Zeeb sc->reg_1, sc->reg_2); 4510566170fSBjoern A. Zeeb } 4520566170fSBjoern A. Zeeb 4530566170fSBjoern A. Zeeb /* 4540566170fSBjoern A. Zeeb * Functions to enter and exit extended function mode. Possibly shared 4550566170fSBjoern A. Zeeb * between different chips. 4560566170fSBjoern A. Zeeb */ 4570566170fSBjoern A. Zeeb static int 4581adb4ebdSXin LI ext_cfg_enter_0x87_0x87(struct wb_softc *sc, u_short baseport) 4590566170fSBjoern A. Zeeb { 4600566170fSBjoern A. Zeeb 4610566170fSBjoern A. Zeeb /* 4620566170fSBjoern A. Zeeb * Enable extended function mode. 4630566170fSBjoern A. Zeeb * Winbond does not allow us to validate so always return success. 4640566170fSBjoern A. Zeeb */ 4651adb4ebdSXin LI write_efir_1(sc, baseport, 0x87); 4661adb4ebdSXin LI write_efir_1(sc, baseport, 0x87); 4670566170fSBjoern A. Zeeb 4680566170fSBjoern A. Zeeb return (0); 4690566170fSBjoern A. Zeeb } 4700566170fSBjoern A. Zeeb 4710566170fSBjoern A. Zeeb static void 4721adb4ebdSXin LI ext_cfg_exit_0xaa(struct wb_softc *sc, u_short baseport) 4730566170fSBjoern A. Zeeb { 4740566170fSBjoern A. Zeeb 4751adb4ebdSXin LI write_efir_1(sc, baseport, 0xaa); 4760566170fSBjoern A. Zeeb } 4770566170fSBjoern A. Zeeb 4780566170fSBjoern A. Zeeb /* 4790566170fSBjoern A. Zeeb * (Re)load the watchdog counter depending on timeout. A timeout of 0 will 4800566170fSBjoern A. Zeeb * disable the watchdog. 4810566170fSBjoern A. Zeeb */ 4820566170fSBjoern A. Zeeb static int 4830566170fSBjoern A. Zeeb wb_set_watchdog(struct wb_softc *sc, unsigned int timeout) 4840566170fSBjoern A. Zeeb { 4850566170fSBjoern A. Zeeb 486baa7dd65SAlexander Motin if (timeout != 0) { 4870566170fSBjoern A. Zeeb /* 4880566170fSBjoern A. Zeeb * In case an override is set, let it override. It may lead 4890566170fSBjoern A. Zeeb * to strange results as we do not check the input of the sysctl. 4900566170fSBjoern A. Zeeb */ 4910566170fSBjoern A. Zeeb if (sc->timeout_override > 0) 4920566170fSBjoern A. Zeeb timeout = sc->timeout_override; 4930566170fSBjoern A. Zeeb 4940566170fSBjoern A. Zeeb /* Make sure we support the requested timeout. */ 4950566170fSBjoern A. Zeeb if (timeout > 255 * 60) 4960566170fSBjoern A. Zeeb return (EINVAL); 497baa7dd65SAlexander Motin } 4980566170fSBjoern A. Zeeb 499baa7dd65SAlexander Motin if (sc->debug_verbose) 500baa7dd65SAlexander Motin wb_print_state(sc, "Before watchdog counter (re)load"); 501baa7dd65SAlexander Motin 502baa7dd65SAlexander Motin if ((*sc->ext_cfg_enter_f)(sc, 0) != 0) 503baa7dd65SAlexander Motin return (ENXIO); 504baa7dd65SAlexander Motin 505baa7dd65SAlexander Motin /* Watchdog is configured as part of LDN 8 (GPIO Port2, Watchdog) */ 506baa7dd65SAlexander Motin write_reg(sc, WB_LDN_REG, WB_LDN_REG_LDN8); 507baa7dd65SAlexander Motin 508baa7dd65SAlexander Motin /* Disable and validate or arm/reset watchdog. */ 509baa7dd65SAlexander Motin if (timeout == 0) { 510baa7dd65SAlexander Motin /* Disable watchdog. */ 511baa7dd65SAlexander Motin write_reg(sc, sc->time_reg, 0x00); 512baa7dd65SAlexander Motin sc->reg_timeout = read_reg(sc, sc->time_reg); 513baa7dd65SAlexander Motin (*sc->ext_cfg_exit_f)(sc, 0); 514baa7dd65SAlexander Motin 515baa7dd65SAlexander Motin /* Re-check. */ 516baa7dd65SAlexander Motin if (sc->reg_timeout != 0x00) { 517baa7dd65SAlexander Motin device_printf(sc->dev, "Failed to disable watchdog: " 518baa7dd65SAlexander Motin "0x%02x.\n", sc->reg_timeout); 519baa7dd65SAlexander Motin return (EIO); 520baa7dd65SAlexander Motin } 521baa7dd65SAlexander Motin 522baa7dd65SAlexander Motin } else { 5230566170fSBjoern A. Zeeb /* Read current scaling factor. */ 524baa7dd65SAlexander Motin sc->reg_1 = read_reg(sc, sc->ctl_reg); 5250566170fSBjoern A. Zeeb 5260566170fSBjoern A. Zeeb if (timeout > 255) { 5270566170fSBjoern A. Zeeb /* Set scaling factor to 60s. */ 5280566170fSBjoern A. Zeeb sc->reg_1 |= WB_LDN8_CRF5_SCALE; 5290566170fSBjoern A. Zeeb sc->reg_timeout = (timeout / 60); 5300566170fSBjoern A. Zeeb if (timeout % 60) 5310566170fSBjoern A. Zeeb sc->reg_timeout++; 5320566170fSBjoern A. Zeeb } else { 5330566170fSBjoern A. Zeeb /* Set scaling factor to 1s. */ 5340566170fSBjoern A. Zeeb sc->reg_1 &= ~WB_LDN8_CRF5_SCALE; 5350566170fSBjoern A. Zeeb sc->reg_timeout = timeout; 5360566170fSBjoern A. Zeeb } 5370566170fSBjoern A. Zeeb 5380566170fSBjoern A. Zeeb /* In case we fired before we need to clear to fire again. */ 539baa7dd65SAlexander Motin sc->reg_2 = read_reg(sc, sc->csr_reg); 5400566170fSBjoern A. Zeeb if (sc->reg_2 & WB_LDN8_CRF7_TS) { 5410566170fSBjoern A. Zeeb sc->reg_2 &= ~WB_LDN8_CRF7_TS; 542baa7dd65SAlexander Motin write_reg(sc, sc->csr_reg, sc->reg_2); 5430566170fSBjoern A. Zeeb } 5440566170fSBjoern A. Zeeb 5450566170fSBjoern A. Zeeb /* Write back scaling factor. */ 546baa7dd65SAlexander Motin write_reg(sc, sc->ctl_reg, sc->reg_1); 5470566170fSBjoern A. Zeeb 5480566170fSBjoern A. Zeeb /* Set timer and arm/reset the watchdog. */ 549baa7dd65SAlexander Motin write_reg(sc, sc->time_reg, sc->reg_timeout); 550baa7dd65SAlexander Motin (*sc->ext_cfg_exit_f)(sc, 0); 5510566170fSBjoern A. Zeeb } 5520566170fSBjoern A. Zeeb 5530566170fSBjoern A. Zeeb if (sc->debug_verbose) 5540566170fSBjoern A. Zeeb wb_print_state(sc, "After watchdog counter (re)load"); 5550566170fSBjoern A. Zeeb 5560566170fSBjoern A. Zeeb return (0); 5570566170fSBjoern A. Zeeb } 5580566170fSBjoern A. Zeeb 5590566170fSBjoern A. Zeeb /* 5600566170fSBjoern A. Zeeb * watchdog(9) EVENTHANDLER function implementation to (re)load the counter 5610566170fSBjoern A. Zeeb * with the given timeout or disable the watchdog. 5620566170fSBjoern A. Zeeb */ 5630566170fSBjoern A. Zeeb static void 5640566170fSBjoern A. Zeeb wb_watchdog_fn(void *private, u_int cmd, int *error) 5650566170fSBjoern A. Zeeb { 5660566170fSBjoern A. Zeeb struct wb_softc *sc; 5670566170fSBjoern A. Zeeb unsigned int timeout; 5680566170fSBjoern A. Zeeb int e; 5690566170fSBjoern A. Zeeb 5700566170fSBjoern A. Zeeb sc = private; 5710566170fSBjoern A. Zeeb KASSERT(sc != NULL, ("%s: watchdog handler function called without " 5720566170fSBjoern A. Zeeb "softc.", __func__)); 5730566170fSBjoern A. Zeeb 5740566170fSBjoern A. Zeeb cmd &= WD_INTERVAL; 5750566170fSBjoern A. Zeeb if (cmd > 0 && cmd <= 63) { 5760566170fSBjoern A. Zeeb /* Reset (and arm) watchdog. */ 5770566170fSBjoern A. Zeeb timeout = ((uint64_t)1 << cmd) / 1000000000; 5780566170fSBjoern A. Zeeb if (timeout == 0) 5790566170fSBjoern A. Zeeb timeout = 1; 5800566170fSBjoern A. Zeeb e = wb_set_watchdog(sc, timeout); 5810566170fSBjoern A. Zeeb if (e == 0) { 5820566170fSBjoern A. Zeeb if (error != NULL) 5830566170fSBjoern A. Zeeb *error = 0; 5840566170fSBjoern A. Zeeb } else { 5850566170fSBjoern A. Zeeb /* On error, try to make sure the WD is disabled. */ 5860566170fSBjoern A. Zeeb wb_set_watchdog(sc, 0); 5870566170fSBjoern A. Zeeb } 5880566170fSBjoern A. Zeeb 5890566170fSBjoern A. Zeeb } else { 5900566170fSBjoern A. Zeeb /* Disable watchdog. */ 5910566170fSBjoern A. Zeeb e = wb_set_watchdog(sc, 0); 5920566170fSBjoern A. Zeeb if (e != 0 && cmd == 0 && error != NULL) { 5930566170fSBjoern A. Zeeb /* Failed to disable watchdog. */ 5940566170fSBjoern A. Zeeb *error = EOPNOTSUPP; 5950566170fSBjoern A. Zeeb } 5960566170fSBjoern A. Zeeb } 5970566170fSBjoern A. Zeeb } 5980566170fSBjoern A. Zeeb 5990566170fSBjoern A. Zeeb /* 6000566170fSBjoern A. Zeeb * Probe/attach the Winbond Super I/O chip. 6010566170fSBjoern A. Zeeb * 6020566170fSBjoern A. Zeeb * Initial abstraction to possibly support more chips: 6030566170fSBjoern A. Zeeb * - Iterate over the well known base ports, try to enable extended function 6040566170fSBjoern A. Zeeb * mode and read and match the device ID and device revision. Unfortunately 6050566170fSBjoern A. Zeeb * the Vendor ID is in the hardware monitoring section accessible by different 6060566170fSBjoern A. Zeeb * base ports only. 6070566170fSBjoern A. Zeeb * - Also HEFRAS, which would tell use the base port, is only accessible after 6080566170fSBjoern A. Zeeb * entering extended function mode, for which the base port is needed. 6090566170fSBjoern A. Zeeb * At least check HEFRAS to match the current base port we are probing. 6100566170fSBjoern A. Zeeb * - On match set the description, remember functions to enter/exit extended 6110566170fSBjoern A. Zeeb * function mode as well as the base port. 6120566170fSBjoern A. Zeeb */ 6130566170fSBjoern A. Zeeb static int 6140566170fSBjoern A. Zeeb wb_probe_enable(device_t dev, int probe) 6150566170fSBjoern A. Zeeb { 6160566170fSBjoern A. Zeeb struct wb_softc *sc; 6170566170fSBjoern A. Zeeb int error, found, i, j; 6180566170fSBjoern A. Zeeb uint8_t dev_id, dev_rev, cr26; 619baa7dd65SAlexander Motin char buf[128]; 6200566170fSBjoern A. Zeeb 6211adb4ebdSXin LI if (dev == NULL) 6221adb4ebdSXin LI sc = NULL; 6231adb4ebdSXin LI else { 6240566170fSBjoern A. Zeeb sc = device_get_softc(dev); 6250566170fSBjoern A. Zeeb bzero(sc, sizeof(*sc)); 6260566170fSBjoern A. Zeeb sc->dev = dev; 6271adb4ebdSXin LI } 6280566170fSBjoern A. Zeeb 6290566170fSBjoern A. Zeeb error = ENXIO; 630baa7dd65SAlexander Motin found = 0; 6310566170fSBjoern A. Zeeb for (i = 0; i < sizeof(probe_addrs) / sizeof(*probe_addrs); i++) { 6320566170fSBjoern A. Zeeb 6331adb4ebdSXin LI if (sc != NULL) { 6340566170fSBjoern A. Zeeb /* Allocate bus resources for IO index/data register access. */ 6350566170fSBjoern A. Zeeb sc->portres = bus_alloc_resource(dev, SYS_RES_IOPORT, &sc->rid, 6360566170fSBjoern A. Zeeb probe_addrs[i].efer, probe_addrs[i].efer + 1, 2, RF_ACTIVE); 6370566170fSBjoern A. Zeeb if (sc->portres == NULL) 6380566170fSBjoern A. Zeeb continue; 6390566170fSBjoern A. Zeeb sc->bst = rman_get_bustag(sc->portres); 6400566170fSBjoern A. Zeeb sc->bsh = rman_get_bushandle(sc->portres); 6411adb4ebdSXin LI } 6420566170fSBjoern A. Zeeb 6431adb4ebdSXin LI error = (*probe_addrs[i].ext_cfg_enter_f)(sc, probe_addrs[i].efer); 6440566170fSBjoern A. Zeeb if (error != 0) 6450566170fSBjoern A. Zeeb goto cleanup; 6460566170fSBjoern A. Zeeb 6470566170fSBjoern A. Zeeb /* Identify the SuperIO chip. */ 6481adb4ebdSXin LI write_efir_1(sc, probe_addrs[i].efer, WB_DEVICE_ID_REG); 6491adb4ebdSXin LI dev_id = read_efdr_1(sc, probe_addrs[i].efer); 6501adb4ebdSXin LI write_efir_1(sc, probe_addrs[i].efer, WB_DEVICE_REV_REG); 6511adb4ebdSXin LI dev_rev = read_efdr_1(sc, probe_addrs[i].efer); 6521adb4ebdSXin LI write_efir_1(sc, probe_addrs[i].efer, WB_CR26); 6531adb4ebdSXin LI cr26 = read_efdr_1(sc, probe_addrs[i].efer); 6540566170fSBjoern A. Zeeb 655baa7dd65SAlexander Motin if (dev_id == 0xff && dev_rev == 0xff) 656baa7dd65SAlexander Motin goto cleanup; 657baa7dd65SAlexander Motin 6580566170fSBjoern A. Zeeb /* HEFRAS of 0 means EFER at 0x2e, 1 means EFER at 0x4e. */ 6590566170fSBjoern A. Zeeb if (((cr26 & 0x40) == 0x00 && probe_addrs[i].efer != 0x2e) || 6600566170fSBjoern A. Zeeb ((cr26 & 0x40) == 0x40 && probe_addrs[i].efer != 0x4e)) { 6611adb4ebdSXin LI if (dev != NULL) 6621adb4ebdSXin LI device_printf(dev, "HEFRAS and EFER do not " 6631adb4ebdSXin LI "align: EFER 0x%02x DevID 0x%02x DevRev " 6641adb4ebdSXin LI "0x%02x CR26 0x%02x\n", 6650566170fSBjoern A. Zeeb probe_addrs[i].efer, dev_id, dev_rev, cr26); 6660566170fSBjoern A. Zeeb goto cleanup; 6670566170fSBjoern A. Zeeb } 6680566170fSBjoern A. Zeeb 6690566170fSBjoern A. Zeeb for (j = 0; j < sizeof(wb_devs) / sizeof(*wb_devs); j++) { 670baa7dd65SAlexander Motin if (wb_devs[j].device_id == dev_id) { 671baa7dd65SAlexander Motin found = 1; 6720566170fSBjoern A. Zeeb break; 6730566170fSBjoern A. Zeeb } 6740566170fSBjoern A. Zeeb } 6759e8bad81SXin LI 6769e8bad81SXin LI if (probe && dev != NULL) { 677baa7dd65SAlexander Motin snprintf(buf, sizeof(buf), 678baa7dd65SAlexander Motin "%s (0x%02x/0x%02x) Watchdog Timer", 679baa7dd65SAlexander Motin found ? wb_devs[j].descr : 680baa7dd65SAlexander Motin "Unknown Winbond/Nuvoton", dev_id, dev_rev); 681baa7dd65SAlexander Motin device_set_desc_copy(dev, buf); 6829e8bad81SXin LI } 6839e8bad81SXin LI 684baa7dd65SAlexander Motin /* If this is hinted attach, try to guess the model. */ 685baa7dd65SAlexander Motin if (dev != NULL && !found) { 686baa7dd65SAlexander Motin found = 1; 687baa7dd65SAlexander Motin j = 0; 688baa7dd65SAlexander Motin } 689baa7dd65SAlexander Motin 6900566170fSBjoern A. Zeeb cleanup: 6910566170fSBjoern A. Zeeb if (probe || !found) { 6921adb4ebdSXin LI (*probe_addrs[i].ext_cfg_exit_f)(sc, probe_addrs[i].efer); 6931adb4ebdSXin LI if (sc != NULL) 6941adb4ebdSXin LI (void) bus_release_resource(dev, SYS_RES_IOPORT, 6951adb4ebdSXin LI sc->rid, sc->portres); 6960566170fSBjoern A. Zeeb } 6970566170fSBjoern A. Zeeb 6980566170fSBjoern A. Zeeb /* 6990566170fSBjoern A. Zeeb * Stop probing if have successfully identified the SuperIO. 7000566170fSBjoern A. Zeeb * Remember the extended function mode enter/exit functions 7010566170fSBjoern A. Zeeb * for operations. 7020566170fSBjoern A. Zeeb */ 7030566170fSBjoern A. Zeeb if (found) { 7041adb4ebdSXin LI if (sc != NULL) { 7050566170fSBjoern A. Zeeb sc->ext_cfg_enter_f = probe_addrs[i].ext_cfg_enter_f; 7060566170fSBjoern A. Zeeb sc->ext_cfg_exit_f = probe_addrs[i].ext_cfg_exit_f; 707baa7dd65SAlexander Motin sc->chip = wb_devs[j].chip; 708baa7dd65SAlexander Motin sc->ctl_reg = 0xf5; 709baa7dd65SAlexander Motin sc->time_reg = 0xf6; 710baa7dd65SAlexander Motin sc->csr_reg = 0xf7; 711baa7dd65SAlexander Motin if (sc->chip == w83697hf || 712baa7dd65SAlexander Motin sc->chip == w83697ug) { 713baa7dd65SAlexander Motin sc->ctl_reg = 0xf3; 714baa7dd65SAlexander Motin sc->time_reg = 0xf4; 715baa7dd65SAlexander Motin } else if (sc->chip == nct6102) { 716baa7dd65SAlexander Motin sc->ctl_reg = 0xf0; 717baa7dd65SAlexander Motin sc->time_reg = 0xf1; 718baa7dd65SAlexander Motin sc->csr_reg = 0xf2; 7191adb4ebdSXin LI } 720baa7dd65SAlexander Motin } 721baa7dd65SAlexander Motin return (BUS_PROBE_SPECIFIC); 7220566170fSBjoern A. Zeeb } else 7230566170fSBjoern A. Zeeb error = ENXIO; 7240566170fSBjoern A. Zeeb } 7250566170fSBjoern A. Zeeb 7260566170fSBjoern A. Zeeb return (error); 7270566170fSBjoern A. Zeeb } 7280566170fSBjoern A. Zeeb 7291adb4ebdSXin LI static void 7301adb4ebdSXin LI wb_identify(driver_t *driver, device_t parent) 7311adb4ebdSXin LI { 7321adb4ebdSXin LI 733baa7dd65SAlexander Motin if (device_find_child(parent, driver->name, 0) == NULL) { 734baa7dd65SAlexander Motin if (wb_probe_enable(NULL, 1) <= 0) 735baa7dd65SAlexander Motin BUS_ADD_CHILD(parent, 0, driver->name, 0); 7361adb4ebdSXin LI } 7371adb4ebdSXin LI } 7381adb4ebdSXin LI 7390566170fSBjoern A. Zeeb static int 7400566170fSBjoern A. Zeeb wb_probe(device_t dev) 7410566170fSBjoern A. Zeeb { 7420566170fSBjoern A. Zeeb 7430566170fSBjoern A. Zeeb /* Make sure we do not claim some ISA PNP device. */ 7440566170fSBjoern A. Zeeb if (isa_get_logicalid(dev) != 0) 7450566170fSBjoern A. Zeeb return (ENXIO); 7460566170fSBjoern A. Zeeb 7470566170fSBjoern A. Zeeb return (wb_probe_enable(dev, 1)); 7480566170fSBjoern A. Zeeb } 7490566170fSBjoern A. Zeeb 7500566170fSBjoern A. Zeeb static int 7510566170fSBjoern A. Zeeb wb_attach(device_t dev) 7520566170fSBjoern A. Zeeb { 7530566170fSBjoern A. Zeeb struct wb_softc *sc; 7540566170fSBjoern A. Zeeb struct sysctl_ctx_list *sctx; 7550566170fSBjoern A. Zeeb struct sysctl_oid *soid; 7560566170fSBjoern A. Zeeb unsigned long timeout; 7570566170fSBjoern A. Zeeb int error; 758baa7dd65SAlexander Motin uint8_t t; 7590566170fSBjoern A. Zeeb 7600566170fSBjoern A. Zeeb error = wb_probe_enable(dev, 0); 7610566170fSBjoern A. Zeeb if (error > 0) 7620566170fSBjoern A. Zeeb return (ENXIO); 7630566170fSBjoern A. Zeeb 7640566170fSBjoern A. Zeeb sc = device_get_softc(dev); 7650566170fSBjoern A. Zeeb KASSERT(sc->ext_cfg_enter_f != NULL && sc->ext_cfg_exit_f != NULL, 7660566170fSBjoern A. Zeeb ("%s: successfull probe result but not setup correctly", __func__)); 7670566170fSBjoern A. Zeeb 7680566170fSBjoern A. Zeeb /* Watchdog is configured as part of LDN 8 (GPIO Port2, Watchdog). */ 769baa7dd65SAlexander Motin write_reg(sc, WB_LDN_REG, WB_LDN_REG_LDN8); 7700566170fSBjoern A. Zeeb 771baa7dd65SAlexander Motin /* Make sure WDT is enabled. */ 772baa7dd65SAlexander Motin write_reg(sc, WB_LDN8_CR30, 773baa7dd65SAlexander Motin read_reg(sc, WB_LDN8_CR30) | WB_LDN8_CR30_ACTIVE); 774baa7dd65SAlexander Motin 775baa7dd65SAlexander Motin switch (sc->chip) { 776baa7dd65SAlexander Motin case w83627hf: 777baa7dd65SAlexander Motin case w83627s: 778baa7dd65SAlexander Motin t = read_reg(sc, 0x2B) & ~0x10; 779baa7dd65SAlexander Motin write_reg(sc, 0x2B, t); /* set GPIO24 to WDT0 */ 780baa7dd65SAlexander Motin break; 781baa7dd65SAlexander Motin case w83697hf: 782baa7dd65SAlexander Motin /* Set pin 119 to WDTO# mode (= CR29, WDT0) */ 783baa7dd65SAlexander Motin t = read_reg(sc, 0x29) & ~0x60; 784baa7dd65SAlexander Motin t |= 0x20; 785baa7dd65SAlexander Motin write_reg(sc, 0x29, t); 786baa7dd65SAlexander Motin break; 787baa7dd65SAlexander Motin case w83697ug: 788baa7dd65SAlexander Motin /* Set pin 118 to WDTO# mode */ 789baa7dd65SAlexander Motin t = read_reg(sc, 0x2b) & ~0x04; 790baa7dd65SAlexander Motin write_reg(sc, 0x2b, t); 791baa7dd65SAlexander Motin break; 792baa7dd65SAlexander Motin case w83627thf: 793baa7dd65SAlexander Motin t = (read_reg(sc, 0x2B) & ~0x08) | 0x04; 794baa7dd65SAlexander Motin write_reg(sc, 0x2B, t); /* set GPIO3 to WDT0 */ 795baa7dd65SAlexander Motin break; 796baa7dd65SAlexander Motin case w83627dhg: 797baa7dd65SAlexander Motin case w83627dhg_p: 798baa7dd65SAlexander Motin t = read_reg(sc, 0x2D) & ~0x01; /* PIN77 -> WDT0# */ 799baa7dd65SAlexander Motin write_reg(sc, 0x2D, t); /* set GPIO5 to WDT0 */ 800baa7dd65SAlexander Motin t = read_reg(sc, sc->ctl_reg); 801baa7dd65SAlexander Motin t |= 0x02; /* enable the WDTO# output low pulse 802baa7dd65SAlexander Motin * to the KBRST# pin */ 803baa7dd65SAlexander Motin write_reg(sc, sc->ctl_reg, t); 804baa7dd65SAlexander Motin break; 805baa7dd65SAlexander Motin case w83637hf: 806baa7dd65SAlexander Motin break; 807baa7dd65SAlexander Motin case w83687thf: 808baa7dd65SAlexander Motin t = read_reg(sc, 0x2C) & ~0x80; /* PIN47 -> WDT0# */ 809baa7dd65SAlexander Motin write_reg(sc, 0x2C, t); 810baa7dd65SAlexander Motin break; 811baa7dd65SAlexander Motin case w83627ehf: 812baa7dd65SAlexander Motin case w83627uhg: 813baa7dd65SAlexander Motin case w83667hg: 814baa7dd65SAlexander Motin case w83667hg_b: 815baa7dd65SAlexander Motin case nct6775: 816baa7dd65SAlexander Motin case nct6776: 817baa7dd65SAlexander Motin case nct6779: 818baa7dd65SAlexander Motin case nct6791: 819baa7dd65SAlexander Motin case nct6792: 820baa7dd65SAlexander Motin case nct6102: 821baa7dd65SAlexander Motin /* 822baa7dd65SAlexander Motin * These chips have a fixed WDTO# output pin (W83627UHG), 823baa7dd65SAlexander Motin * or support more than one WDTO# output pin. 824baa7dd65SAlexander Motin * Don't touch its configuration, and hope the BIOS 825baa7dd65SAlexander Motin * does the right thing. 826baa7dd65SAlexander Motin */ 827baa7dd65SAlexander Motin t = read_reg(sc, sc->ctl_reg); 828baa7dd65SAlexander Motin t |= 0x02; /* enable the WDTO# output low pulse 829baa7dd65SAlexander Motin * to the KBRST# pin */ 830baa7dd65SAlexander Motin write_reg(sc, sc->ctl_reg, t); 831baa7dd65SAlexander Motin break; 832baa7dd65SAlexander Motin default: 833baa7dd65SAlexander Motin break; 834baa7dd65SAlexander Motin } 8350566170fSBjoern A. Zeeb 8360566170fSBjoern A. Zeeb /* Read the current watchdog configuration. */ 837baa7dd65SAlexander Motin sc->reg_1 = read_reg(sc, sc->ctl_reg); 838baa7dd65SAlexander Motin sc->reg_timeout = read_reg(sc, sc->time_reg); 839baa7dd65SAlexander Motin sc->reg_2 = read_reg(sc, sc->csr_reg); 8400566170fSBjoern A. Zeeb 8410566170fSBjoern A. Zeeb /* Print current state if bootverbose or watchdog already enabled. */ 8420566170fSBjoern A. Zeeb if (bootverbose || (sc->reg_timeout > 0x00)) 8430566170fSBjoern A. Zeeb wb_print_state(sc, "Before watchdog attach"); 8440566170fSBjoern A. Zeeb 845baa7dd65SAlexander Motin sc->reg_1 &= ~WB_LDN8_CRF5_KEYB_P20; 846cf864f03SRobert Noland sc->reg_1 |= WB_LDN8_CRF5_KBRST; 847baa7dd65SAlexander Motin write_reg(sc, sc->ctl_reg, sc->reg_1); 8480566170fSBjoern A. Zeeb 849baa7dd65SAlexander Motin /* 850baa7dd65SAlexander Motin * Clear a previous watchdog timeout event (if still set). 851baa7dd65SAlexander Motin * Disable timer reset on mouse interrupts. Leave reset on keyboard, 852baa7dd65SAlexander Motin * since one of my boards is getting stuck in reboot without it. 853baa7dd65SAlexander Motin */ 854baa7dd65SAlexander Motin sc->reg_2 &= ~(WB_LDN8_CRF7_MOUSE|WB_LDN8_CRF7_TS); 855baa7dd65SAlexander Motin write_reg(sc, sc->csr_reg, sc->reg_2); 856baa7dd65SAlexander Motin 857baa7dd65SAlexander Motin (*sc->ext_cfg_exit_f)(sc, 0); 8580566170fSBjoern A. Zeeb 8590566170fSBjoern A. Zeeb /* Read global timeout override tunable, Add per device sysctls. */ 8600566170fSBjoern A. Zeeb if (TUNABLE_ULONG_FETCH("hw.wbwd.timeout_override", &timeout)) { 8610566170fSBjoern A. Zeeb if (timeout > 0) 8620566170fSBjoern A. Zeeb sc->timeout_override = timeout; 8630566170fSBjoern A. Zeeb } 8640566170fSBjoern A. Zeeb sctx = device_get_sysctl_ctx(dev); 8650566170fSBjoern A. Zeeb soid = device_get_sysctl_tree(dev); 8660566170fSBjoern A. Zeeb SYSCTL_ADD_UINT(sctx, SYSCTL_CHILDREN(soid), OID_AUTO, 8670566170fSBjoern A. Zeeb "timeout_override", CTLFLAG_RW, &sc->timeout_override, 0, 8680566170fSBjoern A. Zeeb "Timeout in seconds overriding default watchdog timeout"); 8690566170fSBjoern A. Zeeb SYSCTL_ADD_INT(sctx, SYSCTL_CHILDREN(soid), OID_AUTO, 8700566170fSBjoern A. Zeeb "debug_verbose", CTLFLAG_RW, &sc->debug_verbose, 0, 8710566170fSBjoern A. Zeeb "Enables extra debugging information"); 8720566170fSBjoern A. Zeeb SYSCTL_ADD_PROC(sctx, SYSCTL_CHILDREN(soid), OID_AUTO, "debug", 8730566170fSBjoern A. Zeeb CTLTYPE_STRING|CTLFLAG_RD, sc, 0, sysctl_wb_debug, "A", 8740566170fSBjoern A. Zeeb "Selected register information from last change by driver"); 8750566170fSBjoern A. Zeeb SYSCTL_ADD_PROC(sctx, SYSCTL_CHILDREN(soid), OID_AUTO, "debug_current", 8760566170fSBjoern A. Zeeb CTLTYPE_STRING|CTLFLAG_RD|CTLFLAG_SKIP, sc, 0, 8770566170fSBjoern A. Zeeb sysctl_wb_debug_current, "A", 8780566170fSBjoern A. Zeeb "Selected register information (may interfere)"); 8790566170fSBjoern A. Zeeb SYSCTL_ADD_PROC(sctx, SYSCTL_CHILDREN(soid), OID_AUTO, "force_timeout", 8800566170fSBjoern A. Zeeb CTLTYPE_INT|CTLFLAG_RW|CTLFLAG_SKIP, sc, 0, 8810566170fSBjoern A. Zeeb sysctl_wb_force_test_nmi, "I", "Enable to force watchdog to fire."); 8820566170fSBjoern A. Zeeb 8830566170fSBjoern A. Zeeb /* Register watchdog. */ 8840566170fSBjoern A. Zeeb sc->ev_tag = EVENTHANDLER_REGISTER(watchdog_list, wb_watchdog_fn, sc, 8850566170fSBjoern A. Zeeb 0); 8860566170fSBjoern A. Zeeb 8870566170fSBjoern A. Zeeb if (bootverbose) 8880566170fSBjoern A. Zeeb wb_print_state(sc, "After watchdog attach"); 8890566170fSBjoern A. Zeeb 8900566170fSBjoern A. Zeeb return (0); 8910566170fSBjoern A. Zeeb } 8920566170fSBjoern A. Zeeb 8930566170fSBjoern A. Zeeb static int 8940566170fSBjoern A. Zeeb wb_detach(device_t dev) 8950566170fSBjoern A. Zeeb { 8960566170fSBjoern A. Zeeb struct wb_softc *sc; 8970566170fSBjoern A. Zeeb 8980566170fSBjoern A. Zeeb sc = device_get_softc(dev); 8990566170fSBjoern A. Zeeb 9000566170fSBjoern A. Zeeb /* Unregister and stop the watchdog if running. */ 9010566170fSBjoern A. Zeeb if (sc->ev_tag) 9020566170fSBjoern A. Zeeb EVENTHANDLER_DEREGISTER(watchdog_list, sc->ev_tag); 9030566170fSBjoern A. Zeeb wb_set_watchdog(sc, 0); 9040566170fSBjoern A. Zeeb 9050566170fSBjoern A. Zeeb /* Disable extended function mode. */ 9061adb4ebdSXin LI (*sc->ext_cfg_exit_f)(sc, 0); 9070566170fSBjoern A. Zeeb 9080566170fSBjoern A. Zeeb /* Cleanup resources. */ 9090566170fSBjoern A. Zeeb (void) bus_release_resource(dev, SYS_RES_IOPORT, sc->rid, sc->portres); 9100566170fSBjoern A. Zeeb 9110566170fSBjoern A. Zeeb /* Bus subroutines take care of sysctls already. */ 9120566170fSBjoern A. Zeeb 9130566170fSBjoern A. Zeeb return (0); 9140566170fSBjoern A. Zeeb } 9150566170fSBjoern A. Zeeb 9160566170fSBjoern A. Zeeb static device_method_t wb_methods[] = { 9170566170fSBjoern A. Zeeb /* Device interface */ 9181adb4ebdSXin LI DEVMETHOD(device_identify, wb_identify), 9190566170fSBjoern A. Zeeb DEVMETHOD(device_probe, wb_probe), 9200566170fSBjoern A. Zeeb DEVMETHOD(device_attach, wb_attach), 9210566170fSBjoern A. Zeeb DEVMETHOD(device_detach, wb_detach), 9220566170fSBjoern A. Zeeb 923e25fe6cdSXin LI DEVMETHOD_END 9240566170fSBjoern A. Zeeb }; 9250566170fSBjoern A. Zeeb 9260566170fSBjoern A. Zeeb static driver_t wb_isa_driver = { 9270566170fSBjoern A. Zeeb "wbwd", 9280566170fSBjoern A. Zeeb wb_methods, 9290566170fSBjoern A. Zeeb sizeof(struct wb_softc) 9300566170fSBjoern A. Zeeb }; 9310566170fSBjoern A. Zeeb 9320566170fSBjoern A. Zeeb static devclass_t wb_devclass; 9330566170fSBjoern A. Zeeb 9340566170fSBjoern A. Zeeb DRIVER_MODULE(wb, isa, wb_isa_driver, wb_devclass, NULL, NULL); 935