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