Skip site navigation (1)Skip section navigation (2)
Date:      Tue, 6 Mar 2012 18:44:52 +0000 (UTC)
From:      "Bjoern A. Zeeb" <bz@FreeBSD.org>
To:        src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-head@freebsd.org
Subject:   svn commit: r232614 - in head: share/man/man4 sys/amd64/conf sys/boot/forth sys/conf sys/dev/wbwd sys/i386/conf sys/modules sys/modules/wbwd
Message-ID:  <201203061844.q26IiqZL052653@svn.freebsd.org>

next in thread | raw e-mail | index | archive | help
Author: bz
Date: Tue Mar  6 18:44:52 2012
New Revision: 232614
URL: http://svn.freebsd.org/changeset/base/232614

Log:
  Provide wbwd(4), a driver for the watchdog timer found on various
  Winbond Super I/O chips.
  
  With minor efforts it should be possible the extend the driver to support
  further chips/revisions available from Winbond.  In the simplest case
  only new IDs need to be added, while different chipsets might require
  their own function to enter extended function mode, etc.
  
  Sponsored by:	Sandvine Incorporated ULC (in 2011)
  Reviewed by:	emaste, brueffer
  MFC after:	2 weeks

Added:
  head/share/man/man4/wbwd.4   (contents, props changed)
  head/sys/dev/wbwd/
  head/sys/dev/wbwd/wbwd.c   (contents, props changed)
  head/sys/modules/wbwd/
  head/sys/modules/wbwd/Makefile   (contents, props changed)
Modified:
  head/sys/amd64/conf/GENERIC.hints
  head/sys/amd64/conf/NOTES
  head/sys/boot/forth/loader.conf
  head/sys/conf/files.amd64
  head/sys/conf/files.i386
  head/sys/i386/conf/GENERIC.hints
  head/sys/i386/conf/NOTES
  head/sys/modules/Makefile

Added: head/share/man/man4/wbwd.4
==============================================================================
--- /dev/null	00:00:00 1970	(empty, because file is newly added)
+++ head/share/man/man4/wbwd.4	Tue Mar  6 18:44:52 2012	(r232614)
@@ -0,0 +1,117 @@
+.\"-
+.\" Copyright (c) 2012 Bjoern A. Zeeb <bz@FreeBSD.org>
+.\" All rights reserved.
+.\"
+.\" 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 AUTHOR 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 AUTHOR 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.
+.\"
+.\" $FreeBSD$
+.\"
+.Dd March 6, 2012
+.Dt wbwd 4
+.Os
+.Sh NAME
+.Nm wbwd
+.Nd device driver for watchdog timer found on Winbond Super I/O chips
+.Sh SYNOPSIS
+To compile this driver into the kernel, place the following line in your
+kernel configuration file:
+.Bd -ragged -offset indent
+.Cd "device wbwd"
+.Ed
+.Pp
+Alternatively, to load the driver as a module at boot time, place the following
+line in
+.Xr loader.conf 5 :
+.Bd -literal -offset indent
+wbwd_load="YES"
+.Ed
+.Pp
+In
+.Pa /boot/device.hints :
+.Cd hint.wbwd.0.at="isa"
+.Sh DESCRIPTION
+The
+.Nm
+driver provides
+.Xr watchdog 4
+support for the watchdog interrupt timer present on at least the following
+Winbond Super I/O chips:
+.Pp
+.Bl -bullet -compact
+.It
+83627HF/F/HG/G Rev. G
+.It
+83627HF/F/HG/G Rev. J
+.It
+83627HF/F/HG/G Rev. UD-A
+.It
+83627DHG IC ver. 5
+.El
+.Sh SYSCTL VARIABLES
+The
+.Nm
+driver provides the following options as
+.Xr sysctl 8
+variables.
+.Bl -tag -width "xxxxxx"  
+.It Va dev.wbwd.0.timeout_override
+This variable allows to program the timer to a value independent on the one
+provided by the
+.Xr watchdog 4
+framework while still relying on the regular updates from e.g.
+.Xr watchdogd 8 .
+This is particularly useful if your system provides multiple watchdogs and
+you want them to fire in a special sequence to trigger an NMI after a shorter
+period than the reset timeout for example.
+The value set must not be lower than the sleep time of
+.Xr watchdogd 8 .
+A value of 0 disables this feature and the timeout value provided by
+.Xr watchdog 4
+will be used.
+.It Va dev.wbwd.0.debug_verbose
+If set this sysctl will tell the driver to log its current state before and
+after the timer reset on each invocation from
+.Xr watchdog 9
+to the kernel message buffer for debugging.
+.It Va dev.wbwd.0.debug
+This read-only value gives the state of some registers on last update.
+.El
+.Pp
+The
+.Nm
+driver also provides further sysctl options that are hidden by default.
+See the source code for more information.
+.Sh SEE ALSO
+.Xr watchdog 4 ,
+.Xr device.hints 5 ,
+.Xr watchdog 8 ,
+.Xr watchdogd 8 ,
+.Xr watchdog 9
+.Sh HISTORY
+The
+.Nm
+driver first appeared in
+.Fx 10.0 .
+.Sh AUTHORS
+.An -nosplit
+This manual page was written by
+.An Bjoern A. Zeeb Aq bz@FreeBSD.org .

