1 /* $OpenBSD: tcpcib.c,v 1.9 2022/03/11 18:00:52 mpi Exp $ */ 2 3 /* 4 * Copyright (c) 2012 Matt Dainty <matt@bodgit-n-scarper.com> 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 MIND, USE, DATA OR PROFITS, WHETHER IN 15 * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT 16 * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17 */ 18 19 /* 20 * Intel Atom E600 series LPC bridge also containing HPET and watchdog 21 */ 22 23 #include <sys/param.h> 24 #include <sys/systm.h> 25 #include <sys/device.h> 26 #include <sys/timetc.h> 27 28 #include <machine/bus.h> 29 30 #include <dev/pci/pcireg.h> 31 #include <dev/pci/pcivar.h> 32 #include <dev/pci/pcidevs.h> 33 34 #define E600_LPC_SMBA 0x40 /* SMBus Base Address */ 35 #define E600_LPC_GBA 0x44 /* GPIO Base Address */ 36 #define E600_LPC_WDTBA 0x84 /* WDT Base Address */ 37 38 #define E600_WDT_SIZE 64 /* I/O region size */ 39 #define E600_WDT_PV1 0x00 /* Preload Value 1 Register */ 40 #define E600_WDT_PV2 0x04 /* Preload Value 2 Register */ 41 #define E600_WDT_RR0 0x0c /* Reload Register 0 */ 42 #define E600_WDT_RR1 0x0d /* Reload Register 1 */ 43 #define E600_WDT_RR1_RELOAD (1 << 0) /* WDT Reload Flag */ 44 #define E600_WDT_RR1_TIMEOUT (1 << 1) /* WDT Timeout Flag */ 45 #define E600_WDT_WDTCR 0x10 /* WDT Configuration Register */ 46 #define E600_WDT_WDTCR_PRE (1 << 2) /* WDT Prescalar Select */ 47 #define E600_WDT_WDTCR_RESET (1 << 3) /* WDT Reset Select */ 48 #define E600_WDT_WDTCR_ENABLE (1 << 4) /* WDT Reset Enable */ 49 #define E600_WDT_WDTCR_TIMEOUT (1 << 5) /* WDT Timeout Output Enable */ 50 #define E600_WDT_DCR 0x14 /* Down Counter Register */ 51 #define E600_WDT_WDTLR 0x18 /* WDT Lock Register */ 52 #define E600_WDT_WDTLR_LOCK (1 << 0) /* Watchdog Timer Lock */ 53 #define E600_WDT_WDTLR_ENABLE (1 << 1) /* Watchdog Timer Enable */ 54 #define E600_WDT_WDTLR_TIMEOUT (1 << 2) /* WDT Timeout Configuration */ 55 56 #define E600_HPET_BASE 0xfed00000 /* HPET register base */ 57 #define E600_HPET_SIZE 0x00000400 /* HPET register size */ 58 59 #define E600_HPET_GCID 0x000 /* Capabilities and ID */ 60 #define E600_HPET_GCID_WIDTH (1 << 13) /* Counter Size */ 61 #define E600_HPET_PERIOD 0x004 /* Counter Tick Period */ 62 #define E600_HPET_GC 0x010 /* General Configuration */ 63 #define E600_HPET_GC_ENABLE (1 << 0) /* Overall Enable */ 64 #define E600_HPET_GIS 0x020 /* General Interrupt Status */ 65 #define E600_HPET_MCV 0x0f0 /* Main Counter Value */ 66 #define E600_HPET_T0C 0x100 /* Timer 0 Config and Capabilities */ 67 #define E600_HPET_T0CV 0x108 /* Timer 0 Comparator Value */ 68 #define E600_HPET_T1C 0x120 /* Timer 1 Config and Capabilities */ 69 #define E600_HPET_T1CV 0x128 /* Timer 1 Comparator Value */ 70 #define E600_HPET_T2C 0x140 /* Timer 2 Config and Capabilities */ 71 #define E600_HPET_T2CV 0x148 /* Timer 2 Comparator Value */ 72 73 struct tcpcib_softc { 74 struct device sc_dev; 75 76 /* Keep track of which parts of the hardware are active */ 77 int sc_active; 78 #define E600_WDT_ACTIVE (1 << 0) 79 #define E600_HPET_ACTIVE (1 << 1) 80 81 /* Watchdog interface */ 82 bus_space_tag_t sc_wdt_iot; 83 bus_space_handle_t sc_wdt_ioh; 84 85 int sc_wdt_period; 86 87 /* High Precision Event Timer */ 88 bus_space_tag_t sc_hpet_iot; 89 bus_space_handle_t sc_hpet_ioh; 90 91 struct timecounter sc_hpet_timecounter; 92 }; 93 94 struct cfdriver tcpcib_cd = { 95 NULL, "tcpcib", DV_DULL 96 }; 97 98 int tcpcib_match(struct device *, void *, void *); 99 void tcpcib_attach(struct device *, struct device *, void *); 100 int tcpcib_activate(struct device *, int); 101 102 int tcpcib_wdt_cb(void *, int); 103 void tcpcib_wdt_init(struct tcpcib_softc *, int); 104 void tcpcib_wdt_start(struct tcpcib_softc *); 105 void tcpcib_wdt_stop(struct tcpcib_softc *); 106 107 u_int tcpcib_hpet_get_timecount(struct timecounter *tc); 108 109 const struct cfattach tcpcib_ca = { 110 sizeof(struct tcpcib_softc), tcpcib_match, tcpcib_attach, 111 NULL, tcpcib_activate 112 }; 113 114 /* from arch/<*>/pci/pcib.c */ 115 void pcibattach(struct device *parent, struct device *self, void *aux); 116 117 const struct pci_matchid tcpcib_devices[] = { 118 { PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_E600_LPC } 119 }; 120 121 static __inline 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 void 130 tcpcib_wdt_init(struct tcpcib_softc *sc, int period) 131 { 132 u_int32_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 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 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 int 173 tcpcib_match(struct device *parent, void *match, void *aux) 174 { 175 if (pci_matchbyid((struct pci_attach_args *)aux, tcpcib_devices, 176 sizeof(tcpcib_devices) / sizeof(tcpcib_devices[0]))) 177 return (2); 178 179 return (0); 180 } 181 182 void 183 tcpcib_attach(struct device *parent, struct device *self, void *aux) 184 { 185 struct tcpcib_softc *sc = (struct tcpcib_softc *)self; 186 struct pci_attach_args *pa = aux; 187 struct timecounter *tc = &sc->sc_hpet_timecounter; 188 u_int32_t reg, wdtbase; 189 190 sc->sc_active = 0; 191 192 /* High Precision Event Timer */ 193 sc->sc_hpet_iot = pa->pa_memt; 194 if (bus_space_map(sc->sc_hpet_iot, E600_HPET_BASE, E600_HPET_SIZE, 0, 195 &sc->sc_hpet_ioh) == 0) { 196 tc->tc_get_timecount = tcpcib_hpet_get_timecount; 197 /* XXX 64-bit counter is not supported! */ 198 tc->tc_counter_mask = 0xffffffff; 199 200 reg = bus_space_read_4(sc->sc_hpet_iot, sc->sc_hpet_ioh, 201 E600_HPET_PERIOD); 202 /* femtosecs -> Hz */ 203 tc->tc_frequency = 1000000000000000ULL / reg; 204 205 tc->tc_name = sc->sc_dev.dv_xname; 206 tc->tc_quality = 2000; 207 tc->tc_priv = sc; 208 tc_init(tc); 209 210 /* Enable counting */ 211 bus_space_write_4(sc->sc_hpet_iot, sc->sc_hpet_ioh, 212 E600_HPET_GC, E600_HPET_GC_ENABLE); 213 214 sc->sc_active |= E600_HPET_ACTIVE; 215 216 printf(": %llu Hz timer", tc->tc_frequency); 217 } 218 219 /* Map Watchdog I/O space */ 220 reg = pci_conf_read(pa->pa_pc, pa->pa_tag, E600_LPC_WDTBA); 221 wdtbase = reg & 0xffff; 222 sc->sc_wdt_iot = pa->pa_iot; 223 if (reg & (1U << 31) && wdtbase) { 224 if (PCI_MAPREG_IO_ADDR(wdtbase) == 0 || 225 bus_space_map(sc->sc_wdt_iot, PCI_MAPREG_IO_ADDR(wdtbase), 226 E600_WDT_SIZE, 0, &sc->sc_wdt_ioh)) { 227 printf("%c can't map watchdog I/O space", 228 sc->sc_active ? ',' : ':'); 229 goto corepcib; 230 } 231 printf("%c watchdog", sc->sc_active ? ',' : ':'); 232 233 /* Check for reboot on timeout */ 234 reg = bus_space_read_1(sc->sc_wdt_iot, sc->sc_wdt_ioh, 235 E600_WDT_RR1); 236 if (reg & E600_WDT_RR1_TIMEOUT) { 237 printf(", reboot on timeout"); 238 239 /* Clear timeout bit */ 240 tcpcib_wdt_unlock(sc); 241 bus_space_write_1(sc->sc_wdt_iot, sc->sc_wdt_ioh, 242 E600_WDT_RR1, E600_WDT_RR1_TIMEOUT); 243 } 244 245 /* Check it's not locked already */ 246 reg = bus_space_read_1(sc->sc_wdt_iot, sc->sc_wdt_ioh, 247 E600_WDT_WDTLR); 248 if (reg & E600_WDT_WDTLR_LOCK) { 249 printf(", locked"); 250 goto corepcib; 251 } 252 253 /* Disable watchdog */ 254 tcpcib_wdt_stop(sc); 255 sc->sc_wdt_period = 0; 256 257 sc->sc_active |= E600_WDT_ACTIVE; 258 259 /* Register new watchdog */ 260 wdog_register(tcpcib_wdt_cb, sc); 261 } 262 263 corepcib: 264 /* Provide core pcib(4) functionality */ 265 pcibattach(parent, self, aux); 266 } 267 268 int 269 tcpcib_activate(struct device *self, int act) 270 { 271 struct tcpcib_softc *sc = (struct tcpcib_softc *)self; 272 int rv = 0; 273 274 switch (act) { 275 case DVACT_SUSPEND: 276 rv = config_activate_children(self, act); 277 /* Watchdog is running, disable it */ 278 if (sc->sc_active & E600_WDT_ACTIVE && sc->sc_wdt_period != 0) 279 tcpcib_wdt_stop(sc); 280 break; 281 case DVACT_RESUME: 282 if (sc->sc_active & E600_WDT_ACTIVE) { 283 /* 284 * Watchdog was running prior to suspend so reenable 285 * it, otherwise make sure it stays disabled 286 */ 287 if (sc->sc_wdt_period != 0) { 288 tcpcib_wdt_init(sc, sc->sc_wdt_period); 289 tcpcib_wdt_start(sc); 290 } else 291 tcpcib_wdt_stop(sc); 292 } 293 if (sc->sc_active & E600_HPET_ACTIVE) 294 bus_space_write_4(sc->sc_hpet_iot, sc->sc_hpet_ioh, 295 E600_HPET_GC, E600_HPET_GC_ENABLE); 296 rv = config_activate_children(self, act); 297 break; 298 case DVACT_POWERDOWN: 299 if (sc->sc_active & E600_WDT_ACTIVE) 300 wdog_shutdown(self); 301 rv = config_activate_children(self, act); 302 break; 303 default: 304 rv = config_activate_children(self, act); 305 break; 306 } 307 return (rv); 308 } 309 310 int 311 tcpcib_wdt_cb(void *arg, int period) 312 { 313 struct tcpcib_softc *sc = arg; 314 315 if (period == 0) { 316 if (sc->sc_wdt_period != 0) 317 tcpcib_wdt_stop(sc); 318 } else { 319 /* 600 seconds is the maximum supported timeout value */ 320 if (period > 600) 321 period = 600; 322 if (sc->sc_wdt_period != period) 323 tcpcib_wdt_init(sc, period); 324 if (sc->sc_wdt_period == 0) { 325 tcpcib_wdt_start(sc); 326 } else { 327 /* Reset timer */ 328 tcpcib_wdt_unlock(sc); 329 bus_space_write_1(sc->sc_wdt_iot, sc->sc_wdt_ioh, 330 E600_WDT_RR1, E600_WDT_RR1_RELOAD); 331 } 332 } 333 sc->sc_wdt_period = period; 334 335 return (period); 336 } 337 338 u_int 339 tcpcib_hpet_get_timecount(struct timecounter *tc) 340 { 341 struct tcpcib_softc *sc = tc->tc_priv; 342 343 /* XXX 64-bit counter is not supported! */ 344 return bus_space_read_4(sc->sc_hpet_iot, sc->sc_hpet_ioh, 345 E600_HPET_MCV); 346 } 347