1 /* $NetBSD: tcpcib.c,v 1.1 2012/12/05 16:19:46 christos Exp $ */ 2 /* $OpenBSD: tcpcib.c,v 1.4 2012/10/17 22:32:01 deraadt Exp $ */ 3 4 /* 5 * Copyright (c) 2012 Matt Dainty <matt@bodgit-n-scarper.com> 6 * 7 * Permission to use, copy, modify, and distribute this software for any 8 * purpose with or without fee is hereby granted, provided that the above 9 * copyright notice and this permission notice appear in all copies. 10 * 11 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 12 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 13 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 14 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 15 * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER IN 16 * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT 17 * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 18 */ 19 20 /* 21 * Intel Atom E600 series LPC bridge also containing HPET and watchdog 22 */ 23 24 #include <sys/cdefs.h> 25 __KERNEL_RCSID(0, "$NetBSD: tcpcib.c,v 1.1 2012/12/05 16:19:46 christos Exp $"); 26 27 #include <sys/param.h> 28 #include <sys/systm.h> 29 #include <sys/device.h> 30 #include <sys/timetc.h> 31 #include <sys/bus.h> 32 33 #include <dev/pci/pcireg.h> 34 #include <dev/pci/pcivar.h> 35 #include <dev/pci/pcidevs.h> 36 37 #include <dev/ic/i82801lpcvar.h> 38 39 #include <dev/sysmon/sysmonvar.h> 40 41 #include "pcibvar.h" 42 43 #define E600_LPC_SMBA 0x40 /* SMBus Base Address */ 44 #define E600_LPC_GBA 0x44 /* GPIO Base Address */ 45 #define E600_LPC_WDTBA 0x84 /* WDT Base Address */ 46 47 #define E600_WDT_SIZE 64 /* I/O region size */ 48 #define E600_WDT_PV1 0x00 /* Preload Value 1 Register */ 49 #define E600_WDT_PV2 0x04 /* Preload Value 2 Register */ 50 #define E600_WDT_RR0 0x0c /* Reload Register 0 */ 51 #define E600_WDT_RR1 0x0d /* Reload Register 1 */ 52 #define E600_WDT_RR1_RELOAD (1 << 0) /* WDT Reload Flag */ 53 #define E600_WDT_RR1_TIMEOUT (1 << 1) /* WDT Timeout Flag */ 54 #define E600_WDT_WDTCR 0x10 /* WDT Configuration Register */ 55 #define E600_WDT_WDTCR_PRE (1 << 2) /* WDT Prescalar Select */ 56 #define E600_WDT_WDTCR_RESET (1 << 3) /* WDT Reset Select */ 57 #define E600_WDT_WDTCR_ENABLE (1 << 4) /* WDT Reset Enable */ 58 #define E600_WDT_WDTCR_TIMEOUT (1 << 5) /* WDT Timeout Output Enable */ 59 #define E600_WDT_DCR 0x14 /* Down Counter Register */ 60 #define E600_WDT_WDTLR 0x18 /* WDT Lock Register */ 61 #define E600_WDT_WDTLR_LOCK (1 << 0) /* Watchdog Timer Lock */ 62 #define E600_WDT_WDTLR_ENABLE (1 << 1) /* Watchdog Timer Enable */ 63 #define E600_WDT_WDTLR_TIMEOUT (1 << 2) /* WDT Timeout Configuration */ 64 65 #define E600_HPET_BASE 0xfed00000 /* HPET register base */ 66 #define E600_HPET_SIZE 0x00000400 /* HPET register size */ 67 68 #define E600_HPET_GCID 0x000 /* Capabilities and ID */ 69 #define E600_HPET_GCID_WIDTH (1 << 13) /* Counter Size */ 70 #define E600_HPET_PERIOD 0x004 /* Counter Tick Period */ 71 #define E600_HPET_GC 0x010 /* General Configuration */ 72 #define E600_HPET_GC_ENABLE (1 << 0) /* Overall Enable */ 73 #define E600_HPET_GIS 0x020 /* General Interrupt Status */ 74 #define E600_HPET_MCV 0x0f0 /* Main Counter Value */ 75 #define E600_HPET_T0C 0x100 /* Timer 0 Config and Capabilities */ 76 #define E600_HPET_T0CV 0x108 /* Timer 0 Comparator Value */ 77 #define E600_HPET_T1C 0x120 /* Timer 1 Config and Capabilities */ 78 #define E600_HPET_T1CV 0x128 /* Timer 1 Comparator Value */ 79 #define E600_HPET_T2C 0x140 /* Timer 2 Config and Capabilities */ 80 #define E600_HPET_T2CV 0x148 /* Timer 2 Comparator Value */ 81 82 struct tcpcib_softc { 83 /* we call pcibattach() which assumes this starts like this: */ 84 struct pcib_softc sc_pcib; 85 86 /* Watchdog interface */ 87 bool sc_wdt_valid; 88 struct sysmon_wdog sc_wdt_smw; 89 bus_space_tag_t sc_wdt_iot; 90 bus_space_handle_t sc_wdt_ioh; 91 92 /* High Precision Event Timer */ 93 device_t sc_hpetbus; 94 bus_space_tag_t sc_hpet_memt; 95 }; 96 97 static int tcpcib_match(device_t, cfdata_t, void *); 98 static void tcpcib_attach(device_t, device_t, void *); 99 static int tcpcib_detach(device_t, int); 100 static int tcpcib_rescan(device_t, const char *, const int *); 101 static void tcpcib_childdet(device_t, device_t); 102 static bool tcpcib_suspend(device_t, const pmf_qual_t *); 103 static bool tcpcib_resume(device_t, const pmf_qual_t *); 104 105 static int tcpcib_wdt_setmode(struct sysmon_wdog *); 106 static int tcpcib_wdt_tickle(struct sysmon_wdog *); 107 static void tcpcib_wdt_init(struct tcpcib_softc *, int); 108 static void tcpcib_wdt_start(struct tcpcib_softc *); 109 static void tcpcib_wdt_stop(struct tcpcib_softc *); 110 111 CFATTACH_DECL2_NEW(tcpcib, sizeof(struct tcpcib_softc), 112 tcpcib_match, tcpcib_attach, tcpcib_detach, NULL, 113 tcpcib_rescan, tcpcib_childdet); 114 115 static struct tcpcib_device { 116 pcireg_t vendor, product; 117 } tcpcib_devices[] = { 118 { PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_E600_LPC } 119 }; 120 121 static void 122 tcpcib_wdt_unlock(struct tcpcib_softc *sc) 123 { 124 /* Register unlocking sequence */ 125 bus_space_write_1(sc->sc_wdt_iot, sc->sc_wdt_ioh, E600_WDT_RR0, 0x80); 126 bus_space_write_1(sc->sc_wdt_iot, sc->sc_wdt_ioh, E600_WDT_RR0, 0x86); 127 } 128 129 static void 130 tcpcib_wdt_init(struct tcpcib_softc *sc, int period) 131 { 132 uint32_t preload; 133 134 /* Set new timeout */ 135 preload = (period * 33000000) >> 15; 136 preload--; 137 138 /* 139 * Set watchdog to perform a cold reset toggling the GPIO pin and the 140 * prescaler set to 1ms-10m resolution 141 */ 142 bus_space_write_1(sc->sc_wdt_iot, sc->sc_wdt_ioh, E600_WDT_WDTCR, 143 E600_WDT_WDTCR_ENABLE); 144 tcpcib_wdt_unlock(sc); 145 bus_space_write_4(sc->sc_wdt_iot, sc->sc_wdt_ioh, E600_WDT_PV1, 0); 146 tcpcib_wdt_unlock(sc); 147 bus_space_write_4(sc->sc_wdt_iot, sc->sc_wdt_ioh, E600_WDT_PV2, 148 preload); 149 tcpcib_wdt_unlock(sc); 150 bus_space_write_1(sc->sc_wdt_iot, sc->sc_wdt_ioh, E600_WDT_RR1, 151 E600_WDT_RR1_RELOAD); 152 } 153 154 static void 155 tcpcib_wdt_start(struct tcpcib_softc *sc) 156 { 157 /* Enable watchdog */ 158 bus_space_write_1(sc->sc_wdt_iot, sc->sc_wdt_ioh, E600_WDT_WDTLR, 159 E600_WDT_WDTLR_ENABLE); 160 } 161 162 static void 163 tcpcib_wdt_stop(struct tcpcib_softc *sc) 164 { 165 /* Disable watchdog, with a reload before for safety */ 166 tcpcib_wdt_unlock(sc); 167 bus_space_write_1(sc->sc_wdt_iot, sc->sc_wdt_ioh, E600_WDT_RR1, 168 E600_WDT_RR1_RELOAD); 169 bus_space_write_1(sc->sc_wdt_iot, sc->sc_wdt_ioh, E600_WDT_WDTLR, 0); 170 } 171 172 static int 173 tcpcib_match(device_t parent, cfdata_t match, void *aux) 174 { 175 struct pci_attach_args *pa = aux; 176 unsigned int n; 177 178 if (PCI_CLASS(pa->pa_class) != PCI_CLASS_BRIDGE || 179 PCI_SUBCLASS(pa->pa_class) != PCI_SUBCLASS_BRIDGE_ISA) 180 return 0; 181 182 for (n = 0; n < __arraycount(tcpcib_devices); n++) { 183 if (PCI_VENDOR(pa->pa_id) == tcpcib_devices[n].vendor && 184 PCI_PRODUCT(pa->pa_id) == tcpcib_devices[n].product) 185 return 10; /* beat pcib(4) */ 186 } 187 188 return 0; 189 } 190 191 static void 192 tcpcib_attach(device_t parent, device_t self, void *aux) 193 { 194 struct tcpcib_softc *sc = device_private(self); 195 struct pci_attach_args *pa = aux; 196 uint32_t reg, wdtbase; 197 198 pmf_device_register(self, tcpcib_suspend, tcpcib_resume); 199 200 /* Provide core pcib(4) functionality */ 201 pcibattach(parent, self, aux); 202 203 /* High Precision Event Timer */ 204 sc->sc_hpet_memt = pa->pa_memt; 205 tcpcib_rescan(self, "hpetichbus", NULL); 206 207 /* Map Watchdog I/O space */ 208 reg = pci_conf_read(pa->pa_pc, pa->pa_tag, E600_LPC_WDTBA); 209 wdtbase = reg & 0xffff; 210 sc->sc_wdt_iot = pa->pa_iot; 211 if (reg & (1 << 31) && wdtbase) { 212 if (PCI_MAPREG_IO_ADDR(wdtbase) == 0 || 213 bus_space_map(sc->sc_wdt_iot, PCI_MAPREG_IO_ADDR(wdtbase), 214 E600_WDT_SIZE, 0, &sc->sc_wdt_ioh)) { 215 aprint_error_dev(self, "can't map watchdog I/O space\n"); 216 return; 217 } 218 aprint_normal_dev(self, "watchdog"); 219 220 /* Check for reboot on timeout */ 221 reg = bus_space_read_1(sc->sc_wdt_iot, sc->sc_wdt_ioh, 222 E600_WDT_RR1); 223 if (reg & E600_WDT_RR1_TIMEOUT) { 224 aprint_normal(", reboot on timeout"); 225 226 /* Clear timeout bit */ 227 tcpcib_wdt_unlock(sc); 228 bus_space_write_1(sc->sc_wdt_iot, sc->sc_wdt_ioh, 229 E600_WDT_RR1, E600_WDT_RR1_TIMEOUT); 230 } 231 232 /* Check it's not locked already */ 233 reg = bus_space_read_1(sc->sc_wdt_iot, sc->sc_wdt_ioh, 234 E600_WDT_WDTLR); 235 if (reg & E600_WDT_WDTLR_LOCK) { 236 aprint_error(", locked\n"); 237 return; 238 } 239 240 /* Disable watchdog */ 241 tcpcib_wdt_stop(sc); 242 243 /* Register new watchdog */ 244 sc->sc_wdt_smw.smw_name = device_xname(self); 245 sc->sc_wdt_smw.smw_cookie = sc; 246 sc->sc_wdt_smw.smw_setmode = tcpcib_wdt_setmode; 247 sc->sc_wdt_smw.smw_tickle = tcpcib_wdt_tickle; 248 sc->sc_wdt_smw.smw_period = 600; /* seconds */ 249 if (sysmon_wdog_register(&sc->sc_wdt_smw)) { 250 aprint_error(", unable to register wdog timer\n"); 251 return; 252 } 253 254 sc->sc_wdt_valid = true; 255 aprint_normal("\n"); 256 } 257 258 } 259 260 static int 261 tcpcib_detach(device_t self, int flags) 262 { 263 return pcibdetach(self, flags); 264 } 265 266 static int 267 tcpcib_rescan(device_t self, const char *ifattr, const int *locators) 268 { 269 struct tcpcib_softc *sc = device_private(self); 270 271 if (ifattr_match(ifattr, "hpetichbus") && sc->sc_hpetbus == NULL) { 272 struct lpcib_hpet_attach_args hpet_arg; 273 hpet_arg.hpet_mem_t = sc->sc_hpet_memt; 274 hpet_arg.hpet_reg = E600_HPET_BASE; 275 sc->sc_hpetbus = config_found_ia(self, "hpetichbus", 276 &hpet_arg, NULL); 277 } 278 279 return pcibrescan(self, ifattr, locators); 280 } 281 282 static void 283 tcpcib_childdet(device_t self, device_t child) 284 { 285 struct tcpcib_softc *sc = device_private(self); 286 287 if (sc->sc_hpetbus == child) { 288 sc->sc_hpetbus = NULL; 289 return; 290 } 291 292 pcibchilddet(self, child); 293 } 294 295 static bool 296 tcpcib_suspend(device_t self, const pmf_qual_t *qual) 297 { 298 struct tcpcib_softc *sc = device_private(self); 299 300 if (sc->sc_wdt_valid) 301 tcpcib_wdt_stop(sc); 302 303 return true; 304 } 305 306 static bool 307 tcpcib_resume(device_t self, const pmf_qual_t *qual) 308 { 309 struct tcpcib_softc *sc = device_private(self); 310 struct sysmon_wdog *smw = &sc->sc_wdt_smw; 311 312 if (sc->sc_wdt_valid) { 313 if ((smw->smw_mode & WDOG_MODE_MASK) != WDOG_MODE_DISARMED && 314 smw->smw_period > 0) { 315 tcpcib_wdt_init(sc, smw->smw_period); 316 tcpcib_wdt_start(sc); 317 } else { 318 tcpcib_wdt_stop(sc); 319 } 320 } 321 322 return true; 323 } 324 325 static int 326 tcpcib_wdt_setmode(struct sysmon_wdog *smw) 327 { 328 struct tcpcib_softc *sc = smw->smw_cookie; 329 unsigned int period; 330 331 period = smw->smw_period; 332 if ((smw->smw_mode & WDOG_MODE_MASK) == WDOG_MODE_DISARMED) { 333 tcpcib_wdt_stop(sc); 334 } else { 335 /* 600 seconds is the maximum supported timeout value */ 336 if (period > 600) 337 return EINVAL; 338 339 tcpcib_wdt_stop(sc); 340 tcpcib_wdt_init(sc, period); 341 tcpcib_wdt_start(sc); 342 tcpcib_wdt_tickle(smw); 343 } 344 345 return 0; 346 } 347 348 static int 349 tcpcib_wdt_tickle(struct sysmon_wdog *smw) 350 { 351 struct tcpcib_softc *sc = smw->smw_cookie; 352 353 /* Reset timer */ 354 tcpcib_wdt_unlock(sc); 355 bus_space_write_1(sc->sc_wdt_iot, sc->sc_wdt_ioh, 356 E600_WDT_RR1, E600_WDT_RR1_RELOAD); 357 358 return 0; 359 } 360