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;
+}

Reply via email to