Module Name:    src
Committed By:   pgoyette
Date:           Sun May  3 02:50:59 UTC 2015

Modified Files:
        src/sys/arch/x86/pci: files.pci ichlpcib.c
Added Files:
        src/sys/arch/x86/pci: tco.c tco.h

Log Message:
Separate the watchdog code from the pcib code, and make the watchdog
a loadable module.


To generate a diff of this commit:
cvs rdiff -u -r1.19 -r1.20 src/sys/arch/x86/pci/files.pci
cvs rdiff -u -r1.48 -r1.49 src/sys/arch/x86/pci/ichlpcib.c
cvs rdiff -u -r0 -r1.1 src/sys/arch/x86/pci/tco.c src/sys/arch/x86/pci/tco.h

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/x86/pci/files.pci
diff -u src/sys/arch/x86/pci/files.pci:1.19 src/sys/arch/x86/pci/files.pci:1.20
--- src/sys/arch/x86/pci/files.pci:1.19	Tue Nov 11 02:31:55 2014
+++ src/sys/arch/x86/pci/files.pci	Sun May  3 02:50:59 2015
@@ -1,4 +1,4 @@
-#	$NetBSD: files.pci,v 1.19 2014/11/11 02:31:55 christos Exp $
+#	$NetBSD: files.pci,v 1.20 2015/05/03 02:50:59 pgoyette Exp $
 
 device 	aapic
 attach 	aapic at pci
@@ -46,13 +46,18 @@ file 	arch/x86/pci/rdcpcib.c 		rdcpcib
 
 define	fwhichbus {}
 define	hpetichbus {}
-device	ichlpcib: acpipmtimer, isabus, sysmon_wdog, fwhichbus, hpetichbus, gpiobus
+define	tcoichbus {}
+device	ichlpcib: acpipmtimer, isabus, fwhichbus, hpetichbus, gpiobus, tcoichbus
 attach	ichlpcib at pci
 file 	arch/x86/pci/ichlpcib.c 	ichlpcib
 
 attach	hpet at hpetichbus with ichlpcib_hpet
 file    arch/x86/pci/ichlpcib_hpet.c	ichlpcib_hpet
 
+device	tco: sysmon_wdog
+attach	tco at tcoichbus
+file	arch/x86/pci/tco.c		tco
+
 device	tcpcib: isabus, sysmon_wdog, hpetichbus
 attach	tcpcib at pci
 file	arch/x86/pci/tcpcib.c		tcpcib

Index: src/sys/arch/x86/pci/ichlpcib.c
diff -u src/sys/arch/x86/pci/ichlpcib.c:1.48 src/sys/arch/x86/pci/ichlpcib.c:1.49
--- src/sys/arch/x86/pci/ichlpcib.c:1.48	Fri Mar 20 12:01:32 2015
+++ src/sys/arch/x86/pci/ichlpcib.c	Sun May  3 02:50:59 2015
@@ -1,4 +1,4 @@
-/*	$NetBSD: ichlpcib.c,v 1.48 2015/03/20 12:01:32 msaitoh Exp $	*/
+/*	$NetBSD: ichlpcib.c,v 1.49 2015/05/03 02:50:59 pgoyette Exp $	*/
 
 /*-
  * Copyright (c) 2004 The NetBSD Foundation, Inc.
@@ -34,12 +34,13 @@
  *
  *  LPC Interface Bridge is basically a pcib (PCI-ISA Bridge), but has
  *  some power management and monitoring functions.
- *  Currently we support the watchdog timer, SpeedStep (on some systems)
+ *  Currently we support the watchdog timer, SpeedStep (on some systems),
+ *  the gpio interface, hpet timer, hardware random number generator,
  *  and the power management timer.
  */
 
 #include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: ichlpcib.c,v 1.48 2015/03/20 12:01:32 msaitoh Exp $");
+__KERNEL_RCSID(0, "$NetBSD: ichlpcib.c,v 1.49 2015/05/03 02:50:59 pgoyette Exp $");
 
 #include <sys/types.h>
 #include <sys/param.h>
