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