Module Name:    src
Committed By:   nakayama
Date:           Fri Oct  2 15:09:16 UTC 2009

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

Log Message:
Port lom(4) driver for LOMlite lights out management hardware monitor
and watchdog timer from OpenBSD.

It supports the LOMlite found on Sun Netra t1 and the LOMlite2 found
on Sun Netra T1/X1 and Sun Fire V100/V120.


To generate a diff of this commit:
cvs rdiff -u -r1.1160 -r1.1161 src/distrib/sets/lists/man/mi
cvs rdiff -u -r1.4 -r1.5 src/share/man/man4/man4.sparc64/Makefile
cvs rdiff -u -r0 -r1.1 src/share/man/man4/man4.sparc64/lom.4
cvs rdiff -u -r1.110 -r1.111 src/sys/arch/sparc64/conf/GENERIC
cvs rdiff -u -r1.120 -r1.121 src/sys/arch/sparc64/conf/files.sparc64
cvs rdiff -u -r0 -r1.1 src/sys/arch/sparc64/dev/lom.c

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.1160 src/distrib/sets/lists/man/mi:1.1161
--- src/distrib/sets/lists/man/mi:1.1160	Wed Sep 30 22:32:04 2009
+++ src/distrib/sets/lists/man/mi	Fri Oct  2 15:09:16 2009
@@ -1,4 +1,4 @@
-# $NetBSD: mi,v 1.1160 2009/09/30 22:32:04 jmcneill Exp $
+# $NetBSD: mi,v 1.1161 2009/10/02 15:09:16 nakayama Exp $
 #
 # Note: don't delete entries from here - mark them as "obsolete" instead.
 #
@@ -1461,6 +1461,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
@@ -4012,6 +4013,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
@@ -6479,6 +6481,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.5
--- 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  2 15:09:16 2009
@@ -1,8 +1,8 @@
-# $NetBSD: Makefile,v 1.4 2007/05/08 19:23:18 jnemeth Exp $
+# $NetBSD: Makefile,v 1.5 2009/10/02 15:09:16 nakayama 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.110 src/sys/arch/sparc64/conf/GENERIC:1.111
--- src/sys/arch/sparc64/conf/GENERIC:1.110	Fri Mar  6 20:31:52 2009
+++ src/sys/arch/sparc64/conf/GENERIC	Fri Oct  2 15:09:16 2009
@@ -1,4 +1,4 @@
-# $NetBSD: GENERIC,v 1.110 2009/03/06 20:31:52 joerg Exp $
+# $NetBSD: GENERIC,v 1.111 2009/10/02 15:09:16 nakayama Exp $
 #
 # GENERIC machine description file
 #
@@ -22,7 +22,7 @@
 
 options 	INCLUDE_CONFIG_FILE	# embed config file in kernel binary
 
-#ident 		"GENERIC-$Revision: 1.110 $"
+#ident 		"GENERIC-$Revision: 1.111 $"
 
 maxusers	64
 
@@ -884,6 +884,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.120 src/sys/arch/sparc64/conf/files.sparc64:1.121
--- src/sys/arch/sparc64/conf/files.sparc64:1.120	Wed Dec 10 05:56:22 2008
+++ src/sys/arch/sparc64/conf/files.sparc64	Fri Oct  2 15:09:16 2009
@@ -1,4 +1,4 @@
-#	$NetBSD: files.sparc64,v 1.120 2008/12/10 05:56:22 mrg Exp $
+#	$NetBSD: files.sparc64,v 1.121 2009/10/02 15:09:16 nakayama Exp $
 
 # @(#)files.sparc64	8.1 (Berkeley) 7/19/93
 # sparc64-specific configuration info
@@ -67,6 +67,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

Added files:

Index: src/share/man/man4/man4.sparc64/lom.4
diff -u /dev/null src/share/man/man4/man4.sparc64/lom.4:1.1
--- /dev/null	Fri Oct  2 15:09:16 2009
+++ src/share/man/man4/man4.sparc64/lom.4	Fri Oct  2 15:09:16 2009
@@ -0,0 +1,61 @@
+.\"     $NetBSD: lom.4,v 1.1 2009/10/02 15:09:16 nakayama 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 $Mdocdate: 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
--- /dev/null	Fri Oct  2 15:09:16 2009
+++ src/sys/arch/sparc64/dev/lom.c	Fri Oct  2 15:09:16 2009
@@ -0,0 +1,900 @@
+/*	$NetBSD: lom.c,v 1.1 2009/10/02 15:09:16 nakayama 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 2009/10/02 15:09:16 nakayama 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