Module Name:    src
Committed By:   sborrill
Date:           Fri Oct 16 11:56:11 UTC 2009

Modified Files:
        src/distrib/sets/lists/man [netbsd-5]: mi
        src/share/man/man4/man4.sparc64 [netbsd-5]: Makefile
        src/sys/arch/sparc64/conf [netbsd-5]: GENERIC files.sparc64
        src/sys/dev [netbsd-5]: DEVNAMES
Added Files:
        src/share/man/man4/man4.sparc64 [netbsd-5]: lom.4
        src/sys/arch/sparc64/dev [netbsd-5]: lom.c

Log Message:
Pull up the following revisions(s) (requested by nakayama in ticket #1064):
        distrib/sets/lists/man/mi:      revision 1.1161
        share/man/man4/man4.sparc64/Makefile:   revision 1.5
        share/man/man4/man4.sparc64/lom.4:      revision 1.1-1.2
        sys/arch/sparc64/conf/GENERIC:  revision 1.111
        sys/arch/sparc64/conf/files.sparc64:    revision 1.121
        sys/arch/sparc64/dev/lom.c:     revision 1.1
        sys/dev/DEVNAMES:       revision 1.252

Add a driver for LOMlite lights out management hardware monitor and watchdog
timer.


To generate a diff of this commit:
cvs rdiff -u -r1.1109.2.10 -r1.1109.2.11 src/distrib/sets/lists/man/mi
cvs rdiff -u -r1.4 -r1.4.18.1 src/share/man/man4/man4.sparc64/Makefile
cvs rdiff -u -r0 -r1.2.2.2 src/share/man/man4/man4.sparc64/lom.4
cvs rdiff -u -r1.104.2.1 -r1.104.2.2 src/sys/arch/sparc64/conf/GENERIC
cvs rdiff -u -r1.118 -r1.118.20.1 src/sys/arch/sparc64/conf/files.sparc64
cvs rdiff -u -r0 -r1.1.2.2 src/sys/arch/sparc64/dev/lom.c
cvs rdiff -u -r1.242.4.1 -r1.242.4.2 src/sys/dev/DEVNAMES

Please note that diffs are not public domain; they are subject to the
copyright notices on the relevant files.

Modified files:

Index: src/distrib/sets/lists/man/mi
diff -u src/distrib/sets/lists/man/mi:1.1109.2.10 src/distrib/sets/lists/man/mi:1.1109.2.11
--- src/distrib/sets/lists/man/mi:1.1109.2.10	Thu Oct  8 09:47:08 2009
+++ src/distrib/sets/lists/man/mi	Fri Oct 16 11:56:10 2009
@@ -1,4 +1,4 @@
-# $NetBSD: mi,v 1.1109.2.10 2009/10/08 09:47:08 sborrill Exp $
+# $NetBSD: mi,v 1.1109.2.11 2009/10/16 11:56:10 sborrill Exp $
 #
 # Note: don't delete entries from here - mark them as "obsolete" instead.
 #
@@ -1438,6 +1438,7 @@
 ./usr/share/man/cat4/sparc64/envctrl.0		man-sys-catman		.cat
 ./usr/share/man/cat4/sparc64/fdc.0		man-sys-catman		.cat
 ./usr/share/man/cat4/sparc64/intro.0		man-sys-catman		.cat
+./usr/share/man/cat4/sparc64/lom.0		man-sys-catman		.cat
 ./usr/share/man/cat4/sparc64/sab.0		man-sys-catman		.cat
 ./usr/share/man/cat4/sparc64/sabtty.0		man-sys-catman		.cat
 ./usr/share/man/cat4/spc.0			man-sys-catman		.cat
@@ -3911,6 +3912,7 @@
 ./usr/share/man/html4/sparc64/envctrl.html	man-sys-htmlman		html
 ./usr/share/man/html4/sparc64/fdc.html		man-sys-htmlman		html
 ./usr/share/man/html4/sparc64/intro.html	man-sys-htmlman		html
+./usr/share/man/html4/sparc64/lom.html		man-sys-htmlman		html
 ./usr/share/man/html4/sparc64/sab.html		man-sys-htmlman		html
 ./usr/share/man/html4/sparc64/sabtty.html	man-sys-htmlman		html
 ./usr/share/man/html4/spc.html			man-sys-htmlman		html
@@ -6300,6 +6302,7 @@
 ./usr/share/man/man4/sparc64/envctrl.4		man-sys-man		.man
 ./usr/share/man/man4/sparc64/fdc.4		man-sys-man		.man
 ./usr/share/man/man4/sparc64/intro.4		man-sys-man		.man
+./usr/share/man/man4/sparc64/lom.4		man-sys-man		.man
 ./usr/share/man/man4/sparc64/sab.4		man-sys-man		.man
 ./usr/share/man/man4/sparc64/sabtty.4		man-sys-man		.man
 ./usr/share/man/man4/spc.4			man-sys-man		.man

Index: src/share/man/man4/man4.sparc64/Makefile
diff -u src/share/man/man4/man4.sparc64/Makefile:1.4 src/share/man/man4/man4.sparc64/Makefile:1.4.18.1
--- src/share/man/man4/man4.sparc64/Makefile:1.4	Tue May  8 19:23:18 2007
+++ src/share/man/man4/man4.sparc64/Makefile	Fri Oct 16 11:56:10 2009
@@ -1,8 +1,8 @@
-# $NetBSD: Makefile,v 1.4 2007/05/08 19:23:18 jnemeth Exp $
+# $NetBSD: Makefile,v 1.4.18.1 2009/10/16 11:56:10 sborrill Exp $
 
 MANSUBDIR=/sparc64
 
-MAN=	envctrl.4 fdc.4 intro.4 sab.4
+MAN=	envctrl.4 fdc.4 intro.4 lom.4 sab.4
 
 MLINKS+=	sab.4 sabtty.4
 

Index: src/sys/arch/sparc64/conf/GENERIC
diff -u src/sys/arch/sparc64/conf/GENERIC:1.104.2.1 src/sys/arch/sparc64/conf/GENERIC:1.104.2.2
--- src/sys/arch/sparc64/conf/GENERIC:1.104.2.1	Thu Feb 19 20:23:46 2009
+++ src/sys/arch/sparc64/conf/GENERIC	Fri Oct 16 11:56:10 2009
@@ -1,4 +1,4 @@
-# $NetBSD: GENERIC,v 1.104.2.1 2009/02/19 20:23:46 snj Exp $
+# $NetBSD: GENERIC,v 1.104.2.2 2009/10/16 11:56:10 sborrill Exp $
 #
 # GENERIC machine description file
 #
@@ -22,7 +22,7 @@
 
 options 	INCLUDE_CONFIG_FILE	# embed config file in kernel binary
 
-#ident 		"GENERIC-$Revision: 1.104.2.1 $"
+#ident 		"GENERIC-$Revision: 1.104.2.2 $"
 
 maxusers	64
 
@@ -887,6 +887,7 @@
 
 psm*		at ebus?		# Ultrabook IIi microcontroller
 envctrl*	at ebus?		# Ultra E450 environmental monitoring
+lom*		at ebus?		# LOMlite lights out management
 
 # Netra X1 / T1 style environmental monitoring
 alipm*		at pci?

Index: src/sys/arch/sparc64/conf/files.sparc64
diff -u src/sys/arch/sparc64/conf/files.sparc64:1.118 src/sys/arch/sparc64/conf/files.sparc64:1.118.20.1
--- src/sys/arch/sparc64/conf/files.sparc64:1.118	Wed Feb 20 21:43:35 2008
+++ src/sys/arch/sparc64/conf/files.sparc64	Fri Oct 16 11:56:10 2009
@@ -1,4 +1,4 @@
-#	$NetBSD: files.sparc64,v 1.118 2008/02/20 21:43:35 drochner Exp $
+#	$NetBSD: files.sparc64,v 1.118.20.1 2009/10/16 11:56:10 sborrill Exp $
 
 # @(#)files.sparc64	8.1 (Berkeley) 7/19/93
 # sparc64-specific configuration info
@@ -64,6 +64,10 @@
 attach envctrl at ebus
 file	arch/sparc64/dev/envctrl.c	envctrl
 
+device lom: sysmon_envsys, sysmon_wdog
+attach lom at ebus
+file	arch/sparc64/dev/lom.c			lom
+
 device cpu
 attach cpu at mainbus
 file	arch/sparc64/sparc64/cpu.c

Index: src/sys/dev/DEVNAMES
diff -u src/sys/dev/DEVNAMES:1.242.4.1 src/sys/dev/DEVNAMES:1.242.4.2
--- src/sys/dev/DEVNAMES:1.242.4.1	Fri Jan  9 03:38:18 2009
+++ src/sys/dev/DEVNAMES	Fri Oct 16 11:56:11 2009
@@ -1,4 +1,4 @@
-#	$NetBSD: DEVNAMES,v 1.242.4.1 2009/01/09 03:38:18 snj Exp $
+#	$NetBSD: DEVNAMES,v 1.242.4.2 2009/10/16 11:56:11 sborrill Exp $
 #
 # This file contains all used device names and defined attributes in
 # alphabetical order. New devices added to the system somewhere should first
@@ -729,6 +729,7 @@
 lms			bebox
 lms			i386
 lockstat		MI
+lom			sparc64
 lp			atari
 lp			vax
 lpa			vax

Added files:

Index: src/share/man/man4/man4.sparc64/lom.4
diff -u /dev/null src/share/man/man4/man4.sparc64/lom.4:1.2.2.2
--- /dev/null	Fri Oct 16 11:56:11 2009
+++ src/share/man/man4/man4.sparc64/lom.4	Fri Oct 16 11:56:10 2009
@@ -0,0 +1,61 @@
+.\"     $NetBSD: lom.4,v 1.2.2.2 2009/10/16 11:56:10 sborrill Exp $
+.\"     $OpenBSD: lom.4,v 1.4 2009/09/23 22:08:07 kettenis Exp $
+.\"
+.\" Copyright (c) 2009 Mark Kettenis <kette...@openbsd.org>
+.\"
+.\" 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 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.
+.\"
+.Dd October 2, 2009
+.Dt LOM 4 sparc64
+.Os
+.Sh NAME
+.Nm lom
+.Nd lights out management
+.Sh SYNOPSIS
+.Cd "lom* at ebus?"
+.Sh DESCRIPTION
+The
+.Nm
+driver provides support for the LOMlite lights out management module
+found on Sun Netra t1 systems and the LOMlite2 module found on Sun
+Fire V100/V120 and Sun Netra T1/X1 systems.
+Temperature readings, PSU status and fan status provided by the LOM
+are made available through the
+.Xr envstat 8
+interface.
+The integrated watchdog timer can be configured through the
+.Xr wdogctl 8
+interface.
+The watchdog timer supports timeouts between 1 and 126 seconds.
+.Pp
+The
+.Nm
+driver will update the hostname stored in the LOM whenever the
+system's hostname is changed.
+.Sh SEE ALSO
+.Xr hostname 1 ,
+.Xr envsys 4 ,
+.Xr envstat 8 ,
+.Xr wdogctl 8
+.Sh HISTORY
+The
+.Nm
+driver first appeared in
+.Ox 4.7
+and was then ported to
+.Nx 5.1 .
+.Sh AUTHORS
+The
+.Nm
+driver was written by
+.An Mark Kettenis Aq kette...@openbsd.org .

Index: src/sys/arch/sparc64/dev/lom.c
diff -u /dev/null src/sys/arch/sparc64/dev/lom.c:1.1.2.2
--- /dev/null	Fri Oct 16 11:56:11 2009
+++ src/sys/arch/sparc64/dev/lom.c	Fri Oct 16 11:56:11 2009
@@ -0,0 +1,900 @@
+/*	$NetBSD: lom.c,v 1.1.2.2 2009/10/16 11:56:11 sborrill Exp $	*/
+/*	$OpenBSD: lom.c,v 1.15 2009/09/27 18:08:42 kettenis Exp $	*/
+/*
+ * Copyright (c) 2009 Mark Kettenis
+ *
+ * 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 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.
+ */
+
+#include <sys/cdefs.h>
+__KERNEL_RCSID(0, "$NetBSD: lom.c,v 1.1.2.2 2009/10/16 11:56:11 sborrill Exp $");
+
+#include <sys/param.h>
+#include <sys/device.h>
+#include <sys/kernel.h>
+#include <sys/proc.h>
+#include <sys/envsys.h>
+#include <sys/systm.h>
+#include <sys/callout.h>
+
+#include <machine/autoconf.h>
+
+#include <dev/ebus/ebusreg.h>
+#include <dev/ebus/ebusvar.h>
+#include <dev/sysmon/sysmonvar.h>
+
+/*
+ * LOMlite is a so far unidentified microcontroller.
+ */
+#define LOM1_STATUS		0x00	/* R */
+#define  LOM1_STATUS_BUSY	0x80
+#define LOM1_CMD		0x00	/* W */
+#define LOM1_DATA		0x01	/* R/W */
+
+/*
+ * LOMlite2 is implemented as a H8/3437 microcontroller which has its
+ * on-chip host interface hooked up to EBus.
+ */
+#define LOM2_DATA		0x00	/* R/W */
+#define LOM2_CMD		0x01	/* W */
+#define LOM2_STATUS		0x01	/* R */
+#define  LOM2_STATUS_OBF	0x01	/* Output Buffer Full */
+#define  LOM2_STATUS_IBF	0x02	/* Input Buffer Full  */
+
+#define LOM_IDX_CMD		0x00
+#define  LOM_IDX_CMD_GENERIC	0x00
+#define  LOM_IDX_CMD_TEMP	0x04
+#define  LOM_IDX_CMD_FAN	0x05
+
+#define LOM_IDX_FW_REV		0x01	/* Firmware revision  */
+
+#define LOM_IDX_FAN1		0x04	/* Fan speed */
+#define LOM_IDX_FAN2		0x05
+#define LOM_IDX_FAN3		0x06
+#define LOM_IDX_FAN4		0x07
+#define LOM_IDX_PSU1		0x08	/* PSU status */
+#define LOM_IDX_PSU2		0x09
+#define LOM_IDX_PSU3		0x0a
+#define  LOM_PSU_INPUTA		0x01
+#define  LOM_PSU_INPUTB		0x02
+#define  LOM_PSU_OUTPUT		0x04
+#define  LOM_PSU_PRESENT	0x08
+#define  LOM_PSU_STANDBY	0x10
+
+#define LOM_IDX_TEMP1		0x18	/* Temperature */
+#define LOM_IDX_TEMP2		0x19
+#define LOM_IDX_TEMP3		0x1a
+#define LOM_IDX_TEMP4		0x1b
+#define LOM_IDX_TEMP5		0x1c
+#define LOM_IDX_TEMP6		0x1d
+#define LOM_IDX_TEMP7		0x1e
+#define LOM_IDX_TEMP8		0x1f
+
+#define LOM_IDX_LED1		0x25
+
+#define LOM_IDX_ALARM		0x30
+#define LOM_IDX_WDOG_CTL	0x31
+#define  LOM_WDOG_ENABLE	0x01
+#define  LOM_WDOG_RESET		0x02
+#define  LOM_WDOG_AL3_WDOG	0x04
+#define  LOM_WDOG_AL3_FANPSU	0x08
+#define LOM_IDX_WDOG_TIME	0x32
+#define  LOM_WDOG_TIME_MAX	126
+
+#define LOM1_IDX_HOSTNAME1	0x33
+#define LOM1_IDX_HOSTNAME2	0x34
+#define LOM1_IDX_HOSTNAME3	0x35
+#define LOM1_IDX_HOSTNAME4	0x36
+#define LOM1_IDX_HOSTNAME5	0x37
+#define LOM1_IDX_HOSTNAME6	0x38
+#define LOM1_IDX_HOSTNAME7	0x39
+#define LOM1_IDX_HOSTNAME8	0x3a
+#define LOM1_IDX_HOSTNAME9	0x3b
+#define LOM1_IDX_HOSTNAME10	0x3c
+#define LOM1_IDX_HOSTNAME11	0x3d
+#define LOM1_IDX_HOSTNAME12	0x3e
+
+#define LOM2_IDX_HOSTNAMELEN	0x38
+#define LOM2_IDX_HOSTNAME	0x39
+
+#define LOM_IDX_CONFIG		0x5d
+#define LOM_IDX_FAN1_CAL	0x5e
+#define LOM_IDX_FAN2_CAL	0x5f
+#define LOM_IDX_FAN3_CAL	0x60
+#define LOM_IDX_FAN4_CAL	0x61
+#define LOM_IDX_FAN1_LOW	0x62
+#define LOM_IDX_FAN2_LOW	0x63
+#define LOM_IDX_FAN3_LOW	0x64
+#define LOM_IDX_FAN4_LOW	0x65
+
+#define LOM_IDX_CONFIG2		0x66
+#define LOM_IDX_CONFIG3		0x67
+
+#define LOM_IDX_PROBE55		0x7e	/* Always returns 0x55 */
+#define LOM_IDX_PROBEAA		0x7f	/* Always returns 0xaa */
+
+#define LOM_IDX_WRITE		0x80
+
+#define LOM_IDX4_TEMP_NAME_START	0x40
+#define LOM_IDX4_TEMP_NAME_END		0xff
+
+#define LOM_IDX5_FAN_NAME_START		0x40
+#define LOM_IDX5_FAN_NAME_END		0xff
+
+#define LOM_MAX_FAN	4
+#define LOM_MAX_PSU	3
+#define LOM_MAX_TEMP	8
+
+struct lom_cmd {
+	uint8_t			lc_cmd;
+	uint8_t			lc_data;
+
+	TAILQ_ENTRY(lom_cmd)	lc_next;
+};
+
+struct lom_softc {
+	device_t		sc_dev;
+	bus_space_tag_t		sc_iot;
+	bus_space_handle_t	sc_ioh;
+
+	int			sc_type;
+#define LOM_LOMLITE		0
+#define LOM_LOMLITE2		2
+	int			sc_space;
+
+	struct sysmon_envsys	*sc_sme;
+	envsys_data_t		sc_fan[LOM_MAX_FAN];
+	envsys_data_t		sc_psu[LOM_MAX_PSU];
+	envsys_data_t		sc_temp[LOM_MAX_TEMP];
+
+	int			sc_num_fan;
+	int			sc_num_psu;
+	int			sc_num_temp;
+
+	uint8_t			sc_fan_cal[LOM_MAX_FAN];
+	uint8_t			sc_fan_low[LOM_MAX_FAN];
+
+	char			sc_hostname[MAXHOSTNAMELEN];
+
+	struct sysmon_wdog	sc_smw;
+	int			sc_wdog_period;
+	uint8_t			sc_wdog_ctl;
+	struct lom_cmd		sc_wdog_pat;
+
+	TAILQ_HEAD(, lom_cmd)	sc_queue;
+	kmutex_t		sc_queue_mtx;
+	struct callout		sc_state_to;
+	int			sc_state;
+#define LOM_STATE_IDLE		0
+#define LOM_STATE_CMD		1
+#define LOM_STATE_DATA		2
+	int			sc_retry;
+};
+
+static int	lom_match(device_t, cfdata_t, void *);
+static void	lom_attach(device_t, device_t, void *);
+
+CFATTACH_DECL_NEW(lom, sizeof(struct lom_softc),
+    lom_match, lom_attach, NULL, NULL);
+
+static int	lom_read(struct lom_softc *, uint8_t, uint8_t *);
+static int	lom_write(struct lom_softc *, uint8_t, uint8_t);
+static void	lom_queue_cmd(struct lom_softc *, struct lom_cmd *);
+static int	lom1_read(struct lom_softc *, uint8_t, uint8_t *);
+static int	lom1_write(struct lom_softc *, uint8_t, uint8_t);
+static int	lom1_read_polled(struct lom_softc *, uint8_t, uint8_t *);
+static int	lom1_write_polled(struct lom_softc *, uint8_t, uint8_t);
+static void	lom1_queue_cmd(struct lom_softc *, struct lom_cmd *);
+static void	lom1_dequeue_cmd(struct lom_softc *, struct lom_cmd *);
+static void	lom1_process_queue(void *);
+static void	lom1_process_queue_locked(struct lom_softc *);
+static int	lom2_read(struct lom_softc *, uint8_t, uint8_t *);
+static int	lom2_write(struct lom_softc *, uint8_t, uint8_t);
+static void	lom2_queue_cmd(struct lom_softc *, struct lom_cmd *);
+
+static int	lom_init_desc(struct lom_softc *);
+static void	lom_refresh(struct sysmon_envsys *, envsys_data_t *);
+static void	lom1_write_hostname(struct lom_softc *);
+static void	lom2_write_hostname(struct lom_softc *);
+
+static int	lom_wdog_tickle(struct sysmon_wdog *);
+static int	lom_wdog_setmode(struct sysmon_wdog *);
+
+static int
+lom_match(device_t parent, cfdata_t match, void *aux)
+{
+	struct ebus_attach_args *ea = aux;
+
+	if (strcmp(ea->ea_name, "SUNW,lom") == 0 ||
+	    strcmp(ea->ea_name, "SUNW,lomh") == 0)
+		return (1);
+
+	return (0);
+}
+
+static void
+lom_attach(device_t parent, device_t self, void *aux)
+{
+	struct lom_softc *sc = device_private(self);
+	struct ebus_attach_args *ea = aux;
+	uint8_t reg, fw_rev, config, config2, config3;
+	uint8_t cal, low;
+	int i;
+
+	if (strcmp(ea->ea_name, "SUNW,lomh") == 0)
+		sc->sc_type = LOM_LOMLITE2;
+
+	sc->sc_dev = self;
+	sc->sc_iot = ea->ea_bustag;
+	if (bus_space_map(sc->sc_iot, EBUS_ADDR_FROM_REG(&ea->ea_reg[0]),
+	    ea->ea_reg[0].size, 0, &sc->sc_ioh) != 0) {
+		aprint_error(": can't map register space\n");
+		return;
+	}
+
+	if (sc->sc_type < LOM_LOMLITE2) {
+		/* XXX Magic */
+		(void)bus_space_read_1(sc->sc_iot, sc->sc_ioh, 0);
+		bus_space_write_1(sc->sc_iot, sc->sc_ioh, 3, 0xca);
+
+		TAILQ_INIT(&sc->sc_queue);
+		mutex_init(&sc->sc_queue_mtx, MUTEX_DEFAULT, IPL_VM);
+		callout_init(&sc->sc_state_to, 0);
+		callout_setfunc(&sc->sc_state_to, lom1_process_queue, sc);
+	}
+
+	if (lom_read(sc, LOM_IDX_PROBE55, &reg) || reg != 0x55 ||
+	    lom_read(sc, LOM_IDX_PROBEAA, &reg) || reg != 0xaa ||
+	    lom_read(sc, LOM_IDX_FW_REV, &fw_rev) ||
+	    lom_read(sc, LOM_IDX_CONFIG, &config))
+	{
+		aprint_error(": not responding\n");
+		return;
+	}
+
+	aprint_normal(": %s: %s rev %d.%d\n", ea->ea_name,
+	    sc->sc_type < LOM_LOMLITE2 ? "LOMlite" : "LOMlite2",
+	    fw_rev >> 4, fw_rev & 0x0f);
+
+	config2 = config3 = 0;
+	if (sc->sc_type >= LOM_LOMLITE2) {
+		lom_read(sc, LOM_IDX_CONFIG2, &config2);
+		lom_read(sc, LOM_IDX_CONFIG3, &config3);
+	}
+
+	sc->sc_num_fan = min((config >> 5) & 0x7, LOM_MAX_FAN);
+	sc->sc_num_psu = min((config >> 3) & 0x3, LOM_MAX_PSU);
+	sc->sc_num_temp = min((config2 >> 4) & 0xf, LOM_MAX_TEMP);
+
+	aprint_verbose_dev(self, "%d fan(s), %d PSU(s), %d temp sensor(s)\n",
+	    sc->sc_num_fan, sc->sc_num_psu, sc->sc_num_temp);
+
+	for (i = 0; i < sc->sc_num_fan; i++) {
+		if (lom_read(sc, LOM_IDX_FAN1_CAL + i, &cal) ||
+		    lom_read(sc, LOM_IDX_FAN1_LOW + i, &low)) {
+			aprint_error_dev(self, "can't read fan information\n");
+			return;
+		}
+		sc->sc_fan_cal[i] = cal;
+		sc->sc_fan_low[i] = low;
+	}
+
+	/* Initialize sensor data. */
+	sc->sc_sme = sysmon_envsys_create();
+	for (i = 0; i < sc->sc_num_fan; i++) {
+		sc->sc_fan[i].units = ENVSYS_SFANRPM;
+		snprintf(sc->sc_fan[i].desc, sizeof(sc->sc_fan[i].desc),
+		    "fan%d", i + 1);
+		if (sysmon_envsys_sensor_attach(sc->sc_sme, &sc->sc_fan[i])) {
+			sysmon_envsys_destroy(sc->sc_sme);
+			aprint_error_dev(self, "can't attach fan sensor\n");
+			return;
+		}
+	}
+	for (i = 0; i < sc->sc_num_psu; i++) {
+		sc->sc_psu[i].units = ENVSYS_INDICATOR;
+		snprintf(sc->sc_psu[i].desc, sizeof(sc->sc_psu[i].desc),
+		    "PSU%d", i + 1);
+		if (sysmon_envsys_sensor_attach(sc->sc_sme, &sc->sc_psu[i])) {
+			sysmon_envsys_destroy(sc->sc_sme);
+			aprint_error_dev(self, "can't attach PSU sensor\n");
+			return;
+		}
+	}
+	for (i = 0; i < sc->sc_num_temp; i++) {
+		sc->sc_temp[i].units = ENVSYS_STEMP;
+		snprintf(sc->sc_temp[i].desc, sizeof(sc->sc_temp[i].desc),
+		    "temp%d", i + 1);
+		if (sysmon_envsys_sensor_attach(sc->sc_sme, &sc->sc_temp[i])) {
+			sysmon_envsys_destroy(sc->sc_sme);
+			aprint_error_dev(self, "can't attach temp sensor\n");
+			return;
+		}
+	}
+	if (lom_init_desc(sc)) {
+		aprint_error_dev(self, "can't read sensor names\n");
+		sysmon_envsys_destroy(sc->sc_sme);
+		return;
+	}
+
+	sc->sc_sme->sme_name = device_xname(self);
+	sc->sc_sme->sme_cookie = sc;
+	sc->sc_sme->sme_refresh = lom_refresh;
+	if (sysmon_envsys_register(sc->sc_sme)) {
+		aprint_error_dev(self,
+		    "unable to register envsys with sysmon\n");
+		sysmon_envsys_destroy(sc->sc_sme);
+		return;
+	}
+
+	/* Initialize watchdog. */
+	lom_write(sc, LOM_IDX_WDOG_TIME, LOM_WDOG_TIME_MAX);
+	lom_read(sc, LOM_IDX_WDOG_CTL, &sc->sc_wdog_ctl);
+	sc->sc_wdog_ctl &= ~(LOM_WDOG_ENABLE|LOM_WDOG_RESET);
+	lom_write(sc, LOM_IDX_WDOG_CTL, sc->sc_wdog_ctl);
+
+	sc->sc_wdog_period = LOM_WDOG_TIME_MAX;
+
+	sc->sc_smw.smw_name = device_xname(self);
+	sc->sc_smw.smw_cookie = sc;
+	sc->sc_smw.smw_setmode = lom_wdog_setmode;
+	sc->sc_smw.smw_tickle = lom_wdog_tickle;
+	sc->sc_smw.smw_period = sc->sc_wdog_period;
+	if (sysmon_wdog_register(&sc->sc_smw)) {
+		aprint_error_dev(self,
+		    "unable to register wdog with sysmon\n");
+		return;
+	}
+
+	aprint_verbose_dev(self, "Watchdog timer configured.\n");
+}
+
+static int
+lom_read(struct lom_softc *sc, uint8_t reg, uint8_t *val)
+{
+	if (sc->sc_type < LOM_LOMLITE2)
+		return lom1_read(sc, reg, val);
+	else
+		return lom2_read(sc, reg, val);
+}
+
+static int
+lom_write(struct lom_softc *sc, uint8_t reg, uint8_t val)
+{
+	if (sc->sc_type < LOM_LOMLITE2)
+		return lom1_write(sc, reg, val);
+	else
+		return lom2_write(sc, reg, val);
+}
+
+static void
+lom_queue_cmd(struct lom_softc *sc, struct lom_cmd *lc)
+{
+	if (sc->sc_type < LOM_LOMLITE2)
+		return lom1_queue_cmd(sc, lc);
+	else
+		return lom2_queue_cmd(sc, lc);
+}
+
+static int
+lom1_read(struct lom_softc *sc, uint8_t reg, uint8_t *val)
+{
+	struct lom_cmd lc;
+	int error;
+
+	if (cold)
+		return lom1_read_polled(sc, reg, val);
+
+	lc.lc_cmd = reg;
+	lc.lc_data = 0xff;
+	lom1_queue_cmd(sc, &lc);
+
+	error = tsleep(&lc, PZERO, "lomrd", hz);
+	if (error)
+		lom1_dequeue_cmd(sc, &lc);
+
+	*val = lc.lc_data;
+
+	return (error);
+}
+
+static int
+lom1_write_polled(struct lom_softc *sc, uint8_t reg, uint8_t val)
+{
+	struct lom_cmd lc;
+	int error;
+
+	if (cold)
+		return lom1_write_polled(sc, reg, val);
+
+	lc.lc_cmd = reg | LOM_IDX_WRITE;
+	lc.lc_data = val;
+	lom1_queue_cmd(sc, &lc);
+
+	error = tsleep(&lc, PZERO, "lomwr", hz);
+	if (error)
+		lom1_dequeue_cmd(sc, &lc);
+
+	return (error);
+}
+
+static int
+lom1_read_polled(struct lom_softc *sc, uint8_t reg, uint8_t *val)
+{
+	uint8_t str;
+	int i;
+
+	/* Wait for input buffer to become available. */
+	for (i = 30; i > 0; i--) {
+		str = bus_space_read_1(sc->sc_iot, sc->sc_ioh, LOM1_STATUS);
+		delay(1000);
+		if ((str & LOM1_STATUS_BUSY) == 0)
+			break;
+	}
+	if (i == 0)
+		return (ETIMEDOUT);
+
+	bus_space_write_1(sc->sc_iot, sc->sc_ioh, LOM1_CMD, reg);
+
+	/* Wait until the microcontroller fills output buffer. */
+	for (i = 30; i > 0; i--) {
+		str = bus_space_read_1(sc->sc_iot, sc->sc_ioh, LOM1_STATUS);
+		delay(1000);
+		if ((str & LOM1_STATUS_BUSY) == 0)
+			break;
+	}
+	if (i == 0)
+		return (ETIMEDOUT);
+
+	*val = bus_space_read_1(sc->sc_iot, sc->sc_ioh, LOM1_DATA);
+	return (0);
+}
+
+static int
+lom1_write(struct lom_softc *sc, uint8_t reg, uint8_t val)
+{
+	uint8_t str;
+	int i;
+
+	/* Wait for input buffer to become available. */
+	for (i = 30; i > 0; i--) {
+		str = bus_space_read_1(sc->sc_iot, sc->sc_ioh, LOM1_STATUS);
+		delay(1000);
+		if ((str & LOM1_STATUS_BUSY) == 0)
+			break;
+	}
+	if (i == 0)
+		return (ETIMEDOUT);
+
+	reg |= LOM_IDX_WRITE;
+	bus_space_write_1(sc->sc_iot, sc->sc_ioh, LOM1_CMD, reg);
+
+	/* Wait until the microcontroller fills output buffer. */
+	for (i = 30; i > 0; i--) {
+		str = bus_space_read_1(sc->sc_iot, sc->sc_ioh, LOM1_STATUS);
+		delay(1000);
+		if ((str & LOM1_STATUS_BUSY) == 0)
+			break;
+	}
+	if (i == 0)
+		return (ETIMEDOUT);
+
+	bus_space_write_1(sc->sc_iot, sc->sc_ioh, LOM1_DATA, val);
+
+	return (0);
+}
+
+static void
+lom1_queue_cmd(struct lom_softc *sc, struct lom_cmd *lc)
+{
+	mutex_enter(&sc->sc_queue_mtx);
+	TAILQ_INSERT_TAIL(&sc->sc_queue, lc, lc_next);
+	if (sc->sc_state == LOM_STATE_IDLE) {
+		sc->sc_state = LOM_STATE_CMD;
+		lom1_process_queue_locked(sc);
+	}
+	mutex_exit(&sc->sc_queue_mtx);
+}
+
+static void
+lom1_dequeue_cmd(struct lom_softc *sc, struct lom_cmd *lc)
+{
+	struct lom_cmd *lcp;
+
+	mutex_enter(&sc->sc_queue_mtx);
+	TAILQ_FOREACH(lcp, &sc->sc_queue, lc_next) {
+		if (lcp == lc) {
+			TAILQ_REMOVE(&sc->sc_queue, lc, lc_next);
+			break;
+		}
+	}
+	mutex_exit(&sc->sc_queue_mtx);
+}
+
+static void
+lom1_process_queue(void *arg)
+{
+	struct lom_softc *sc = arg;
+
+	mutex_enter(&sc->sc_queue_mtx);
+	lom1_process_queue_locked(sc);
+	mutex_exit(&sc->sc_queue_mtx);
+}
+
+static void
+lom1_process_queue_locked(struct lom_softc *sc)
+{
+	struct lom_cmd *lc;
+	uint8_t str;
+
+	lc = TAILQ_FIRST(&sc->sc_queue);
+	KASSERT(lc != NULL);
+
+	str = bus_space_read_1(sc->sc_iot, sc->sc_ioh, LOM1_STATUS);
+	if (str & LOM1_STATUS_BUSY) {
+		if (sc->sc_retry++ > 30)
+			return;
+		callout_schedule(&sc->sc_state_to, mstohz(1));
+		return;
+	}
+
+	sc->sc_retry = 0;
+
+	if (sc->sc_state == LOM_STATE_CMD) {
+		bus_space_write_1(sc->sc_iot, sc->sc_ioh, LOM1_CMD, lc->lc_cmd);
+		sc->sc_state = LOM_STATE_DATA;
+		callout_schedule(&sc->sc_state_to, mstohz(250));
+		return;
+	}
+
+	KASSERT(sc->sc_state == LOM_STATE_DATA);
+	if ((lc->lc_cmd & LOM_IDX_WRITE) == 0)
+		lc->lc_data = bus_space_read_1(sc->sc_iot, sc->sc_ioh, LOM1_DATA);
+	else
+		bus_space_write_1(sc->sc_iot, sc->sc_ioh, LOM1_DATA, lc->lc_data);
+
+	TAILQ_REMOVE(&sc->sc_queue, lc, lc_next);
+
+	wakeup(lc);
+
+	if (!TAILQ_EMPTY(&sc->sc_queue)) {
+		sc->sc_state = LOM_STATE_CMD;
+		callout_schedule(&sc->sc_state_to, mstohz(1));
+		return;
+	}
+
+	sc->sc_state = LOM_STATE_IDLE;
+}
+
+static int
+lom2_read(struct lom_softc *sc, uint8_t reg, uint8_t *val)
+{
+	uint8_t str;
+	int i;
+
+	/* Wait for input buffer to become available. */
+	for (i = 1000; i > 0; i--) {
+		str = bus_space_read_1(sc->sc_iot, sc->sc_ioh, LOM2_STATUS);
+		delay(10);
+		if ((str & LOM2_STATUS_IBF) == 0)
+			break;
+	}
+	if (i == 0)
+		return (ETIMEDOUT);
+
+	bus_space_write_1(sc->sc_iot, sc->sc_ioh, LOM2_CMD, reg);
+
+	/* Wait until the microcontroller fills output buffer. */
+	for (i = 1000; i > 0; i--) {
+		str = bus_space_read_1(sc->sc_iot, sc->sc_ioh, LOM2_STATUS);
+		delay(10);
+		if (str & LOM2_STATUS_OBF)
+			break;
+	}
+	if (i == 0)
+		return (ETIMEDOUT);
+
+	*val = bus_space_read_1(sc->sc_iot, sc->sc_ioh, LOM2_DATA);
+	return (0);
+}
+
+static int
+lom2_write(struct lom_softc *sc, uint8_t reg, uint8_t val)
+{
+	uint8_t str;
+	int i;
+
+	/* Wait for input buffer to become available. */
+	for (i = 1000; i > 0; i--) {
+		str = bus_space_read_1(sc->sc_iot, sc->sc_ioh, LOM2_STATUS);
+		delay(10);
+		if ((str & LOM2_STATUS_IBF) == 0)
+			break;
+	}
+	if (i == 0)
+		return (ETIMEDOUT);
+
+	if (sc->sc_space == LOM_IDX_CMD_GENERIC && reg != LOM_IDX_CMD)
+		reg |= 0x80;
+
+	bus_space_write_1(sc->sc_iot, sc->sc_ioh, LOM2_CMD, reg);
+
+	/* Wait until the microcontroller fills output buffer. */
+	for (i = 1000; i > 0; i--) {
+		str = bus_space_read_1(sc->sc_iot, sc->sc_ioh, LOM2_STATUS);
+		delay(10);
+		if (str & LOM2_STATUS_OBF)
+			break;
+	}
+	if (i == 0)
+		return (ETIMEDOUT);
+
+	(void)bus_space_read_1(sc->sc_iot, sc->sc_ioh, LOM2_DATA);
+
+	/* Wait for input buffer to become available. */
+	for (i = 1000; i > 0; i--) {
+		str = bus_space_read_1(sc->sc_iot, sc->sc_ioh, LOM2_STATUS);
+		delay(10);
+		if ((str & LOM2_STATUS_IBF) == 0)
+			break;
+	}
+	if (i == 0)
+		return (ETIMEDOUT);
+
+	bus_space_write_1(sc->sc_iot, sc->sc_ioh, LOM2_DATA, val);
+
+	/* Wait until the microcontroller fills output buffer. */
+	for (i = 1000; i > 0; i--) {
+		str = bus_space_read_1(sc->sc_iot, sc->sc_ioh, LOM2_STATUS);
+		delay(10);
+		if (str & LOM2_STATUS_OBF)
+			break;
+	}
+	if (i == 0)
+		return (ETIMEDOUT);
+
+	(void)bus_space_read_1(sc->sc_iot, sc->sc_ioh, LOM2_DATA);
+
+	/* If we switched spaces, remember the one we're in now. */
+	if (reg == LOM_IDX_CMD)
+		sc->sc_space = val;
+
+	return (0);
+}
+
+static void
+lom2_queue_cmd(struct lom_softc *sc, struct lom_cmd *lc)
+{
+	KASSERT(lc->lc_cmd & LOM_IDX_WRITE);
+	lom2_write(sc, lc->lc_cmd, lc->lc_data);
+}
+
+static int
+lom_init_desc(struct lom_softc *sc)
+{
+	uint8_t val;
+	int i, j, k;
+	int error;
+
+	/* LOMlite doesn't provide sensor descriptions. */
+	if (sc->sc_type < LOM_LOMLITE2)
+		return (0);
+
+	/*
+	 * Read temperature sensor names.
+	 */
+	error = lom_write(sc, LOM_IDX_CMD, LOM_IDX_CMD_TEMP);
+	if (error)
+		return (error);
+
+	i = 0;
+	j = 0;
+	k = LOM_IDX4_TEMP_NAME_START;
+	while (k <= LOM_IDX4_TEMP_NAME_END) {
+		error = lom_read(sc, k++, &val);
+		if (error)
+			goto fail;
+
+		if (val == 0xff)
+			break;
+
+		if (j < sizeof (sc->sc_temp[i].desc) - 1)
+			sc->sc_temp[i].desc[j++] = val;
+
+		if (val == '\0') {
+			i++;
+			j = 0;
+			if (i < sc->sc_num_temp)
+				continue;
+
+			break;
+		}
+	}
+
+	/*
+	 * Read fan names.
+	 */
+	error = lom_write(sc, LOM_IDX_CMD, LOM_IDX_CMD_FAN);
+	if (error)
+		return (error);
+
+	i = 0;
+	j = 0;
+	k = LOM_IDX5_FAN_NAME_START;
+	while (k <= LOM_IDX5_FAN_NAME_END) {
+		error = lom_read(sc, k++, &val);
+		if (error)
+			goto fail;
+
+		if (val == 0xff)
+			break;
+
+		if (j < sizeof (sc->sc_fan[i].desc) - 1)
+			sc->sc_fan[i].desc[j++] = val;
+
+		if (val == '\0') {
+			i++;
+			j = 0;
+			if (i < sc->sc_num_fan)
+				continue;
+
+			break;
+		}
+	}
+
+fail:
+	lom_write(sc, LOM_IDX_CMD, LOM_IDX_CMD_GENERIC);
+	return (error);
+}
+
+static void
+lom_refresh(struct sysmon_envsys *sme, envsys_data_t *edata)
+{
+	struct lom_softc *sc = sme->sme_cookie;
+	uint8_t val;
+	int i;
+
+	for (i = 0; i < sc->sc_num_fan; i++) {
+		if (lom_read(sc, LOM_IDX_FAN1 + i, &val)) {
+			sc->sc_fan[i].state = ENVSYS_SINVALID;
+			continue;
+		}
+
+		sc->sc_fan[i].value_cur = (60 * sc->sc_fan_cal[i] * val) / 100;
+		if (val < sc->sc_fan_low[i])
+			sc->sc_fan[i].state = ENVSYS_SCRITICAL;
+		else
+			sc->sc_fan[i].state = ENVSYS_SVALID;
+	}
+
+	for (i = 0; i < sc->sc_num_psu; i++) {
+		if (lom_read(sc, LOM_IDX_PSU1 + i, &val) ||
+		    !ISSET(val, LOM_PSU_PRESENT)) {
+			sc->sc_psu[i].state = ENVSYS_SINVALID;
+			continue;
+		}
+
+		if (val & LOM_PSU_STANDBY) {
+			sc->sc_psu[i].value_cur = 0;
+			sc->sc_psu[i].state = ENVSYS_SVALID;
+		} else {
+			sc->sc_psu[i].value_cur = 1;
+			if (ISSET(val, LOM_PSU_INPUTA) &&
+			    ISSET(val, LOM_PSU_INPUTB) &&
+			    ISSET(val, LOM_PSU_OUTPUT))
+				sc->sc_psu[i].state = ENVSYS_SVALID;
+			else
+				sc->sc_psu[i].state = ENVSYS_SCRITICAL;
+		}
+	}
+
+	for (i = 0; i < sc->sc_num_temp; i++) {
+		if (lom_read(sc, LOM_IDX_TEMP1 + i, &val)) {
+			sc->sc_temp[i].state = ENVSYS_SINVALID;
+			continue;
+		}
+
+		sc->sc_temp[i].value_cur = val * 1000000 + 273150000;
+		sc->sc_temp[i].state = ENVSYS_SVALID;
+	}
+
+	/*
+	 * If our hostname is set and differs from what's stored in
+	 * the LOM, write the new hostname back to the LOM.  Note that
+	 * we include the terminating NUL when writing the hostname
+	 * back to the LOM, otherwise the LOM will print any trailing
+	 * garbage.
+	 */
+	if (hostnamelen > 0 &&
+	    strncmp(sc->sc_hostname, hostname, sizeof(hostname)) != 0) {
+		if (sc->sc_type < LOM_LOMLITE2)
+			lom1_write_hostname(sc);
+		else
+			lom2_write_hostname(sc);
+		strlcpy(sc->sc_hostname, hostname, sizeof(hostname));
+	}
+}
+
+static void
+lom1_write_hostname(struct lom_softc *sc)
+{
+	char name[LOM1_IDX_HOSTNAME12 - LOM1_IDX_HOSTNAME1 + 1];
+	char *p;
+	int i;
+
+	/*
+	 * LOMlite generally doesn't have enough space to store the
+	 * fully qualified hostname.  If the hostname is too long,
+	 * strip off the domain name.
+	 */
+	strlcpy(name, hostname, sizeof(name));
+	if (hostnamelen > sizeof(name)) {
+		p = strchr(name, '.');
+		if (p)
+			*p = '\0';
+	}
+
+	for (i = 0; i < strlen(name) + 1; i++)
+		if (lom_write(sc, LOM1_IDX_HOSTNAME1 + i, name[i]))
+			break;
+}
+
+static void
+lom2_write_hostname(struct lom_softc *sc)
+{
+	int i;
+
+	lom_write(sc, LOM2_IDX_HOSTNAMELEN, hostnamelen + 1);
+	for (i = 0; i < hostnamelen + 1; i++)
+		lom_write(sc, LOM2_IDX_HOSTNAME, hostname[i]);
+}
+
+static int
+lom_wdog_tickle(struct sysmon_wdog *smw)
+{
+	struct lom_softc *sc = smw->smw_cookie;
+
+	/* Pat the dog. */
+	sc->sc_wdog_pat.lc_cmd = LOM_IDX_WDOG_CTL | LOM_IDX_WRITE;
+	sc->sc_wdog_pat.lc_data = sc->sc_wdog_ctl;
+	lom_queue_cmd(sc, &sc->sc_wdog_pat);
+
+	return 0;
+}
+
+static int
+lom_wdog_setmode(struct sysmon_wdog *smw)
+{
+	struct lom_softc *sc = smw->smw_cookie;
+
+	if ((smw->smw_mode & WDOG_MODE_MASK) == WDOG_MODE_DISARMED) {
+		/* disable watchdog */
+		sc->sc_wdog_ctl &= ~(LOM_WDOG_ENABLE|LOM_WDOG_RESET);
+		lom_write(sc, LOM_IDX_WDOG_CTL, sc->sc_wdog_ctl);
+	} else {
+		if (smw->smw_period == WDOG_PERIOD_DEFAULT)
+			smw->smw_period = sc->sc_wdog_period;
+		else if (smw->smw_period == 0 ||
+		    smw->smw_period > LOM_WDOG_TIME_MAX)
+			return EINVAL;
+		lom_write(sc, LOM_IDX_WDOG_TIME, smw->smw_period);
+
+		/* enable watchdog */
+		sc->sc_wdog_ctl |= LOM_WDOG_ENABLE|LOM_WDOG_RESET;
+		sc->sc_wdog_pat.lc_cmd = LOM_IDX_WDOG_CTL | LOM_IDX_WRITE;
+		sc->sc_wdog_pat.lc_data = sc->sc_wdog_ctl;
+		lom_queue_cmd(sc, &sc->sc_wdog_pat);
+	}
+
+	return 0;
+}

Reply via email to