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 __FBSDID("$FreeBSD$"); 30 31 #include <sys/param.h> 32 #include <sys/kernel.h> 33 #include <sys/bus.h> 34 #include <sys/eventhandler.h> 35 #include <sys/module.h> 36 #include <sys/rman.h> 37 #include <sys/systm.h> 38 #include <sys/watchdog.h> 39 40 #include <dev/superio/superio.h> 41 42 #include <machine/bus.h> 43 #include <machine/resource.h> 44 45 46 struct itwd_softc { 47 eventhandler_tag wd_ev; 48 void *intr_handle; 49 struct resource *intr_res; 50 int intr_rid; 51 }; 52 53 static void 54 wd_func(void *priv, u_int cmd, int *error) 55 { 56 device_t dev = priv; 57 uint64_t timeout; 58 uint8_t val; 59 60 61 if (cmd != 0) { 62 cmd &= WD_INTERVAL; 63 64 /* 65 * Convert the requested timeout to seconds. 66 * If the timeout is smaller than the minimal supported value 67 * then set it to the minimum. 68 * TODO This hardware actually supports 64ms resolution 69 * when bit 5 of 0x72 is set. Switch to that resolution when 70 * needed. 71 */ 72 if (cmd >= WD_TO_1SEC) 73 timeout = (uint64_t)1 << (cmd - WD_TO_1SEC); 74 else 75 timeout = 1; 76 77 /* TODO If timeout is greater than maximum value 78 * that can be specified in seconds, we should 79 * switch the timer to minutes mode by clearing 80 * bit 7 of 0x72 (ensure that bit 5 is also cleared). 81 * 82 * For now, just disable the timer to honor the 83 * watchdog(9) protocol. 84 * 85 * XXX The timeout actually can be up to 65535 units 86 * as it is set via two registers 0x73, LSB, and 0x74, 87 * MSB. But it is not clear what the protocol for writing 88 * those two registers is. 89 */ 90 if (timeout <= UINT8_MAX) { 91 val = timeout; 92 *error = 0; 93 } else { 94 /* error left unchanged */ 95 val = 0; 96 } 97 } else { 98 val = 0; 99 } 100 #ifdef DIAGNOSTIC 101 if (bootverbose) 102 device_printf(dev, "setting timeout to %d\n", val); 103 #endif 104 superio_write(dev, 0x73, val); 105 if (superio_read(dev, 0x73) != val) 106 superio_write(dev, 0x73, val); 107 } 108 109 static void 110 itwd_intr(void *cookie) 111 { 112 device_t dev = cookie; 113 uint8_t val; 114 115 val = superio_read(dev, 0x71); 116 if (bootverbose) 117 device_printf(dev, "got interrupt, wdt status = %d\n", val & 1); 118 superio_write(dev, 0x71, val & ~((uint8_t)0x01)); 119 } 120 121 static int 122 itwd_probe(device_t dev) 123 { 124 125 if (superio_vendor(dev) != SUPERIO_VENDOR_ITE) 126 return (ENXIO); 127 if (superio_get_type(dev) != SUPERIO_DEV_WDT) 128 return (ENXIO); 129 device_set_desc(dev, "Watchdog Timer on ITE SuperIO"); 130 return (BUS_PROBE_DEFAULT); 131 } 132 133 static int 134 itwd_attach(device_t dev) 135 { 136 struct itwd_softc *sc = device_get_softc(dev); 137 int irq = 0; 138 int nmi = 0; 139 int error; 140 141 /* First, reset the timeout, just in case. */ 142 superio_write(dev, 0x74, 0); 143 superio_write(dev, 0x73, 0); 144 145 TUNABLE_INT_FETCH("dev.itwd.irq", &irq); 146 TUNABLE_INT_FETCH("dev.itwd.nmi", &nmi); 147 if (irq < 0 || irq > 15) { 148 device_printf(dev, "Ignoring invalid IRQ value %d\n", irq); 149 irq = 0; 150 } 151 if (irq == 0 && nmi) { 152 device_printf(dev, "Ignoring NMI mode if IRQ is not set\n"); 153 nmi = 0; 154 } 155 156 /* 157 * NB: if the interrupt has been configured for the NMI delivery, 158 * then it is not available for the regular interrupt allocation. 159 * Thus, we configure the hardware to generate the interrupt, 160 * but do not attempt to allocate and setup it as a regular 161 * interrupt. 162 */ 163 if (irq != 0 && !nmi) { 164 sc->intr_rid = 0; 165 bus_set_resource(dev, SYS_RES_IRQ, sc->intr_rid, irq, 1); 166 167 sc->intr_res = bus_alloc_resource_any(dev, SYS_RES_IRQ, 168 &sc->intr_rid, RF_ACTIVE); 169 if (sc->intr_res == NULL) { 170 device_printf(dev, "unable to map interrupt\n"); 171 return (ENXIO); 172 } 173 error = bus_setup_intr(dev, sc->intr_res, 174 INTR_TYPE_MISC | INTR_MPSAFE, NULL, itwd_intr, dev, 175 &sc->intr_handle); 176 if (error != 0) { 177 bus_release_resource(dev, SYS_RES_IRQ, 178 sc->intr_rid, sc->intr_res); 179 device_printf(dev, "Unable to setup irq: error %d\n", 180 error); 181 return (ENXIO); 182 } 183 } 184 if (irq != 0) { 185 device_printf(dev, "Using IRQ%d to signal timeout\n", irq); 186 } else { 187 /* System reset via KBRST. */ 188 irq = 0x40; 189 device_printf(dev, "Configured for system reset on timeout\n"); 190 } 191 192 superio_write(dev, 0x71, 0); 193 superio_write(dev, 0x72, 0x80 | (uint8_t)irq); 194 195 sc->wd_ev = EVENTHANDLER_REGISTER(watchdog_list, wd_func, dev, 0); 196 return (0); 197 } 198 199 static int 200 itwd_detach(device_t dev) 201 { 202 struct itwd_softc *sc = device_get_softc(dev); 203 int dummy; 204 205 if (sc->wd_ev != NULL) 206 EVENTHANDLER_DEREGISTER(watchdog_list, sc->wd_ev); 207 wd_func(dev, 0, &dummy); 208 if (sc->intr_handle) 209 bus_teardown_intr(dev, sc->intr_res, sc->intr_handle); 210 if (sc->intr_res) 211 bus_release_resource(dev, SYS_RES_IRQ, sc->intr_rid, 212 sc->intr_res); 213 return (0); 214 } 215 216 static device_method_t itwd_methods[] = { 217 /* Methods from the device interface */ 218 DEVMETHOD(device_probe, itwd_probe), 219 DEVMETHOD(device_attach, itwd_attach), 220 DEVMETHOD(device_detach, itwd_detach), 221 222 /* Terminate method list */ 223 { 0, 0 } 224 }; 225 226 static driver_t itwd_driver = { 227 "itwd", 228 itwd_methods, 229 sizeof (struct itwd_softc) 230 }; 231 232 DRIVER_MODULE(itwd, superio, itwd_driver, NULL, NULL); 233 MODULE_DEPEND(itwd, superio, 1, 1, 1); 234 MODULE_VERSION(itwd, 1); 235