Modified: head/sys/amd64/conf/GENERIC.hints
==============================================================================
--- head/sys/amd64/conf/GENERIC.hints	Tue Mar  6 18:39:07 2012	(r232613)
+++ head/sys/amd64/conf/GENERIC.hints	Tue Mar  6 18:44:52 2012	(r232614)
@@ -30,3 +30,4 @@ hint.atrtc.0.irq="8"
 hint.attimer.0.at="isa"
 hint.attimer.0.port="0x40"
 hint.attimer.0.irq="0"
+hint.wbwd.0.at="isa"

Modified: head/sys/amd64/conf/NOTES
==============================================================================
--- head/sys/amd64/conf/NOTES	Tue Mar  6 18:39:07 2012	(r232613)
+++ head/sys/amd64/conf/NOTES	Tue Mar  6 18:44:52 2012	(r232614)
@@ -465,10 +465,12 @@ device		tpm
 # ichwd: Intel ICH watchdog timer
 # amdsbwd: AMD SB7xx watchdog timer
 # viawd: VIA south bridge watchdog timer
+# wbwd: Winbond watchdog timer
 #
 device		ichwd
 device		amdsbwd
 device		viawd
+device		wbwd
 
 #
 # Temperature sensors:

Modified: head/sys/boot/forth/loader.conf
==============================================================================
--- head/sys/boot/forth/loader.conf	Tue Mar  6 18:39:07 2012	(r232613)
+++ head/sys/boot/forth/loader.conf	Tue Mar  6 18:44:52 2012	(r232614)
@@ -488,6 +488,7 @@ vpd_load="NO"			# Vital Product Data ker
 vpo_load="NO"			# Parallel to SCSI interface driver
 amdtemp_load="NO"		# AMD K8/K10/K11 temperature monitor
 tpm_load="NO"			# Trusted Platform Module
+wbwd_load="NO"			# Winbond watchdog
 
 ##############################################################
 ###  ACPI settings  ##########################################

Modified: head/sys/conf/files.amd64
==============================================================================
--- head/sys/conf/files.amd64	Tue Mar  6 18:39:07 2012	(r232613)
+++ head/sys/conf/files.amd64	Tue Mar  6 18:44:52 2012	(r232614)
@@ -270,6 +270,7 @@ dev/tpm/tpm_acpi.c		optional	tpm acpi
 dev/tpm/tpm_isa.c		optional	tpm isa
 dev/uart/uart_cpu_amd64.c	optional	uart
 dev/viawd/viawd.c		optional	viawd
+dev/wbwd/wbwd.c			optional	wbwd
 dev/wpi/if_wpi.c		optional	wpi
 dev/isci/isci.c							optional isci
 dev/isci/isci_controller.c					optional isci

Modified: head/sys/conf/files.i386
==============================================================================
--- head/sys/conf/files.i386	Tue Mar  6 18:39:07 2012	(r232613)
+++ head/sys/conf/files.i386	Tue Mar  6 18:44:52 2012	(r232614)
@@ -247,6 +247,7 @@ dev/uart/uart_cpu_i386.c	optional uart
 dev/viawd/viawd.c		optional viawd
 dev/acpica/acpi_if.m		standard
 dev/acpi_support/acpi_wmi_if.m	standard
+dev/wbwd/wbwd.c			optional wbwd
 dev/wpi/if_wpi.c		optional wpi
 dev/isci/isci.c							optional isci
 dev/isci/isci_controller.c					optional isci

