Home | History | Annotate | Line # | Download | only in pci
      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 (at) 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 
     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 =
    277 		    config_found(self, &hpet_arg, NULL,
    278 				 CFARGS(.iattr = "hpetichbus"));
    279 	}
    280 
    281 	return pcibrescan(self, ifattr, locators);
    282 }
    283 
    284 static void
    285 tcpcib_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 
    297 static bool
    298 tcpcib_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 
    308 static bool
    309 tcpcib_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 
    327 static int
    328 tcpcib_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 
    350 static int
    351 tcpcib_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