mirror of
https://github.com/Stichting-MINIX-Research-Foundation/netbsd.git
synced 2025-08-07 13:09:33 -04:00
1271 lines
31 KiB
C
1271 lines
31 KiB
C
/* $NetBSD: lom.c,v 1.15 2015/04/27 11:55:29 martin Exp $ */
|
|
/* $OpenBSD: lom.c,v 1.21 2010/02/28 20:44:39 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.15 2015/04/27 11:55:29 martin 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 <sys/sysctl.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_ALARM_1 0x01
|
|
#define LOM_ALARM_2 0x02
|
|
#define LOM_ALARM_3 0x04
|
|
#define LOM_ALARM_FAULT 0xf0
|
|
#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_ALARM 4
|
|
#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_alarm[LOM_MAX_ALARM];
|
|
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_alarm;
|
|
int sc_num_fan;
|
|
int sc_num_psu;
|
|
int sc_num_temp;
|
|
|
|
int32_t sc_sysctl_num[LOM_MAX_ALARM];
|
|
|
|
struct timeval sc_alarm_lastread;
|
|
uint8_t sc_alarm_lastval;
|
|
struct timeval sc_fan_lastread[LOM_MAX_FAN];
|
|
struct timeval sc_psu_lastread[LOM_MAX_PSU];
|
|
struct timeval sc_temp_lastread[LOM_MAX_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 void lom_dequeue_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_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 int lom2_read_polled(struct lom_softc *, uint8_t, uint8_t *);
|
|
static int lom2_write_polled(struct lom_softc *, uint8_t, uint8_t);
|
|
static void lom2_queue_cmd(struct lom_softc *, struct lom_cmd *);
|
|
static int lom2_intr(void *);
|
|
|
|
static int lom_init_desc(struct lom_softc *);
|
|
static void lom_refresh(struct sysmon_envsys *, envsys_data_t *);
|
|
static void lom_refresh_alarm(struct lom_softc *, envsys_data_t *, uint32_t);
|
|
static void lom_refresh_fan(struct lom_softc *, envsys_data_t *, uint32_t);
|
|
static void lom_refresh_psu(struct lom_softc *, envsys_data_t *, uint32_t);
|
|
static void lom_refresh_temp(struct lom_softc *, envsys_data_t *, uint32_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 bool lom_shutdown(device_t, int);
|
|
|
|
SYSCTL_SETUP_PROTO(sysctl_lom_setup);
|
|
static int lom_sysctl_alarm(SYSCTLFN_PROTO);
|
|
|
|
static const char *nodename[LOM_MAX_ALARM] =
|
|
{ "fault_led", "alarm1", "alarm2", "alarm3" };
|
|
#ifdef SYSCTL_INCLUDE_DESCR
|
|
static const char *nodedesc[LOM_MAX_ALARM] =
|
|
{ "Fault LED status", "Alarm1 status", "Alarm2 status ", "Alarm3 status" };
|
|
#endif
|
|
static const struct timeval refresh_interval = { 1, 0 };
|
|
|
|
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, err;
|
|
const struct sysctlnode *node = NULL, *newnode;
|
|
|
|
if (strcmp(ea->ea_name, "SUNW,lomh") == 0) {
|
|
if (ea->ea_nintr < 1) {
|
|
aprint_error(": no interrupt\n");
|
|
return;
|
|
}
|
|
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);
|
|
}
|
|
|
|
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);
|
|
|
|
TAILQ_INIT(&sc->sc_queue);
|
|
mutex_init(&sc->sc_queue_mtx, MUTEX_DEFAULT, IPL_BIO);
|
|
|
|
config2 = config3 = 0;
|
|
if (sc->sc_type < LOM_LOMLITE2) {
|
|
/*
|
|
* LOMlite doesn't do interrupts so we limp along on
|
|
* timeouts.
|
|
*/
|
|
callout_init(&sc->sc_state_to, 0);
|
|
callout_setfunc(&sc->sc_state_to, lom1_process_queue, sc);
|
|
} else {
|
|
lom_read(sc, LOM_IDX_CONFIG2, &config2);
|
|
lom_read(sc, LOM_IDX_CONFIG3, &config3);
|
|
|
|
bus_intr_establish(sc->sc_iot, ea->ea_intr[0],
|
|
IPL_BIO, lom2_intr, sc);
|
|
}
|
|
|
|
sc->sc_num_alarm = LOM_MAX_ALARM;
|
|
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;
|
|
}
|
|
|
|
/* Setup our sysctl subtree, hw.lomN */
|
|
sysctl_createv(NULL, 0, NULL, &node,
|
|
0, CTLTYPE_NODE, device_xname(self), NULL,
|
|
NULL, 0, NULL, 0, CTL_HW, CTL_CREATE, CTL_EOL);
|
|
|
|
/* Initialize sensor data. */
|
|
sc->sc_sme = sysmon_envsys_create();
|
|
for (i = 0; i < sc->sc_num_alarm; i++) {
|
|
sc->sc_alarm[i].units = ENVSYS_INDICATOR;
|
|
sc->sc_alarm[i].state = ENVSYS_SINVALID;
|
|
if (i == 0)
|
|
strlcpy(sc->sc_alarm[i].desc, "Fault LED",
|
|
sizeof(sc->sc_alarm[i].desc));
|
|
else
|
|
snprintf(sc->sc_alarm[i].desc,
|
|
sizeof(sc->sc_alarm[i].desc), "Alarm%d", i);
|
|
if (sysmon_envsys_sensor_attach(sc->sc_sme, &sc->sc_alarm[i])) {
|
|
sysmon_envsys_destroy(sc->sc_sme);
|
|
aprint_error_dev(self, "can't attach alarm sensor\n");
|
|
return;
|
|
}
|
|
if (node != NULL) {
|
|
sysctl_createv(NULL, 0, NULL, &newnode,
|
|
CTLFLAG_READWRITE, CTLTYPE_INT, nodename[i],
|
|
SYSCTL_DESCR(nodedesc[i]),
|
|
lom_sysctl_alarm, 0, (void *)sc, 0,
|
|
CTL_HW, node->sysctl_num, CTL_CREATE, CTL_EOL);
|
|
if (newnode != NULL)
|
|
sc->sc_sysctl_num[i] = newnode->sysctl_num;
|
|
else
|
|
sc->sc_sysctl_num[i] = 0;
|
|
}
|
|
}
|
|
for (i = 0; i < sc->sc_num_fan; i++) {
|
|
sc->sc_fan[i].units = ENVSYS_SFANRPM;
|
|
sc->sc_fan[i].state = ENVSYS_SINVALID;
|
|
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;
|
|
sc->sc_psu[i].state = ENVSYS_SINVALID;
|
|
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;
|
|
sc->sc_temp[i].state = ENVSYS_SINVALID;
|
|
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;
|
|
err = sysmon_envsys_register(sc->sc_sme);
|
|
if (err) {
|
|
aprint_error_dev(self,
|
|
"unable to register envsys with sysmon, error %d\n", err);
|
|
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");
|
|
|
|
if (!pmf_device_register1(self, NULL, NULL, lom_shutdown))
|
|
aprint_error_dev(self, "unable to register power handler\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 void
|
|
lom_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 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)
|
|
lom_dequeue_cmd(sc, &lc);
|
|
|
|
*val = lc.lc_data;
|
|
|
|
return (error);
|
|
}
|
|
|
|
static int
|
|
lom1_write(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", 2 * hz);
|
|
if (error)
|
|
lom_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_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);
|
|
|
|
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)
|
|
{
|
|
struct lom_cmd *lcp;
|
|
|
|
mutex_enter(&sc->sc_queue_mtx);
|
|
TAILQ_FOREACH(lcp, &sc->sc_queue, lc_next) {
|
|
if (lcp == lc) {
|
|
mutex_exit(&sc->sc_queue_mtx);
|
|
return;
|
|
}
|
|
}
|
|
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_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);
|
|
if (lc == NULL) {
|
|
sc->sc_state = LOM_STATE_IDLE;
|
|
return;
|
|
}
|
|
|
|
str = bus_space_read_1(sc->sc_iot, sc->sc_ioh, LOM1_STATUS);
|
|
if (str & LOM1_STATUS_BUSY) {
|
|
if (sc->sc_retry++ < 30) {
|
|
callout_schedule(&sc->sc_state_to, mstohz(1));
|
|
return;
|
|
}
|
|
|
|
/*
|
|
* Looks like the microcontroller got wedged. Unwedge
|
|
* it by writing this magic value. Give it some time
|
|
* to recover.
|
|
*/
|
|
bus_space_write_1(sc->sc_iot, sc->sc_ioh, LOM1_DATA, 0xac);
|
|
callout_schedule(&sc->sc_state_to, mstohz(1000));
|
|
sc->sc_state = LOM_STATE_CMD;
|
|
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)
|
|
{
|
|
struct lom_cmd lc;
|
|
int error;
|
|
|
|
if (cold)
|
|
return lom2_read_polled(sc, reg, val);
|
|
|
|
lc.lc_cmd = reg;
|
|
lc.lc_data = 0xff;
|
|
lom2_queue_cmd(sc, &lc);
|
|
|
|
error = tsleep(&lc, PZERO, "lom2rd", hz);
|
|
if (error)
|
|
lom_dequeue_cmd(sc, &lc);
|
|
|
|
*val = lc.lc_data;
|
|
|
|
return (error);
|
|
}
|
|
|
|
static int
|
|
lom2_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 = 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)
|
|
{
|
|
struct lom_cmd lc;
|
|
int error;
|
|
|
|
if (cold)
|
|
return lom2_write_polled(sc, reg, val);
|
|
|
|
lc.lc_cmd = reg | LOM_IDX_WRITE;
|
|
lc.lc_data = val;
|
|
lom2_queue_cmd(sc, &lc);
|
|
|
|
error = tsleep(&lc, PZERO, "lom2wr", hz);
|
|
if (error)
|
|
lom_dequeue_cmd(sc, &lc);
|
|
|
|
return (error);
|
|
}
|
|
|
|
static int
|
|
lom2_write_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 = 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 |= LOM_IDX_WRITE;
|
|
|
|
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)
|
|
{
|
|
struct lom_cmd *lcp;
|
|
uint8_t str;
|
|
|
|
mutex_enter(&sc->sc_queue_mtx);
|
|
TAILQ_FOREACH(lcp, &sc->sc_queue, lc_next) {
|
|
if (lcp == lc) {
|
|
mutex_exit(&sc->sc_queue_mtx);
|
|
return;
|
|
}
|
|
}
|
|
TAILQ_INSERT_TAIL(&sc->sc_queue, lc, lc_next);
|
|
if (sc->sc_state == LOM_STATE_IDLE) {
|
|
str = bus_space_read_1(sc->sc_iot, sc->sc_ioh, LOM2_STATUS);
|
|
if ((str & LOM2_STATUS_IBF) == 0) {
|
|
lc = TAILQ_FIRST(&sc->sc_queue);
|
|
bus_space_write_1(sc->sc_iot, sc->sc_ioh,
|
|
LOM2_CMD, lc->lc_cmd);
|
|
sc->sc_state = LOM_STATE_DATA;
|
|
}
|
|
}
|
|
mutex_exit(&sc->sc_queue_mtx);
|
|
}
|
|
|
|
static int
|
|
lom2_intr(void *arg)
|
|
{
|
|
struct lom_softc *sc = arg;
|
|
struct lom_cmd *lc;
|
|
uint8_t str, obr;
|
|
|
|
mutex_enter(&sc->sc_queue_mtx);
|
|
|
|
str = bus_space_read_1(sc->sc_iot, sc->sc_ioh, LOM2_STATUS);
|
|
obr = bus_space_read_1(sc->sc_iot, sc->sc_ioh, LOM2_DATA);
|
|
|
|
lc = TAILQ_FIRST(&sc->sc_queue);
|
|
if (lc == NULL) {
|
|
mutex_exit(&sc->sc_queue_mtx);
|
|
return (0);
|
|
}
|
|
|
|
if (lc->lc_cmd & LOM_IDX_WRITE) {
|
|
if ((str & LOM2_STATUS_IBF) == 0) {
|
|
bus_space_write_1(sc->sc_iot, sc->sc_ioh,
|
|
LOM2_DATA, lc->lc_data);
|
|
lc->lc_cmd &= ~LOM_IDX_WRITE;
|
|
}
|
|
mutex_exit(&sc->sc_queue_mtx);
|
|
return (1);
|
|
}
|
|
|
|
KASSERT(sc->sc_state == LOM_STATE_DATA);
|
|
lc->lc_data = obr;
|
|
|
|
TAILQ_REMOVE(&sc->sc_queue, lc, lc_next);
|
|
|
|
wakeup(lc);
|
|
|
|
sc->sc_state = LOM_STATE_IDLE;
|
|
|
|
if (!TAILQ_EMPTY(&sc->sc_queue)) {
|
|
str = bus_space_read_1(sc->sc_iot, sc->sc_ioh, LOM2_STATUS);
|
|
if ((str & LOM2_STATUS_IBF) == 0) {
|
|
lc = TAILQ_FIRST(&sc->sc_queue);
|
|
bus_space_write_1(sc->sc_iot, sc->sc_ioh,
|
|
LOM2_CMD, lc->lc_cmd);
|
|
sc->sc_state = LOM_STATE_DATA;
|
|
}
|
|
}
|
|
|
|
mutex_exit(&sc->sc_queue_mtx);
|
|
|
|
return (1);
|
|
}
|
|
|
|
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;
|
|
uint32_t i;
|
|
|
|
/* Sensor number */
|
|
i = edata->sensor;
|
|
|
|
/* Sensor type */
|
|
switch (edata->units) {
|
|
case ENVSYS_INDICATOR:
|
|
if (i < sc->sc_num_alarm)
|
|
lom_refresh_alarm(sc, edata, i);
|
|
else
|
|
lom_refresh_psu(sc, edata,
|
|
i - sc->sc_num_alarm - sc->sc_num_fan);
|
|
break;
|
|
case ENVSYS_SFANRPM:
|
|
lom_refresh_fan(sc, edata, i - sc->sc_num_alarm);
|
|
break;
|
|
case ENVSYS_STEMP:
|
|
lom_refresh_temp(sc, edata,
|
|
i - sc->sc_num_alarm - sc->sc_num_fan - sc->sc_num_psu);
|
|
break;
|
|
default:
|
|
edata->state = ENVSYS_SINVALID;
|
|
break;
|
|
}
|
|
|
|
/*
|
|
* 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 (i == 0 && 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
|
|
lom_refresh_alarm(struct lom_softc *sc, envsys_data_t *edata, uint32_t i)
|
|
{
|
|
uint8_t val;
|
|
|
|
/* Fault LED or Alarms */
|
|
KASSERT(i < sc->sc_num_alarm);
|
|
|
|
/* Read new value at most once every second. */
|
|
if (ratecheck(&sc->sc_alarm_lastread, &refresh_interval)) {
|
|
if (lom_read(sc, LOM_IDX_ALARM, &val)) {
|
|
edata->state = ENVSYS_SINVALID;
|
|
return;
|
|
}
|
|
sc->sc_alarm_lastval = val;
|
|
} else {
|
|
val = sc->sc_alarm_lastval;
|
|
}
|
|
|
|
if (i == 0) {
|
|
/* Fault LED */
|
|
if ((val & LOM_ALARM_FAULT) == LOM_ALARM_FAULT)
|
|
edata->value_cur = 0;
|
|
else
|
|
edata->value_cur = 1;
|
|
} else {
|
|
/* Alarms */
|
|
if ((val & (LOM_ALARM_1 << (i - 1))) == 0)
|
|
edata->value_cur = 0;
|
|
else
|
|
edata->value_cur = 1;
|
|
}
|
|
edata->state = ENVSYS_SVALID;
|
|
}
|
|
|
|
static void
|
|
lom_refresh_fan(struct lom_softc *sc, envsys_data_t *edata, uint32_t i)
|
|
{
|
|
uint8_t val;
|
|
|
|
/* Fan speed */
|
|
KASSERT(i < sc->sc_num_fan);
|
|
|
|
/* Read new value at most once every second. */
|
|
if (!ratecheck(&sc->sc_fan_lastread[i], &refresh_interval))
|
|
return;
|
|
|
|
if (lom_read(sc, LOM_IDX_FAN1 + i, &val)) {
|
|
edata->state = ENVSYS_SINVALID;
|
|
} else {
|
|
edata->value_cur = (60 * sc->sc_fan_cal[i] * val) / 100;
|
|
if (val < sc->sc_fan_low[i])
|
|
edata->state = ENVSYS_SCRITICAL;
|
|
else
|
|
edata->state = ENVSYS_SVALID;
|
|
}
|
|
}
|
|
|
|
static void
|
|
lom_refresh_psu(struct lom_softc *sc, envsys_data_t *edata, uint32_t i)
|
|
{
|
|
uint8_t val;
|
|
|
|
/* PSU status */
|
|
KASSERT(i < sc->sc_num_psu);
|
|
|
|
/* Read new value at most once every second. */
|
|
if (!ratecheck(&sc->sc_psu_lastread[i], &refresh_interval))
|
|
return;
|
|
|
|
if (lom_read(sc, LOM_IDX_PSU1 + i, &val) ||
|
|
!ISSET(val, LOM_PSU_PRESENT)) {
|
|
edata->state = ENVSYS_SINVALID;
|
|
} else {
|
|
if (val & LOM_PSU_STANDBY) {
|
|
edata->value_cur = 0;
|
|
edata->state = ENVSYS_SVALID;
|
|
} else {
|
|
edata->value_cur = 1;
|
|
if (ISSET(val, LOM_PSU_INPUTA) &&
|
|
ISSET(val, LOM_PSU_INPUTB) &&
|
|
ISSET(val, LOM_PSU_OUTPUT))
|
|
edata->state = ENVSYS_SVALID;
|
|
else
|
|
edata->state = ENVSYS_SCRITICAL;
|
|
}
|
|
}
|
|
}
|
|
|
|
static void
|
|
lom_refresh_temp(struct lom_softc *sc, envsys_data_t *edata, uint32_t i)
|
|
{
|
|
uint8_t val;
|
|
|
|
/* Temperature */
|
|
KASSERT(i < sc->sc_num_temp);
|
|
|
|
/* Read new value at most once every second. */
|
|
if (!ratecheck(&sc->sc_temp_lastread[i], &refresh_interval))
|
|
return;
|
|
|
|
if (lom_read(sc, LOM_IDX_TEMP1 + i, &val)) {
|
|
edata->state = ENVSYS_SINVALID;
|
|
} else {
|
|
edata->value_cur = val * 1000000 + 273150000;
|
|
edata->state = ENVSYS_SVALID;
|
|
}
|
|
}
|
|
|
|
static void
|
|
lom1_write_hostname(struct lom_softc *sc)
|
|
{
|
|
char name[(LOM1_IDX_HOSTNAME12 - LOM1_IDX_HOSTNAME1 + 1) + 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 */
|
|
lom_dequeue_cmd(sc, &sc->sc_wdog_pat);
|
|
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;
|
|
}
|
|
|
|
static bool
|
|
lom_shutdown(device_t dev, int how)
|
|
{
|
|
struct lom_softc *sc = device_private(dev);
|
|
|
|
sc->sc_wdog_ctl &= ~LOM_WDOG_ENABLE;
|
|
lom_write(sc, LOM_IDX_WDOG_CTL, sc->sc_wdog_ctl);
|
|
return true;
|
|
}
|
|
|
|
static int
|
|
lom_sysctl_alarm(SYSCTLFN_ARGS)
|
|
{
|
|
struct sysctlnode node;
|
|
struct lom_softc *sc;
|
|
int i, tmp, error;
|
|
uint8_t val;
|
|
|
|
node = *rnode;
|
|
sc = node.sysctl_data;
|
|
|
|
for (i = 0; i < sc->sc_num_alarm; i++) {
|
|
if (node.sysctl_num == sc->sc_sysctl_num[i]) {
|
|
lom_refresh_alarm(sc, &sc->sc_alarm[i], i);
|
|
tmp = sc->sc_alarm[i].value_cur;
|
|
node.sysctl_data = &tmp;
|
|
error = sysctl_lookup(SYSCTLFN_CALL(&node));
|
|
if (error || newp == NULL)
|
|
return error;
|
|
if (tmp < 0 || tmp > 1)
|
|
return EINVAL;
|
|
|
|
if (lom_read(sc, LOM_IDX_ALARM, &val))
|
|
return EINVAL;
|
|
if (i == 0) {
|
|
/* Fault LED */
|
|
if (tmp != 0)
|
|
val &= ~LOM_ALARM_FAULT;
|
|
else
|
|
val |= LOM_ALARM_FAULT;
|
|
} else {
|
|
/* Alarms */
|
|
if (tmp != 0)
|
|
val |= LOM_ALARM_1 << (i - 1);
|
|
else
|
|
val &= ~(LOM_ALARM_1 << (i - 1));
|
|
}
|
|
if (lom_write(sc, LOM_IDX_ALARM, val))
|
|
return EINVAL;
|
|
|
|
sc->sc_alarm[i].value_cur = tmp;
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
return ENOENT;
|
|
}
|