Added: head/sys/dev/wbwd/wbwd.c
==============================================================================
--- /dev/null	00:00:00 1970	(empty, because file is newly added)
+++ head/sys/dev/wbwd/wbwd.c	Tue Mar  6 18:44:52 2012	(r232614)
@@ -0,0 +1,721 @@
+/*-
+ * Copyright (c) 2011 Sandvine Incorporated ULC.
+ * All rights reserved.
+ *
+ * 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 AUTHOR 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 AUTHOR 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.
+ */
+/*
+ * Support for Winbond watchdog.
+ *
+ * With minor abstractions it might be possible to add support for other
+ * different Winbond Super I/O chips as well.  Winbond seems to have four
+ * different types of chips, four different ways to get into extended config
+ * mode.
+ *
+ * Note: there is no serialization between the debugging sysctl handlers and
+ * the watchdog functions and possibly others poking the registers at the same
+ * time.  For that at least possibly interfering sysctls are hidden by default.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/kernel.h>
+#include <sys/systm.h>
+#include <sys/bus.h>
+#include <sys/eventhandler.h>
+#include <sys/lock.h>
+#include <sys/module.h>
+#include <sys/rman.h>
+#include <sys/sbuf.h>
+#include <sys/sysctl.h>
+#include <sys/watchdog.h>
+
+#include <isa/isavar.h>
+
+#include <machine/bus.h>
+#include <machine/resource.h>
+
+/*
+ * Global registers.
+ */
+#define	WB_DEVICE_ID_REG	0x20	/* Device ID */
+#define	WB_DEVICE_REV_REG	0x21	/* Device revision */
+#define	WB_CR26			0x26	/* Bit6: HEFRAS (base port selector) */
+
+/* LDN selection. */
+#define	WB_LDN_REG		0x07
+#define	WB_LDN_REG_LDN8		0x08	/* GPIO 2, Watchdog */
+
+/*
+ * LDN8 (GPIO 2, Watchdog) specific registers and options.
+ */
+/* CR30: LDN8 activation control. */
+#define	WB_LDN8_CR30		0x30
+#define	WB_LDN8_CR30_ACTIVE	0x01	/* 1: LD active */
+
+/* CRF5: Watchdog scale, P20. Mapped to reg_1. */
+#define	WB_LDN8_CRF5		0xF5
+#define	WB_LDN8_CRF5_SCALE	0x08	/* 0: 1s, 1: 60s */
+#define	WB_LDN8_CRF5_KEYB_P20	0x04	/* 1: keyb P20 forces timeout */
+
+/* CRF6: Watchdog Timeout (0 == off). Mapped to reg_timeout. */
+#define	WB_LDN8_CRF6		0xF6
+
+/* CRF7: Watchdog mouse, keyb, force, .. Mapped to reg_2. */
+#define	WB_LDN8_CRF7		0xF7
+#define	WB_LDN8_CRF7_MOUSE	0x80	/* 1: mouse irq resets wd timer */
+#define	WB_LDN8_CRF7_KEYB	0x40	/* 1: keyb irq resets wd timer */
+#define	WB_LDN8_CRF7_FORCE	0x20	/* 1: force timeout (self-clear) */
+#define	WB_LDN8_CRF7_TS		0x10	/* 0: counting, 1: fired */
+#define	WB_LDN8_CRF7_IRQS	0x0f	/* irq source for watchdog, 2 == SMI */
+#define	WB_LDN8_CRF7_CLEAR_MASK	\
+    (WB_LDN8_CRF7_MOUSE|WB_LDN8_CRF7_KEYB|WB_LDN8_CRF7_TS|WB_LDN8_CRF7_IRQS)
+
+#define	write_efir_1(sc, value)						\
+	bus_space_write_1((sc)->bst, (sc)->bsh, 0, (value))
+#define	read_efir_1(sc)							\
+	bus_space_read_1((sc)->bst, (sc)->bsh, 0)
+#define	write_efdr_1(sc, value)						\
+	bus_space_write_1((sc)->bst, (sc)->bsh, 1, (value))
+#define	read_efdr_1(sc)							\
+	bus_space_read_1((sc)->bst, (sc)->bsh, 1)
+
+struct wb_softc {
+	device_t		dev;
+	struct resource		*portres;
+	bus_space_tag_t		bst;
+	bus_space_handle_t	bsh;
+	int			rid;
+	eventhandler_tag	ev_tag;
+	int			(*ext_cfg_enter_f)(struct wb_softc *);
+	void			(*ext_cfg_exit_f)(struct wb_softc *);
+	int			debug_verbose;
+
+	/*
+	 * Special feature to let the watchdog fire at a different
+	 * timeout as set by watchdog(4) but still use that API to
+	 * re-load it periodically.
+	 */
+	unsigned int		timeout_override;
+
+	/*
+	 * Space to save current state temporary and for sysctls.
+	 * We want to know the timeout value and usually need two
+	 * additional registers for options. Do not name them by
+	 * register as these might be different by chip.
+	 */
+	uint8_t			reg_timeout;
+	uint8_t			reg_1;
+	uint8_t			reg_2;
+};
+
+static int	ext_cfg_enter_0x87_0x87(struct wb_softc *);
+static void	ext_cfg_exit_0xaa(struct wb_softc *);
+
+struct winbond_superio_cfg {
+	uint8_t			efer;	/* and efir */
+	int			(*ext_cfg_enter_f)(struct wb_softc *);
+	void			(*ext_cfg_exit_f)(struct wb_softc *);
+} probe_addrs[] = {
+	{
+		.efer			= 0x2e,
+		.ext_cfg_enter_f	= ext_cfg_enter_0x87_0x87,
+		.ext_cfg_exit_f		= ext_cfg_exit_0xaa,
+	},
+	{
+		.efer			= 0x4e,
+		.ext_cfg_enter_f	= ext_cfg_enter_0x87_0x87,
+		.ext_cfg_exit_f		= ext_cfg_exit_0xaa,
+	},
+};
+
+struct winbond_vendor_device_id {
+	uint16_t		vendor_id;
+	uint8_t			device_id;
+	uint8_t			device_rev;
+	const char *		descr;
+} wb_devs[] = {
+	{
+		.vendor_id	= 0x5ca3,
+		.device_id	= 0x52,
+		.device_rev	= 0x17,
+		.descr		= "Winbond 83627HF/F/HG/G Rev. G",
+	},
+	{
+		.vendor_id	= 0x5ca3,
+		.device_id	= 0x52,
+		.device_rev	= 0x3a,
+		.descr		= "Winbond 83627HF/F/HG/G Rev. J",
+	},
+	{
+		.vendor_id	= 0x5ca3,
+		.device_id	= 0x52,
+		.device_rev	= 0x41,
+		.descr		= "Winbond 83627HF/F/HG/G Rev. UD-A",
+	},
+	{
+		.vendor_id	= 0x5ca3,
+		.device_id	= 0xa0,
+		.device_rev	= 0x25,
+		.descr		= "Winbond 83627DHG IC ver. 5",   
+	},
+};
+
+/*
+ * Return the watchdog related registers as we last read them.  This will
+ * usually not give the current timeout or state on whether the watchdog
+ * fired.
+ */
+static int
+sysctl_wb_debug(SYSCTL_HANDLER_ARGS)
+{
+	struct wb_softc *sc;
+	struct sbuf sb;
+	int error;
+
+	sc = arg1;
+
+	sbuf_new_for_sysctl(&sb, NULL, 64, req);
+
+	sbuf_printf(&sb, "LDN8 (GPIO2, Watchdog): ");
+	sbuf_printf(&sb, "CRF5 0x%02x ", sc->reg_1);
+	sbuf_printf(&sb, "CRF6 0x%02x ", sc->reg_timeout);
+	sbuf_printf(&sb, "CRF7 0x%02x ", sc->reg_2);
+
+	sbuf_trim(&sb);
+	error = sbuf_finish(&sb);
+	sbuf_delete(&sb);
+	return (error);
+}
+
+/*
+ * Read the current values before returning them.  Given this might poke
+ * the registers the same time as the watchdog, this sysctl handler should
+ * be marked CTLFLAG_SKIP to not show up by default.
+ */
+static int
+sysctl_wb_debug_current(SYSCTL_HANDLER_ARGS)
+{
+	struct wb_softc *sc;
+
+	sc = arg1;
+
+	/*
+	 * Enter extended function mode in case someone else has been
+	 * poking on the registers.  We will not leave it though.
+	 */
+	if ((*sc->ext_cfg_enter_f)(sc) != 0)
+		return (ENXIO);
+
+	/* Watchdog is configured as part of LDN 8 (GPIO Port2, Watchdog). */
+	write_efir_1(sc, WB_LDN_REG);
+	write_efdr_1(sc, WB_LDN_REG_LDN8);
+
+	write_efir_1(sc, WB_LDN8_CRF5);
+	sc->reg_1 = read_efdr_1(sc);
+	write_efir_1(sc, WB_LDN8_CRF6);
+	sc->reg_timeout = read_efdr_1(sc);
+	write_efir_1(sc, WB_LDN8_CRF7);
+	sc->reg_2 = read_efdr_1(sc);
+
+	return (sysctl_wb_debug(oidp, arg1, arg2, req));
+}
+
+/*
+ * Sysctl handlers to force a watchdog timeout or to test the NMI functionality
+ * works as expetced.
+ * For testing we could set a test_nmi flag in the softc that, in case of NMI, a
+ * callback function from trap.c could check whether we fired and not report the
+ * timeout but clear the flag for the sysctl again.  This is interesting given a
+ * lot of boards have jumpers to change the action on watchdog timeout or
+ * disable the watchdog completely.
+ * XXX-BZ notyet: currently no general infrastructure exists to do this.
+ */
+static int
+sysctl_wb_force_test_nmi(SYSCTL_HANDLER_ARGS)
+{
+	struct wb_softc *sc;
+	int error, test, val;
+
+	sc = arg1;
+	test = arg2;
+
+#ifdef notyet
+	val = sc->test_nmi;
+#else
+	val = 0;
+#endif
+	error = sysctl_handle_int(oidp, &val, 0, req);
+        if (error || !req->newptr)
+                return (error);
+
+#ifdef notyet
+	/* Manually clear the test for a value of 0 and do nothing else. */
+	if (test && val == 0) {
+		sc->test_nmi = 0;
+		return (0);
+	}
+#endif
+
+	/*
+	 * Enter extended function mode in case someone else has been
+	 * poking on the registers.  We will not leave it though.
+	 */
+	if ((*sc->ext_cfg_enter_f)(sc) != 0)
+		return (ENXIO);
+
+#ifdef notyet
+	/*
+	 * If we are testing the NMI functionality, set the flag before
+	 * forcing the timeout.
+	 */
+	if (test)
+		sc->test_nmi = 1;
+#endif
+
+	/* Watchdog is configured as part of LDN 8 (GPIO Port2, Watchdog). */
+	write_efir_1(sc, WB_LDN_REG);
+	write_efdr_1(sc, WB_LDN_REG_LDN8);
+
+	/* Force watchdog to fire. */
+	write_efir_1(sc, WB_LDN8_CRF7);
+	sc->reg_2 = read_efdr_1(sc);
+	sc->reg_2 |= WB_LDN8_CRF7_FORCE;
+
+	write_efir_1(sc, WB_LDN8_CRF7);
+	write_efdr_1(sc, sc->reg_2);
+
+	return (0);
+}
+
+/*
+ * Print current watchdog state.
+ *
+ * Note: it is the responsibility of the caller to update the registers
+ * upfront.
+ */
+static void
+wb_print_state(struct wb_softc *sc, const char *msg)
+{
+
+	device_printf(sc->dev, "%s%sWatchdog %sabled. %s"
+	    "Scaling by %ds, timer at %d (%s=%ds%s). "
+	    "CRF5 0x%02x CRF7 0x%02x\n",
+	    (msg != NULL) ? msg : "", (msg != NULL) ? ": " : "",
+	    (sc->reg_timeout > 0x00) ? "en" : "dis",
+	    (sc->reg_2 & WB_LDN8_CRF7_TS) ? "Watchdog fired. " : "",
+	    (sc->reg_1 & WB_LDN8_CRF5_SCALE) ? 60 : 1,
+	    sc->reg_timeout,
+	    (sc->reg_timeout > 0x00) ? "<" : "",
+	    sc->reg_timeout * ((sc->reg_1 & WB_LDN8_CRF5_SCALE) ? 60 : 1),
+	    (sc->reg_timeout > 0x00) ? " left" : "",
+	    sc->reg_1, sc->reg_2);
+}
+
+/*
+ * Functions to enter and exit extended function mode.  Possibly shared
+ * between different chips.
+ */
+static int
+ext_cfg_enter_0x87_0x87(struct wb_softc *sc)
+{
+
+	/*
+	 * Enable extended function mode.
+	 * Winbond does not allow us to validate so always return success.
+	 */
+	write_efir_1(sc, 0x87);
+	write_efir_1(sc, 0x87);
+
+	return (0);
+}
+
+static void
+ext_cfg_exit_0xaa(struct wb_softc *sc)
+{
+
+	write_efir_1(sc, 0xaa);
+}
+
+/*
+ * (Re)load the watchdog counter depending on timeout.  A timeout of 0 will
+ * disable the watchdog.
+ */
+static int
+wb_set_watchdog(struct wb_softc *sc, unsigned int timeout)
+{
+
+	if (sc->debug_verbose)
+		wb_print_state(sc, "Before watchdog counter (re)load");
+
+	/*
+	 * Enter extended function mode in case someone else has been
+	 * poking on the registers.  We will not leave it though.
+	 */
+	if ((*sc->ext_cfg_enter_f)(sc) != 0)
+		return (ENXIO);
+
+	/* Watchdog is configured as part of LDN 8 (GPIO Port2, Watchdog) */
+	write_efir_1(sc, WB_LDN_REG);
+	write_efdr_1(sc, WB_LDN_REG_LDN8);
+
+	/* Disable and validate or arm/reset watchdog. */
+	if (timeout == 0) {
+		/* Disable watchdog. */
+		write_efir_1(sc, WB_LDN8_CRF6);
+		write_efdr_1(sc, 0x00);
+
+		/* Re-check. */
+		write_efir_1(sc, WB_LDN8_CRF6);
+		sc->reg_timeout = read_efdr_1(sc);
+		
+		if (sc->reg_timeout != 0x00) {
+			device_printf(sc->dev, "Failed to disable watchdog: "
+			    "0x%02x.\n", sc->reg_timeout);
+			return (EIO);
+		}
+
+	} else {
+		/*
+		 * In case an override is set, let it override.  It may lead
+		 * to strange results as we do not check the input of the sysctl.
+		 */
+		if (sc->timeout_override > 0)
+			timeout = sc->timeout_override;
+
+		/* Make sure we support the requested timeout. */
+		if (timeout > 255 * 60)
+			return (EINVAL);
+
+		/* Read current scaling factor. */
+		write_efir_1(sc, WB_LDN8_CRF5);
+		sc->reg_1 = read_efdr_1(sc);
+
+		if (timeout > 255) {
+			/* Set scaling factor to 60s. */
+			sc->reg_1 |= WB_LDN8_CRF5_SCALE;
+			sc->reg_timeout = (timeout / 60);
+			if (timeout % 60)
+				sc->reg_timeout++;
+		} else {
+			/* Set scaling factor to 1s. */
+			sc->reg_1 &= ~WB_LDN8_CRF5_SCALE;
+			sc->reg_timeout = timeout;
+		}
+
+		/* In case we fired before we need to clear to fire again. */
+		write_efir_1(sc, WB_LDN8_CRF7);
+		sc->reg_2 = read_efdr_1(sc);
+		if (sc->reg_2 & WB_LDN8_CRF7_TS) {
+			sc->reg_2 &= ~WB_LDN8_CRF7_TS;
+			write_efir_1(sc, WB_LDN8_CRF7);
+			write_efdr_1(sc, sc->reg_2);
+		}
+
+		/* Write back scaling factor. */
+		write_efir_1(sc, WB_LDN8_CRF5);
+		write_efdr_1(sc, sc->reg_1);
+
+		/* Set timer and arm/reset the watchdog. */
+		write_efir_1(sc, WB_LDN8_CRF6);
+		write_efdr_1(sc, sc->reg_timeout);
+	}
+
+	if (sc->debug_verbose)
+		wb_print_state(sc, "After watchdog counter (re)load");
+
+	return (0);
+}
+
+/*
+ * watchdog(9) EVENTHANDLER function implementation to (re)load the counter
+ * with the given timeout or disable the watchdog.
+ */
+static void
+wb_watchdog_fn(void *private, u_int cmd, int *error)
+{
+	struct wb_softc *sc;
+	unsigned int timeout;
+	int e;
+
+	sc = private;
+	KASSERT(sc != NULL, ("%s: watchdog handler function called without "
+	    "softc.", __func__));
+
+	cmd &= WD_INTERVAL;
+	if (cmd > 0 && cmd <= 63) {
+		/* Reset (and arm) watchdog. */
+		timeout = ((uint64_t)1 << cmd) / 1000000000;
+		if (timeout == 0)
+			timeout = 1;
+		e = wb_set_watchdog(sc, timeout);
+		if (e == 0) {
+			if (error != NULL)
+				*error = 0;
+		} else {
+			/* On error, try to make sure the WD is disabled. */
+			wb_set_watchdog(sc, 0);
+		}
+
+	} else {
+		/* Disable watchdog. */
+		e = wb_set_watchdog(sc, 0);
+		if (e != 0 && cmd == 0 && error != NULL) {
+			/* Failed to disable watchdog. */
+			*error = EOPNOTSUPP;
+		}
+	}
+}
+
+/*
+ * Probe/attach the Winbond Super I/O chip.
+ *
+ * Initial abstraction to possibly support more chips:
+ * - Iterate over the well known base ports, try to enable extended function
+ *   mode and read and match the device ID and device revision.  Unfortunately
+ *   the Vendor ID is in the hardware monitoring section accessible by different
+ *   base ports only.
+ * - Also HEFRAS, which would tell use the base port, is only accessible after
+ *   entering extended function mode, for which the base port is needed.
+ *   At least check HEFRAS to match the current base port we are probing.
+ * - On match set the description, remember functions to enter/exit extended
+ *   function mode as well as the base port.
+ */
+static int
+wb_probe_enable(device_t dev, int probe)
+{
+	struct wb_softc *sc;
+	int error, found, i, j;
+	uint8_t dev_id, dev_rev, cr26;
+
+	sc = device_get_softc(dev);
+	bzero(sc, sizeof(*sc));
+	sc->dev = dev;
+
+	error = ENXIO;
+	for (i = 0; i < sizeof(probe_addrs) / sizeof(*probe_addrs); i++) {
+
+		/* Allocate bus resources for IO index/data register access. */
+		sc->portres = bus_alloc_resource(dev, SYS_RES_IOPORT, &sc->rid,
+		    probe_addrs[i].efer, probe_addrs[i].efer + 1, 2, RF_ACTIVE);
+		if (sc->portres == NULL)
+			continue;
+		sc->bst = rman_get_bustag(sc->portres);
+		sc->bsh = rman_get_bushandle(sc->portres);
+
+		found = 0;
+		error = (*probe_addrs[i].ext_cfg_enter_f)(sc);
+		if (error != 0)
+			goto cleanup;
+
+		/* Identify the SuperIO chip. */
+		write_efir_1(sc, WB_DEVICE_ID_REG);
+		dev_id = read_efdr_1(sc);
+		write_efir_1(sc, WB_DEVICE_REV_REG);
+		dev_rev = read_efdr_1(sc);
+		write_efir_1(sc, WB_CR26);
+		cr26 = read_efdr_1(sc);
+
+		/* HEFRAS of 0 means EFER at 0x2e, 1 means EFER at 0x4e. */
+		if (((cr26 & 0x40) == 0x00 && probe_addrs[i].efer != 0x2e) ||
+		    ((cr26 & 0x40) == 0x40 && probe_addrs[i].efer != 0x4e)) {
+			device_printf(dev, "HEFRAS and EFER do not align: EFER "
+			    "0x%02x DevID 0x%02x DevRev 0x%02x CR26 0x%02x\n",
+			     probe_addrs[i].efer, dev_id, dev_rev, cr26);
+			goto cleanup;
+		}
+
+		for (j = 0; j < sizeof(wb_devs) / sizeof(*wb_devs); j++) {
+			if (wb_devs[j].device_id == dev_id &&
+			    wb_devs[j].device_rev == dev_rev) {
+				if (probe)
+					device_set_desc(dev, wb_devs[j].descr);
+				found++;
+				break;
+			}
+		}
+		if (probe && found && bootverbose)
+			device_printf(dev, "%s EFER 0x%02x ID 0x%02x Rev 0x%02x"
+			     " CR26 0x%02x (probing)\n", device_get_desc(dev),
+			     probe_addrs[i].efer, dev_id, dev_rev, cr26);
+cleanup:
+		if (probe || !found) {
+			(*probe_addrs[i].ext_cfg_exit_f)(sc);
+
+			(void) bus_release_resource(dev, SYS_RES_IOPORT, sc->rid,
+			    sc->portres);
+		}
+
+		/*
+		 * Stop probing if have successfully identified the SuperIO.
+		 * Remember the extended function mode enter/exit functions
+		 * for operations.
+		 */
+		if (found) {
+			sc->ext_cfg_enter_f = probe_addrs[i].ext_cfg_enter_f;
+			sc->ext_cfg_exit_f = probe_addrs[i].ext_cfg_exit_f;
+			error = BUS_PROBE_DEFAULT;
+			break;
+		} else
+			error = ENXIO;
+	}
+
+	return (error);
+}
+
+static int
+wb_probe(device_t dev)
+{
+
+	/* Make sure we do not claim some ISA PNP device. */
+	if (isa_get_logicalid(dev) != 0)
+		return (ENXIO);
+
+	return (wb_probe_enable(dev, 1));
+}
+
+static int
+wb_attach(device_t dev)
+{
+	struct wb_softc *sc;
+	struct sysctl_ctx_list *sctx;
+	struct sysctl_oid *soid;
+	unsigned long timeout;
+	int error;
+
+	error = wb_probe_enable(dev, 0);
+	if (error > 0)
+		return (ENXIO);
+
+	sc = device_get_softc(dev);
+	KASSERT(sc->ext_cfg_enter_f != NULL && sc->ext_cfg_exit_f != NULL,
+	    ("%s: successfull probe result but not setup correctly", __func__));
+
+	/* Watchdog is configured as part of LDN 8 (GPIO Port2, Watchdog). */
+	write_efir_1(sc, WB_LDN_REG);
+	write_efdr_1(sc, WB_LDN_REG_LDN8);
+
+	/* Make sure LDN8 is enabled (Do we need to? Also affects GPIO). */
+	write_efir_1(sc, WB_LDN8_CR30);
+	write_efdr_1(sc, WB_LDN8_CR30_ACTIVE);
+
+	/* Read the current watchdog configuration. */
+	write_efir_1(sc, WB_LDN8_CRF5);
+	sc->reg_1 = read_efdr_1(sc);
+	write_efir_1(sc, WB_LDN8_CRF6);
+	sc->reg_timeout = read_efdr_1(sc);
+	write_efir_1(sc, WB_LDN8_CRF7);
+	sc->reg_2 = read_efdr_1(sc);
+
+	/* Print current state if bootverbose or watchdog already enabled. */
+	if (bootverbose || (sc->reg_timeout > 0x00))
+		wb_print_state(sc, "Before watchdog attach");
+
+	/*
+	 * Clear a previous watchdog timeout event (if (still) set).
+	 * Disable all all interrupt reset sources (defaults).
+	 */
+	sc->reg_1 &= ~(WB_LDN8_CRF5_KEYB_P20);
+	write_efir_1(sc, WB_LDN8_CRF5);
+	write_efir_1(sc, sc->reg_1);
+
+	sc->reg_2 &= ~WB_LDN8_CRF7_CLEAR_MASK;
+	write_efir_1(sc, WB_LDN8_CRF7);
+	write_efdr_1(sc, sc->reg_2);
+
+	/* Read global timeout override tunable, Add per device sysctls. */
+	if (TUNABLE_ULONG_FETCH("hw.wbwd.timeout_override", &timeout)) {
+		if (timeout > 0)
+			sc->timeout_override = timeout;
+	}
+	sctx = device_get_sysctl_ctx(dev);
+	soid = device_get_sysctl_tree(dev);
+        SYSCTL_ADD_UINT(sctx, SYSCTL_CHILDREN(soid), OID_AUTO,
+	    "timeout_override", CTLFLAG_RW, &sc->timeout_override, 0,
+            "Timeout in seconds overriding default watchdog timeout");
+        SYSCTL_ADD_INT(sctx, SYSCTL_CHILDREN(soid), OID_AUTO,
+	    "debug_verbose", CTLFLAG_RW, &sc->debug_verbose, 0,
+            "Enables extra debugging information");
+        SYSCTL_ADD_PROC(sctx, SYSCTL_CHILDREN(soid), OID_AUTO, "debug",
+	    CTLTYPE_STRING|CTLFLAG_RD, sc, 0, sysctl_wb_debug, "A",
+            "Selected register information from last change by driver");
+        SYSCTL_ADD_PROC(sctx, SYSCTL_CHILDREN(soid), OID_AUTO, "debug_current",
+	    CTLTYPE_STRING|CTLFLAG_RD|CTLFLAG_SKIP, sc, 0,
+	     sysctl_wb_debug_current, "A",
+	     "Selected register information (may interfere)");
+	SYSCTL_ADD_PROC(sctx, SYSCTL_CHILDREN(soid), OID_AUTO, "force_timeout",
+	    CTLTYPE_INT|CTLFLAG_RW|CTLFLAG_SKIP, sc, 0,
+	    sysctl_wb_force_test_nmi, "I", "Enable to force watchdog to fire.");
+
+	/* Register watchdog. */
+	sc->ev_tag = EVENTHANDLER_REGISTER(watchdog_list, wb_watchdog_fn, sc,
+	    0);
+
+	if (bootverbose)
+		wb_print_state(sc, "After watchdog attach");
+
+	return (0);
+}
+
+static int
+wb_detach(device_t dev)
+{
+	struct wb_softc *sc;
+
+	sc = device_get_softc(dev);
+
+	/* Unregister and stop the watchdog if running. */
+	if (sc->ev_tag)
+		EVENTHANDLER_DEREGISTER(watchdog_list, sc->ev_tag);
+	wb_set_watchdog(sc, 0);
+
+	/* Disable extended function mode. */
+	(*sc->ext_cfg_exit_f)(sc);
+
+	/* Cleanup resources. */
+	(void) bus_release_resource(dev, SYS_RES_IOPORT, sc->rid, sc->portres);
+
+	/* Bus subroutines take care of sysctls already. */
+
+	return (0);
+}
+
+static device_method_t wb_methods[] = {
+	/* Device interface */
+	DEVMETHOD(device_probe,		wb_probe),
+	DEVMETHOD(device_attach,	wb_attach),
+	DEVMETHOD(device_detach,	wb_detach),
+
+	{ 0, 0 }
+};
+
+static driver_t wb_isa_driver = {
+	"wbwd",
+	wb_methods,
+	sizeof(struct wb_softc)
+};
+
+static devclass_t wb_devclass;
+
+DRIVER_MODULE(wb, isa, wb_isa_driver, wb_devclass, NULL, NULL);

