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