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