Modified: head/sys/i386/conf/GENERIC.hints
==============================================================================
--- head/sys/i386/conf/GENERIC.hints	Tue Mar  6 18:39:07 2012	(r232613)
+++ head/sys/i386/conf/GENERIC.hints	Tue Mar  6 18:44:52 2012	(r232614)
@@ -38,3 +38,4 @@ hint.atrtc.0.irq="8"
 hint.attimer.0.at="isa"
 hint.attimer.0.port="0x40"
 hint.attimer.0.irq="0"
+hint.wbwd.0.at="isa"

Modified: head/sys/i386/conf/NOTES
==============================================================================
--- head/sys/i386/conf/NOTES	Tue Mar  6 18:39:07 2012	(r232613)
+++ head/sys/i386/conf/NOTES	Tue Mar  6 18:44:52 2012	(r232614)
@@ -838,10 +838,12 @@ hint.pcf.0.irq="5"
 # ichwd: Intel ICH watchdog timer
 # amdsbwd: AMD SB7xx watchdog timer
 # viawd: VIA south bridge watchdog timer
+# wbwd: Winbond watchdog timer
 #
 device		ichwd
 device		amdsbwd
 device		viawd
+device		wbwd
 
 #
 # Temperature sensors:

Modified: head/sys/modules/Makefile
==============================================================================
--- head/sys/modules/Makefile	Tue Mar  6 18:39:07 2012	(r232613)
+++ head/sys/modules/Makefile	Tue Mar  6 18:44:52 2012	(r232614)
@@ -333,6 +333,7 @@ SUBDIR=	${_3dfx} \
 	vx \
 	${_vxge} \
 	wb \
