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 <[email protected]>
+.\"
+.\" 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 [email protected] .
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 != 0x55 ||
+ lom_read(sc, LOM_IDX_PROBEAA, ®) || 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;
+}