1*edca4938SAndriy Gapon /*- 2*edca4938SAndriy Gapon * SPDX-License-Identifier: BSD-2-Clause 3*edca4938SAndriy Gapon * 4*edca4938SAndriy Gapon * Copyright (c) 2019 Andriy Gapon 5*edca4938SAndriy Gapon * 6*edca4938SAndriy Gapon * Redistribution and use in source and binary forms, with or without 7*edca4938SAndriy Gapon * modification, are permitted provided that the following conditions 8*edca4938SAndriy Gapon * are met: 9*edca4938SAndriy Gapon * 1. Redistributions of source code must retain the above copyright 10*edca4938SAndriy Gapon * notice, this list of conditions and the following disclaimer. 11*edca4938SAndriy Gapon * 2. Redistributions in binary form must reproduce the above copyright 12*edca4938SAndriy Gapon * notice, this list of conditions and the following disclaimer in the 13*edca4938SAndriy Gapon * documentation and/or other materials provided with the distribution. 14*edca4938SAndriy Gapon * 15*edca4938SAndriy Gapon * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 16*edca4938SAndriy Gapon * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 17*edca4938SAndriy Gapon * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 18*edca4938SAndriy Gapon * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 19*edca4938SAndriy Gapon * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 20*edca4938SAndriy Gapon * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 21*edca4938SAndriy Gapon * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 22*edca4938SAndriy Gapon * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 23*edca4938SAndriy Gapon * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 24*edca4938SAndriy Gapon * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 25*edca4938SAndriy Gapon * SUCH DAMAGE. 26*edca4938SAndriy Gapon */ 27*edca4938SAndriy Gapon 28*edca4938SAndriy Gapon #include <sys/cdefs.h> 29*edca4938SAndriy Gapon __FBSDID("$FreeBSD$"); 30*edca4938SAndriy Gapon 31*edca4938SAndriy Gapon #include <sys/param.h> 32*edca4938SAndriy Gapon #include <sys/kernel.h> 33*edca4938SAndriy Gapon #include <sys/bus.h> 34*edca4938SAndriy Gapon #include <sys/eventhandler.h> 35*edca4938SAndriy Gapon #include <sys/module.h> 36*edca4938SAndriy Gapon #include <sys/rman.h> 37*edca4938SAndriy Gapon #include <sys/systm.h> 38*edca4938SAndriy Gapon #include <sys/watchdog.h> 39*edca4938SAndriy Gapon 40*edca4938SAndriy Gapon #include <dev/superio/superio.h> 41*edca4938SAndriy Gapon 42*edca4938SAndriy Gapon #include <machine/bus.h> 43*edca4938SAndriy Gapon #include <machine/resource.h> 44*edca4938SAndriy Gapon 45*edca4938SAndriy Gapon 46*edca4938SAndriy Gapon struct itwd_softc { 47*edca4938SAndriy Gapon eventhandler_tag wd_ev; 48*edca4938SAndriy Gapon void *intr_handle; 49*edca4938SAndriy Gapon struct resource *intr_res; 50*edca4938SAndriy Gapon int intr_rid; 51*edca4938SAndriy Gapon }; 52*edca4938SAndriy Gapon 53*edca4938SAndriy Gapon static void 54*edca4938SAndriy Gapon wd_func(void *priv, u_int cmd, int *error) 55*edca4938SAndriy Gapon { 56*edca4938SAndriy Gapon device_t dev = priv; 57*edca4938SAndriy Gapon uint64_t timeout; 58*edca4938SAndriy Gapon uint8_t val; 59*edca4938SAndriy Gapon 60*edca4938SAndriy Gapon 61*edca4938SAndriy Gapon if (cmd != 0) { 62*edca4938SAndriy Gapon cmd &= WD_INTERVAL; 63*edca4938SAndriy Gapon 64*edca4938SAndriy Gapon /* 65*edca4938SAndriy Gapon * Convert the requested timeout to seconds. 66*edca4938SAndriy Gapon * If the timeout is smaller than the minimal supported value 67*edca4938SAndriy Gapon * then set it to the minimum. 68*edca4938SAndriy Gapon * TODO This hardware actually supports 64ms resolution 69*edca4938SAndriy Gapon * when bit 5 of 0x72 is set. Switch to that resolution when 70*edca4938SAndriy Gapon * needed. 71*edca4938SAndriy Gapon */ 72*edca4938SAndriy Gapon if (cmd >= WD_TO_1SEC) 73*edca4938SAndriy Gapon timeout = (uint64_t)1 << (cmd - WD_TO_1SEC); 74*edca4938SAndriy Gapon else 75*edca4938SAndriy Gapon timeout = 1; 76*edca4938SAndriy Gapon 77*edca4938SAndriy Gapon /* TODO If timeout is greater than maximum value 78*edca4938SAndriy Gapon * that can be specified in seconds, we should 79*edca4938SAndriy Gapon * switch the timer to minutes mode by clearing 80*edca4938SAndriy Gapon * bit 7 of 0x72 (ensure that bit 5 is also cleared). 81*edca4938SAndriy Gapon * 82*edca4938SAndriy Gapon * For now, just disable the timer to honor the 83*edca4938SAndriy Gapon * watchdog(9) protocol. 84*edca4938SAndriy Gapon * 85*edca4938SAndriy Gapon * XXX The timeout actually can be up to 65535 units 86*edca4938SAndriy Gapon * as it is set via two registers 0x73, LSB, and 0x74, 87*edca4938SAndriy Gapon * MSB. But it is not clear what the protocol for writing 88*edca4938SAndriy Gapon * those two registers is. 89*edca4938SAndriy Gapon */ 90*edca4938SAndriy Gapon if (timeout <= UINT8_MAX) { 91*edca4938SAndriy Gapon val = timeout; 92*edca4938SAndriy Gapon *error = 0; 93*edca4938SAndriy Gapon } else { 94*edca4938SAndriy Gapon /* error left unchanged */ 95*edca4938SAndriy Gapon val = 0; 96*edca4938SAndriy Gapon } 97*edca4938SAndriy Gapon } else { 98*edca4938SAndriy Gapon val = 0; 99*edca4938SAndriy Gapon } 100*edca4938SAndriy Gapon #ifdef DIAGNOSTIC 101*edca4938SAndriy Gapon if (bootverbose) 102*edca4938SAndriy Gapon device_printf(dev, "setting timeout to %d\n", val); 103*edca4938SAndriy Gapon #endif 104*edca4938SAndriy Gapon superio_write(dev, 0x73, val); 105*edca4938SAndriy Gapon if (superio_read(dev, 0x73) != val) 106*edca4938SAndriy Gapon superio_write(dev, 0x73, val); 107*edca4938SAndriy Gapon } 108*edca4938SAndriy Gapon 109*edca4938SAndriy Gapon static void 110*edca4938SAndriy Gapon itwd_intr(void *cookie) 111*edca4938SAndriy Gapon { 112*edca4938SAndriy Gapon device_t dev = cookie; 113*edca4938SAndriy Gapon uint8_t val; 114*edca4938SAndriy Gapon 115*edca4938SAndriy Gapon val = superio_read(dev, 0x71); 116*edca4938SAndriy Gapon if (bootverbose) 117*edca4938SAndriy Gapon device_printf(dev, "got interrupt, wdt status = %d\n", val & 1); 118*edca4938SAndriy Gapon superio_write(dev, 0x71, val & ~((uint8_t)0x01)); 119*edca4938SAndriy Gapon } 120*edca4938SAndriy Gapon 121*edca4938SAndriy Gapon static int 122*edca4938SAndriy Gapon itwd_probe(device_t dev) 123*edca4938SAndriy Gapon { 124*edca4938SAndriy Gapon 125*edca4938SAndriy Gapon if (superio_vendor(dev) != SUPERIO_VENDOR_ITE) 126*edca4938SAndriy Gapon return (ENXIO); 127*edca4938SAndriy Gapon if (superio_get_type(dev) != SUPERIO_DEV_WDT) 128*edca4938SAndriy Gapon return (ENXIO); 129*edca4938SAndriy Gapon device_set_desc(dev, "Watchdog Timer on ITE SuperIO"); 130*edca4938SAndriy Gapon return (BUS_PROBE_DEFAULT); 131*edca4938SAndriy Gapon } 132*edca4938SAndriy Gapon 133*edca4938SAndriy Gapon static int 134*edca4938SAndriy Gapon itwd_attach(device_t dev) 135*edca4938SAndriy Gapon { 136*edca4938SAndriy Gapon struct itwd_softc *sc = device_get_softc(dev); 137*edca4938SAndriy Gapon int irq = 0; 138*edca4938SAndriy Gapon int nmi = 0; 139*edca4938SAndriy Gapon int error; 140*edca4938SAndriy Gapon 141*edca4938SAndriy Gapon /* First, reset the timeout, just in case. */ 142*edca4938SAndriy Gapon superio_write(dev, 0x74, 0); 143*edca4938SAndriy Gapon superio_write(dev, 0x73, 0); 144*edca4938SAndriy Gapon 145*edca4938SAndriy Gapon TUNABLE_INT_FETCH("dev.itwd.irq", &irq); 146*edca4938SAndriy Gapon TUNABLE_INT_FETCH("dev.itwd.nmi", &nmi); 147*edca4938SAndriy Gapon if (irq < 0 || irq > 15) { 148*edca4938SAndriy Gapon device_printf(dev, "Ignoring invalid IRQ value %d\n", irq); 149*edca4938SAndriy Gapon irq = 0; 150*edca4938SAndriy Gapon } 151*edca4938SAndriy Gapon if (irq == 0 && nmi) { 152*edca4938SAndriy Gapon device_printf(dev, "Ignoring NMI mode if IRQ is not set\n"); 153*edca4938SAndriy Gapon nmi = 0; 154*edca4938SAndriy Gapon } 155*edca4938SAndriy Gapon 156*edca4938SAndriy Gapon /* 157*edca4938SAndriy Gapon * NB: if the interrupt has been configured for the NMI delivery, 158*edca4938SAndriy Gapon * then it is not available for the regular interrupt allocation. 159*edca4938SAndriy Gapon * Thus, we configure the hardware to generate the interrupt, 160*edca4938SAndriy Gapon * but do not attempt to allocate and setup it as a regular 161*edca4938SAndriy Gapon * interrupt. 162*edca4938SAndriy Gapon */ 163*edca4938SAndriy Gapon if (irq != 0 && !nmi) { 164*edca4938SAndriy Gapon sc->intr_rid = 0; 165*edca4938SAndriy Gapon bus_set_resource(dev, SYS_RES_IRQ, sc->intr_rid, irq, 1); 166*edca4938SAndriy Gapon 167*edca4938SAndriy Gapon sc->intr_res = bus_alloc_resource_any(dev, SYS_RES_IRQ, 168*edca4938SAndriy Gapon &sc->intr_rid, RF_ACTIVE); 169*edca4938SAndriy Gapon if (sc->intr_res == NULL) { 170*edca4938SAndriy Gapon device_printf(dev, "unable to map interrupt\n"); 171*edca4938SAndriy Gapon return (ENXIO); 172*edca4938SAndriy Gapon } 173*edca4938SAndriy Gapon error = bus_setup_intr(dev, sc->intr_res, 174*edca4938SAndriy Gapon INTR_TYPE_MISC | INTR_MPSAFE, NULL, itwd_intr, dev, 175*edca4938SAndriy Gapon &sc->intr_handle); 176*edca4938SAndriy Gapon if (error != 0) { 177*edca4938SAndriy Gapon bus_release_resource(dev, SYS_RES_IRQ, 178*edca4938SAndriy Gapon sc->intr_rid, sc->intr_res); 179*edca4938SAndriy Gapon device_printf(dev, "Unable to setup irq: error %d\n", 180*edca4938SAndriy Gapon error); 181*edca4938SAndriy Gapon return (ENXIO); 182*edca4938SAndriy Gapon } 183*edca4938SAndriy Gapon } 184*edca4938SAndriy Gapon if (irq != 0) { 185*edca4938SAndriy Gapon device_printf(dev, "Using IRQ%d to signal timeout\n", irq); 186*edca4938SAndriy Gapon } else { 187*edca4938SAndriy Gapon /* System reset via KBRST. */ 188*edca4938SAndriy Gapon irq = 0x40; 189*edca4938SAndriy Gapon device_printf(dev, "Configured for system reset on timeout\n"); 190*edca4938SAndriy Gapon } 191*edca4938SAndriy Gapon 192*edca4938SAndriy Gapon superio_write(dev, 0x71, 0); 193*edca4938SAndriy Gapon superio_write(dev, 0x72, 0x80 | (uint8_t)irq); 194*edca4938SAndriy Gapon 195*edca4938SAndriy Gapon sc->wd_ev = EVENTHANDLER_REGISTER(watchdog_list, wd_func, dev, 0); 196*edca4938SAndriy Gapon return (0); 197*edca4938SAndriy Gapon } 198*edca4938SAndriy Gapon 199*edca4938SAndriy Gapon static int 200*edca4938SAndriy Gapon itwd_detach(device_t dev) 201*edca4938SAndriy Gapon { 202*edca4938SAndriy Gapon struct itwd_softc *sc = device_get_softc(dev); 203*edca4938SAndriy Gapon int dummy; 204*edca4938SAndriy Gapon 205*edca4938SAndriy Gapon if (sc->wd_ev != NULL) 206*edca4938SAndriy Gapon EVENTHANDLER_DEREGISTER(watchdog_list, sc->wd_ev); 207*edca4938SAndriy Gapon wd_func(dev, 0, &dummy); 208*edca4938SAndriy Gapon if (sc->intr_handle) 209*edca4938SAndriy Gapon bus_teardown_intr(dev, sc->intr_res, sc->intr_handle); 210*edca4938SAndriy Gapon if (sc->intr_res) 211*edca4938SAndriy Gapon bus_release_resource(dev, SYS_RES_IRQ, sc->intr_rid, 212*edca4938SAndriy Gapon sc->intr_res); 213*edca4938SAndriy Gapon return (0); 214*edca4938SAndriy Gapon } 215*edca4938SAndriy Gapon 216*edca4938SAndriy Gapon static device_method_t itwd_methods[] = { 217*edca4938SAndriy Gapon /* Methods from the device interface */ 218*edca4938SAndriy Gapon DEVMETHOD(device_probe, itwd_probe), 219*edca4938SAndriy Gapon DEVMETHOD(device_attach, itwd_attach), 220*edca4938SAndriy Gapon DEVMETHOD(device_detach, itwd_detach), 221*edca4938SAndriy Gapon 222*edca4938SAndriy Gapon /* Terminate method list */ 223*edca4938SAndriy Gapon { 0, 0 } 224*edca4938SAndriy Gapon }; 225*edca4938SAndriy Gapon 226*edca4938SAndriy Gapon static driver_t itwd_driver = { 227*edca4938SAndriy Gapon "itwd", 228*edca4938SAndriy Gapon itwd_methods, 229*edca4938SAndriy Gapon sizeof (struct itwd_softc) 230*edca4938SAndriy Gapon }; 231*edca4938SAndriy Gapon 232*edca4938SAndriy Gapon static devclass_t itwd_devclass; 233*edca4938SAndriy Gapon 234*edca4938SAndriy Gapon DRIVER_MODULE(itwd, superio, itwd_driver, itwd_devclass, NULL, NULL); 235*edca4938SAndriy Gapon MODULE_DEPEND(itwd, superio, 1, 1, 1); 236*edca4938SAndriy Gapon MODULE_VERSION(itwd, 1); 237