+	${_wbwd} \
 	${_wi} \
 	wlan \
 	wlan_acl \
@@ -510,6 +511,7 @@ _stg=		stg
 _streams=	streams
 _svr4=		svr4
 _vxge=  	vxge
+_wbwd=		wbwd
 _wi=		wi
 _xe=		xe
 .if ${MK_ZFS} != "no" || defined(ALL_MODULES)
@@ -704,6 +706,7 @@ _viawd=		viawd
 _virtio=	virtio
 _vxge=  	vxge
 _x86bios=	x86bios
+_wbwd=		wbwd
 _wi=		wi
 _wpi=		wpi
 .if ${MK_SOURCELESS_UCODE} != "no"

Added: head/sys/modules/wbwd/Makefile
==============================================================================
--- /dev/null	00:00:00 1970	(empty, because file is newly added)
+++ head/sys/modules/wbwd/Makefile	Tue Mar  6 18:44:52 2012	(r232614)
@@ -0,0 +1,9 @@
+# $FreeBSD$
+
+.PATH: ${.CURDIR}/../../dev/wbwd
+
+KMOD=	wbwd
+SRCS=	wbwd.c
+SRCS+=	bus_if.h device_if.h isa_if.h
+
+.include <bsd.kmod.mk>



Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?201203061844.q26IiqZL052653>