@@ -55,7 +56,6 @@ __KERNEL_RCSID(0, "$NetBSD: ichlpcib.c,v
 #include <dev/pci/pcidevs.h>
 
 #include <dev/gpio/gpiovar.h>
-#include <dev/sysmon/sysmonvar.h>
 
 #include <dev/ic/acpipmtimer.h>
 #include <dev/ic/i82801lpcreg.h>
@@ -63,6 +63,8 @@ __KERNEL_RCSID(0, "$NetBSD: ichlpcib.c,v
 #include <dev/ic/hpetreg.h>
 #include <dev/ic/hpetvar.h>
 
+#include <arch/x86/pci/tco.h>
+
 #include "pcibvar.h"
 #include "gpio.h"
 #include "fwhrng.h"
@@ -82,8 +84,7 @@ struct lpcib_softc {
 	bus_space_handle_t	sc_rcbah;
 	pcireg_t		sc_rcba_reg;
 
-	/* Watchdog variables. */
-	struct sysmon_wdog	sc_smw;
+	/* Power management variables. */
 	bus_space_tag_t		sc_iot;
 	bus_space_handle_t	sc_ioh;
 	bus_size_t		sc_iosize;
@@ -114,6 +115,7 @@ struct lpcib_softc {
 	pcireg_t		sc_fwhsel2;
 
 	/* Child devices */
+	device_t		sc_tco;
 	device_t		sc_hpetbus;
 	acpipmtimer_t		sc_pmtimer;
 	pcireg_t		sc_acpi_cntl;
@@ -135,12 +137,6 @@ static int pmtimer_unconfigure(device_t,
 
 static void tcotimer_configure(device_t);
 static int tcotimer_unconfigure(device_t, int);
-static int tcotimer_setmode(struct sysmon_wdog *);
-static int tcotimer_tickle(struct sysmon_wdog *);
-static void tcotimer_stop(struct lpcib_softc *);
-static void tcotimer_start(struct lpcib_softc *);
-static void tcotimer_status_reset(struct lpcib_softc *);
-static int  tcotimer_disable_noreboot(device_t);
 
 static void speedstep_configure(device_t);
 static void speedstep_unconfigure(device_t);
@@ -411,6 +407,11 @@ lpcibchilddet(device_t self, device_t ch
 		return;
 	}
 #endif
+	if (sc->sc_tco == child) {
+		sc->sc_tco = NULL;
+		return;
+	}
+
 	if (sc->sc_hpetbus != child) {
 		pcibchilddet(self, child);
 		return;
@@ -454,6 +455,9 @@ lpcibrescan(device_t self, const char *i
 {
 	struct lpcib_softc *sc = device_private(self);
 
+	if(ifattr_match(ifattr, "tcoichbus") && sc->sc_tco == NULL)
+		tcotimer_configure(self);
+
 #if NFWHRNG > 0
 	if (ifattr_match(ifattr, "fwhichbus") && sc->sc_fwhbus == NULL)
 		lpcib_fwh_configure(self);
@@ -613,59 +617,22 @@ pmtimer_unconfigure(device_t self, int f
 }
 
 /*
- * Initialize the watchdog timer.
+ * Configure the watchdog timer.
  */
 static void
 tcotimer_configure(device_t self)
 {
 	struct lpcib_softc *sc = device_private(self);
-	uint32_t ioreg;
-	unsigned int period;
-
-	/* Explicitly stop the TCO timer. */
-	tcotimer_stop(sc);
-
-	/*
-	 * Enable TCO timeout SMI only if the hardware reset does not
-	 * work. We don't know what the SMBIOS does.
-	 */
-	ioreg = bus_space_read_4(sc->sc_iot, sc->sc_ioh, LPCIB_SMI_EN);
-	ioreg &= ~LPCIB_SMI_EN_TCO_EN;
-
-	/* 
-	 * Clear the No Reboot (NR) bit. If this fails, enabling the TCO_EN bit
-	 * in the SMI_EN register is the last chance.
-	 */
-	if (tcotimer_disable_noreboot(self)) {
-		ioreg |= LPCIB_SMI_EN_TCO_EN;
-	}
-	if ((ioreg & LPCIB_SMI_EN_GBL_SMI_EN) != 0) {
-		bus_space_write_4(sc->sc_iot, sc->sc_ioh, LPCIB_SMI_EN, ioreg);
-	}
-
-	/* Reset the watchdog status registers. */
-	tcotimer_status_reset(sc);
-
-	/* 
-	 * Register the driver with the sysmon watchdog framework.
-	 */
-	sc->sc_smw.smw_name = device_xname(self);
-	sc->sc_smw.smw_cookie = sc;
-	sc->sc_smw.smw_setmode = tcotimer_setmode;
-	sc->sc_smw.smw_tickle = tcotimer_tickle;
-	if (sc->sc_has_rcba)
-		period = LPCIB_TCOTIMER2_MAX_TICK;
-	else
-		period = LPCIB_TCOTIMER_MAX_TICK;
-	sc->sc_smw.smw_period = lpcib_tcotimer_tick_to_second(period);
+	struct lpcib_tco_attach_args arg;
 
-	if (sysmon_wdog_register(&sc->sc_smw)) {
-		aprint_error_dev(self, "unable to register TCO timer"
-		       "as a sysmon watchdog device.\n");
-		return;
-	}
+	arg.ta_iot = sc->sc_iot;
+	arg.ta_ioh = sc->sc_ioh;
+	arg.ta_rcbat = sc->sc_rcbat;
+	arg.ta_rcbah = sc->sc_rcbah;
+	arg.ta_has_rcba = sc->sc_has_rcba;
+	arg.ta_pcib = &(sc->sc_pcib);
 
-	aprint_verbose_dev(self, "TCO (watchdog) timer configured.\n");
+	sc->sc_tco = config_found_ia(self, "tcoichbus", &arg, NULL);
 }
 
 static int
@@ -674,165 +641,11 @@ tcotimer_unconfigure(device_t self, int 
 	struct lpcib_softc *sc = device_private(self);
 	int rc;
 
-	if ((rc = sysmon_wdog_unregister(&sc->sc_smw)) != 0) {
-		if (rc == ERESTART)
-			rc = EINTR;
+	if (sc->sc_tco != NULL &&
+	    (rc = config_detach(sc->sc_tco, flags)) != 0)
 		return rc;
-	}
-
-	/* Explicitly stop the TCO timer. */
-	tcotimer_stop(sc);
-
-	/* XXX Set No Reboot? */
-
-	return 0;
-}
-
-
-/*
- * Sysmon watchdog callbacks.
- */
-static int
-tcotimer_setmode(struct sysmon_wdog *smw)
-{
-	struct lpcib_softc *sc = smw->smw_cookie;
-	unsigned int period;
-	uint16_t ich6period = 0;
-	uint8_t ich5period = 0;
-
-	if ((smw->smw_mode & WDOG_MODE_MASK) == WDOG_MODE_DISARMED) {
-		/* Stop the TCO timer. */
-		tcotimer_stop(sc);
-	} else {
-		/* 
-		 * ICH6 or newer are limited to 2s min and 613s max.
-		 * ICH5 or older are limited to 4s min and 39s max.
-		 */
-		period = lpcib_tcotimer_second_to_tick(smw->smw_period);
-		if (sc->sc_has_rcba) {
-			if (period < LPCIB_TCOTIMER2_MIN_TICK ||
-			    period > LPCIB_TCOTIMER2_MAX_TICK)
-				return EINVAL;
-		} else {
-			if (period < LPCIB_TCOTIMER_MIN_TICK ||
-			    period > LPCIB_TCOTIMER_MAX_TICK)
-				return EINVAL;
-		}
-		
-		/* Stop the TCO timer, */
-		tcotimer_stop(sc);
-
-		/* set the timeout, */
-		if (sc->sc_has_rcba) {
-			/* ICH6 or newer */
-			ich6period = bus_space_read_2(sc->sc_iot, sc->sc_ioh,
-						      LPCIB_TCO_TMR2);
-			ich6period &= 0xfc00;
-			bus_space_write_2(sc->sc_iot, sc->sc_ioh,
-					  LPCIB_TCO_TMR2, ich6period | period);
-		} else {
-			/* ICH5 or older */
-			ich5period = bus_space_read_1(sc->sc_iot, sc->sc_ioh,
-						   LPCIB_TCO_TMR);
-			ich5period &= 0xc0;
-			bus_space_write_1(sc->sc_iot, sc->sc_ioh,
-					  LPCIB_TCO_TMR, ich5period | period);
-		}
-
-		/* and start/reload the timer. */
-		tcotimer_start(sc);
-		tcotimer_tickle(smw);
-	}
-
-	return 0;
-}
-
-static int
-tcotimer_tickle(struct sysmon_wdog *smw)
-{
-	struct lpcib_softc *sc = smw->smw_cookie;
-
-	/* any value is allowed */
-	if (sc->sc_has_rcba)
-		bus_space_write_2(sc->sc_iot, sc->sc_ioh, LPCIB_TCO_RLD, 1);
-	else
-		bus_space_write_1(sc->sc_iot, sc->sc_ioh, LPCIB_TCO_RLD, 1);
-
-	return 0;
-}
-
-static void
-tcotimer_stop(struct lpcib_softc *sc)
-{
-	uint16_t ioreg;
-
-	ioreg = bus_space_read_2(sc->sc_iot, sc->sc_ioh, LPCIB_TCO1_CNT);
-	ioreg |= LPCIB_TCO1_CNT_TCO_TMR_HLT;
-	bus_space_write_2(sc->sc_iot, sc->sc_ioh, LPCIB_TCO1_CNT, ioreg);
-}
-
-static void
-tcotimer_start(struct lpcib_softc *sc)
-{
-	uint16_t ioreg;
-
-	ioreg = bus_space_read_2(sc->sc_iot, sc->sc_ioh, LPCIB_TCO1_CNT);
-	ioreg &= ~LPCIB_TCO1_CNT_TCO_TMR_HLT;
-	bus_space_write_2(sc->sc_iot, sc->sc_ioh, LPCIB_TCO1_CNT, ioreg);
-}
-
-static void
-tcotimer_status_reset(struct lpcib_softc *sc)
-{
-	bus_space_write_2(sc->sc_iot, sc->sc_ioh, LPCIB_TCO1_STS,
-			  LPCIB_TCO1_STS_TIMEOUT);
-	bus_space_write_2(sc->sc_iot, sc->sc_ioh, LPCIB_TCO2_STS,
-			  LPCIB_TCO2_STS_BOOT_STS);
-	bus_space_write_2(sc->sc_iot, sc->sc_ioh, LPCIB_TCO2_STS,
-			  LPCIB_TCO2_STS_SECONDS_TO_STS);
-}
-
-/*
- * Clear the No Reboot (NR) bit, this enables reboots when the timer
- * reaches the timeout for the second time.
- */
-static int
-tcotimer_disable_noreboot(device_t self)
-{
-	struct lpcib_softc *sc = device_private(self);
-
-	if (sc->sc_has_rcba) {
-		uint32_t status;
-
-		status = bus_space_read_4(sc->sc_rcbat, sc->sc_rcbah,
-		    LPCIB_GCS_OFFSET);
-		status &= ~LPCIB_GCS_NO_REBOOT;
-		bus_space_write_4(sc->sc_rcbat, sc->sc_rcbah,
-		    LPCIB_GCS_OFFSET, status);
-		status = bus_space_read_4(sc->sc_rcbat, sc->sc_rcbah,
-		    LPCIB_GCS_OFFSET);
-		if (status & LPCIB_GCS_NO_REBOOT)
-			goto error;
-	} else {
-		pcireg_t pcireg;
-
-		pcireg = pci_conf_read(sc->sc_pcib.sc_pc, sc->sc_pcib.sc_tag,
-				       LPCIB_PCI_GEN_STA);
-		if (pcireg & LPCIB_PCI_GEN_STA_NO_REBOOT) {
-			/* TCO timeout reset is disabled; try to enable it */
-			pcireg &= ~LPCIB_PCI_GEN_STA_NO_REBOOT;
-			pci_conf_write(sc->sc_pcib.sc_pc, sc->sc_pcib.sc_tag,
-				       LPCIB_PCI_GEN_STA, pcireg);
-			if (pcireg & LPCIB_PCI_GEN_STA_NO_REBOOT)
-				goto error;
-		}
-	}
 
 	return 0;
-error:
-	aprint_error_dev(self, "TCO timer reboot disabled by hardware; "
-	    "hope SMBIOS properly handles it.\n");
-	return EINVAL;
 }
 
 

Added files:

Index: src/sys/arch/x86/pci/tco.c
diff -u /dev/null src/sys/arch/x86/pci/tco.c:1.1
--- /dev/null	Sun May  3 02:50:59 2015
+++ src/sys/arch/x86/pci/tco.c	Sun May  3 02:50:59 2015
@@ -0,0 +1,375 @@
+/*	$NetBSD: tco.c,v 1.1 2015/05/03 02:50:59 pgoyette Exp $	*/
+
+/*-
+ * Copyright (c) 2015 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Minoura Makoto and Matthew R. Green.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/*
+ * Intel I/O Controller Hub (ICHn) watchdog timer
+ */
+
+#include <sys/cdefs.h>
+__KERNEL_RCSID(0, "$NetBSD: tco.c,v 1.1 2015/05/03 02:50:59 pgoyette Exp $");
+
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/device.h>
+#include <sys/timetc.h>
+#include <sys/module.h>
+
+#include <dev/pci/pcivar.h>
+#include <dev/pci/pcireg.h>
+#include <dev/ic/i82801lpcreg.h>
+
+#include <dev/sysmon/sysmonvar.h>
+
+#include <arch/x86/pci/tco.h>
+
+#include "pcibvar.h"
+
+struct tco_softc{
+	struct sysmon_wdog	sc_smw;
+	bus_space_tag_t		sc_iot;
+	bus_space_handle_t	sc_ioh;
+	bus_space_tag_t		sc_rcbat;
+	bus_space_handle_t	sc_rcbah;
+	struct pcib_softc *	sc_pcib;
+	int			sc_armed;
+	unsigned int		sc_min_t;
+	unsigned int		sc_max_t;
+	int			sc_has_rcba;
+};
+
+static int tco_match(device_t, cfdata_t, void *);
+static void tco_attach(device_t, device_t, void *);
+static int tco_detach(device_t, int);
+
+static bool tco_suspend(device_t, const pmf_qual_t *);
+
+static int tcotimer_setmode(struct sysmon_wdog *);
+static int tcotimer_tickle(struct sysmon_wdog *);
+static void tcotimer_stop(struct tco_softc *);
+static void tcotimer_start(struct tco_softc *);
+static void tcotimer_status_reset(struct tco_softc *);
+static int  tcotimer_disable_noreboot(device_t);
+
+CFATTACH_DECL3_NEW(tco, sizeof(struct tco_softc),
+    tco_match, tco_attach, tco_detach, NULL, NULL, NULL, 0);
+
+/*
+ * Autoconf callbacks.
+ */
+static int
+tco_match(device_t parent, cfdata_t match, void *aux)
+{
+	struct lpcib_tco_attach_args *ta = aux;
+
+	if (ta->ta_iot != 0)
+		return 1;
+
+	return 0;
+}
+
+static void
+tco_attach(device_t parent, device_t self, void *aux)
+{
+	struct tco_softc *sc = device_private(self);
+	struct lpcib_tco_attach_args *ta = aux;
+	uint32_t ioreg;
+
+	/* Retrieve bus info shared with parent/siblings */
+
+	sc->sc_iot = ta->ta_iot;
+	sc->sc_ioh = ta->ta_ioh;
+	sc->sc_rcbat = ta->ta_rcbat;
+	sc->sc_rcbah = ta->ta_rcbah;
+	sc->sc_pcib = ta->ta_pcib;
+	sc->sc_has_rcba = ta->ta_has_rcba;
+
+	/* Explicitly stop the TCO timer. */
+	tcotimer_stop(sc);
+
+	/*
+	 * Enable TCO timeout SMI only if the hardware reset does not
+	 * work. We don't know what the SMBIOS does.
+	 */
+	ioreg = bus_space_read_4(sc->sc_iot, sc->sc_ioh, LPCIB_SMI_EN);
+	ioreg &= ~LPCIB_SMI_EN_TCO_EN;
+
+	/* 
+	 * Clear the No Reboot (NR) bit. If this fails, enabling the TCO_EN bit
+	 * in the SMI_EN register is the last chance.
+	 */
+	if (tcotimer_disable_noreboot(self)) {
+		ioreg |= LPCIB_SMI_EN_TCO_EN;
+	}
+	if ((ioreg & LPCIB_SMI_EN_GBL_SMI_EN) != 0) {
+		bus_space_write_4(sc->sc_iot, sc->sc_ioh, LPCIB_SMI_EN, ioreg);
+	}
+
+	/* Reset the watchdog status registers. */
+	tcotimer_status_reset(sc);
+
+	/* 
+	 * Register the driver with the sysmon watchdog framework.
+	 */
+	sc->sc_smw.smw_name = device_xname(self);
+	sc->sc_smw.smw_cookie = sc;
+	sc->sc_smw.smw_setmode = tcotimer_setmode;
+	sc->sc_smw.smw_tickle = tcotimer_tickle;
+
+	/* 
+	 * ICH6 or newer are limited to 2ticks min and 613ticks max.
+	 *                              1sec           367secs
+	 *
+	 * ICH5 or older are limited to 4ticks min and 39ticks max.
+	 *                              2secs          23secs
+	 */
+	if (sc->sc_has_rcba) {
+		sc->sc_max_t = LPCIB_TCOTIMER2_MAX_TICK;
+		sc->sc_min_t = LPCIB_TCOTIMER2_MIN_TICK;
+	} else {
+		sc->sc_max_t = LPCIB_TCOTIMER_MAX_TICK;
+		sc->sc_min_t = LPCIB_TCOTIMER_MIN_TICK;
+	}
+	sc->sc_smw.smw_period = lpcib_tcotimer_tick_to_second(sc->sc_max_t);
+
+	aprint_normal(": TCO (watchdog) timer configured.\n");
+	aprint_naive("\n");
+	aprint_verbose_dev(self, "Min/Max interval %u/%u seconds\n",
+		lpcib_tcotimer_tick_to_second(sc->sc_min_t),
+		lpcib_tcotimer_tick_to_second(sc->sc_max_t));
+
+	if (sysmon_wdog_register(&sc->sc_smw))
+		aprint_error_dev(self, "unable to register TCO timer"
+		       "as a sysmon watchdog device.\n");
+
+	if (!pmf_device_register(self, tco_suspend, NULL))
+		aprint_error_dev(self, "unable to register with pmf\n");
+}
+
+static int
+tco_detach(device_t self, int flags)
+{
+	struct tco_softc *sc = device_private(self);
+	int rc;
+
+	if ((rc = sysmon_wdog_unregister(&sc->sc_smw)) != 0) {
+		if (rc == ERESTART)
+			rc = EINTR;
+		return rc;
+	}
+
+	/* Explicitly stop the TCO timer. */
+	tcotimer_stop(sc);
+
+	/* XXX Set No Reboot? */
+
+	pmf_device_deregister(self);
+
+	return 0;
+}
+
+static bool
+tco_suspend(device_t self, const pmf_qual_t *quals)
+{
+	struct tco_softc *sc = device_private(self);
+
+	/* Allow suspend only if watchdog is not armed */
+
+	return ((sc->sc_smw.smw_mode & WDOG_MODE_MASK) == WDOG_MODE_DISARMED);
+}
+
+/*
+ * Sysmon watchdog callbacks.
+ */
+static int
+tcotimer_setmode(struct sysmon_wdog *smw)
+{
+	struct tco_softc *sc = smw->smw_cookie;
+	unsigned int period;
+	uint16_t ich6period = 0;
+	uint8_t ich5period = 0;
+
+	if ((smw->smw_mode & WDOG_MODE_MASK) == WDOG_MODE_DISARMED) {
+		/* Stop the TCO timer. */
+		tcotimer_stop(sc);
+	} else {
+		period = lpcib_tcotimer_second_to_tick(smw->smw_period);
+		if (period < sc->sc_min_t || period > sc->sc_max_t)
+			return EINVAL;
+		
+		/* Stop the TCO timer, */
+		tcotimer_stop(sc);
+
+		/* set the timeout, */
+		if (sc->sc_has_rcba) {
+			/* ICH6 or newer */
+			ich6period = bus_space_read_2(sc->sc_iot, sc->sc_ioh,
+						      LPCIB_TCO_TMR2);
+			ich6period &= 0xfc00;
+			bus_space_write_2(sc->sc_iot, sc->sc_ioh,
+					  LPCIB_TCO_TMR2, ich6period | period);
+		} else {
+			/* ICH5 or older */
+			ich5period = bus_space_read_1(sc->sc_iot, sc->sc_ioh,
+						   LPCIB_TCO_TMR);
+			ich5period &= 0xc0;
+			bus_space_write_1(sc->sc_iot, sc->sc_ioh,
+					  LPCIB_TCO_TMR, ich5period | period);
+		}
+
+		/* and start/reload the timer. */
+		tcotimer_start(sc);
+		tcotimer_tickle(smw);
+	}
+
+	return 0;
+}
+
+static int
+tcotimer_tickle(struct sysmon_wdog *smw)
+{
+	struct tco_softc *sc = smw->smw_cookie;
+
+	/* any value is allowed */
+	if (sc->sc_has_rcba)
+		bus_space_write_2(sc->sc_iot, sc->sc_ioh, LPCIB_TCO_RLD, 1);
+	else
+		bus_space_write_1(sc->sc_iot, sc->sc_ioh, LPCIB_TCO_RLD, 1);
+
+	return 0;
+}
+
+static void
+tcotimer_stop(struct tco_softc *sc)
+{
+	uint16_t ioreg;
+
+	ioreg = bus_space_read_2(sc->sc_iot, sc->sc_ioh, LPCIB_TCO1_CNT);
+	ioreg |= LPCIB_TCO1_CNT_TCO_TMR_HLT;
+	bus_space_write_2(sc->sc_iot, sc->sc_ioh, LPCIB_TCO1_CNT, ioreg);
+}
+
+static void
+tcotimer_start(struct tco_softc *sc)
+{
+	uint16_t ioreg;
+
+	ioreg = bus_space_read_2(sc->sc_iot, sc->sc_ioh, LPCIB_TCO1_CNT);
+	ioreg &= ~LPCIB_TCO1_CNT_TCO_TMR_HLT;
+	bus_space_write_2(sc->sc_iot, sc->sc_ioh, LPCIB_TCO1_CNT, ioreg);
+}
+
+static void
+tcotimer_status_reset(struct tco_softc *sc)
+{
+	bus_space_write_2(sc->sc_iot, sc->sc_ioh, LPCIB_TCO1_STS,
+			  LPCIB_TCO1_STS_TIMEOUT);
+	bus_space_write_2(sc->sc_iot, sc->sc_ioh, LPCIB_TCO2_STS,
+			  LPCIB_TCO2_STS_BOOT_STS);
+	bus_space_write_2(sc->sc_iot, sc->sc_ioh, LPCIB_TCO2_STS,
+			  LPCIB_TCO2_STS_SECONDS_TO_STS);
+}
+
+/*
+ * Clear the No Reboot (NR) bit, this enables reboots when the timer
+ * reaches the timeout for the second time.
+ */
+static int
+tcotimer_disable_noreboot(device_t self)
+{
+	struct tco_softc *sc = device_private(self);
+
+	if (sc->sc_has_rcba) {
+		uint32_t status;
+
+		status = bus_space_read_4(sc->sc_rcbat, sc->sc_rcbah,
+		    LPCIB_GCS_OFFSET);
+		status &= ~LPCIB_GCS_NO_REBOOT;
+		bus_space_write_4(sc->sc_rcbat, sc->sc_rcbah,
+		    LPCIB_GCS_OFFSET, status);
+		status = bus_space_read_4(sc->sc_rcbat, sc->sc_rcbah,
+		    LPCIB_GCS_OFFSET);
+		if (status & LPCIB_GCS_NO_REBOOT)
+			goto error;
+	} else {
+		pcireg_t pcireg;
+
+		pcireg = pci_conf_read(sc->sc_pcib->sc_pc, sc->sc_pcib->sc_tag,
+				       LPCIB_PCI_GEN_STA);
+		if (pcireg & LPCIB_PCI_GEN_STA_NO_REBOOT) {
+			/* TCO timeout reset is disabled; try to enable it */
+			pcireg &= ~LPCIB_PCI_GEN_STA_NO_REBOOT;
+			pci_conf_write(sc->sc_pcib->sc_pc, sc->sc_pcib->sc_tag,
+				       LPCIB_PCI_GEN_STA, pcireg);
+			if (pcireg & LPCIB_PCI_GEN_STA_NO_REBOOT)
+				goto error;
+		}
+	}
+
+	return 0;
+error:
+	aprint_error_dev(self, "TCO timer reboot disabled by hardware; "
+	    "hope SMBIOS properly handles it.\n");
+	return EINVAL;
+}
+
+MODULE(MODULE_CLASS_DRIVER, tco, "sysmon_wdog");
+
+#ifdef _MODULE
+#include "ioconf.c" 
+#endif
+
+static int
+tco_modcmd(modcmd_t cmd, void *arg)
+{
+	int ret = 0;
+
+	switch (cmd) {
+	case MODULE_CMD_INIT:
+#ifdef _MODULE
+		ret = config_init_component(cfdriver_ioconf_tco,
+					    cfattach_ioconf_tco,
+					    cfdata_ioconf_tco);
+#endif
+		break;
+	case MODULE_CMD_FINI:
+#ifdef _MODULE
+		ret = config_fini_component(cfdriver_ioconf_tco,
+					    cfattach_ioconf_tco,
+					    cfdata_ioconf_tco);
+#endif
+		break;
+	default:
+		ret = ENOTTY;
+	}
+
+	return ret;
+}
Index: src/sys/arch/x86/pci/tco.h
diff -u /dev/null src/sys/arch/x86/pci/tco.h:1.1
--- /dev/null	Sun May  3 02:50:59 2015
+++ src/sys/arch/x86/pci/tco.h	Sun May  3 02:50:59 2015
@@ -0,0 +1,48 @@
+/*	$NetBSD: tco.h,v 1.1 2015/05/03 02:50:59 pgoyette Exp $	*/
+
+/*-
+ * Copyright (c) 2015 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Minoura Makoto and Matthew R. Green.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/*
+ * Intel I/O Controller Hub (ICHn) watchdog timer
+ */
+
+#ifndef _X86_PCI_TCO_H_
+#define _X86_PCI_TCO_H_
+
+struct lpcib_tco_attach_args {  
+	int			ta_has_rcba;
+	bus_space_tag_t		ta_iot;
+	bus_space_handle_t	ta_ioh;
+	bus_space_tag_t		ta_rcbat;
+	bus_space_handle_t	ta_rcbah;
+	struct pcib_softc *	ta_pcib;
+};
+
+#endif

Reply via email to