1 /* $NetBSD: tcpcib.c,v 1.2 2016/07/11 11:31:50 msaitoh 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.2 2016/07/11 11:31:50 msaitoh 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, 216 "can't map watchdog I/O space\n"); 217 return; 218 } 219 aprint_normal_dev(self, "watchdog"); 220 221 /* Check for reboot on timeout */ 222 reg = bus_space_read_1(sc->sc_wdt_iot, sc->sc_wdt_ioh, 223 E600_WDT_RR1); 224 if (reg & E600_WDT_RR1_TIMEOUT) { 225 aprint_normal(", reboot on timeout"); 226 227 /* Clear timeout bit */ 228 tcpcib_wdt_unlock(sc); 229 bus_space_write_1(sc->sc_wdt_iot, sc->sc_wdt_ioh, 230 E600_WDT_RR1, E600_WDT_RR1_TIMEOUT); 231 } 232 233 /* Check it's not locked already */ 234 reg = bus_space_read_1(sc->sc_wdt_iot, sc->sc_wdt_ioh, 235 E600_WDT_WDTLR); 236 if (reg & E600_WDT_WDTLR_LOCK) { 237 aprint_error(", locked\n"); 238 return; 239 } 240 241 /* Disable watchdog */ 242 tcpcib_wdt_stop(sc); 243 244 /* Register new watchdog */ 245 sc->sc_wdt_smw.smw_name = device_xname(self); 246 sc->sc_wdt_smw.smw_cookie = sc; 247 sc->sc_wdt_smw.smw_setmode = tcpcib_wdt_setmode; 248 sc->sc_wdt_smw.smw_tickle = tcpcib_wdt_tickle; 249 sc->sc_wdt_smw.smw_period = 600; /* seconds */ 250 if (sysmon_wdog_register(&sc->sc_wdt_smw)) { 251 aprint_error(", unable to register wdog timer\n"); 252 return; 253 } 254 255 sc->sc_wdt_valid = true; 256 aprint_normal("\n"); 257 } 258 259 } 260 261 static int 262 tcpcib_detach(device_t self, int flags) 263 { 264 return pcibdetach(self, flags); 265 } 266 267 static int 268 tcpcib_rescan(device_t self, const char *ifattr, const int *locators) 269 { 270 struct tcpcib_softc *sc = device_private(self); 271 272 if (ifattr_match(ifattr, "hpetichbus") && sc->sc_hpetbus == NULL) { 273 struct lpcib_hpet_attach_args hpet_arg; 274 hpet_arg.hpet_mem_t = sc->sc_hpet_memt; 275 hpet_arg.hpet_reg = E600_HPET_BASE; 276 sc->sc_hpetbus = config_found_ia(self, "hpetichbus", 277 &hpet_arg, NULL); 278 } 279 280 return pcibrescan(self, ifattr, locators); 281 } 282 283 static void 284 tcpcib_childdet(device_t self, device_t child) 285 { 286 struct tcpcib_softc *sc = device_private(self); 287 288 if (sc->sc_hpetbus == child) { 289 sc->sc_hpetbus = NULL; 290 return; 291 } 292 293 pcibchilddet(self, child); 294 } 295 296 static bool 297 tcpcib_suspend(device_t self, const pmf_qual_t *qual) 298 { 299 struct tcpcib_softc *sc = device_private(self); 300 301 if (sc->sc_wdt_valid) 302 tcpcib_wdt_stop(sc); 303 304 return true; 305 } 306 307 static bool 308 tcpcib_resume(device_t self, const pmf_qual_t *qual) 309 { 310 struct tcpcib_softc *sc = device_private(self); 311 struct sysmon_wdog *smw = &sc->sc_wdt_smw; 312 313 if (sc->sc_wdt_valid) { 314 if ((smw->smw_mode & WDOG_MODE_MASK) != WDOG_MODE_DISARMED && 315 smw->smw_period > 0) { 316 tcpcib_wdt_init(sc, smw->smw_period); 317 tcpcib_wdt_start(sc); 318 } else { 319 tcpcib_wdt_stop(sc); 320 } 321 } 322 323 return true; 324 } 325 326 static int 327 tcpcib_wdt_setmode(struct sysmon_wdog *smw) 328 { 329 struct tcpcib_softc *sc = smw->smw_cookie; 330 unsigned int period; 331 332 period = smw->smw_period; 333 if ((smw->smw_mode & WDOG_MODE_MASK) == WDOG_MODE_DISARMED) { 334 tcpcib_wdt_stop(sc); 335 } else { 336 /* 600 seconds is the maximum supported timeout value */ 337 if (period > 600) 338 return EINVAL; 339 340 tcpcib_wdt_stop(sc); 341 tcpcib_wdt_init(sc, period); 342 tcpcib_wdt_start(sc); 343 tcpcib_wdt_tickle(smw); 344 } 345 346 return 0; 347 } 348 349 static int 350 tcpcib_wdt_tickle(struct sysmon_wdog *smw) 351 { 352 struct tcpcib_softc *sc = smw->smw_cookie; 353 354 /* Reset timer */ 355 tcpcib_wdt_unlock(sc); 356 bus_space_write_1(sc->sc_wdt_iot, sc->sc_wdt_ioh, 357 E600_WDT_RR1, E600_WDT_RR1_RELOAD); 358 359 return 0; 360 } 361