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