/* $OpenBSD: lom.c,v 1.29 2022/07/04 19:06:10 miod 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 #include #include #include #include #include #include #include #include #include #include /* * 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 { struct device 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 ksensor sc_fan[LOM_MAX_FAN]; struct ksensor sc_psu[LOM_MAX_PSU]; struct ksensor sc_temp[LOM_MAX_TEMP]; struct ksensordev sc_sensordev; 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 timeout sc_wdog_to; int sc_wdog_period; uint8_t sc_wdog_ctl; struct lom_cmd sc_wdog_pat; TAILQ_HEAD(, lom_cmd) sc_queue; struct mutex sc_queue_mtx; struct timeout sc_state_to; int sc_state; #define LOM_STATE_IDLE 0 #define LOM_STATE_CMD 1 #define LOM_STATE_DATA 2 int sc_retry; }; int lom_match(struct device *, void *, void *); void lom_attach(struct device *, struct device *, void *); int lom_activate(struct device *, int); const struct cfattach lom_ca = { sizeof(struct lom_softc), lom_match, lom_attach, NULL, lom_activate }; struct cfdriver lom_cd = { NULL, "lom", DV_DULL }; int lom_read(struct lom_softc *, uint8_t, uint8_t *); int lom_write(struct lom_softc *, uint8_t, uint8_t); void lom_queue_cmd(struct lom_softc *, struct lom_cmd *); void lom_dequeue_cmd(struct lom_softc *, struct lom_cmd *); int lom1_read(struct lom_softc *, uint8_t, uint8_t *); int lom1_write(struct lom_softc *, uint8_t, uint8_t); int lom1_read_polled(struct lom_softc *, uint8_t, uint8_t *); int lom1_write_polled(struct lom_softc *, uint8_t, uint8_t); void lom1_queue_cmd(struct lom_softc *, struct lom_cmd *); void lom1_process_queue(void *); void lom1_process_queue_locked(struct lom_softc *); int lom2_read(struct lom_softc *, uint8_t, uint8_t *); int lom2_write(struct lom_softc *, uint8_t, uint8_t); int lom2_read_polled(struct lom_softc *, uint8_t, uint8_t *); int lom2_write_polled(struct lom_softc *, uint8_t, uint8_t); void lom2_queue_cmd(struct lom_softc *, struct lom_cmd *); int lom2_intr(void *); int lom_init_desc(struct lom_softc *sc); void lom_refresh(void *); void lom1_write_hostname(struct lom_softc *); void lom2_write_hostname(struct lom_softc *); void lom_wdog_pat(void *); int lom_wdog_cb(void *, int); void lom_shutdown(void *); int lom_match(struct device *parent, void *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); } void lom_attach(struct device *parent, struct device *self, void *aux) { struct lom_softc *sc = (void *)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) { if (ea->ea_nintrs < 1) { printf(": no interrupt\n"); return; } sc->sc_type = LOM_LOMLITE2; } if (ebus_bus_map(ea->ea_iotag, 0, EBUS_PADDR_FROM_REG(&ea->ea_regs[0]), ea->ea_regs[0].size, 0, 0, &sc->sc_ioh) == 0) { sc->sc_iot = ea->ea_iotag; } else if (ebus_bus_map(ea->ea_memtag, 0, EBUS_PADDR_FROM_REG(&ea->ea_regs[0]), ea->ea_regs[0].size, 0, 0, &sc->sc_ioh) == 0) { sc->sc_iot = ea->ea_memtag; } else { printf(": can't map register space\n"); return; } if (sc->sc_type < LOM_LOMLITE2) { /* XXX Magic */ 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)) { printf(": not responding\n"); return; } TAILQ_INIT(&sc->sc_queue); mtx_init(&sc->sc_queue_mtx, IPL_BIO); config2 = config3 = 0; if (sc->sc_type < LOM_LOMLITE2) { /* * LOMlite doesn't do interrupts so we limp along on * timeouts. */ timeout_set(&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_intrs[0], IPL_BIO, 0, lom2_intr, sc, self->dv_xname); } 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); 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)) { printf(": can't read fan information\n"); return; } sc->sc_fan_cal[i] = cal; sc->sc_fan_low[i] = low; } /* Initialize sensor data. */ strlcpy(sc->sc_sensordev.xname, sc->sc_dev.dv_xname, sizeof(sc->sc_sensordev.xname)); for (i = 0; i < sc->sc_num_fan; i++) { sc->sc_fan[i].type = SENSOR_FANRPM; sensor_attach(&sc->sc_sensordev, &sc->sc_fan[i]); snprintf(sc->sc_fan[i].desc, sizeof(sc->sc_fan[i].desc), "fan%d", i + 1); } for (i = 0; i < sc->sc_num_psu; i++) { sc->sc_psu[i].type = SENSOR_INDICATOR; sensor_attach(&sc->sc_sensordev, &sc->sc_psu[i]); snprintf(sc->sc_psu[i].desc, sizeof(sc->sc_psu[i].desc), "PSU%d", i + 1); } for (i = 0; i < sc->sc_num_temp; i++) { sc->sc_temp[i].type = SENSOR_TEMP; sensor_attach(&sc->sc_sensordev, &sc->sc_temp[i]); } if (lom_init_desc(sc)) { printf(": can't read sensor names\n"); return; } if (sensor_task_register(sc, lom_refresh, 5) == NULL) { printf(": unable to register update task\n"); return; } sensordev_install(&sc->sc_sensordev); /* * We configure the watchdog to turn on the fault LED when the * watchdog timer expires. We run our own timeout to pat it * such that this won't happen unless the kernel hangs. When * the watchdog is explicitly configured using sysctl(8), we * reconfigure it to reset the machine and let the standard * watchdog(4) machinery take over. */ 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_RESET; sc->sc_wdog_ctl |= LOM_WDOG_ENABLE; lom_write(sc, LOM_IDX_WDOG_CTL, sc->sc_wdog_ctl); timeout_set(&sc->sc_wdog_to, lom_wdog_pat, sc); timeout_add_sec(&sc->sc_wdog_to, LOM_WDOG_TIME_MAX / 2); wdog_register(lom_wdog_cb, sc); printf(": %s rev %d.%d\n", sc->sc_type < LOM_LOMLITE2 ? "LOMlite" : "LOMlite2", fw_rev >> 4, fw_rev & 0x0f); } int lom_activate(struct device *self, int act) { int ret = 0; switch (act) { case DVACT_POWERDOWN: wdog_shutdown(self); lom_shutdown(self); break; } return (ret); } 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); } 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); } 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); } void lom_dequeue_cmd(struct lom_softc *sc, struct lom_cmd *lc) { struct lom_cmd *lcp; mtx_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; } } mtx_leave(&sc->sc_queue_mtx); } 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_nsec(&lc, PZERO, "lomrd", SEC_TO_NSEC(1)); if (error) lom_dequeue_cmd(sc, &lc); *val = lc.lc_data; return (error); } 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_nsec(&lc, PZERO, "lomwr", SEC_TO_NSEC(2)); if (error) lom_dequeue_cmd(sc, &lc); return (error); } 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); } 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); } void lom1_queue_cmd(struct lom_softc *sc, struct lom_cmd *lc) { mtx_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); } mtx_leave(&sc->sc_queue_mtx); } void lom1_process_queue(void *arg) { struct lom_softc *sc = arg; mtx_enter(&sc->sc_queue_mtx); lom1_process_queue_locked(sc); mtx_leave(&sc->sc_queue_mtx); } 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) { timeout_add_msec(&sc->sc_state_to, 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); timeout_add_msec(&sc->sc_state_to, 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; timeout_add_msec(&sc->sc_state_to, 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; timeout_add_msec(&sc->sc_state_to, 1); return; } sc->sc_state = LOM_STATE_IDLE; } 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_nsec(&lc, PZERO, "lom2rd", SEC_TO_NSEC(1)); if (error) lom_dequeue_cmd(sc, &lc); *val = lc.lc_data; return (error); } 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); } 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_nsec(&lc, PZERO, "lom2wr", SEC_TO_NSEC(1)); if (error) lom_dequeue_cmd(sc, &lc); return (error); } 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); 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); 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); } void lom2_queue_cmd(struct lom_softc *sc, struct lom_cmd *lc) { uint8_t str; mtx_enter(&sc->sc_queue_mtx); 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) { bus_space_write_1(sc->sc_iot, sc->sc_ioh, LOM2_CMD, lc->lc_cmd); sc->sc_state = LOM_STATE_DATA; } } mtx_leave(&sc->sc_queue_mtx); } int lom2_intr(void *arg) { struct lom_softc *sc = arg; struct lom_cmd *lc; uint8_t str, obr; mtx_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) { mtx_leave(&sc->sc_queue_mtx); return (0); } if (lc->lc_cmd & LOM_IDX_WRITE) { bus_space_write_1(sc->sc_iot, sc->sc_ioh, LOM2_DATA, lc->lc_data); lc->lc_cmd &= ~LOM_IDX_WRITE; mtx_leave(&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) { bus_space_write_1(sc->sc_iot, sc->sc_ioh, LOM2_CMD, lc->lc_cmd); sc->sc_state = LOM_STATE_DATA; } } mtx_leave(&sc->sc_queue_mtx); return (1); } 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); } void lom_refresh(void *arg) { struct lom_softc *sc = arg; 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].flags |= SENSOR_FINVALID; continue; } sc->sc_fan[i].value = (60 * sc->sc_fan_cal[i] * val) / 100; if (val < sc->sc_fan_low[i]) sc->sc_fan[i].status = SENSOR_S_CRIT; else sc->sc_fan[i].status = SENSOR_S_OK; sc->sc_fan[i].flags &= ~SENSOR_FINVALID; } 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].flags |= SENSOR_FINVALID; continue; } if (val & LOM_PSU_STANDBY) { sc->sc_psu[i].value = 0; sc->sc_psu[i].status = SENSOR_S_UNSPEC; } else { sc->sc_psu[i].value = 1; if (ISSET(val, LOM_PSU_INPUTA) && ISSET(val, LOM_PSU_INPUTB) && ISSET(val, LOM_PSU_OUTPUT)) sc->sc_psu[i].status = SENSOR_S_OK; else sc->sc_psu[i].status = SENSOR_S_CRIT; } sc->sc_psu[i].flags &= ~SENSOR_FINVALID; } for (i = 0; i < sc->sc_num_temp; i++) { if (lom_read(sc, LOM_IDX_TEMP1 + i, &val)) { sc->sc_temp[i].flags |= SENSOR_FINVALID; continue; } sc->sc_temp[i].value = val * 1000000 + 273150000; sc->sc_temp[i].flags &= ~SENSOR_FINVALID; } /* * 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(sc->sc_hostname)); } } 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; } 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]); } void lom_wdog_pat(void *arg) { struct lom_softc *sc = arg; /* 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); timeout_add_sec(&sc->sc_wdog_to, LOM_WDOG_TIME_MAX / 2); } int lom_wdog_cb(void *arg, int period) { struct lom_softc *sc = arg; if (period > LOM_WDOG_TIME_MAX) period = LOM_WDOG_TIME_MAX; else if (period < 0) period = 0; if (period == 0) { if (sc->sc_wdog_period != 0) { /* Stop watchdog from resetting the machine. */ sc->sc_wdog_ctl &= ~LOM_WDOG_RESET; lom_write(sc, LOM_IDX_WDOG_CTL, sc->sc_wdog_ctl); lom_write(sc, LOM_IDX_WDOG_TIME, LOM_WDOG_TIME_MAX); timeout_add_sec(&sc->sc_wdog_to, LOM_WDOG_TIME_MAX / 2); } } else { if (sc->sc_wdog_period != period) { /* Set new timeout. */ lom_write(sc, LOM_IDX_WDOG_TIME, period); } if (sc->sc_wdog_period == 0) { /* Make watchdog reset the machine. */ sc->sc_wdog_ctl |= LOM_WDOG_RESET; lom_write(sc, LOM_IDX_WDOG_CTL, sc->sc_wdog_ctl); timeout_del(&sc->sc_wdog_to); } else { /* Pat the dog. */ lom_dequeue_cmd(sc, &sc->sc_wdog_pat); 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); } } sc->sc_wdog_period = period; return (period); } void lom_shutdown(void *arg) { struct lom_softc *sc = arg; sc->sc_wdog_ctl &= ~LOM_WDOG_ENABLE; lom_write(sc, LOM_IDX_WDOG_CTL, sc->sc_wdog_ctl); }