1/* $NetBSD: tcpcib.c,v 1.4 2021/08/07 16:19:08 thorpej 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.4 2021/08/07 16:19:08 thorpej 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 82struct 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 97static int tcpcib_match(device_t, cfdata_t, void *); 98static void tcpcib_attach(device_t, device_t, void *); 99static int tcpcib_detach(device_t, int); 100static int tcpcib_rescan(device_t, const char *, const int *); 101static void tcpcib_childdet(device_t, device_t); 102static bool tcpcib_suspend(device_t, const pmf_qual_t *); 103static bool tcpcib_resume(device_t, const pmf_qual_t *); 104 105static int tcpcib_wdt_setmode(struct sysmon_wdog *); 106static int tcpcib_wdt_tickle(struct sysmon_wdog *); 107static void tcpcib_wdt_init(struct tcpcib_softc *, int); 108static void tcpcib_wdt_start(struct tcpcib_softc *); 109static void tcpcib_wdt_stop(struct tcpcib_softc *); 110 111CFATTACH_DECL2_NEW(tcpcib, sizeof(struct tcpcib_softc), 112 tcpcib_match, tcpcib_attach, tcpcib_detach, NULL, 113 tcpcib_rescan, tcpcib_childdet); 114 115static struct tcpcib_device { 116 pcireg_t vendor, product; 117} tcpcib_devices[] = { 118 { PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_E600_LPC } 119}; 120 121static void 122tcpcib_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 129static void 130tcpcib_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 154static void 155tcpcib_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 162static void 163tcpcib_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 172static int 173tcpcib_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 191static void 192tcpcib_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 261static int 262tcpcib_detach(device_t self, int flags) 263{ 264 return pcibdetach(self, flags); 265} 266 267static int 268tcpcib_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 = 277 config_found(self, &hpet_arg, NULL, 278 CFARGS(.iattr = "hpetichbus")); 279 } 280 281 return pcibrescan(self, ifattr, locators); 282} 283 284static void 285tcpcib_childdet(device_t self, device_t child) 286{ 287 struct tcpcib_softc *sc = device_private(self); 288 289 if (sc->sc_hpetbus == child) { 290 sc->sc_hpetbus = NULL; 291 return; 292 } 293 294 pcibchilddet(self, child); 295} 296 297static bool 298tcpcib_suspend(device_t self, const pmf_qual_t *qual) 299{ 300 struct tcpcib_softc *sc = device_private(self); 301 302 if (sc->sc_wdt_valid) 303 tcpcib_wdt_stop(sc); 304 305 return true; 306} 307 308static bool 309tcpcib_resume(device_t self, const pmf_qual_t *qual) 310{ 311 struct tcpcib_softc *sc = device_private(self); 312 struct sysmon_wdog *smw = &sc->sc_wdt_smw; 313 314 if (sc->sc_wdt_valid) { 315 if ((smw->smw_mode & WDOG_MODE_MASK) != WDOG_MODE_DISARMED && 316 smw->smw_period > 0) { 317 tcpcib_wdt_init(sc, smw->smw_period); 318 tcpcib_wdt_start(sc); 319 } else { 320 tcpcib_wdt_stop(sc); 321 } 322 } 323 324 return true; 325} 326 327static int 328tcpcib_wdt_setmode(struct sysmon_wdog *smw) 329{ 330 struct tcpcib_softc *sc = smw->smw_cookie; 331 unsigned int period; 332 333 period = smw->smw_period; 334 if ((smw->smw_mode & WDOG_MODE_MASK) == WDOG_MODE_DISARMED) { 335 tcpcib_wdt_stop(sc); 336 } else { 337 /* 600 seconds is the maximum supported timeout value */ 338 if (period > 600) 339 return EINVAL; 340 341 tcpcib_wdt_stop(sc); 342 tcpcib_wdt_init(sc, period); 343 tcpcib_wdt_start(sc); 344 tcpcib_wdt_tickle(smw); 345 } 346 347 return 0; 348} 349 350static int 351tcpcib_wdt_tickle(struct sysmon_wdog *smw) 352{ 353 struct tcpcib_softc *sc = smw->smw_cookie; 354 355 /* Reset timer */ 356 tcpcib_wdt_unlock(sc); 357 bus_space_write_1(sc->sc_wdt_iot, sc->sc_wdt_ioh, 358 E600_WDT_RR1, E600_WDT_RR1_RELOAD); 359 360 return 0; 361} 362