1 /* $OpenBSD: ichwdt.c,v 1.3 2009/03/29 21:53:52 sthen 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 57 int ichwdt_cb(void *, int); 58 59 struct cfattach ichwdt_ca = { 60 sizeof(struct ichwdt_softc), 61 ichwdt_match, 62 ichwdt_attach 63 }; 64 65 struct cfdriver ichwdt_cd = { 66 NULL, "ichwdt", DV_DULL 67 }; 68 69 const struct pci_matchid ichwdt_devices[] = { 70 { PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_6300ESB_WDT } 71 }; 72 73 static __inline void 74 ichwdt_unlock_write(struct ichwdt_softc *sc, int reg, u_int32_t val) 75 { 76 /* Register unlocking sequence */ 77 bus_space_write_4(sc->sc_iot, sc->sc_ioh, ICH_WDT_RELOAD, 0x80); 78 bus_space_write_4(sc->sc_iot, sc->sc_ioh, ICH_WDT_RELOAD, 0x86); 79 80 /* Now it's possible to write to the register */ 81 bus_space_write_4(sc->sc_iot, sc->sc_ioh, reg, val); 82 } 83 84 int 85 ichwdt_match(struct device *parent, void *match, void *aux) 86 { 87 return (pci_matchbyid((struct pci_attach_args *)aux, ichwdt_devices, 88 sizeof(ichwdt_devices) / sizeof(ichwdt_devices[0]))); 89 } 90 91 void 92 ichwdt_attach(struct device *parent, struct device *self, void *aux) 93 { 94 struct ichwdt_softc *sc = (struct ichwdt_softc *)self; 95 struct pci_attach_args *pa = aux; 96 u_int32_t reg; 97 98 sc->sc_pc = pa->pa_pc; 99 sc->sc_tag = pa->pa_tag; 100 101 /* Map memory space */ 102 sc->sc_iot = pa->pa_iot; 103 if (pci_mapreg_map(pa, ICH_WDT_BASE, PCI_MAPREG_TYPE_MEM, 0, 104 &sc->sc_iot, &sc->sc_ioh, NULL, NULL, 0)) { 105 printf(": can't map mem space\n"); 106 return; 107 } 108 109 /* Read current configuration */ 110 reg = pci_conf_read(sc->sc_pc, sc->sc_tag, ICH_WDT_CONF); 111 DPRINTF((": conf 0x%x", reg)); 112 113 /* Get clock divisor */ 114 sc->sc_divisor = (reg & ICH_WDT_CONF_PRE ? 32 : 32768); 115 printf(": %s clock", (reg & ICH_WDT_CONF_PRE ? "1MHz" : "1kHz")); 116 117 /* Disable interrupts since we don't use first stage timeout alarm */ 118 reg &= ~ICH_WDT_CONF_INT_MASK; 119 reg |= ICH_WDT_CONF_INT_DIS; 120 pci_conf_write(sc->sc_pc, sc->sc_tag, ICH_WDT_CONF, reg); 121 122 /* Check for reboot on timeout */ 123 reg = bus_space_read_4(sc->sc_iot, sc->sc_ioh, ICH_WDT_RELOAD); 124 if (reg & ICH_WDT_RELOAD_TIMEOUT) { 125 printf(": reboot on timeout"); 126 127 /* Clear timeout bit */ 128 ichwdt_unlock_write(sc, ICH_WDT_RELOAD, 129 ICH_WDT_RELOAD_TIMEOUT); 130 } 131 132 /* Disable watchdog */ 133 pci_conf_write(sc->sc_pc, sc->sc_tag, ICH_WDT_LOCK, 0); 134 sc->sc_period = 0; 135 136 printf("\n"); 137 138 /* Register new watchdog */ 139 wdog_register(sc, ichwdt_cb); 140 } 141 142 int 143 ichwdt_cb(void *arg, int period) 144 { 145 struct ichwdt_softc *sc = arg; 146 int ticks; 147 148 if (period == 0) { 149 if (sc->sc_period != 0) { 150 /* Disable watchdog */ 151 ichwdt_unlock_write(sc, ICH_WDT_RELOAD, 152 ICH_WDT_RELOAD_RLD); 153 pci_conf_write(sc->sc_pc, sc->sc_tag, ICH_WDT_LOCK, 0); 154 DPRINTF(("%s: disabled, conf 0x%x\n", 155 sc->sc_dev.dv_xname, 156 pci_conf_read(sc->sc_pc, sc->sc_tag, 157 ICH_WDT_LOCK))); 158 } 159 } else { 160 /* 1000s should be enough for everyone */ 161 if (period > 1000) 162 period = 1000; 163 164 if (sc->sc_period != period) { 165 /* Set new timeout */ 166 ticks = (period * 33000000) / sc->sc_divisor; 167 ichwdt_unlock_write(sc, ICH_WDT_PRE1, ticks); 168 ichwdt_unlock_write(sc, ICH_WDT_PRE2, 2); 169 DPRINTF(("%s: timeout %ds (%d ticks)\n", 170 sc->sc_dev.dv_xname, period, ticks)); 171 } 172 if (sc->sc_period == 0) { 173 /* Enable watchdog */ 174 pci_conf_write(sc->sc_pc, sc->sc_tag, ICH_WDT_LOCK, 175 ICH_WDT_LOCK_ENABLED); 176 DPRINTF(("%s: enabled, conf 0x%x\n", 177 sc->sc_dev.dv_xname, 178 pci_conf_read(sc->sc_pc, sc->sc_tag, 179 ICH_WDT_LOCK))); 180 } else { 181 /* Reset timer */ 182 ichwdt_unlock_write(sc, ICH_WDT_RELOAD, 183 ICH_WDT_RELOAD_RLD); 184 DPRINTF(("%s: reloaded\n", sc->sc_dev.dv_xname)); 185 } 186 } 187 sc->sc_period = period; 188 189 return (period); 190 } 191