10566170fSBjoern A. Zeeb /*- 2718cf2ccSPedro F. Giffuni * SPDX-License-Identifier: BSD-2-Clause-FreeBSD 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/lock.h> 510566170fSBjoern A. Zeeb #include <sys/module.h> 520566170fSBjoern A. Zeeb #include <sys/rman.h> 530566170fSBjoern A. Zeeb #include <sys/sbuf.h> 540566170fSBjoern A. Zeeb #include <sys/sysctl.h> 550566170fSBjoern A. Zeeb #include <sys/watchdog.h> 560566170fSBjoern A. Zeeb 570566170fSBjoern A. Zeeb #include <isa/isavar.h> 580566170fSBjoern A. Zeeb 590566170fSBjoern A. Zeeb #include <machine/bus.h> 600566170fSBjoern A. Zeeb #include <machine/resource.h> 610566170fSBjoern A. Zeeb 620566170fSBjoern A. Zeeb /* 630566170fSBjoern A. Zeeb * Global registers. 640566170fSBjoern A. Zeeb */ 650566170fSBjoern A. Zeeb #define WB_DEVICE_ID_REG 0x20 /* Device ID */ 660566170fSBjoern A. Zeeb #define WB_DEVICE_REV_REG 0x21 /* Device revision */ 670566170fSBjoern A. Zeeb #define WB_CR26 0x26 /* Bit6: HEFRAS (base port selector) */ 680566170fSBjoern A. Zeeb 690566170fSBjoern A. Zeeb /* LDN selection. */ 700566170fSBjoern A. Zeeb #define WB_LDN_REG 0x07 710566170fSBjoern A. Zeeb #define WB_LDN_REG_LDN8 0x08 /* GPIO 2, Watchdog */ 720566170fSBjoern A. Zeeb 730566170fSBjoern A. Zeeb /* 740566170fSBjoern A. Zeeb * LDN8 (GPIO 2, Watchdog) specific registers and options. 750566170fSBjoern A. Zeeb */ 760566170fSBjoern A. Zeeb /* CR30: LDN8 activation control. */ 770566170fSBjoern A. Zeeb #define WB_LDN8_CR30 0x30 780566170fSBjoern A. Zeeb #define WB_LDN8_CR30_ACTIVE 0x01 /* 1: LD active */ 790566170fSBjoern A. Zeeb 800566170fSBjoern A. Zeeb /* CRF5: Watchdog scale, P20. Mapped to reg_1. */ 810566170fSBjoern A. Zeeb #define WB_LDN8_CRF5 0xF5 820566170fSBjoern A. Zeeb #define WB_LDN8_CRF5_SCALE 0x08 /* 0: 1s, 1: 60s */ 830566170fSBjoern A. Zeeb #define WB_LDN8_CRF5_KEYB_P20 0x04 /* 1: keyb P20 forces timeout */ 84cf864f03SRobert Noland #define WB_LDN8_CRF5_KBRST 0x02 /* 1: timeout causes pin60 kbd reset */ 850566170fSBjoern A. Zeeb 860566170fSBjoern A. Zeeb /* CRF6: Watchdog Timeout (0 == off). Mapped to reg_timeout. */ 870566170fSBjoern A. Zeeb #define WB_LDN8_CRF6 0xF6 880566170fSBjoern A. Zeeb 890566170fSBjoern A. Zeeb /* CRF7: Watchdog mouse, keyb, force, .. Mapped to reg_2. */ 900566170fSBjoern A. Zeeb #define WB_LDN8_CRF7 0xF7 910566170fSBjoern A. Zeeb #define WB_LDN8_CRF7_MOUSE 0x80 /* 1: mouse irq resets wd timer */ 920566170fSBjoern A. Zeeb #define WB_LDN8_CRF7_KEYB 0x40 /* 1: keyb irq resets wd timer */ 930566170fSBjoern A. Zeeb #define WB_LDN8_CRF7_FORCE 0x20 /* 1: force timeout (self-clear) */ 940566170fSBjoern A. Zeeb #define WB_LDN8_CRF7_TS 0x10 /* 0: counting, 1: fired */ 950566170fSBjoern A. Zeeb #define WB_LDN8_CRF7_IRQS 0x0f /* irq source for watchdog, 2 == SMI */ 96baa7dd65SAlexander Motin 97baa7dd65SAlexander Motin enum chips { w83627hf, w83627s, w83697hf, w83697ug, w83637hf, w83627thf, 98baa7dd65SAlexander Motin w83687thf, w83627ehf, w83627dhg, w83627uhg, w83667hg, 99baa7dd65SAlexander Motin w83627dhg_p, w83667hg_b, nct6775, nct6776, nct6779, nct6791, 100f531e075SAlexander Motin nct6792, nct6793, nct6795, nct6102 }; 1010566170fSBjoern A. Zeeb 1020566170fSBjoern A. Zeeb struct wb_softc { 1030566170fSBjoern A. Zeeb device_t dev; 1040566170fSBjoern A. Zeeb struct resource *portres; 1050566170fSBjoern A. Zeeb bus_space_tag_t bst; 1060566170fSBjoern A. Zeeb bus_space_handle_t bsh; 1070566170fSBjoern A. Zeeb int rid; 1080566170fSBjoern A. Zeeb eventhandler_tag ev_tag; 1091adb4ebdSXin LI int (*ext_cfg_enter_f)(struct wb_softc *, u_short); 1101adb4ebdSXin LI void (*ext_cfg_exit_f)(struct wb_softc *, u_short); 111baa7dd65SAlexander Motin enum chips chip; 112baa7dd65SAlexander Motin uint8_t ctl_reg; 113baa7dd65SAlexander Motin uint8_t time_reg; 114baa7dd65SAlexander Motin uint8_t csr_reg; 1150566170fSBjoern A. Zeeb int debug_verbose; 1160566170fSBjoern A. Zeeb 1170566170fSBjoern A. Zeeb /* 1180566170fSBjoern A. Zeeb * Special feature to let the watchdog fire at a different 1190566170fSBjoern A. Zeeb * timeout as set by watchdog(4) but still use that API to 1200566170fSBjoern A. Zeeb * re-load it periodically. 1210566170fSBjoern A. Zeeb */ 1220566170fSBjoern A. Zeeb unsigned int timeout_override; 1230566170fSBjoern A. Zeeb 1240566170fSBjoern A. Zeeb /* 1250566170fSBjoern A. Zeeb * Space to save current state temporary and for sysctls. 1260566170fSBjoern A. Zeeb * We want to know the timeout value and usually need two 1270566170fSBjoern A. Zeeb * additional registers for options. Do not name them by 1280566170fSBjoern A. Zeeb * register as these might be different by chip. 1290566170fSBjoern A. Zeeb */ 1300566170fSBjoern A. Zeeb uint8_t reg_timeout; 1310566170fSBjoern A. Zeeb uint8_t reg_1; 1320566170fSBjoern A. Zeeb uint8_t reg_2; 1330566170fSBjoern A. Zeeb }; 1340566170fSBjoern A. Zeeb 1351adb4ebdSXin LI static int ext_cfg_enter_0x87_0x87(struct wb_softc *, u_short); 1361adb4ebdSXin LI static void ext_cfg_exit_0xaa(struct wb_softc *, u_short); 1370566170fSBjoern A. Zeeb 1380566170fSBjoern A. Zeeb struct winbond_superio_cfg { 1390566170fSBjoern A. Zeeb uint8_t efer; /* and efir */ 1401adb4ebdSXin LI int (*ext_cfg_enter_f)(struct wb_softc *, u_short); 1411adb4ebdSXin LI void (*ext_cfg_exit_f)(struct wb_softc *, u_short); 1420566170fSBjoern A. Zeeb } probe_addrs[] = { 1430566170fSBjoern A. Zeeb { 1440566170fSBjoern A. Zeeb .efer = 0x2e, 1450566170fSBjoern A. Zeeb .ext_cfg_enter_f = ext_cfg_enter_0x87_0x87, 1460566170fSBjoern A. Zeeb .ext_cfg_exit_f = ext_cfg_exit_0xaa, 1470566170fSBjoern A. Zeeb }, 1480566170fSBjoern A. Zeeb { 1490566170fSBjoern A. Zeeb .efer = 0x4e, 1500566170fSBjoern A. Zeeb .ext_cfg_enter_f = ext_cfg_enter_0x87_0x87, 1510566170fSBjoern A. Zeeb .ext_cfg_exit_f = ext_cfg_exit_0xaa, 1520566170fSBjoern A. Zeeb }, 1530566170fSBjoern A. Zeeb }; 1540566170fSBjoern A. Zeeb 1550566170fSBjoern A. Zeeb struct winbond_vendor_device_id { 1560566170fSBjoern A. Zeeb uint8_t device_id; 157baa7dd65SAlexander Motin enum chips chip; 1580566170fSBjoern A. Zeeb const char * descr; 1590566170fSBjoern A. Zeeb } wb_devs[] = { 1600566170fSBjoern A. Zeeb { 1610566170fSBjoern A. Zeeb .device_id = 0x52, 162baa7dd65SAlexander Motin .chip = w83627hf, 163baa7dd65SAlexander Motin .descr = "Winbond 83627HF/F/HG/G", 1640566170fSBjoern A. Zeeb }, 1650566170fSBjoern A. Zeeb { 166baa7dd65SAlexander Motin .device_id = 0x59, 167baa7dd65SAlexander Motin .chip = w83627s, 168baa7dd65SAlexander Motin .descr = "Winbond 83627S", 1690566170fSBjoern A. Zeeb }, 1700566170fSBjoern A. Zeeb { 171baa7dd65SAlexander Motin .device_id = 0x60, 172baa7dd65SAlexander Motin .chip = w83697hf, 173baa7dd65SAlexander Motin .descr = "Winbond 83697HF", 1740566170fSBjoern A. Zeeb }, 1750566170fSBjoern A. Zeeb { 176baa7dd65SAlexander Motin .device_id = 0x68, 177baa7dd65SAlexander Motin .chip = w83697ug, 178baa7dd65SAlexander Motin .descr = "Winbond 83697UG", 179baa7dd65SAlexander Motin }, 180baa7dd65SAlexander Motin { 181baa7dd65SAlexander Motin .device_id = 0x70, 182baa7dd65SAlexander Motin .chip = w83637hf, 183baa7dd65SAlexander Motin .descr = "Winbond 83637HF", 184baa7dd65SAlexander Motin }, 185baa7dd65SAlexander Motin { 186baa7dd65SAlexander Motin .device_id = 0x82, 187baa7dd65SAlexander Motin .chip = w83627thf, 188baa7dd65SAlexander Motin .descr = "Winbond 83627THF", 189baa7dd65SAlexander Motin }, 190baa7dd65SAlexander Motin { 191baa7dd65SAlexander Motin .device_id = 0x85, 192baa7dd65SAlexander Motin .chip = w83687thf, 193baa7dd65SAlexander Motin .descr = "Winbond 83687THF", 194baa7dd65SAlexander Motin }, 195baa7dd65SAlexander Motin { 196baa7dd65SAlexander Motin .device_id = 0x88, 197baa7dd65SAlexander Motin .chip = w83627ehf, 198baa7dd65SAlexander Motin .descr = "Winbond 83627EHF", 199baa7dd65SAlexander Motin }, 200baa7dd65SAlexander Motin { 2010566170fSBjoern A. Zeeb .device_id = 0xa0, 202baa7dd65SAlexander Motin .chip = w83627dhg, 203baa7dd65SAlexander Motin .descr = "Winbond 83627DHG", 2040566170fSBjoern A. Zeeb }, 205cf864f03SRobert Noland { 206baa7dd65SAlexander Motin .device_id = 0xa2, 207baa7dd65SAlexander Motin .chip = w83627uhg, 208baa7dd65SAlexander Motin .descr = "Winbond 83627UHG", 209baa7dd65SAlexander Motin }, 210baa7dd65SAlexander Motin { 211baa7dd65SAlexander Motin .device_id = 0xa5, 212baa7dd65SAlexander Motin .chip = w83667hg, 213baa7dd65SAlexander Motin .descr = "Winbond 83667HG", 214baa7dd65SAlexander Motin }, 215baa7dd65SAlexander Motin { 216cf864f03SRobert Noland .device_id = 0xb0, 217baa7dd65SAlexander Motin .chip = w83627dhg_p, 218cf864f03SRobert Noland .descr = "Winbond 83627DHG-P", 219cf864f03SRobert Noland }, 2209e8bad81SXin LI { 221baa7dd65SAlexander Motin .device_id = 0xb3, 222baa7dd65SAlexander Motin .chip = w83667hg_b, 223baa7dd65SAlexander Motin .descr = "Winbond 83667HG-B", 224baa7dd65SAlexander Motin }, 225baa7dd65SAlexander Motin { 226baa7dd65SAlexander Motin .device_id = 0xb4, 227baa7dd65SAlexander Motin .chip = nct6775, 228baa7dd65SAlexander Motin .descr = "Nuvoton NCT6775", 229baa7dd65SAlexander Motin }, 230baa7dd65SAlexander Motin { 2319e8bad81SXin LI .device_id = 0xc3, 232baa7dd65SAlexander Motin .chip = nct6776, 233baa7dd65SAlexander Motin .descr = "Nuvoton NCT6776", 234baa7dd65SAlexander Motin }, 235baa7dd65SAlexander Motin { 236baa7dd65SAlexander Motin .device_id = 0xc4, 237baa7dd65SAlexander Motin .chip = nct6102, 238baa7dd65SAlexander Motin .descr = "Nuvoton NCT6102", 239baa7dd65SAlexander Motin }, 240baa7dd65SAlexander Motin { 241baa7dd65SAlexander Motin .device_id = 0xc5, 242baa7dd65SAlexander Motin .chip = nct6779, 243baa7dd65SAlexander Motin .descr = "Nuvoton NCT6779", 244baa7dd65SAlexander Motin }, 245baa7dd65SAlexander Motin { 246baa7dd65SAlexander Motin .device_id = 0xc8, 247baa7dd65SAlexander Motin .chip = nct6791, 248baa7dd65SAlexander Motin .descr = "Nuvoton NCT6791", 249baa7dd65SAlexander Motin }, 250baa7dd65SAlexander Motin { 251baa7dd65SAlexander Motin .device_id = 0xc9, 252baa7dd65SAlexander Motin .chip = nct6792, 253baa7dd65SAlexander Motin .descr = "Nuvoton NCT6792", 2549e8bad81SXin LI }, 255f531e075SAlexander Motin { 256f531e075SAlexander Motin .device_id = 0xd1, 257f531e075SAlexander Motin .chip = nct6793, 258f531e075SAlexander Motin .descr = "Nuvoton NCT6793", 259f531e075SAlexander Motin }, 260f531e075SAlexander Motin { 261f531e075SAlexander Motin .device_id = 0xd3, 262f531e075SAlexander Motin .chip = nct6795, 263f531e075SAlexander Motin .descr = "Nuvoton NCT6795", 264f531e075SAlexander Motin }, 2650566170fSBjoern A. Zeeb }; 2660566170fSBjoern A. Zeeb 2671adb4ebdSXin LI static void 2681adb4ebdSXin LI write_efir_1(struct wb_softc *sc, u_short baseport, uint8_t value) 2691adb4ebdSXin LI { 2701adb4ebdSXin LI 2711adb4ebdSXin LI MPASS(sc != NULL || baseport != 0); 2721adb4ebdSXin LI if (sc != NULL) 2731adb4ebdSXin LI bus_space_write_1((sc)->bst, (sc)->bsh, 0, (value)); 2741adb4ebdSXin LI else 2751adb4ebdSXin LI outb(baseport, value); 2761adb4ebdSXin LI } 2771adb4ebdSXin LI 2781adb4ebdSXin LI static uint8_t __unused 2791adb4ebdSXin LI read_efir_1(struct wb_softc *sc, u_short baseport) 2801adb4ebdSXin LI { 2811adb4ebdSXin LI 2821adb4ebdSXin LI MPASS(sc != NULL || baseport != 0); 2831adb4ebdSXin LI if (sc != NULL) 2841adb4ebdSXin LI return (bus_space_read_1((sc)->bst, (sc)->bsh, 0)); 2851adb4ebdSXin LI else 2861adb4ebdSXin LI return (inb(baseport)); 2871adb4ebdSXin LI } 2881adb4ebdSXin LI 2891adb4ebdSXin LI static void 2901adb4ebdSXin LI write_efdr_1(struct wb_softc *sc, u_short baseport, uint8_t value) 2911adb4ebdSXin LI { 2921adb4ebdSXin LI 2931adb4ebdSXin LI MPASS(sc != NULL || baseport != 0); 2941adb4ebdSXin LI if (sc != NULL) 2951adb4ebdSXin LI bus_space_write_1((sc)->bst, (sc)->bsh, 1, (value)); 2961adb4ebdSXin LI else 2971adb4ebdSXin LI outb(baseport + 1, value); 2981adb4ebdSXin LI } 2991adb4ebdSXin LI 3001adb4ebdSXin LI static uint8_t 3011adb4ebdSXin LI read_efdr_1(struct wb_softc *sc, u_short baseport) 3021adb4ebdSXin LI { 3031adb4ebdSXin LI 3041adb4ebdSXin LI MPASS(sc != NULL || baseport != 0); 3051adb4ebdSXin LI if (sc != NULL) 3061adb4ebdSXin LI return (bus_space_read_1((sc)->bst, (sc)->bsh, 1)); 3071adb4ebdSXin LI else 3081adb4ebdSXin LI return (inb(baseport + 1)); 3091adb4ebdSXin LI } 3101adb4ebdSXin LI 311baa7dd65SAlexander Motin static void 312baa7dd65SAlexander Motin write_reg(struct wb_softc *sc, uint8_t reg, uint8_t value) 313baa7dd65SAlexander Motin { 314baa7dd65SAlexander Motin 315baa7dd65SAlexander Motin write_efir_1(sc, 0, reg); 316baa7dd65SAlexander Motin write_efdr_1(sc, 0, value); 317baa7dd65SAlexander Motin } 318baa7dd65SAlexander Motin 319baa7dd65SAlexander Motin static uint8_t 320baa7dd65SAlexander Motin read_reg(struct wb_softc *sc, uint8_t reg) 321baa7dd65SAlexander Motin { 322baa7dd65SAlexander Motin 323baa7dd65SAlexander Motin write_efir_1(sc, 0, reg); 324baa7dd65SAlexander Motin return (read_efdr_1(sc, 0)); 325baa7dd65SAlexander Motin } 326baa7dd65SAlexander Motin 3270566170fSBjoern A. Zeeb /* 3280566170fSBjoern A. Zeeb * Return the watchdog related registers as we last read them. This will 3290566170fSBjoern A. Zeeb * usually not give the current timeout or state on whether the watchdog 3300566170fSBjoern A. Zeeb * fired. 3310566170fSBjoern A. Zeeb */ 3320566170fSBjoern A. Zeeb static int 3330566170fSBjoern A. Zeeb sysctl_wb_debug(SYSCTL_HANDLER_ARGS) 3340566170fSBjoern A. Zeeb { 3350566170fSBjoern A. Zeeb struct wb_softc *sc; 3360566170fSBjoern A. Zeeb struct sbuf sb; 3370566170fSBjoern A. Zeeb int error; 3380566170fSBjoern A. Zeeb 3390566170fSBjoern A. Zeeb sc = arg1; 3400566170fSBjoern A. Zeeb 3410566170fSBjoern A. Zeeb sbuf_new_for_sysctl(&sb, NULL, 64, req); 3420566170fSBjoern A. Zeeb 3430566170fSBjoern A. Zeeb sbuf_printf(&sb, "LDN8 (GPIO2, Watchdog): "); 344baa7dd65SAlexander Motin sbuf_printf(&sb, "CR%02X 0x%02x ", sc->ctl_reg, sc->reg_1); 345baa7dd65SAlexander Motin sbuf_printf(&sb, "CR%02X 0x%02x ", sc->time_reg, sc->reg_timeout); 346baa7dd65SAlexander Motin sbuf_printf(&sb, "CR%02X 0x%02x", sc->csr_reg, sc->reg_2); 3470566170fSBjoern A. Zeeb 3480566170fSBjoern A. Zeeb error = sbuf_finish(&sb); 3490566170fSBjoern A. Zeeb sbuf_delete(&sb); 3500566170fSBjoern A. Zeeb return (error); 3510566170fSBjoern A. Zeeb } 3520566170fSBjoern A. Zeeb 3530566170fSBjoern A. Zeeb /* 3540566170fSBjoern A. Zeeb * Read the current values before returning them. Given this might poke 3550566170fSBjoern A. Zeeb * the registers the same time as the watchdog, this sysctl handler should 3560566170fSBjoern A. Zeeb * be marked CTLFLAG_SKIP to not show up by default. 3570566170fSBjoern A. Zeeb */ 3580566170fSBjoern A. Zeeb static int 3590566170fSBjoern A. Zeeb sysctl_wb_debug_current(SYSCTL_HANDLER_ARGS) 3600566170fSBjoern A. Zeeb { 3610566170fSBjoern A. Zeeb struct wb_softc *sc; 3620566170fSBjoern A. Zeeb 3630566170fSBjoern A. Zeeb sc = arg1; 3640566170fSBjoern A. Zeeb 3651adb4ebdSXin LI if ((*sc->ext_cfg_enter_f)(sc, 0) != 0) 3660566170fSBjoern A. Zeeb return (ENXIO); 3670566170fSBjoern A. Zeeb 3680566170fSBjoern A. Zeeb /* Watchdog is configured as part of LDN 8 (GPIO Port2, Watchdog). */ 369baa7dd65SAlexander Motin write_reg(sc, WB_LDN_REG, WB_LDN_REG_LDN8); 3700566170fSBjoern A. Zeeb 371baa7dd65SAlexander Motin sc->reg_1 = read_reg(sc, sc->ctl_reg); 372baa7dd65SAlexander Motin sc->reg_timeout = read_reg(sc, sc->time_reg); 373baa7dd65SAlexander Motin sc->reg_2 = read_reg(sc, sc->csr_reg); 374baa7dd65SAlexander Motin 375baa7dd65SAlexander Motin (*sc->ext_cfg_exit_f)(sc, 0); 3760566170fSBjoern A. Zeeb 3770566170fSBjoern A. Zeeb return (sysctl_wb_debug(oidp, arg1, arg2, req)); 3780566170fSBjoern A. Zeeb } 3790566170fSBjoern A. Zeeb 3800566170fSBjoern A. Zeeb /* 3810566170fSBjoern A. Zeeb * Sysctl handlers to force a watchdog timeout or to test the NMI functionality 3820566170fSBjoern A. Zeeb * works as expetced. 3830566170fSBjoern A. Zeeb * For testing we could set a test_nmi flag in the softc that, in case of NMI, a 3840566170fSBjoern A. Zeeb * callback function from trap.c could check whether we fired and not report the 3850566170fSBjoern A. Zeeb * timeout but clear the flag for the sysctl again. This is interesting given a 3860566170fSBjoern A. Zeeb * lot of boards have jumpers to change the action on watchdog timeout or 3870566170fSBjoern A. Zeeb * disable the watchdog completely. 3880566170fSBjoern A. Zeeb * XXX-BZ notyet: currently no general infrastructure exists to do this. 3890566170fSBjoern A. Zeeb */ 3900566170fSBjoern A. Zeeb static int 3910566170fSBjoern A. Zeeb sysctl_wb_force_test_nmi(SYSCTL_HANDLER_ARGS) 3920566170fSBjoern A. Zeeb { 3930566170fSBjoern A. Zeeb struct wb_softc *sc; 3940566170fSBjoern A. Zeeb int error, test, val; 3950566170fSBjoern A. Zeeb 3960566170fSBjoern A. Zeeb sc = arg1; 3970566170fSBjoern A. Zeeb test = arg2; 3980566170fSBjoern A. Zeeb 3990566170fSBjoern A. Zeeb #ifdef notyet 4000566170fSBjoern A. Zeeb val = sc->test_nmi; 4010566170fSBjoern A. Zeeb #else 4020566170fSBjoern A. Zeeb val = 0; 4030566170fSBjoern A. Zeeb #endif 4040566170fSBjoern A. Zeeb error = sysctl_handle_int(oidp, &val, 0, req); 4050566170fSBjoern A. Zeeb if (error || !req->newptr) 4060566170fSBjoern A. Zeeb return (error); 4070566170fSBjoern A. Zeeb 4080566170fSBjoern A. Zeeb #ifdef notyet 4090566170fSBjoern A. Zeeb /* Manually clear the test for a value of 0 and do nothing else. */ 4100566170fSBjoern A. Zeeb if (test && val == 0) { 4110566170fSBjoern A. Zeeb sc->test_nmi = 0; 4120566170fSBjoern A. Zeeb return (0); 4130566170fSBjoern A. Zeeb } 4140566170fSBjoern A. Zeeb #endif 4150566170fSBjoern A. Zeeb 4161adb4ebdSXin LI if ((*sc->ext_cfg_enter_f)(sc, 0) != 0) 4170566170fSBjoern A. Zeeb return (ENXIO); 4180566170fSBjoern A. Zeeb 4190566170fSBjoern A. Zeeb #ifdef notyet 4200566170fSBjoern A. Zeeb /* 4210566170fSBjoern A. Zeeb * If we are testing the NMI functionality, set the flag before 4220566170fSBjoern A. Zeeb * forcing the timeout. 4230566170fSBjoern A. Zeeb */ 4240566170fSBjoern A. Zeeb if (test) 4250566170fSBjoern A. Zeeb sc->test_nmi = 1; 4260566170fSBjoern A. Zeeb #endif 4270566170fSBjoern A. Zeeb 4280566170fSBjoern A. Zeeb /* Watchdog is configured as part of LDN 8 (GPIO Port2, Watchdog). */ 429baa7dd65SAlexander Motin write_reg(sc, WB_LDN_REG, WB_LDN_REG_LDN8); 4300566170fSBjoern A. Zeeb 4310566170fSBjoern A. Zeeb /* Force watchdog to fire. */ 432baa7dd65SAlexander Motin sc->reg_2 = read_reg(sc, sc->csr_reg); 4330566170fSBjoern A. Zeeb sc->reg_2 |= WB_LDN8_CRF7_FORCE; 434baa7dd65SAlexander Motin write_reg(sc, sc->csr_reg, sc->reg_2); 4350566170fSBjoern A. Zeeb 436baa7dd65SAlexander Motin (*sc->ext_cfg_exit_f)(sc, 0); 4370566170fSBjoern A. Zeeb 4380566170fSBjoern A. Zeeb return (0); 4390566170fSBjoern A. Zeeb } 4400566170fSBjoern A. Zeeb 4410566170fSBjoern A. Zeeb /* 4420566170fSBjoern A. Zeeb * Print current watchdog state. 4430566170fSBjoern A. Zeeb * 4440566170fSBjoern A. Zeeb * Note: it is the responsibility of the caller to update the registers 4450566170fSBjoern A. Zeeb * upfront. 4460566170fSBjoern A. Zeeb */ 4470566170fSBjoern A. Zeeb static void 4480566170fSBjoern A. Zeeb wb_print_state(struct wb_softc *sc, const char *msg) 4490566170fSBjoern A. Zeeb { 4500566170fSBjoern A. Zeeb 4510566170fSBjoern A. Zeeb device_printf(sc->dev, "%s%sWatchdog %sabled. %s" 4520566170fSBjoern A. Zeeb "Scaling by %ds, timer at %d (%s=%ds%s). " 4530566170fSBjoern A. Zeeb "CRF5 0x%02x CRF7 0x%02x\n", 4540566170fSBjoern A. Zeeb (msg != NULL) ? msg : "", (msg != NULL) ? ": " : "", 4550566170fSBjoern A. Zeeb (sc->reg_timeout > 0x00) ? "en" : "dis", 4560566170fSBjoern A. Zeeb (sc->reg_2 & WB_LDN8_CRF7_TS) ? "Watchdog fired. " : "", 4570566170fSBjoern A. Zeeb (sc->reg_1 & WB_LDN8_CRF5_SCALE) ? 60 : 1, 4580566170fSBjoern A. Zeeb sc->reg_timeout, 4590566170fSBjoern A. Zeeb (sc->reg_timeout > 0x00) ? "<" : "", 4600566170fSBjoern A. Zeeb sc->reg_timeout * ((sc->reg_1 & WB_LDN8_CRF5_SCALE) ? 60 : 1), 4610566170fSBjoern A. Zeeb (sc->reg_timeout > 0x00) ? " left" : "", 4620566170fSBjoern A. Zeeb sc->reg_1, sc->reg_2); 4630566170fSBjoern A. Zeeb } 4640566170fSBjoern A. Zeeb 4650566170fSBjoern A. Zeeb /* 4660566170fSBjoern A. Zeeb * Functions to enter and exit extended function mode. Possibly shared 4670566170fSBjoern A. Zeeb * between different chips. 4680566170fSBjoern A. Zeeb */ 4690566170fSBjoern A. Zeeb static int 4701adb4ebdSXin LI ext_cfg_enter_0x87_0x87(struct wb_softc *sc, u_short baseport) 4710566170fSBjoern A. Zeeb { 4720566170fSBjoern A. Zeeb 4730566170fSBjoern A. Zeeb /* 4740566170fSBjoern A. Zeeb * Enable extended function mode. 4750566170fSBjoern A. Zeeb * Winbond does not allow us to validate so always return success. 4760566170fSBjoern A. Zeeb */ 4771adb4ebdSXin LI write_efir_1(sc, baseport, 0x87); 4781adb4ebdSXin LI write_efir_1(sc, baseport, 0x87); 4790566170fSBjoern A. Zeeb 4800566170fSBjoern A. Zeeb return (0); 4810566170fSBjoern A. Zeeb } 4820566170fSBjoern A. Zeeb 4830566170fSBjoern A. Zeeb static void 4841adb4ebdSXin LI ext_cfg_exit_0xaa(struct wb_softc *sc, u_short baseport) 4850566170fSBjoern A. Zeeb { 4860566170fSBjoern A. Zeeb 4871adb4ebdSXin LI write_efir_1(sc, baseport, 0xaa); 4880566170fSBjoern A. Zeeb } 4890566170fSBjoern A. Zeeb 4900566170fSBjoern A. Zeeb /* 4910566170fSBjoern A. Zeeb * (Re)load the watchdog counter depending on timeout. A timeout of 0 will 4920566170fSBjoern A. Zeeb * disable the watchdog. 4930566170fSBjoern A. Zeeb */ 4940566170fSBjoern A. Zeeb static int 4950566170fSBjoern A. Zeeb wb_set_watchdog(struct wb_softc *sc, unsigned int timeout) 4960566170fSBjoern A. Zeeb { 4970566170fSBjoern A. Zeeb 498baa7dd65SAlexander Motin if (timeout != 0) { 4990566170fSBjoern A. Zeeb /* 5000566170fSBjoern A. Zeeb * In case an override is set, let it override. It may lead 5010566170fSBjoern A. Zeeb * to strange results as we do not check the input of the sysctl. 5020566170fSBjoern A. Zeeb */ 5030566170fSBjoern A. Zeeb if (sc->timeout_override > 0) 5040566170fSBjoern A. Zeeb timeout = sc->timeout_override; 5050566170fSBjoern A. Zeeb 5060566170fSBjoern A. Zeeb /* Make sure we support the requested timeout. */ 5070566170fSBjoern A. Zeeb if (timeout > 255 * 60) 5080566170fSBjoern A. Zeeb return (EINVAL); 509baa7dd65SAlexander Motin } 5100566170fSBjoern A. Zeeb 511baa7dd65SAlexander Motin if (sc->debug_verbose) 512baa7dd65SAlexander Motin wb_print_state(sc, "Before watchdog counter (re)load"); 513baa7dd65SAlexander Motin 514baa7dd65SAlexander Motin if ((*sc->ext_cfg_enter_f)(sc, 0) != 0) 515baa7dd65SAlexander Motin return (ENXIO); 516baa7dd65SAlexander Motin 517baa7dd65SAlexander Motin /* Watchdog is configured as part of LDN 8 (GPIO Port2, Watchdog) */ 518baa7dd65SAlexander Motin write_reg(sc, WB_LDN_REG, WB_LDN_REG_LDN8); 519baa7dd65SAlexander Motin 520baa7dd65SAlexander Motin if (timeout == 0) { 521baa7dd65SAlexander Motin /* Disable watchdog. */ 5224f6dd82aSAlexander Motin sc->reg_timeout = 0; 5234f6dd82aSAlexander Motin write_reg(sc, sc->time_reg, sc->reg_timeout); 524baa7dd65SAlexander Motin 525baa7dd65SAlexander Motin } else { 5260566170fSBjoern A. Zeeb /* Read current scaling factor. */ 527baa7dd65SAlexander Motin sc->reg_1 = read_reg(sc, sc->ctl_reg); 5280566170fSBjoern A. Zeeb 5290566170fSBjoern A. Zeeb if (timeout > 255) { 5300566170fSBjoern A. Zeeb /* Set scaling factor to 60s. */ 5310566170fSBjoern A. Zeeb sc->reg_1 |= WB_LDN8_CRF5_SCALE; 5320566170fSBjoern A. Zeeb sc->reg_timeout = (timeout / 60); 5330566170fSBjoern A. Zeeb if (timeout % 60) 5340566170fSBjoern A. Zeeb sc->reg_timeout++; 5350566170fSBjoern A. Zeeb } else { 5360566170fSBjoern A. Zeeb /* Set scaling factor to 1s. */ 5370566170fSBjoern A. Zeeb sc->reg_1 &= ~WB_LDN8_CRF5_SCALE; 5380566170fSBjoern A. Zeeb sc->reg_timeout = timeout; 5390566170fSBjoern A. Zeeb } 5400566170fSBjoern A. Zeeb 5410566170fSBjoern A. Zeeb /* In case we fired before we need to clear to fire again. */ 542baa7dd65SAlexander Motin sc->reg_2 = read_reg(sc, sc->csr_reg); 5430566170fSBjoern A. Zeeb if (sc->reg_2 & WB_LDN8_CRF7_TS) { 5440566170fSBjoern A. Zeeb sc->reg_2 &= ~WB_LDN8_CRF7_TS; 545baa7dd65SAlexander Motin write_reg(sc, sc->csr_reg, sc->reg_2); 5460566170fSBjoern A. Zeeb } 5470566170fSBjoern A. Zeeb 5480566170fSBjoern A. Zeeb /* Write back scaling factor. */ 549baa7dd65SAlexander Motin write_reg(sc, sc->ctl_reg, sc->reg_1); 5500566170fSBjoern A. Zeeb 5510566170fSBjoern A. Zeeb /* Set timer and arm/reset the watchdog. */ 552baa7dd65SAlexander Motin write_reg(sc, sc->time_reg, sc->reg_timeout); 5530566170fSBjoern A. Zeeb } 5540566170fSBjoern A. Zeeb 5554f6dd82aSAlexander Motin (*sc->ext_cfg_exit_f)(sc, 0); 5564f6dd82aSAlexander Motin 5570566170fSBjoern A. Zeeb if (sc->debug_verbose) 5580566170fSBjoern A. Zeeb wb_print_state(sc, "After watchdog counter (re)load"); 5590566170fSBjoern A. Zeeb return (0); 5600566170fSBjoern A. Zeeb } 5610566170fSBjoern A. Zeeb 5620566170fSBjoern A. Zeeb /* 5630566170fSBjoern A. Zeeb * watchdog(9) EVENTHANDLER function implementation to (re)load the counter 5640566170fSBjoern A. Zeeb * with the given timeout or disable the watchdog. 5650566170fSBjoern A. Zeeb */ 5660566170fSBjoern A. Zeeb static void 5670566170fSBjoern A. Zeeb wb_watchdog_fn(void *private, u_int cmd, int *error) 5680566170fSBjoern A. Zeeb { 5690566170fSBjoern A. Zeeb struct wb_softc *sc; 5700566170fSBjoern A. Zeeb unsigned int timeout; 5710566170fSBjoern A. Zeeb int e; 5720566170fSBjoern A. Zeeb 5730566170fSBjoern A. Zeeb sc = private; 5740566170fSBjoern A. Zeeb KASSERT(sc != NULL, ("%s: watchdog handler function called without " 5750566170fSBjoern A. Zeeb "softc.", __func__)); 5760566170fSBjoern A. Zeeb 5770566170fSBjoern A. Zeeb cmd &= WD_INTERVAL; 5780566170fSBjoern A. Zeeb if (cmd > 0 && cmd <= 63) { 5790566170fSBjoern A. Zeeb /* Reset (and arm) watchdog. */ 5800566170fSBjoern A. Zeeb timeout = ((uint64_t)1 << cmd) / 1000000000; 5810566170fSBjoern A. Zeeb if (timeout == 0) 5820566170fSBjoern A. Zeeb timeout = 1; 5830566170fSBjoern A. Zeeb e = wb_set_watchdog(sc, timeout); 5840566170fSBjoern A. Zeeb if (e == 0) { 5850566170fSBjoern A. Zeeb if (error != NULL) 5860566170fSBjoern A. Zeeb *error = 0; 5870566170fSBjoern A. Zeeb } else { 5880566170fSBjoern A. Zeeb /* On error, try to make sure the WD is disabled. */ 5890566170fSBjoern A. Zeeb wb_set_watchdog(sc, 0); 5900566170fSBjoern A. Zeeb } 5910566170fSBjoern A. Zeeb 5920566170fSBjoern A. Zeeb } else { 5930566170fSBjoern A. Zeeb /* Disable watchdog. */ 5940566170fSBjoern A. Zeeb e = wb_set_watchdog(sc, 0); 5950566170fSBjoern A. Zeeb if (e != 0 && cmd == 0 && error != NULL) { 5960566170fSBjoern A. Zeeb /* Failed to disable watchdog. */ 5970566170fSBjoern A. Zeeb *error = EOPNOTSUPP; 5980566170fSBjoern A. Zeeb } 5990566170fSBjoern A. Zeeb } 6000566170fSBjoern A. Zeeb } 6010566170fSBjoern A. Zeeb 6020566170fSBjoern A. Zeeb /* 6030566170fSBjoern A. Zeeb * Probe/attach the Winbond Super I/O chip. 6040566170fSBjoern A. Zeeb * 6050566170fSBjoern A. Zeeb * Initial abstraction to possibly support more chips: 6060566170fSBjoern A. Zeeb * - Iterate over the well known base ports, try to enable extended function 6070566170fSBjoern A. Zeeb * mode and read and match the device ID and device revision. Unfortunately 6080566170fSBjoern A. Zeeb * the Vendor ID is in the hardware monitoring section accessible by different 6090566170fSBjoern A. Zeeb * base ports only. 6100566170fSBjoern A. Zeeb * - Also HEFRAS, which would tell use the base port, is only accessible after 6110566170fSBjoern A. Zeeb * entering extended function mode, for which the base port is needed. 6120566170fSBjoern A. Zeeb * At least check HEFRAS to match the current base port we are probing. 6130566170fSBjoern A. Zeeb * - On match set the description, remember functions to enter/exit extended 6140566170fSBjoern A. Zeeb * function mode as well as the base port. 6150566170fSBjoern A. Zeeb */ 6160566170fSBjoern A. Zeeb static int 6170566170fSBjoern A. Zeeb wb_probe_enable(device_t dev, int probe) 6180566170fSBjoern A. Zeeb { 6190566170fSBjoern A. Zeeb struct wb_softc *sc; 6200566170fSBjoern A. Zeeb int error, found, i, j; 6210566170fSBjoern A. Zeeb uint8_t dev_id, dev_rev, cr26; 622baa7dd65SAlexander Motin char buf[128]; 6230566170fSBjoern A. Zeeb 6241adb4ebdSXin LI if (dev == NULL) 6251adb4ebdSXin LI sc = NULL; 6261adb4ebdSXin LI else { 6270566170fSBjoern A. Zeeb sc = device_get_softc(dev); 6280566170fSBjoern A. Zeeb bzero(sc, sizeof(*sc)); 6290566170fSBjoern A. Zeeb sc->dev = dev; 6301adb4ebdSXin LI } 6310566170fSBjoern A. Zeeb 6320566170fSBjoern A. Zeeb error = ENXIO; 633baa7dd65SAlexander Motin found = 0; 63473a1170aSPedro F. Giffuni for (i = 0; i < nitems(probe_addrs); i++) { 6350566170fSBjoern A. Zeeb 6361adb4ebdSXin LI if (sc != NULL) { 6370566170fSBjoern A. Zeeb /* Allocate bus resources for IO index/data register access. */ 6380566170fSBjoern A. Zeeb sc->portres = bus_alloc_resource(dev, SYS_RES_IOPORT, &sc->rid, 6390566170fSBjoern A. Zeeb probe_addrs[i].efer, probe_addrs[i].efer + 1, 2, RF_ACTIVE); 6400566170fSBjoern A. Zeeb if (sc->portres == NULL) 6410566170fSBjoern A. Zeeb continue; 6420566170fSBjoern A. Zeeb sc->bst = rman_get_bustag(sc->portres); 6430566170fSBjoern A. Zeeb sc->bsh = rman_get_bushandle(sc->portres); 6441adb4ebdSXin LI } 6450566170fSBjoern A. Zeeb 6461adb4ebdSXin LI error = (*probe_addrs[i].ext_cfg_enter_f)(sc, probe_addrs[i].efer); 6470566170fSBjoern A. Zeeb if (error != 0) 6480566170fSBjoern A. Zeeb goto cleanup; 6490566170fSBjoern A. Zeeb 6500566170fSBjoern A. Zeeb /* Identify the SuperIO chip. */ 6511adb4ebdSXin LI write_efir_1(sc, probe_addrs[i].efer, WB_DEVICE_ID_REG); 6521adb4ebdSXin LI dev_id = read_efdr_1(sc, probe_addrs[i].efer); 6531adb4ebdSXin LI write_efir_1(sc, probe_addrs[i].efer, WB_DEVICE_REV_REG); 6541adb4ebdSXin LI dev_rev = read_efdr_1(sc, probe_addrs[i].efer); 6551adb4ebdSXin LI write_efir_1(sc, probe_addrs[i].efer, WB_CR26); 6561adb4ebdSXin LI cr26 = read_efdr_1(sc, probe_addrs[i].efer); 6570566170fSBjoern A. Zeeb 658baa7dd65SAlexander Motin if (dev_id == 0xff && dev_rev == 0xff) 659baa7dd65SAlexander Motin goto cleanup; 660baa7dd65SAlexander Motin 6610566170fSBjoern A. Zeeb /* HEFRAS of 0 means EFER at 0x2e, 1 means EFER at 0x4e. */ 6620566170fSBjoern A. Zeeb if (((cr26 & 0x40) == 0x00 && probe_addrs[i].efer != 0x2e) || 6630566170fSBjoern A. Zeeb ((cr26 & 0x40) == 0x40 && probe_addrs[i].efer != 0x4e)) { 6641adb4ebdSXin LI if (dev != NULL) 6651adb4ebdSXin LI device_printf(dev, "HEFRAS and EFER do not " 6661adb4ebdSXin LI "align: EFER 0x%02x DevID 0x%02x DevRev " 6671adb4ebdSXin LI "0x%02x CR26 0x%02x\n", 6680566170fSBjoern A. Zeeb probe_addrs[i].efer, dev_id, dev_rev, cr26); 6690566170fSBjoern A. Zeeb goto cleanup; 6700566170fSBjoern A. Zeeb } 6710566170fSBjoern A. Zeeb 67273a1170aSPedro F. Giffuni for (j = 0; j < nitems(wb_devs); j++) { 673baa7dd65SAlexander Motin if (wb_devs[j].device_id == dev_id) { 674baa7dd65SAlexander Motin found = 1; 6750566170fSBjoern A. Zeeb break; 6760566170fSBjoern A. Zeeb } 6770566170fSBjoern A. Zeeb } 6789e8bad81SXin LI 6799e8bad81SXin LI if (probe && dev != NULL) { 680baa7dd65SAlexander Motin snprintf(buf, sizeof(buf), 681baa7dd65SAlexander Motin "%s (0x%02x/0x%02x) Watchdog Timer", 682baa7dd65SAlexander Motin found ? wb_devs[j].descr : 683baa7dd65SAlexander Motin "Unknown Winbond/Nuvoton", dev_id, dev_rev); 684baa7dd65SAlexander Motin device_set_desc_copy(dev, buf); 6859e8bad81SXin LI } 6869e8bad81SXin LI 687baa7dd65SAlexander Motin /* If this is hinted attach, try to guess the model. */ 688baa7dd65SAlexander Motin if (dev != NULL && !found) { 689baa7dd65SAlexander Motin found = 1; 690baa7dd65SAlexander Motin j = 0; 691baa7dd65SAlexander Motin } 692baa7dd65SAlexander Motin 6930566170fSBjoern A. Zeeb cleanup: 6940566170fSBjoern A. Zeeb if (probe || !found) { 6951adb4ebdSXin LI (*probe_addrs[i].ext_cfg_exit_f)(sc, probe_addrs[i].efer); 6961adb4ebdSXin LI if (sc != NULL) 6971adb4ebdSXin LI (void) bus_release_resource(dev, SYS_RES_IOPORT, 6981adb4ebdSXin LI sc->rid, sc->portres); 6990566170fSBjoern A. Zeeb } 7000566170fSBjoern A. Zeeb 7010566170fSBjoern A. Zeeb /* 7020566170fSBjoern A. Zeeb * Stop probing if have successfully identified the SuperIO. 7030566170fSBjoern A. Zeeb * Remember the extended function mode enter/exit functions 7040566170fSBjoern A. Zeeb * for operations. 7050566170fSBjoern A. Zeeb */ 7060566170fSBjoern A. Zeeb if (found) { 7071adb4ebdSXin LI if (sc != NULL) { 7080566170fSBjoern A. Zeeb sc->ext_cfg_enter_f = probe_addrs[i].ext_cfg_enter_f; 7090566170fSBjoern A. Zeeb sc->ext_cfg_exit_f = probe_addrs[i].ext_cfg_exit_f; 710baa7dd65SAlexander Motin sc->chip = wb_devs[j].chip; 711baa7dd65SAlexander Motin sc->ctl_reg = 0xf5; 712baa7dd65SAlexander Motin sc->time_reg = 0xf6; 713baa7dd65SAlexander Motin sc->csr_reg = 0xf7; 714baa7dd65SAlexander Motin if (sc->chip == w83697hf || 715baa7dd65SAlexander Motin sc->chip == w83697ug) { 716baa7dd65SAlexander Motin sc->ctl_reg = 0xf3; 717baa7dd65SAlexander Motin sc->time_reg = 0xf4; 718baa7dd65SAlexander Motin } else if (sc->chip == nct6102) { 719baa7dd65SAlexander Motin sc->ctl_reg = 0xf0; 720baa7dd65SAlexander Motin sc->time_reg = 0xf1; 721baa7dd65SAlexander Motin sc->csr_reg = 0xf2; 7221adb4ebdSXin LI } 723baa7dd65SAlexander Motin } 724baa7dd65SAlexander Motin return (BUS_PROBE_SPECIFIC); 7250566170fSBjoern A. Zeeb } else 7260566170fSBjoern A. Zeeb error = ENXIO; 7270566170fSBjoern A. Zeeb } 7280566170fSBjoern A. Zeeb 7290566170fSBjoern A. Zeeb return (error); 7300566170fSBjoern A. Zeeb } 7310566170fSBjoern A. Zeeb 7321adb4ebdSXin LI static void 7331adb4ebdSXin LI wb_identify(driver_t *driver, device_t parent) 7341adb4ebdSXin LI { 7351adb4ebdSXin LI 736baa7dd65SAlexander Motin if (device_find_child(parent, driver->name, 0) == NULL) { 737baa7dd65SAlexander Motin if (wb_probe_enable(NULL, 1) <= 0) 738baa7dd65SAlexander Motin BUS_ADD_CHILD(parent, 0, driver->name, 0); 7391adb4ebdSXin LI } 7401adb4ebdSXin LI } 7411adb4ebdSXin LI 7420566170fSBjoern A. Zeeb static int 7430566170fSBjoern A. Zeeb wb_probe(device_t dev) 7440566170fSBjoern A. Zeeb { 7450566170fSBjoern A. Zeeb 7460566170fSBjoern A. Zeeb /* Make sure we do not claim some ISA PNP device. */ 7470566170fSBjoern A. Zeeb if (isa_get_logicalid(dev) != 0) 7480566170fSBjoern A. Zeeb return (ENXIO); 7490566170fSBjoern A. Zeeb 7500566170fSBjoern A. Zeeb return (wb_probe_enable(dev, 1)); 7510566170fSBjoern A. Zeeb } 7520566170fSBjoern A. Zeeb 7530566170fSBjoern A. Zeeb static int 7540566170fSBjoern A. Zeeb wb_attach(device_t dev) 7550566170fSBjoern A. Zeeb { 7560566170fSBjoern A. Zeeb struct wb_softc *sc; 7570566170fSBjoern A. Zeeb struct sysctl_ctx_list *sctx; 7580566170fSBjoern A. Zeeb struct sysctl_oid *soid; 7590566170fSBjoern A. Zeeb unsigned long timeout; 7600566170fSBjoern A. Zeeb int error; 761baa7dd65SAlexander Motin uint8_t t; 7620566170fSBjoern A. Zeeb 7630566170fSBjoern A. Zeeb error = wb_probe_enable(dev, 0); 7640566170fSBjoern A. Zeeb if (error > 0) 7650566170fSBjoern A. Zeeb return (ENXIO); 7660566170fSBjoern A. Zeeb 7670566170fSBjoern A. Zeeb sc = device_get_softc(dev); 7680566170fSBjoern A. Zeeb KASSERT(sc->ext_cfg_enter_f != NULL && sc->ext_cfg_exit_f != NULL, 769b790c193SPedro F. Giffuni ("%s: successful probe result but not setup correctly", __func__)); 7700566170fSBjoern A. Zeeb 7710566170fSBjoern A. Zeeb /* Watchdog is configured as part of LDN 8 (GPIO Port2, Watchdog). */ 772baa7dd65SAlexander Motin write_reg(sc, WB_LDN_REG, WB_LDN_REG_LDN8); 7730566170fSBjoern A. Zeeb 774baa7dd65SAlexander Motin /* Make sure WDT is enabled. */ 775baa7dd65SAlexander Motin write_reg(sc, WB_LDN8_CR30, 776baa7dd65SAlexander Motin read_reg(sc, WB_LDN8_CR30) | WB_LDN8_CR30_ACTIVE); 777baa7dd65SAlexander Motin 778baa7dd65SAlexander Motin switch (sc->chip) { 779baa7dd65SAlexander Motin case w83627hf: 780baa7dd65SAlexander Motin case w83627s: 781baa7dd65SAlexander Motin t = read_reg(sc, 0x2B) & ~0x10; 782baa7dd65SAlexander Motin write_reg(sc, 0x2B, t); /* set GPIO24 to WDT0 */ 783baa7dd65SAlexander Motin break; 784baa7dd65SAlexander Motin case w83697hf: 785baa7dd65SAlexander Motin /* Set pin 119 to WDTO# mode (= CR29, WDT0) */ 786baa7dd65SAlexander Motin t = read_reg(sc, 0x29) & ~0x60; 787baa7dd65SAlexander Motin t |= 0x20; 788baa7dd65SAlexander Motin write_reg(sc, 0x29, t); 789baa7dd65SAlexander Motin break; 790baa7dd65SAlexander Motin case w83697ug: 791baa7dd65SAlexander Motin /* Set pin 118 to WDTO# mode */ 792baa7dd65SAlexander Motin t = read_reg(sc, 0x2b) & ~0x04; 793baa7dd65SAlexander Motin write_reg(sc, 0x2b, t); 794baa7dd65SAlexander Motin break; 795baa7dd65SAlexander Motin case w83627thf: 796baa7dd65SAlexander Motin t = (read_reg(sc, 0x2B) & ~0x08) | 0x04; 797baa7dd65SAlexander Motin write_reg(sc, 0x2B, t); /* set GPIO3 to WDT0 */ 798baa7dd65SAlexander Motin break; 799baa7dd65SAlexander Motin case w83627dhg: 800baa7dd65SAlexander Motin case w83627dhg_p: 801baa7dd65SAlexander Motin t = read_reg(sc, 0x2D) & ~0x01; /* PIN77 -> WDT0# */ 802baa7dd65SAlexander Motin write_reg(sc, 0x2D, t); /* set GPIO5 to WDT0 */ 803baa7dd65SAlexander Motin t = read_reg(sc, sc->ctl_reg); 804baa7dd65SAlexander Motin t |= 0x02; /* enable the WDTO# output low pulse 805baa7dd65SAlexander Motin * to the KBRST# pin */ 806baa7dd65SAlexander Motin write_reg(sc, sc->ctl_reg, t); 807baa7dd65SAlexander Motin break; 808baa7dd65SAlexander Motin case w83637hf: 809baa7dd65SAlexander Motin break; 810baa7dd65SAlexander Motin case w83687thf: 811baa7dd65SAlexander Motin t = read_reg(sc, 0x2C) & ~0x80; /* PIN47 -> WDT0# */ 812baa7dd65SAlexander Motin write_reg(sc, 0x2C, t); 813baa7dd65SAlexander Motin break; 814baa7dd65SAlexander Motin case w83627ehf: 815baa7dd65SAlexander Motin case w83627uhg: 816baa7dd65SAlexander Motin case w83667hg: 817baa7dd65SAlexander Motin case w83667hg_b: 818baa7dd65SAlexander Motin case nct6775: 819baa7dd65SAlexander Motin case nct6776: 820baa7dd65SAlexander Motin case nct6779: 821baa7dd65SAlexander Motin case nct6791: 822baa7dd65SAlexander Motin case nct6792: 823f531e075SAlexander Motin case nct6793: 824f531e075SAlexander Motin case nct6795: 825baa7dd65SAlexander Motin case nct6102: 826baa7dd65SAlexander Motin /* 827baa7dd65SAlexander Motin * These chips have a fixed WDTO# output pin (W83627UHG), 828baa7dd65SAlexander Motin * or support more than one WDTO# output pin. 829baa7dd65SAlexander Motin * Don't touch its configuration, and hope the BIOS 830baa7dd65SAlexander Motin * does the right thing. 831baa7dd65SAlexander Motin */ 832baa7dd65SAlexander Motin t = read_reg(sc, sc->ctl_reg); 833baa7dd65SAlexander Motin t |= 0x02; /* enable the WDTO# output low pulse 834baa7dd65SAlexander Motin * to the KBRST# pin */ 835baa7dd65SAlexander Motin write_reg(sc, sc->ctl_reg, t); 836baa7dd65SAlexander Motin break; 837baa7dd65SAlexander Motin default: 838baa7dd65SAlexander Motin break; 839baa7dd65SAlexander Motin } 8400566170fSBjoern A. Zeeb 8410566170fSBjoern A. Zeeb /* Read the current watchdog configuration. */ 842baa7dd65SAlexander Motin sc->reg_1 = read_reg(sc, sc->ctl_reg); 843baa7dd65SAlexander Motin sc->reg_timeout = read_reg(sc, sc->time_reg); 844baa7dd65SAlexander Motin sc->reg_2 = read_reg(sc, sc->csr_reg); 8450566170fSBjoern A. Zeeb 8460566170fSBjoern A. Zeeb /* Print current state if bootverbose or watchdog already enabled. */ 8470566170fSBjoern A. Zeeb if (bootverbose || (sc->reg_timeout > 0x00)) 8480566170fSBjoern A. Zeeb wb_print_state(sc, "Before watchdog attach"); 8490566170fSBjoern A. Zeeb 850baa7dd65SAlexander Motin sc->reg_1 &= ~WB_LDN8_CRF5_KEYB_P20; 851cf864f03SRobert Noland sc->reg_1 |= WB_LDN8_CRF5_KBRST; 852baa7dd65SAlexander Motin write_reg(sc, sc->ctl_reg, sc->reg_1); 8530566170fSBjoern A. Zeeb 854baa7dd65SAlexander Motin /* 855baa7dd65SAlexander Motin * Clear a previous watchdog timeout event (if still set). 856baa7dd65SAlexander Motin * Disable timer reset on mouse interrupts. Leave reset on keyboard, 857baa7dd65SAlexander Motin * since one of my boards is getting stuck in reboot without it. 858baa7dd65SAlexander Motin */ 859baa7dd65SAlexander Motin sc->reg_2 &= ~(WB_LDN8_CRF7_MOUSE|WB_LDN8_CRF7_TS); 860baa7dd65SAlexander Motin write_reg(sc, sc->csr_reg, sc->reg_2); 861baa7dd65SAlexander Motin 862baa7dd65SAlexander Motin (*sc->ext_cfg_exit_f)(sc, 0); 8630566170fSBjoern A. Zeeb 8640566170fSBjoern A. Zeeb /* Read global timeout override tunable, Add per device sysctls. */ 8650566170fSBjoern A. Zeeb if (TUNABLE_ULONG_FETCH("hw.wbwd.timeout_override", &timeout)) { 8660566170fSBjoern A. Zeeb if (timeout > 0) 8670566170fSBjoern A. Zeeb sc->timeout_override = timeout; 8680566170fSBjoern A. Zeeb } 8690566170fSBjoern A. Zeeb sctx = device_get_sysctl_ctx(dev); 8700566170fSBjoern A. Zeeb soid = device_get_sysctl_tree(dev); 8710566170fSBjoern A. Zeeb SYSCTL_ADD_UINT(sctx, SYSCTL_CHILDREN(soid), OID_AUTO, 8720566170fSBjoern A. Zeeb "timeout_override", CTLFLAG_RW, &sc->timeout_override, 0, 8730566170fSBjoern A. Zeeb "Timeout in seconds overriding default watchdog timeout"); 8740566170fSBjoern A. Zeeb SYSCTL_ADD_INT(sctx, SYSCTL_CHILDREN(soid), OID_AUTO, 8750566170fSBjoern A. Zeeb "debug_verbose", CTLFLAG_RW, &sc->debug_verbose, 0, 8760566170fSBjoern A. Zeeb "Enables extra debugging information"); 8770566170fSBjoern A. Zeeb SYSCTL_ADD_PROC(sctx, SYSCTL_CHILDREN(soid), OID_AUTO, "debug", 8780566170fSBjoern A. Zeeb CTLTYPE_STRING|CTLFLAG_RD, sc, 0, sysctl_wb_debug, "A", 8790566170fSBjoern A. Zeeb "Selected register information from last change by driver"); 8800566170fSBjoern A. Zeeb SYSCTL_ADD_PROC(sctx, SYSCTL_CHILDREN(soid), OID_AUTO, "debug_current", 8810566170fSBjoern A. Zeeb CTLTYPE_STRING|CTLFLAG_RD|CTLFLAG_SKIP, sc, 0, 8820566170fSBjoern A. Zeeb sysctl_wb_debug_current, "A", 8830566170fSBjoern A. Zeeb "Selected register information (may interfere)"); 8840566170fSBjoern A. Zeeb SYSCTL_ADD_PROC(sctx, SYSCTL_CHILDREN(soid), OID_AUTO, "force_timeout", 8850566170fSBjoern A. Zeeb CTLTYPE_INT|CTLFLAG_RW|CTLFLAG_SKIP, sc, 0, 8860566170fSBjoern A. Zeeb sysctl_wb_force_test_nmi, "I", "Enable to force watchdog to fire."); 8870566170fSBjoern A. Zeeb 8880566170fSBjoern A. Zeeb /* Register watchdog. */ 8890566170fSBjoern A. Zeeb sc->ev_tag = EVENTHANDLER_REGISTER(watchdog_list, wb_watchdog_fn, sc, 8900566170fSBjoern A. Zeeb 0); 8910566170fSBjoern A. Zeeb 8920566170fSBjoern A. Zeeb if (bootverbose) 8930566170fSBjoern A. Zeeb wb_print_state(sc, "After watchdog attach"); 8940566170fSBjoern A. Zeeb 8950566170fSBjoern A. Zeeb return (0); 8960566170fSBjoern A. Zeeb } 8970566170fSBjoern A. Zeeb 8980566170fSBjoern A. Zeeb static int 8990566170fSBjoern A. Zeeb wb_detach(device_t dev) 9000566170fSBjoern A. Zeeb { 9010566170fSBjoern A. Zeeb struct wb_softc *sc; 9020566170fSBjoern A. Zeeb 9030566170fSBjoern A. Zeeb sc = device_get_softc(dev); 9040566170fSBjoern A. Zeeb 9050566170fSBjoern A. Zeeb /* Unregister and stop the watchdog if running. */ 9060566170fSBjoern A. Zeeb if (sc->ev_tag) 9070566170fSBjoern A. Zeeb EVENTHANDLER_DEREGISTER(watchdog_list, sc->ev_tag); 9080566170fSBjoern A. Zeeb wb_set_watchdog(sc, 0); 9090566170fSBjoern A. Zeeb 9100566170fSBjoern A. Zeeb /* Disable extended function mode. */ 9111adb4ebdSXin LI (*sc->ext_cfg_exit_f)(sc, 0); 9120566170fSBjoern A. Zeeb 9130566170fSBjoern A. Zeeb /* Cleanup resources. */ 9140566170fSBjoern A. Zeeb (void) bus_release_resource(dev, SYS_RES_IOPORT, sc->rid, sc->portres); 9150566170fSBjoern A. Zeeb 9160566170fSBjoern A. Zeeb /* Bus subroutines take care of sysctls already. */ 9170566170fSBjoern A. Zeeb 9180566170fSBjoern A. Zeeb return (0); 9190566170fSBjoern A. Zeeb } 9200566170fSBjoern A. Zeeb 9210566170fSBjoern A. Zeeb static device_method_t wb_methods[] = { 9220566170fSBjoern A. Zeeb /* Device interface */ 9231adb4ebdSXin LI DEVMETHOD(device_identify, wb_identify), 9240566170fSBjoern A. Zeeb DEVMETHOD(device_probe, wb_probe), 9250566170fSBjoern A. Zeeb DEVMETHOD(device_attach, wb_attach), 9260566170fSBjoern A. Zeeb DEVMETHOD(device_detach, wb_detach), 9270566170fSBjoern A. Zeeb 928e25fe6cdSXin LI DEVMETHOD_END 9290566170fSBjoern A. Zeeb }; 9300566170fSBjoern A. Zeeb 9310566170fSBjoern A. Zeeb static driver_t wb_isa_driver = { 9320566170fSBjoern A. Zeeb "wbwd", 9330566170fSBjoern A. Zeeb wb_methods, 9340566170fSBjoern A. Zeeb sizeof(struct wb_softc) 9350566170fSBjoern A. Zeeb }; 9360566170fSBjoern A. Zeeb 9370566170fSBjoern A. Zeeb static devclass_t wb_devclass; 9380566170fSBjoern A. Zeeb 9390566170fSBjoern A. Zeeb DRIVER_MODULE(wb, isa, wb_isa_driver, wb_devclass, NULL, NULL); 940