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 != 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; +}