Home | History | Annotate | Line # | Download | only in pci
tcpcib.c revision 1.3
      1  1.3   thorpej /*	$NetBSD: tcpcib.c,v 1.3 2021/04/24 23:36:51 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.3   thorpej __KERNEL_RCSID(0, "$NetBSD: tcpcib.c,v 1.3 2021/04/24 23:36:51 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.3   thorpej 				 CFARG_IATTR, "hpetichbus",
    279  1.3   thorpej 				 CFARG_EOL);
    280  1.1  christos 	}
    281  1.1  christos 
    282  1.1  christos 	return pcibrescan(self, ifattr, locators);
    283  1.1  christos }
    284  1.1  christos 
    285  1.1  christos static void
    286  1.1  christos tcpcib_childdet(device_t self, device_t child)
    287  1.1  christos {
    288  1.1  christos 	struct tcpcib_softc *sc = device_private(self);
    289  1.1  christos 
    290  1.1  christos 	if (sc->sc_hpetbus == child) {
    291  1.1  christos 		sc->sc_hpetbus = NULL;
    292  1.1  christos 		return;
    293  1.1  christos 	}
    294  1.1  christos 
    295  1.1  christos 	pcibchilddet(self, child);
    296  1.1  christos }
    297  1.1  christos 
    298  1.1  christos static bool
    299  1.1  christos tcpcib_suspend(device_t self, const pmf_qual_t *qual)
    300  1.1  christos {
    301  1.1  christos 	struct tcpcib_softc *sc = device_private(self);
    302  1.1  christos 
    303  1.1  christos 	if (sc->sc_wdt_valid)
    304  1.1  christos 		tcpcib_wdt_stop(sc);
    305  1.1  christos 
    306  1.1  christos 	return true;
    307  1.1  christos }
    308  1.1  christos 
    309  1.1  christos static bool
    310  1.1  christos tcpcib_resume(device_t self, const pmf_qual_t *qual)
    311  1.1  christos {
    312  1.1  christos 	struct tcpcib_softc *sc = device_private(self);
    313  1.1  christos 	struct sysmon_wdog *smw = &sc->sc_wdt_smw;
    314  1.1  christos 
    315  1.1  christos 	if (sc->sc_wdt_valid) {
    316  1.1  christos 		if ((smw->smw_mode & WDOG_MODE_MASK) != WDOG_MODE_DISARMED &&
    317  1.1  christos 		    smw->smw_period > 0) {
    318  1.1  christos 			tcpcib_wdt_init(sc, smw->smw_period);
    319  1.1  christos 			tcpcib_wdt_start(sc);
    320  1.1  christos 		} else {
    321  1.1  christos 			tcpcib_wdt_stop(sc);
    322  1.1  christos 		}
    323  1.1  christos 	}
    324  1.1  christos 
    325  1.1  christos 	return true;
    326  1.1  christos }
    327  1.1  christos 
    328  1.1  christos static int
    329  1.1  christos tcpcib_wdt_setmode(struct sysmon_wdog *smw)
    330  1.1  christos {
    331  1.1  christos 	struct tcpcib_softc *sc = smw->smw_cookie;
    332  1.1  christos 	unsigned int period;
    333  1.1  christos 
    334  1.1  christos 	period = smw->smw_period;
    335  1.1  christos 	if ((smw->smw_mode & WDOG_MODE_MASK) == WDOG_MODE_DISARMED) {
    336  1.1  christos 		tcpcib_wdt_stop(sc);
    337  1.1  christos 	} else {
    338  1.1  christos 		/* 600 seconds is the maximum supported timeout value */
    339  1.1  christos 		if (period > 600)
    340  1.1  christos 			return EINVAL;
    341  1.1  christos 
    342  1.1  christos 		tcpcib_wdt_stop(sc);
    343  1.1  christos 		tcpcib_wdt_init(sc, period);
    344  1.1  christos 		tcpcib_wdt_start(sc);
    345  1.1  christos 		tcpcib_wdt_tickle(smw);
    346  1.1  christos 	}
    347  1.1  christos 
    348  1.1  christos 	return 0;
    349  1.1  christos }
    350  1.1  christos 
    351  1.1  christos static int
    352  1.1  christos tcpcib_wdt_tickle(struct sysmon_wdog *smw)
    353  1.1  christos {
    354  1.1  christos 	struct tcpcib_softc *sc = smw->smw_cookie;
    355  1.1  christos 
    356  1.1  christos 	/* Reset timer */
    357  1.1  christos 	tcpcib_wdt_unlock(sc);
    358  1.1  christos 	bus_space_write_1(sc->sc_wdt_iot, sc->sc_wdt_ioh,
    359  1.1  christos 	    E600_WDT_RR1, E600_WDT_RR1_RELOAD);
    360  1.1  christos 
    361  1.1  christos 	return 0;
    362  1.1  christos }
    363