1*61af1d13SFabien Thomas /*- 2*61af1d13SFabien Thomas * Copyright (c) 2011 Fabien Thomas <fthomas@FreeBSD.org> 3*61af1d13SFabien Thomas * All rights reserved. 4*61af1d13SFabien Thomas * 5*61af1d13SFabien Thomas * Redistribution and use in source and binary forms, with or without 6*61af1d13SFabien Thomas * modification, are permitted provided that the following conditions 7*61af1d13SFabien Thomas * are met: 8*61af1d13SFabien Thomas * 1. Redistributions of source code must retain the above copyright 9*61af1d13SFabien Thomas * notice, this list of conditions and the following disclaimer. 10*61af1d13SFabien Thomas * 2. Redistributions in binary form must reproduce the above copyright 11*61af1d13SFabien Thomas * notice, this list of conditions and the following disclaimer in the 12*61af1d13SFabien Thomas * documentation and/or other materials provided with the distribution. 13*61af1d13SFabien Thomas * 14*61af1d13SFabien Thomas * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 15*61af1d13SFabien Thomas * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16*61af1d13SFabien Thomas * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17*61af1d13SFabien Thomas * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 18*61af1d13SFabien Thomas * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19*61af1d13SFabien Thomas * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20*61af1d13SFabien Thomas * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21*61af1d13SFabien Thomas * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22*61af1d13SFabien Thomas * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23*61af1d13SFabien Thomas * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24*61af1d13SFabien Thomas * SUCH DAMAGE. 25*61af1d13SFabien Thomas */ 26*61af1d13SFabien Thomas 27*61af1d13SFabien Thomas #include <sys/cdefs.h> 28*61af1d13SFabien Thomas __FBSDID("$FreeBSD$"); 29*61af1d13SFabien Thomas 30*61af1d13SFabien Thomas #include <sys/param.h> 31*61af1d13SFabien Thomas #include <sys/kernel.h> 32*61af1d13SFabien Thomas #include <sys/module.h> 33*61af1d13SFabien Thomas #include <sys/systm.h> 34*61af1d13SFabien Thomas #include <sys/bus.h> 35*61af1d13SFabien Thomas #include <machine/bus.h> 36*61af1d13SFabien Thomas #include <sys/rman.h> 37*61af1d13SFabien Thomas #include <machine/resource.h> 38*61af1d13SFabien Thomas #include <sys/watchdog.h> 39*61af1d13SFabien Thomas 40*61af1d13SFabien Thomas #include <isa/isavar.h> 41*61af1d13SFabien Thomas #include <dev/pci/pcivar.h> 42*61af1d13SFabien Thomas 43*61af1d13SFabien Thomas #include "viawd.h" 44*61af1d13SFabien Thomas 45*61af1d13SFabien Thomas #define viawd_read_wd_4(sc, off) \ 46*61af1d13SFabien Thomas bus_space_read_4((sc)->wd_bst, (sc)->wd_bsh, (off)) 47*61af1d13SFabien Thomas #define viawd_write_wd_4(sc, off, val) \ 48*61af1d13SFabien Thomas bus_space_write_4((sc)->wd_bst, (sc)->wd_bsh, (off), (val)) 49*61af1d13SFabien Thomas 50*61af1d13SFabien Thomas static struct viawd_device viawd_devices[] = { 51*61af1d13SFabien Thomas { DEVICEID_VT8251, "VIA VT8251 watchdog timer" }, 52*61af1d13SFabien Thomas { DEVICEID_CX700, "VIA CX700 watchdog timer" }, 53*61af1d13SFabien Thomas { DEVICEID_VX800, "VIA VX800 watchdog timer" }, 54*61af1d13SFabien Thomas { DEVICEID_VX855, "VIA VX855 watchdog timer" }, 55*61af1d13SFabien Thomas { DEVICEID_VX900, "VIA VX900 watchdog timer" }, 56*61af1d13SFabien Thomas { 0, NULL }, 57*61af1d13SFabien Thomas }; 58*61af1d13SFabien Thomas 59*61af1d13SFabien Thomas static devclass_t viawd_devclass; 60*61af1d13SFabien Thomas 61*61af1d13SFabien Thomas static device_t 62*61af1d13SFabien Thomas viawd_find(struct viawd_device **id_p) 63*61af1d13SFabien Thomas { 64*61af1d13SFabien Thomas struct viawd_device *id; 65*61af1d13SFabien Thomas device_t sb_dev = NULL; 66*61af1d13SFabien Thomas 67*61af1d13SFabien Thomas /* Look for a supported VIA south bridge. */ 68*61af1d13SFabien Thomas for (id = viawd_devices; id->desc != NULL; ++id) 69*61af1d13SFabien Thomas if ((sb_dev = pci_find_device(VENDORID_VIA, id->device)) != NULL) 70*61af1d13SFabien Thomas break; 71*61af1d13SFabien Thomas 72*61af1d13SFabien Thomas if (sb_dev == NULL) 73*61af1d13SFabien Thomas return (NULL); 74*61af1d13SFabien Thomas 75*61af1d13SFabien Thomas if (id_p != NULL) 76*61af1d13SFabien Thomas *id_p = id; 77*61af1d13SFabien Thomas 78*61af1d13SFabien Thomas return (sb_dev); 79*61af1d13SFabien Thomas } 80*61af1d13SFabien Thomas 81*61af1d13SFabien Thomas static void 82*61af1d13SFabien Thomas viawd_tmr_state(struct viawd_softc *sc, int enable) 83*61af1d13SFabien Thomas { 84*61af1d13SFabien Thomas uint32_t reg; 85*61af1d13SFabien Thomas 86*61af1d13SFabien Thomas reg = viawd_read_wd_4(sc, VIAWD_MEM_CTRL); 87*61af1d13SFabien Thomas if (enable) 88*61af1d13SFabien Thomas reg |= VIAWD_MEM_CTRL_TRIGGER | VIAWD_MEM_CTRL_ENABLE; 89*61af1d13SFabien Thomas else 90*61af1d13SFabien Thomas reg &= ~VIAWD_MEM_CTRL_ENABLE; 91*61af1d13SFabien Thomas viawd_write_wd_4(sc, VIAWD_MEM_CTRL, reg); 92*61af1d13SFabien Thomas } 93*61af1d13SFabien Thomas 94*61af1d13SFabien Thomas static void 95*61af1d13SFabien Thomas viawd_tmr_set(struct viawd_softc *sc, unsigned int timeout) 96*61af1d13SFabien Thomas { 97*61af1d13SFabien Thomas 98*61af1d13SFabien Thomas /* Keep value in range. */ 99*61af1d13SFabien Thomas if (timeout < VIAWD_MEM_COUNT_MIN) 100*61af1d13SFabien Thomas timeout = VIAWD_MEM_COUNT_MIN; 101*61af1d13SFabien Thomas else if (timeout > VIAWD_MEM_COUNT_MAX) 102*61af1d13SFabien Thomas timeout = VIAWD_MEM_COUNT_MAX; 103*61af1d13SFabien Thomas 104*61af1d13SFabien Thomas viawd_write_wd_4(sc, VIAWD_MEM_COUNT, timeout); 105*61af1d13SFabien Thomas sc->timeout = timeout; 106*61af1d13SFabien Thomas } 107*61af1d13SFabien Thomas 108*61af1d13SFabien Thomas /* 109*61af1d13SFabien Thomas * Watchdog event handler - called by the framework to enable or disable 110*61af1d13SFabien Thomas * the watchdog or change the initial timeout value. 111*61af1d13SFabien Thomas */ 112*61af1d13SFabien Thomas static void 113*61af1d13SFabien Thomas viawd_event(void *arg, unsigned int cmd, int *error) 114*61af1d13SFabien Thomas { 115*61af1d13SFabien Thomas struct viawd_softc *sc = arg; 116*61af1d13SFabien Thomas unsigned int timeout; 117*61af1d13SFabien Thomas 118*61af1d13SFabien Thomas /* Convert from power-of-two-ns to second. */ 119*61af1d13SFabien Thomas cmd &= WD_INTERVAL; 120*61af1d13SFabien Thomas timeout = ((uint64_t)1 << cmd) / 1000000000; 121*61af1d13SFabien Thomas if (cmd) { 122*61af1d13SFabien Thomas if (timeout != sc->timeout) 123*61af1d13SFabien Thomas viawd_tmr_set(sc, timeout); 124*61af1d13SFabien Thomas viawd_tmr_state(sc, 1); 125*61af1d13SFabien Thomas *error = 0; 126*61af1d13SFabien Thomas } else 127*61af1d13SFabien Thomas viawd_tmr_state(sc, 0); 128*61af1d13SFabien Thomas } 129*61af1d13SFabien Thomas 130*61af1d13SFabien Thomas static void 131*61af1d13SFabien Thomas viawd_identify(driver_t *driver, device_t parent) 132*61af1d13SFabien Thomas { 133*61af1d13SFabien Thomas device_t dev; 134*61af1d13SFabien Thomas device_t sb_dev; 135*61af1d13SFabien Thomas struct viawd_device *id_p; 136*61af1d13SFabien Thomas 137*61af1d13SFabien Thomas sb_dev = viawd_find(&id_p); 138*61af1d13SFabien Thomas if (sb_dev == NULL) 139*61af1d13SFabien Thomas return; 140*61af1d13SFabien Thomas 141*61af1d13SFabien Thomas /* Good, add child to bus. */ 142*61af1d13SFabien Thomas if ((dev = device_find_child(parent, driver->name, 0)) == NULL) 143*61af1d13SFabien Thomas dev = BUS_ADD_CHILD(parent, 0, driver->name, 0); 144*61af1d13SFabien Thomas 145*61af1d13SFabien Thomas if (dev == NULL) 146*61af1d13SFabien Thomas return; 147*61af1d13SFabien Thomas 148*61af1d13SFabien Thomas device_set_desc_copy(dev, id_p->desc); 149*61af1d13SFabien Thomas } 150*61af1d13SFabien Thomas 151*61af1d13SFabien Thomas static int 152*61af1d13SFabien Thomas viawd_probe(device_t dev) 153*61af1d13SFabien Thomas { 154*61af1d13SFabien Thomas 155*61af1d13SFabien Thomas /* Do not claim some ISA PnP device by accident. */ 156*61af1d13SFabien Thomas if (isa_get_logicalid(dev) != 0) 157*61af1d13SFabien Thomas return (ENXIO); 158*61af1d13SFabien Thomas return (0); 159*61af1d13SFabien Thomas } 160*61af1d13SFabien Thomas 161*61af1d13SFabien Thomas static int 162*61af1d13SFabien Thomas viawd_attach(device_t dev) 163*61af1d13SFabien Thomas { 164*61af1d13SFabien Thomas device_t sb_dev; 165*61af1d13SFabien Thomas struct viawd_softc *sc; 166*61af1d13SFabien Thomas struct viawd_device *id_p; 167*61af1d13SFabien Thomas uint32_t pmbase, reg; 168*61af1d13SFabien Thomas 169*61af1d13SFabien Thomas sc = device_get_softc(dev); 170*61af1d13SFabien Thomas sc->dev = dev; 171*61af1d13SFabien Thomas 172*61af1d13SFabien Thomas sb_dev = viawd_find(&id_p); 173*61af1d13SFabien Thomas if (sb_dev == NULL) { 174*61af1d13SFabien Thomas device_printf(dev, "Can not find watchdog device.\n"); 175*61af1d13SFabien Thomas goto fail; 176*61af1d13SFabien Thomas } 177*61af1d13SFabien Thomas sc->sb_dev = sb_dev; 178*61af1d13SFabien Thomas 179*61af1d13SFabien Thomas /* Get watchdog memory base. */ 180*61af1d13SFabien Thomas pmbase = pci_read_config(sb_dev, VIAWD_CONFIG_BASE, 4); 181*61af1d13SFabien Thomas if (pmbase == 0) { 182*61af1d13SFabien Thomas device_printf(dev, 183*61af1d13SFabien Thomas "Watchdog disabled in BIOS or hardware\n"); 184*61af1d13SFabien Thomas goto fail; 185*61af1d13SFabien Thomas } 186*61af1d13SFabien Thomas 187*61af1d13SFabien Thomas /* Allocate I/O register space. */ 188*61af1d13SFabien Thomas sc->wd_rid = 0; 189*61af1d13SFabien Thomas sc->wd_res = bus_alloc_resource(dev, SYS_RES_MEMORY, &sc->wd_rid, 190*61af1d13SFabien Thomas pmbase, pmbase + VIAWD_MEM_LEN - 1, VIAWD_MEM_LEN, 191*61af1d13SFabien Thomas RF_ACTIVE | RF_SHAREABLE); 192*61af1d13SFabien Thomas if (sc->wd_res == NULL) { 193*61af1d13SFabien Thomas device_printf(dev, "Unable to map watchdog memory\n"); 194*61af1d13SFabien Thomas goto fail; 195*61af1d13SFabien Thomas } 196*61af1d13SFabien Thomas sc->wd_bst = rman_get_bustag(sc->wd_res); 197*61af1d13SFabien Thomas sc->wd_bsh = rman_get_bushandle(sc->wd_res); 198*61af1d13SFabien Thomas 199*61af1d13SFabien Thomas /* Check if watchdog fired last boot. */ 200*61af1d13SFabien Thomas reg = viawd_read_wd_4(sc, VIAWD_MEM_CTRL); 201*61af1d13SFabien Thomas if (reg & VIAWD_MEM_CTRL_FIRED) { 202*61af1d13SFabien Thomas device_printf(dev, 203*61af1d13SFabien Thomas "ERROR: watchdog rebooted the system\n"); 204*61af1d13SFabien Thomas /* Reset bit state. */ 205*61af1d13SFabien Thomas viawd_write_wd_4(sc, VIAWD_MEM_CTRL, reg); 206*61af1d13SFabien Thomas } 207*61af1d13SFabien Thomas 208*61af1d13SFabien Thomas /* Register the watchdog event handler. */ 209*61af1d13SFabien Thomas sc->ev_tag = EVENTHANDLER_REGISTER(watchdog_list, viawd_event, sc, 0); 210*61af1d13SFabien Thomas 211*61af1d13SFabien Thomas return (0); 212*61af1d13SFabien Thomas fail: 213*61af1d13SFabien Thomas if (sc->wd_res != NULL) 214*61af1d13SFabien Thomas bus_release_resource(dev, SYS_RES_MEMORY, 215*61af1d13SFabien Thomas sc->wd_rid, sc->wd_res); 216*61af1d13SFabien Thomas return (ENXIO); 217*61af1d13SFabien Thomas } 218*61af1d13SFabien Thomas 219*61af1d13SFabien Thomas static int 220*61af1d13SFabien Thomas viawd_detach(device_t dev) 221*61af1d13SFabien Thomas { 222*61af1d13SFabien Thomas struct viawd_softc *sc; 223*61af1d13SFabien Thomas uint32_t reg; 224*61af1d13SFabien Thomas 225*61af1d13SFabien Thomas sc = device_get_softc(dev); 226*61af1d13SFabien Thomas 227*61af1d13SFabien Thomas /* Deregister event handler. */ 228*61af1d13SFabien Thomas if (sc->ev_tag != NULL) 229*61af1d13SFabien Thomas EVENTHANDLER_DEREGISTER(watchdog_list, sc->ev_tag); 230*61af1d13SFabien Thomas sc->ev_tag = NULL; 231*61af1d13SFabien Thomas 232*61af1d13SFabien Thomas /* 233*61af1d13SFabien Thomas * Do not stop the watchdog on shutdown if active but bump the 234*61af1d13SFabien Thomas * timer to avoid spurious reset. 235*61af1d13SFabien Thomas */ 236*61af1d13SFabien Thomas reg = viawd_read_wd_4(sc, VIAWD_MEM_CTRL); 237*61af1d13SFabien Thomas if (reg & VIAWD_MEM_CTRL_ENABLE) { 238*61af1d13SFabien Thomas viawd_tmr_set(sc, VIAWD_TIMEOUT_SHUTDOWN); 239*61af1d13SFabien Thomas viawd_tmr_state(sc, 1); 240*61af1d13SFabien Thomas device_printf(dev, 241*61af1d13SFabien Thomas "Keeping watchog alive during shutdown for %d seconds\n", 242*61af1d13SFabien Thomas VIAWD_TIMEOUT_SHUTDOWN); 243*61af1d13SFabien Thomas } 244*61af1d13SFabien Thomas 245*61af1d13SFabien Thomas if (sc->wd_res != NULL) 246*61af1d13SFabien Thomas bus_release_resource(sc->dev, SYS_RES_MEMORY, 247*61af1d13SFabien Thomas sc->wd_rid, sc->wd_res); 248*61af1d13SFabien Thomas 249*61af1d13SFabien Thomas return (0); 250*61af1d13SFabien Thomas } 251*61af1d13SFabien Thomas 252*61af1d13SFabien Thomas static device_method_t viawd_methods[] = { 253*61af1d13SFabien Thomas DEVMETHOD(device_identify, viawd_identify), 254*61af1d13SFabien Thomas DEVMETHOD(device_probe, viawd_probe), 255*61af1d13SFabien Thomas DEVMETHOD(device_attach, viawd_attach), 256*61af1d13SFabien Thomas DEVMETHOD(device_detach, viawd_detach), 257*61af1d13SFabien Thomas DEVMETHOD(device_shutdown, viawd_detach), 258*61af1d13SFabien Thomas {0,0} 259*61af1d13SFabien Thomas }; 260*61af1d13SFabien Thomas 261*61af1d13SFabien Thomas static driver_t viawd_driver = { 262*61af1d13SFabien Thomas "viawd", 263*61af1d13SFabien Thomas viawd_methods, 264*61af1d13SFabien Thomas sizeof(struct viawd_softc), 265*61af1d13SFabien Thomas }; 266*61af1d13SFabien Thomas 267*61af1d13SFabien Thomas static int 268*61af1d13SFabien Thomas viawd_modevent(module_t mode, int type, void *data) 269*61af1d13SFabien Thomas { 270*61af1d13SFabien Thomas int error = 0; 271*61af1d13SFabien Thomas 272*61af1d13SFabien Thomas switch (type) { 273*61af1d13SFabien Thomas case MOD_LOAD: 274*61af1d13SFabien Thomas printf("viawd module loaded\n"); 275*61af1d13SFabien Thomas break; 276*61af1d13SFabien Thomas case MOD_UNLOAD: 277*61af1d13SFabien Thomas printf("viawd module unloaded\n"); 278*61af1d13SFabien Thomas break; 279*61af1d13SFabien Thomas case MOD_SHUTDOWN: 280*61af1d13SFabien Thomas printf("viawd module shutting down\n"); 281*61af1d13SFabien Thomas break; 282*61af1d13SFabien Thomas } 283*61af1d13SFabien Thomas return (error); 284*61af1d13SFabien Thomas } 285*61af1d13SFabien Thomas 286*61af1d13SFabien Thomas DRIVER_MODULE(viawd, isa, viawd_driver, viawd_devclass, viawd_modevent, NULL); 287