1 /* $OpenBSD: ichwdt.c,v 1.6 2016/03/19 11:34:22 mpi Exp $ */ 2 3 /* 4 * Copyright (c) 2004, 2005 Alexander Yurchenko <grange@openbsd.org> 5 * 6 * Permission to use, copy, modify, and distribute this software for any 7 * purpose with or without fee is hereby granted, provided that the above 8 * copyright notice and this permission notice appear in all copies. 9 * 10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 13 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 14 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 15 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 16 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17 */ 18 19 /* 20 * Intel 6300ESB ICH watchdog timer driver. 21 */ 22 23 #include <sys/param.h> 24 #include <sys/systm.h> 25 #include <sys/device.h> 26 27 #include <machine/bus.h> 28 29 #include <dev/pci/pcireg.h> 30 #include <dev/pci/pcivar.h> 31 #include <dev/pci/pcidevs.h> 32 33 #include <dev/pci/ichreg.h> 34 35 #ifdef ICHWDT_DEBUG 36 #define DPRINTF(x) printf x 37 #else 38 #define DPRINTF(x) 39 #endif 40 41 struct ichwdt_softc { 42 struct device sc_dev; 43 44 pci_chipset_tag_t sc_pc; 45 pcitag_t sc_tag; 46 47 bus_space_tag_t sc_iot; 48 bus_space_handle_t sc_ioh; 49 50 int sc_divisor; 51 int sc_period; 52 }; 53 54 int ichwdt_match(struct device *, void *, void *); 55 void ichwdt_attach(struct device *, struct device *, void *); 56 int ichwdt_activate(struct device *, int); 57 58 int ichwdt_cb(void *, int); 59 60 struct cfattach ichwdt_ca = { 61 sizeof(struct ichwdt_softc), 62 ichwdt_match, 63 ichwdt_attach, 64 NULL, 65 ichwdt_activate 66 }; 67 68 struct cfdriver ichwdt_cd = { 69 NULL, "ichwdt", DV_DULL 70 }; 71 72 const struct pci_matchid ichwdt_devices[] = { 73 { PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_6300ESB_WDT } 74 }; 75 76 static __inline void 77 ichwdt_unlock_write(struct ichwdt_softc *sc, int reg, u_int32_t val) 78 { 79 /* Register unlocking sequence */ 80 bus_space_write_4(sc->sc_iot, sc->sc_ioh, ICH_WDT_RELOAD, 0x80); 81 bus_space_write_4(sc->sc_iot, sc->sc_ioh, ICH_WDT_RELOAD, 0x86); 82 83 /* Now it's possible to write to the register */ 84 bus_space_write_4(sc->sc_iot, sc->sc_ioh, reg, val); 85 } 86 87 int 88 ichwdt_match(struct device *parent, void *match, void *aux) 89 { 90 return (pci_matchbyid((struct pci_attach_args *)aux, ichwdt_devices, 91 sizeof(ichwdt_devices) / sizeof(ichwdt_devices[0]))); 92 } 93 94 void 95 ichwdt_attach(struct device *parent, struct device *self, void *aux) 96 { 97 struct ichwdt_softc *sc = (struct ichwdt_softc *)self; 98 struct pci_attach_args *pa = aux; 99 u_int32_t reg; 100 101 sc->sc_pc = pa->pa_pc; 102 sc->sc_tag = pa->pa_tag; 103 104 /* Map memory space */ 105 sc->sc_iot = pa->pa_iot; 106 if (pci_mapreg_map(pa, ICH_WDT_BASE, PCI_MAPREG_TYPE_MEM, 0, 107 &sc->sc_iot, &sc->sc_ioh, NULL, NULL, 0)) { 108 printf(": can't map mem space\n"); 109 return; 110 } 111 112 /* Read current configuration */ 113 reg = pci_conf_read(sc->sc_pc, sc->sc_tag, ICH_WDT_CONF); 114 DPRINTF((": conf 0x%x", reg)); 115 116 /* Get clock divisor */ 117 sc->sc_divisor = (reg & ICH_WDT_CONF_PRE ? 32 : 32768); 118 printf(": %s clock", (reg & ICH_WDT_CONF_PRE ? "1MHz" : "1kHz")); 119 120 /* Disable interrupts since we don't use first stage timeout alarm */ 121 reg &= ~ICH_WDT_CONF_INT_MASK; 122 reg |= ICH_WDT_CONF_INT_DIS; 123 pci_conf_write(sc->sc_pc, sc->sc_tag, ICH_WDT_CONF, reg); 124 125 /* Check for reboot on timeout */ 126 reg = bus_space_read_4(sc->sc_iot, sc->sc_ioh, ICH_WDT_RELOAD); 127 if (reg & ICH_WDT_RELOAD_TIMEOUT) { 128 printf(": reboot on timeout"); 129 130 /* Clear timeout bit */ 131 ichwdt_unlock_write(sc, ICH_WDT_RELOAD, 132 ICH_WDT_RELOAD_TIMEOUT); 133 } 134 135 /* Disable watchdog */ 136 pci_conf_write(sc->sc_pc, sc->sc_tag, ICH_WDT_LOCK, 0); 137 sc->sc_period = 0; 138 139 printf("\n"); 140 141 /* Register new watchdog */ 142 wdog_register(ichwdt_cb, sc); 143 } 144 145 int 146 ichwdt_activate(struct device *self, int act) 147 { 148 switch (act) { 149 case DVACT_POWERDOWN: 150 wdog_shutdown(self); 151 break; 152 } 153 154 return (0); 155 } 156 157 int 158 ichwdt_cb(void *arg, int period) 159 { 160 struct ichwdt_softc *sc = arg; 161 int nticks; 162 163 if (period == 0) { 164 if (sc->sc_period != 0) { 165 /* Disable watchdog */ 166 ichwdt_unlock_write(sc, ICH_WDT_RELOAD, 167 ICH_WDT_RELOAD_RLD); 168 pci_conf_write(sc->sc_pc, sc->sc_tag, ICH_WDT_LOCK, 0); 169 DPRINTF(("%s: disabled, conf 0x%x\n", 170 sc->sc_dev.dv_xname, 171 pci_conf_read(sc->sc_pc, sc->sc_tag, 172 ICH_WDT_LOCK))); 173 } 174 } else { 175 /* 1000s should be enough for everyone */ 176 if (period > 1000) 177 period = 1000; 178 179 if (sc->sc_period != period) { 180 /* Set new timeout */ 181 nticks = (period * 33000000) / sc->sc_divisor; 182 ichwdt_unlock_write(sc, ICH_WDT_PRE1, nticks); 183 ichwdt_unlock_write(sc, ICH_WDT_PRE2, 2); 184 DPRINTF(("%s: timeout %ds (%d nticks)\n", 185 sc->sc_dev.dv_xname, period, nticks)); 186 } 187 if (sc->sc_period == 0) { 188 /* Enable watchdog */ 189 pci_conf_write(sc->sc_pc, sc->sc_tag, ICH_WDT_LOCK, 190 ICH_WDT_LOCK_ENABLED); 191 DPRINTF(("%s: enabled, conf 0x%x\n", 192 sc->sc_dev.dv_xname, 193 pci_conf_read(sc->sc_pc, sc->sc_tag, 194 ICH_WDT_LOCK))); 195 } else { 196 /* Reset timer */ 197 ichwdt_unlock_write(sc, ICH_WDT_RELOAD, 198 ICH_WDT_RELOAD_RLD); 199 DPRINTF(("%s: reloaded\n", sc->sc_dev.dv_xname)); 200 } 201 } 202 sc->sc_period = period; 203 204 return (period); 205 } 206