Module Name: src Committed By: christos Date: Wed Dec 5 16:19:47 UTC 2012
Modified Files: src/sys/arch/i386/conf: GENERIC src/sys/arch/x86/pci: files.pci src/sys/dev/pci: pcidevs Added Files: src/sys/arch/x86/pci: tcpcib.c Log Message: Intel Atom E600 PCI-LPC bridge, adds a watchdog + HPET support. Tested on a Soekris net6501. (jmcneill) To generate a diff of this commit: cvs rdiff -u -r1.1082 -r1.1083 src/sys/arch/i386/conf/GENERIC cvs rdiff -u -r1.14 -r1.15 src/sys/arch/x86/pci/files.pci cvs rdiff -u -r0 -r1.1 src/sys/arch/x86/pci/tcpcib.c cvs rdiff -u -r1.1144 -r1.1145 src/sys/dev/pci/pcidevs Please note that diffs are not public domain; they are subject to the copyright notices on the relevant files.
Modified files: Index: src/sys/arch/i386/conf/GENERIC diff -u src/sys/arch/i386/conf/GENERIC:1.1082 src/sys/arch/i386/conf/GENERIC:1.1083 --- src/sys/arch/i386/conf/GENERIC:1.1082 Wed Oct 17 10:48:13 2012 +++ src/sys/arch/i386/conf/GENERIC Wed Dec 5 11:19:46 2012 @@ -1,4 +1,4 @@ -# $NetBSD: GENERIC,v 1.1082 2012/10/17 14:48:13 apb Exp $ +# $NetBSD: GENERIC,v 1.1083 2012/12/05 16:19:46 christos Exp $ # # GENERIC machine description file # @@ -22,7 +22,7 @@ include "arch/i386/conf/std.i386" options INCLUDE_CONFIG_FILE # embed config file in kernel binary -#ident "GENERIC-$Revision: 1.1082 $" +#ident "GENERIC-$Revision: 1.1083 $" maxusers 64 # estimated number of users @@ -457,6 +457,8 @@ ichlpcib* at pci? dev ? function ? # Int # watchdog, SpeedStep and HPET fwhrng* at ichlpcib? # Intel 82802 FWH Random Number Generator #hpet* at ichlpcib? +tcpcib* at pci? dev ? function ? # Intel Atom E6xx PCI-LPC +hpet* at tcpcib? gcscpcib* at pci? dev ? function ? # AMD CS5535/CS5536 PCI-ISA w/ # timecounter, watchdog and GPIO #piixpcib* at pci? dev ? function ? # Intel PIIX4 PCI-ISA w/ SpeedStep @@ -486,6 +488,7 @@ eisa0 at pceb? #isa0 at amdpcib? isa0 at gcscpcib? isa0 at ichlpcib? +isa0 at tcpcib? #isa0 at piixpcib? #isa0 at gscpcib? isa0 at viapcib? Index: src/sys/arch/x86/pci/files.pci diff -u src/sys/arch/x86/pci/files.pci:1.14 src/sys/arch/x86/pci/files.pci:1.15 --- src/sys/arch/x86/pci/files.pci:1.14 Fri Apr 13 09:11:17 2012 +++ src/sys/arch/x86/pci/files.pci Wed Dec 5 11:19:46 2012 @@ -1,4 +1,4 @@ -# $NetBSD: files.pci,v 1.14 2012/04/13 13:11:17 cegger Exp $ +# $NetBSD: files.pci,v 1.15 2012/12/05 16:19:46 christos Exp $ device aapic attach aapic at pci @@ -19,7 +19,8 @@ file arch/x86/pci/pchb.c pchb needs-fl device pcib: isabus attach pcib at pci file arch/x86/pci/pcib.c pcib | ichlpcib | gscpcib | piixpcib | - viapcib | amdpcib | gcscpcib | rdcpcib + viapcib | amdpcib | gcscpcib | rdcpcib | + tcpcib device amdpcib {} : isabus attach amdpcib at pci @@ -51,6 +52,10 @@ file arch/x86/pci/ichlpcib.c ichlpcib attach hpet at hpetichbus with ichlpcib_hpet file arch/x86/pci/ichlpcib_hpet.c ichlpcib_hpet +device tcpcib: isabus, sysmon_wdog, hpetichbus +attach tcpcib at pci +file arch/x86/pci/tcpcib.c tcpcib + device fwhrng attach fwhrng at fwhichbus file arch/x86/pci/fwhrng.c fwhrng needs-flag Index: src/sys/dev/pci/pcidevs diff -u src/sys/dev/pci/pcidevs:1.1144 src/sys/dev/pci/pcidevs:1.1145 --- src/sys/dev/pci/pcidevs:1.1144 Thu Nov 29 13:45:20 2012 +++ src/sys/dev/pci/pcidevs Wed Dec 5 11:19:46 2012 @@ -1,4 +1,4 @@ -$NetBSD: pcidevs,v 1.1144 2012/11/29 18:45:20 msaitoh Exp $ +$NetBSD: pcidevs,v 1.1145 2012/12/05 16:19:46 christos Exp $ /* * Copyright (c) 1995, 1996 Christopher G. Demetriou @@ -3386,6 +3386,7 @@ product INTEL 82443GX_AGP 0x71a1 82443GX product INTEL 82443GX_NOAGP 0x71a2 82443GX Host Bridge/Controller (AGP disabled) product INTEL I740 0x7800 i740 Graphics Accelerator product INTEL SCH_IDE 0x811a SCH IDE Controller +product INTEL E600_LPC 0x8186 Atom Processor E6xx LPC Bridge product INTEL PCI450_PB 0x84c4 82454KX/GX PCI Bridge (PB) product INTEL PCI450_MC 0x84c5 82451KX/GX Memory Controller (MC) product INTEL 82451NX_MIOC 0x84ca 82451NX Memory & I/O Controller (MIOC) Added files: Index: src/sys/arch/x86/pci/tcpcib.c diff -u /dev/null src/sys/arch/x86/pci/tcpcib.c:1.1 --- /dev/null Wed Dec 5 11:19:47 2012 +++ src/sys/arch/x86/pci/tcpcib.c Wed Dec 5 11:19:46 2012 @@ -0,0 +1,359 @@ +/* $NetBSD: tcpcib.c,v 1.1 2012/12/05 16:19:46 christos Exp $ */ +/* $OpenBSD: tcpcib.c,v 1.4 2012/10/17 22:32:01 deraadt Exp $ */ + +/* + * Copyright (c) 2012 Matt Dainty <m...@bodgit-n-scarper.com> + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER IN + * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT + * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +/* + * Intel Atom E600 series LPC bridge also containing HPET and watchdog + */ + +#include <sys/cdefs.h> +__KERNEL_RCSID(0, "$NetBSD: tcpcib.c,v 1.1 2012/12/05 16:19:46 christos Exp $"); + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/device.h> +#include <sys/timetc.h> +#include <sys/bus.h> + +#include <dev/pci/pcireg.h> +#include <dev/pci/pcivar.h> +#include <dev/pci/pcidevs.h> + +#include <dev/ic/i82801lpcvar.h> + +#include <dev/sysmon/sysmonvar.h> + +#include "pcibvar.h" + +#define E600_LPC_SMBA 0x40 /* SMBus Base Address */ +#define E600_LPC_GBA 0x44 /* GPIO Base Address */ +#define E600_LPC_WDTBA 0x84 /* WDT Base Address */ + +#define E600_WDT_SIZE 64 /* I/O region size */ +#define E600_WDT_PV1 0x00 /* Preload Value 1 Register */ +#define E600_WDT_PV2 0x04 /* Preload Value 2 Register */ +#define E600_WDT_RR0 0x0c /* Reload Register 0 */ +#define E600_WDT_RR1 0x0d /* Reload Register 1 */ +#define E600_WDT_RR1_RELOAD (1 << 0) /* WDT Reload Flag */ +#define E600_WDT_RR1_TIMEOUT (1 << 1) /* WDT Timeout Flag */ +#define E600_WDT_WDTCR 0x10 /* WDT Configuration Register */ +#define E600_WDT_WDTCR_PRE (1 << 2) /* WDT Prescalar Select */ +#define E600_WDT_WDTCR_RESET (1 << 3) /* WDT Reset Select */ +#define E600_WDT_WDTCR_ENABLE (1 << 4) /* WDT Reset Enable */ +#define E600_WDT_WDTCR_TIMEOUT (1 << 5) /* WDT Timeout Output Enable */ +#define E600_WDT_DCR 0x14 /* Down Counter Register */ +#define E600_WDT_WDTLR 0x18 /* WDT Lock Register */ +#define E600_WDT_WDTLR_LOCK (1 << 0) /* Watchdog Timer Lock */ +#define E600_WDT_WDTLR_ENABLE (1 << 1) /* Watchdog Timer Enable */ +#define E600_WDT_WDTLR_TIMEOUT (1 << 2) /* WDT Timeout Configuration */ + +#define E600_HPET_BASE 0xfed00000 /* HPET register base */ +#define E600_HPET_SIZE 0x00000400 /* HPET register size */ + +#define E600_HPET_GCID 0x000 /* Capabilities and ID */ +#define E600_HPET_GCID_WIDTH (1 << 13) /* Counter Size */ +#define E600_HPET_PERIOD 0x004 /* Counter Tick Period */ +#define E600_HPET_GC 0x010 /* General Configuration */ +#define E600_HPET_GC_ENABLE (1 << 0) /* Overall Enable */ +#define E600_HPET_GIS 0x020 /* General Interrupt Status */ +#define E600_HPET_MCV 0x0f0 /* Main Counter Value */ +#define E600_HPET_T0C 0x100 /* Timer 0 Config and Capabilities */ +#define E600_HPET_T0CV 0x108 /* Timer 0 Comparator Value */ +#define E600_HPET_T1C 0x120 /* Timer 1 Config and Capabilities */ +#define E600_HPET_T1CV 0x128 /* Timer 1 Comparator Value */ +#define E600_HPET_T2C 0x140 /* Timer 2 Config and Capabilities */ +#define E600_HPET_T2CV 0x148 /* Timer 2 Comparator Value */ + +struct tcpcib_softc { + /* we call pcibattach() which assumes this starts like this: */ + struct pcib_softc sc_pcib; + + /* Watchdog interface */ + bool sc_wdt_valid; + struct sysmon_wdog sc_wdt_smw; + bus_space_tag_t sc_wdt_iot; + bus_space_handle_t sc_wdt_ioh; + + /* High Precision Event Timer */ + device_t sc_hpetbus; + bus_space_tag_t sc_hpet_memt; +}; + +static int tcpcib_match(device_t, cfdata_t, void *); +static void tcpcib_attach(device_t, device_t, void *); +static int tcpcib_detach(device_t, int); +static int tcpcib_rescan(device_t, const char *, const int *); +static void tcpcib_childdet(device_t, device_t); +static bool tcpcib_suspend(device_t, const pmf_qual_t *); +static bool tcpcib_resume(device_t, const pmf_qual_t *); + +static int tcpcib_wdt_setmode(struct sysmon_wdog *); +static int tcpcib_wdt_tickle(struct sysmon_wdog *); +static void tcpcib_wdt_init(struct tcpcib_softc *, int); +static void tcpcib_wdt_start(struct tcpcib_softc *); +static void tcpcib_wdt_stop(struct tcpcib_softc *); + +CFATTACH_DECL2_NEW(tcpcib, sizeof(struct tcpcib_softc), + tcpcib_match, tcpcib_attach, tcpcib_detach, NULL, + tcpcib_rescan, tcpcib_childdet); + +static struct tcpcib_device { + pcireg_t vendor, product; +} tcpcib_devices[] = { + { PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_E600_LPC } +}; + +static void +tcpcib_wdt_unlock(struct tcpcib_softc *sc) +{ + /* Register unlocking sequence */ + bus_space_write_1(sc->sc_wdt_iot, sc->sc_wdt_ioh, E600_WDT_RR0, 0x80); + bus_space_write_1(sc->sc_wdt_iot, sc->sc_wdt_ioh, E600_WDT_RR0, 0x86); +} + +static void +tcpcib_wdt_init(struct tcpcib_softc *sc, int period) +{ + uint32_t preload; + + /* Set new timeout */ + preload = (period * 33000000) >> 15; + preload--; + + /* + * Set watchdog to perform a cold reset toggling the GPIO pin and the + * prescaler set to 1ms-10m resolution + */ + bus_space_write_1(sc->sc_wdt_iot, sc->sc_wdt_ioh, E600_WDT_WDTCR, + E600_WDT_WDTCR_ENABLE); + tcpcib_wdt_unlock(sc); + bus_space_write_4(sc->sc_wdt_iot, sc->sc_wdt_ioh, E600_WDT_PV1, 0); + tcpcib_wdt_unlock(sc); + bus_space_write_4(sc->sc_wdt_iot, sc->sc_wdt_ioh, E600_WDT_PV2, + preload); + tcpcib_wdt_unlock(sc); + bus_space_write_1(sc->sc_wdt_iot, sc->sc_wdt_ioh, E600_WDT_RR1, + E600_WDT_RR1_RELOAD); +} + +static void +tcpcib_wdt_start(struct tcpcib_softc *sc) +{ + /* Enable watchdog */ + bus_space_write_1(sc->sc_wdt_iot, sc->sc_wdt_ioh, E600_WDT_WDTLR, + E600_WDT_WDTLR_ENABLE); +} + +static void +tcpcib_wdt_stop(struct tcpcib_softc *sc) +{ + /* Disable watchdog, with a reload before for safety */ + tcpcib_wdt_unlock(sc); + bus_space_write_1(sc->sc_wdt_iot, sc->sc_wdt_ioh, E600_WDT_RR1, + E600_WDT_RR1_RELOAD); + bus_space_write_1(sc->sc_wdt_iot, sc->sc_wdt_ioh, E600_WDT_WDTLR, 0); +} + +static int +tcpcib_match(device_t parent, cfdata_t match, void *aux) +{ + struct pci_attach_args *pa = aux; + unsigned int n; + + if (PCI_CLASS(pa->pa_class) != PCI_CLASS_BRIDGE || + PCI_SUBCLASS(pa->pa_class) != PCI_SUBCLASS_BRIDGE_ISA) + return 0; + + for (n = 0; n < __arraycount(tcpcib_devices); n++) { + if (PCI_VENDOR(pa->pa_id) == tcpcib_devices[n].vendor && + PCI_PRODUCT(pa->pa_id) == tcpcib_devices[n].product) + return 10; /* beat pcib(4) */ + } + + return 0; +} + +static void +tcpcib_attach(device_t parent, device_t self, void *aux) +{ + struct tcpcib_softc *sc = device_private(self); + struct pci_attach_args *pa = aux; + uint32_t reg, wdtbase; + + pmf_device_register(self, tcpcib_suspend, tcpcib_resume); + + /* Provide core pcib(4) functionality */ + pcibattach(parent, self, aux); + + /* High Precision Event Timer */ + sc->sc_hpet_memt = pa->pa_memt; + tcpcib_rescan(self, "hpetichbus", NULL); + + /* Map Watchdog I/O space */ + reg = pci_conf_read(pa->pa_pc, pa->pa_tag, E600_LPC_WDTBA); + wdtbase = reg & 0xffff; + sc->sc_wdt_iot = pa->pa_iot; + if (reg & (1 << 31) && wdtbase) { + if (PCI_MAPREG_IO_ADDR(wdtbase) == 0 || + bus_space_map(sc->sc_wdt_iot, PCI_MAPREG_IO_ADDR(wdtbase), + E600_WDT_SIZE, 0, &sc->sc_wdt_ioh)) { + aprint_error_dev(self, "can't map watchdog I/O space\n"); + return; + } + aprint_normal_dev(self, "watchdog"); + + /* Check for reboot on timeout */ + reg = bus_space_read_1(sc->sc_wdt_iot, sc->sc_wdt_ioh, + E600_WDT_RR1); + if (reg & E600_WDT_RR1_TIMEOUT) { + aprint_normal(", reboot on timeout"); + + /* Clear timeout bit */ + tcpcib_wdt_unlock(sc); + bus_space_write_1(sc->sc_wdt_iot, sc->sc_wdt_ioh, + E600_WDT_RR1, E600_WDT_RR1_TIMEOUT); + } + + /* Check it's not locked already */ + reg = bus_space_read_1(sc->sc_wdt_iot, sc->sc_wdt_ioh, + E600_WDT_WDTLR); + if (reg & E600_WDT_WDTLR_LOCK) { + aprint_error(", locked\n"); + return; + } + + /* Disable watchdog */ + tcpcib_wdt_stop(sc); + + /* Register new watchdog */ + sc->sc_wdt_smw.smw_name = device_xname(self); + sc->sc_wdt_smw.smw_cookie = sc; + sc->sc_wdt_smw.smw_setmode = tcpcib_wdt_setmode; + sc->sc_wdt_smw.smw_tickle = tcpcib_wdt_tickle; + sc->sc_wdt_smw.smw_period = 600; /* seconds */ + if (sysmon_wdog_register(&sc->sc_wdt_smw)) { + aprint_error(", unable to register wdog timer\n"); + return; + } + + sc->sc_wdt_valid = true; + aprint_normal("\n"); + } + +} + +static int +tcpcib_detach(device_t self, int flags) +{ + return pcibdetach(self, flags); +} + +static int +tcpcib_rescan(device_t self, const char *ifattr, const int *locators) +{ + struct tcpcib_softc *sc = device_private(self); + + if (ifattr_match(ifattr, "hpetichbus") && sc->sc_hpetbus == NULL) { + struct lpcib_hpet_attach_args hpet_arg; + hpet_arg.hpet_mem_t = sc->sc_hpet_memt; + hpet_arg.hpet_reg = E600_HPET_BASE; + sc->sc_hpetbus = config_found_ia(self, "hpetichbus", + &hpet_arg, NULL); + } + + return pcibrescan(self, ifattr, locators); +} + +static void +tcpcib_childdet(device_t self, device_t child) +{ + struct tcpcib_softc *sc = device_private(self); + + if (sc->sc_hpetbus == child) { + sc->sc_hpetbus = NULL; + return; + } + + pcibchilddet(self, child); +} + +static bool +tcpcib_suspend(device_t self, const pmf_qual_t *qual) +{ + struct tcpcib_softc *sc = device_private(self); + + if (sc->sc_wdt_valid) + tcpcib_wdt_stop(sc); + + return true; +} + +static bool +tcpcib_resume(device_t self, const pmf_qual_t *qual) +{ + struct tcpcib_softc *sc = device_private(self); + struct sysmon_wdog *smw = &sc->sc_wdt_smw; + + if (sc->sc_wdt_valid) { + if ((smw->smw_mode & WDOG_MODE_MASK) != WDOG_MODE_DISARMED && + smw->smw_period > 0) { + tcpcib_wdt_init(sc, smw->smw_period); + tcpcib_wdt_start(sc); + } else { + tcpcib_wdt_stop(sc); + } + } + + return true; +} + +static int +tcpcib_wdt_setmode(struct sysmon_wdog *smw) +{ + struct tcpcib_softc *sc = smw->smw_cookie; + unsigned int period; + + period = smw->smw_period; + if ((smw->smw_mode & WDOG_MODE_MASK) == WDOG_MODE_DISARMED) { + tcpcib_wdt_stop(sc); + } else { + /* 600 seconds is the maximum supported timeout value */ + if (period > 600) + return EINVAL; + + tcpcib_wdt_stop(sc); + tcpcib_wdt_init(sc, period); + tcpcib_wdt_start(sc); + tcpcib_wdt_tickle(smw); + } + + return 0; +} + +static int +tcpcib_wdt_tickle(struct sysmon_wdog *smw) +{ + struct tcpcib_softc *sc = smw->smw_cookie; + + /* Reset timer */ + tcpcib_wdt_unlock(sc); + bus_space_write_1(sc->sc_wdt_iot, sc->sc_wdt_ioh, + E600_WDT_RR1, E600_WDT_RR1_RELOAD); + + return 0; +}