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