/* $OpenBSD: esm.c,v 1.46 2007/03/22 16:55:31 deraadt Exp $ */
/*
* Copyright (c) 2005 Jordan Hargrave <jordan@openbsd.org>
* Copyright (c) 2005 David Gwynne <dlg@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.
*/
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/device.h>
#include <sys/kernel.h>
#include <sys/malloc.h>
#include <sys/timeout.h>
#include <sys/proc.h>
#include <sys/queue.h>
#include <sys/sensors.h>
#include <dev/isa/isareg.h>
#include <machine/bus.h>
#include <machine/intr.h>
#include <arch/i386/i386/esmvar.h>
#include <arch/i386/i386/esmreg.h>
#include <arch/i386/isa/isa_machdep.h>
#ifdef ESM_DEBUG
#define DPRINTF(x...) do { if (esmdebug) printf(x); } while (0)
#define DPRINTFN(n,x...) do { if (esmdebug > (n)) printf(x); } while (0)
int esmdebug = 3;
#else
#define DPRINTF(x...) /* x */
#define DPRINTFN(n,x...) /* n: x */
#endif
int esm_match(struct device *, void *, void *);
void esm_attach(struct device *, struct device *, void *);
enum esm_sensor_type {
ESM_S_UNKNOWN, /* XXX */
ESM_S_INTRUSION,
ESM_S_TEMP,
ESM_S_FANRPM,
ESM_S_VOLTS,
ESM_S_VOLTSx10,
ESM_S_AMPS,
ESM_S_PWRSUP,
ESM_S_PCISLOT,
ESM_S_SCSICONN,
ESM_S_DRIVES, /* argument is the base index of the drive */
ESM_S_DRIVE,
ESM_S_HPSLOT,
ESM_S_ACSWITCH
};
/*
* map esm sensor types to kernel sensor types.
* keep this in sync with the esm_sensor_type enum above.
*/
enum sensor_type esm_typemap[] = {
SENSOR_INTEGER,
SENSOR_INDICATOR,
SENSOR_TEMP,
SENSOR_FANRPM,
SENSOR_VOLTS_DC,
SENSOR_VOLTS_DC,
SENSOR_AMPS,
SENSOR_INDICATOR,
SENSOR_INTEGER,
SENSOR_INDICATOR,
SENSOR_DRIVE,
SENSOR_DRIVE,
SENSOR_INTEGER,
SENSOR_INDICATOR
};
struct esm_sensor_map {
enum esm_sensor_type type;
long arg;
const char *name;
};
struct esm_sensor {
u_int8_t es_dev;
u_int8_t es_id;
enum esm_sensor_type es_type;
struct {
u_int16_t th_lo_crit;
u_int16_t th_lo_warn;
u_int16_t th_hi_warn;
u_int16_t th_hi_crit;
} es_thresholds;
struct ksensor *es_sensor;
TAILQ_ENTRY(esm_sensor) es_entry;
};
struct esm_softc {
struct device sc_dev;
bus_space_tag_t sc_iot;
bus_space_handle_t sc_ioh;
TAILQ_HEAD(, esm_sensor) sc_sensors;
struct esm_sensor *sc_nextsensor;
struct ksensordev sc_sensordev;
int sc_retries;
volatile int sc_step;
struct timeout sc_timeout;
int sc_wdog_period;
volatile int sc_wdog_tickle;
};
struct cfattach esm_ca = {
sizeof(struct esm_softc), esm_match, esm_attach
};
struct cfdriver esm_cd = {
NULL, "esm", DV_DULL
};
#define DEVNAME(s) ((s)->sc_dev.dv_xname)
#define EREAD(s, r) bus_space_read_1((s)->sc_iot, (s)->sc_ioh, (r))
#define EWRITE(s, r, v) bus_space_write_1((s)->sc_iot, (s)->sc_ioh, (r), (v))
#define ECTRLWR(s, v) EWRITE((s), ESM2_CTRL_REG, (v))
#define EDATARD(s) EREAD((s), ESM2_DATA_REG)
#define EDATAWR(s, v) EWRITE((s), ESM2_DATA_REG, (v))
int esm_watchdog(void *, int);
void esm_refresh(void *);
int esm_get_devmap(struct esm_softc *, int, struct esm_devmap *);
void esm_devmap(struct esm_softc *, struct esm_devmap *);
void esm_make_sensors(struct esm_softc *, struct esm_devmap *,
struct esm_sensor_map *, int);
int esm_thresholds(struct esm_softc *, struct esm_devmap *,
struct esm_sensor *);
int esm_bmc_ready(struct esm_softc *, int, u_int8_t, u_int8_t, int);
int esm_cmd(struct esm_softc *, void *, size_t, void *, size_t,
int, int);
int esm_smb_cmd(struct esm_softc *, struct esm_smb_req *,
struct esm_smb_resp *, int, int);
int64_t esm_val2temp(u_int16_t);
int64_t esm_val2volts(u_int16_t);
int64_t esm_val2amps(u_int16_t);
/* Determine if this is a Dell server */
int
esm_probe(void *aux)
{
const char *pdellstr;
struct dell_sysid *pdellid;
uint16_t sysid;
pdellstr = (const char *)ISA_HOLE_VADDR(DELL_SYSSTR_ADDR);
DPRINTF("Dell String: %s\n", pdellstr);
if (strncmp(pdellstr, "Dell System", 11))
return (0);
pdellid = (struct dell_sysid *)ISA_HOLE_VADDR(DELL_SYSID_ADDR);
if ((sysid = pdellid->sys_id) == DELL_SYSID_EXT)
sysid = pdellid->ext_id;
DPRINTF("SysId: %x\n", sysid);
switch (sysid) {
case DELL_SYSID_2300:
case DELL_SYSID_4300:
case DELL_SYSID_4350:
case DELL_SYSID_6300:
case DELL_SYSID_6350:
case DELL_SYSID_2400:
case DELL_SYSID_2450:
case DELL_SYSID_4400:
case DELL_SYSID_6400:
case DELL_SYSID_6450:
case DELL_SYSID_2500:
case DELL_SYSID_2550:
case DELL_SYSID_PV530F:
case DELL_SYSID_PV735N:
case DELL_SYSID_PV750N:
case DELL_SYSID_PV755N:
case DELL_SYSID_PA200:
return (1);
}
return (0);
}
int
esm_match(struct device *parent, void *match, void *aux)
{
struct esm_attach_args *eaa = aux;
if (strncmp(eaa->eaa_name, esm_cd.cd_name, sizeof(esm_cd.cd_name)) == 0 &&
esm_probe(eaa))
return (1);
return (0);
}
void
esm_attach(struct device *parent, struct device *self, void *aux)
{
struct esm_softc *sc = (struct esm_softc *)self;
struct esm_attach_args *eaa = aux;
u_int8_t x;
struct esm_devmap devmap;
int i;
sc->sc_iot = eaa->eaa_iot;
TAILQ_INIT(&sc->sc_sensors);
if (bus_space_map(sc->sc_iot, ESM2_BASE_PORT, 8, 0, &sc->sc_ioh) != 0) {
printf(": unable to map memory\n");
return;
}
/* turn off interrupts here */
x = EREAD(sc, ESM2_INTMASK_REG);
x &= ~(ESM2_TIM_SCI_EN|ESM2_TIM_SMI_EN|ESM2_TIM_NMI2SMI);
x |= ESM2_TIM_POWER_UP_BITS;
EWRITE(sc, ESM2_INTMASK_REG, x);
/* clear event doorbells */
x = EREAD(sc, ESM2_CTRL_REG);
x &= ~ESM2_TC_HOSTBUSY;
x |= ESM2_TC_POWER_UP_BITS;
EWRITE(sc, ESM2_CTRL_REG, x);
/* see if card is alive */
if (esm_bmc_ready(sc, ESM2_CTRL_REG, ESM2_TC_ECBUSY, 0, 1) != 0) {
printf(": card is not alive\n");
bus_space_unmap(sc->sc_iot, sc->sc_ioh, 8);
return;
}
sc->sc_wdog_period = 0;
wdog_register(sc, esm_watchdog);
printf("\n");
strlcpy(sc->sc_sensordev.xname, DEVNAME(sc),
sizeof(sc->sc_sensordev.xname));
for (i = 0; i <= 0xff; i++) {
if (esm_get_devmap(sc, i, &devmap) != 0)
break; /* XXX not continue? */
esm_devmap(sc, &devmap);
}
if (!TAILQ_EMPTY(&sc->sc_sensors)) {
sensordev_install(&sc->sc_sensordev);
DPRINTF("%s: starting refresh\n", DEVNAME(sc));
sc->sc_nextsensor = TAILQ_FIRST(&sc->sc_sensors);
sc->sc_retries = 0;
timeout_set(&sc->sc_timeout, esm_refresh, sc);
timeout_add(&sc->sc_timeout, hz);
}
}
int
esm_watchdog(void *arg, int period)
{
struct esm_softc *sc = arg;
struct esm_wdog_prop prop;
struct esm_wdog_state state;
int s;
if (sc->sc_wdog_period == period) {
if (period != 0) {
s = splclock();
if (sc->sc_step != 0) {
/* defer tickling to the sensor refresh */
sc->sc_wdog_tickle = 1;
} else {
/* tickle the watchdog */
EWRITE(sc, ESM2_CTRL_REG, ESM2_TC_HBDB);
}
splx(s);
}
return (period);
}
/* we're changing the watchdog period */
memset(&prop, 0, sizeof(prop));
memset(&state, 0, sizeof(state));
if (period < 10 && period > 0)
period = 10;
s = splclock();
prop.cmd = ESM2_CMD_HWDC;
prop.subcmd = ESM2_HWDC_WRITE_PROPERTY;
prop.action = (period == 0) ? ESM_WDOG_DISABLE : ESM_WDOG_RESET;
prop.time = period;
/*
* if we're doing a refresh, we need to wait till the hardware is
* available again. since period changes only happen via sysctl we
* should have a process context we can sleep in.
*/
while (sc->sc_step != 0) {
if (tsleep(sc, PUSER | PCATCH, "esm", 0) == EINTR) {
splx(s);
return (sc->sc_wdog_period);
}
}
if (esm_cmd(sc, &prop, sizeof(prop), NULL, 0, 1, 0) != 0) {
splx(s);
return (sc->sc_wdog_period);
}
state.cmd = ESM2_CMD_HWDC;
state.subcmd = ESM2_HWDC_WRITE_STATE;
state.state = (period == 0) ? 0 : 1;
/* we have the hw, this can't (shouldn't) fail */
esm_cmd(sc, &state, sizeof(state), NULL, 0, 1, 0);
splx(s);
sc->sc_wdog_period = period;
return (period);
}
void
esm_refresh(void *arg)
{
struct esm_softc *sc = arg;
struct esm_sensor *es = sc->sc_nextsensor;
struct esm_smb_req req;
struct esm_smb_resp resp;
struct esm_smb_resp_val *val = &resp.resp_val;
int nsensors, i, step;
memset(&req, 0, sizeof(req));
req.h_cmd = ESM2_CMD_SMB_XMIT_RECV;
req.h_dev = es->es_dev;
req.h_txlen = sizeof(req.req_val);
req.h_rxlen = sizeof(resp.resp_val);
req.req_val.v_cmd = ESM2_SMB_SENSOR_VALUE;
req.req_val.v_sensor = es->es_id;
switch (es->es_type) {
case ESM_S_DRIVES:
nsensors = 4;
break;
case ESM_S_PWRSUP:
nsensors = 6;
break;
default:
nsensors = 1;
break;
}
if ((step = esm_smb_cmd(sc, &req, &resp, 0, sc->sc_step)) != 0) {
sc->sc_step = step;
if (++sc->sc_retries < 10)
goto tick;
for (i = 0; i < nsensors; i++)
es->es_sensor[i].flags |= SENSOR_FINVALID;
} else {
switch (es->es_type) {
case ESM_S_TEMP:
es->es_sensor->value = esm_val2temp(val->v_reading);
break;
case ESM_S_VOLTS:
es->es_sensor->value = esm_val2volts(val->v_reading);
break;
case ESM_S_VOLTSx10:
es->es_sensor->value =
esm_val2volts(val->v_reading) * 10;
break;
case ESM_S_AMPS:
es->es_sensor->value = esm_val2amps(val->v_reading);
break;
case ESM_S_DRIVES:
for (i = 0; i < nsensors; i++) {
es->es_sensor[i].value =
(val->v_reading >> i * 8) & 0xf;
}
break;
case ESM_S_PWRSUP:
for (i = 0; i < nsensors; i++) {
es->es_sensor[i].value =
(val->v_reading >> i) & 0x1;
}
break;
default:
es->es_sensor->value = val->v_reading;
break;
}
switch (es->es_type) {
case ESM_S_TEMP:
case ESM_S_FANRPM:
case ESM_S_VOLTS:
case ESM_S_AMPS:
if (val->v_reading >= es->es_thresholds.th_hi_crit ||
val->v_reading <= es->es_thresholds.th_lo_crit) {
es->es_sensor->status = SENSOR_S_CRIT;
break;
}
if (val->v_reading >= es->es_thresholds.th_hi_warn ||
val->v_reading <= es->es_thresholds.th_lo_warn) {
es->es_sensor->status = SENSOR_S_WARN;
break;
}
es->es_sensor->status = SENSOR_S_OK;
break;
case ESM_S_PWRSUP:
if (val->v_status & ESM2_VS_PSU_FAIL) {
es->es_sensor[3].status = SENSOR_S_CRIT;
break;
}
es->es_sensor[3].status = SENSOR_S_OK;
break;
default:
break;
}
for (i = 0; i < nsensors; i++)
es->es_sensor->flags &= ~SENSOR_FINVALID;
}
sc->sc_nextsensor = TAILQ_NEXT(es, es_entry);
sc->sc_retries = 0;
sc->sc_step = 0;
if (sc->sc_wdog_tickle) {
/*
* the controller was busy in a refresh when the watchdog
* needed a tickle, so do it now.
*/
EWRITE(sc, ESM2_CTRL_REG, ESM2_TC_HBDB);
sc->sc_wdog_tickle = 0;
}
wakeup(sc);
if (sc->sc_nextsensor == NULL) {
sc->sc_nextsensor = TAILQ_FIRST(&sc->sc_sensors);
timeout_add(&sc->sc_timeout, hz * 10);
return;
}
tick:
timeout_add(&sc->sc_timeout, hz / 20);
}
int
esm_get_devmap(struct esm_softc *sc, int dev, struct esm_devmap *devmap)
{
struct esm_devmap_req req;
struct esm_devmap_resp resp;
#ifdef ESM_DEBUG
int i;
#endif
memset(&req, 0, sizeof(req));
memset(&resp, 0, sizeof(resp));
req.cmd = ESM2_CMD_DEVICEMAP;
req.action = ESM2_DEVICEMAP_READ;
req.index = dev;
req.ndev = 1;
if (esm_cmd(sc, &req, sizeof(req), &resp, sizeof(resp), 1, 0) != 0)
return (1);
if (resp.status != 0)
return (1);
memcpy(devmap, &resp.devmap[0], sizeof(struct esm_devmap));
#ifdef ESM_DEBUG
if (esmdebug > 5) {
printf("\n");
printf("Device Map(%d) returns:\n", dev);
printf(" status: %.2x\n", resp.status);
printf(" #devs : %.2x\n", resp.ndev);
printf(" index: %.2x\n", resp.devmap[0].index);
printf(" Type : %.2x.%.2x\n", resp.devmap[0].dev_major,
resp.devmap[0].dev_minor);
printf(" Rev : %.2x.%.2x\n", resp.devmap[0].rev_major,
resp.devmap[0].rev_minor);
printf(" ROM : %.2x\n", resp.devmap[0].rev_rom);
printf(" SMB : %.2x\n", resp.devmap[0].smb_addr);
printf(" Stat : %.2x\n", resp.devmap[0].status);
printf(" MonTy: %.2x\n", resp.devmap[0].monitor_type);
printf(" Poll : %.2x\n", resp.devmap[0].pollcycle);
printf(" UUID : ");
for (i = 0; i < ESM2_UUID_LEN; i++) {
printf("%02x", resp.devmap[0].uniqueid[i]);
}
printf("\n");
}
#endif /* ESM_DEBUG */
return (0);
}
struct esm_sensor_map esm_sensors_esm2[] = {
{ ESM_S_UNKNOWN, 0, "Motherboard" },
{ ESM_S_TEMP, 0, "CPU 1" },
{ ESM_S_TEMP, 0, "CPU 2" },
{ ESM_S_TEMP, 0, "CPU 3" },
{ ESM_S_TEMP, 0, "CPU 4" },
{ ESM_S_TEMP, 0, "Mainboard" },
{ ESM_S_TEMP, 0, "Ambient" },
{ ESM_S_VOLTS, 0, "CPU 1 Core" },
{ ESM_S_VOLTS, 0, "CPU 2 Core" },
{ ESM_S_VOLTS, 0, "CPU 3 Core" },
{ ESM_S_VOLTS, 0, "CPU 4 Core" },
{ ESM_S_VOLTS, 0, "Motherboard +5V" },
{ ESM_S_VOLTS, 0, "Motherboard +12V" },
{ ESM_S_VOLTS, 0, "Motherboard +3.3V" },
{ ESM_S_VOLTS, 0, "Motherboard +2.5V" },
{ ESM_S_VOLTS, 0, "Motherboard GTL Term" },
{ ESM_S_VOLTS, 0, "Motherboard Battery" },
{ ESM_S_INTRUSION, 0, "Chassis Intrusion", },
{ ESM_S_UNKNOWN, 0, "Chassis Fan Ctrl", },
{ ESM_S_FANRPM, 0, "Fan 1" },
{ ESM_S_FANRPM, 0, "Fan 2" }, /* 20 */
{ ESM_S_FANRPM, 0, "Fan 3" },
{ ESM_S_FANRPM, 0, "Power Supply Fan" },
{ ESM_S_VOLTS, 0, "CPU 1 cache" },
{ ESM_S_VOLTS, 0, "CPU 2 cache" },
{ ESM_S_VOLTS, 0, "CPU 3 cache" },
{ ESM_S_VOLTS, 0, "CPU 4 cache" },
{ ESM_S_UNKNOWN, 0, "Power Ctrl" },
{ ESM_S_PWRSUP, 0, "Power Supply 1" },
{ ESM_S_PWRSUP, 0, "Power Supply 2" },
{ ESM_S_VOLTS, 0, "Mainboard +1.5V" }, /* 30 */
{ ESM_S_VOLTS, 0, "Motherboard +2.8V" },
{ ESM_S_UNKNOWN, 0, "HotPlug Status" },
{ ESM_S_PCISLOT, 0, "PCI Slot 1" },
{ ESM_S_PCISLOT, 0, "PCI Slot 2" },
{ ESM_S_PCISLOT, 0, "PCI Slot 3" },
{ ESM_S_PCISLOT, 0, "PCI Slot 4" },
{ ESM_S_PCISLOT, 0, "PCI Slot 5" },
{ ESM_S_PCISLOT, 0, "PCI Slot 6" },
{ ESM_S_PCISLOT, 0, "PCI Slot 7" },
{ ESM_S_VOLTS, 0, "CPU 1 Cartridge" }, /* 40 */
{ ESM_S_VOLTS, 0, "CPU 2 Cartridge" },
{ ESM_S_VOLTS, 0, "CPU 3 Cartridge" },
{ ESM_S_VOLTS, 0, "CPU 4 Cartridge" },
{ ESM_S_VOLTS, 0, "Gigabit NIC +1.8V" },
{ ESM_S_VOLTS, 0, "Gigabit NIC +2.5V" },
{ ESM_S_VOLTS, 0, "Memory +3.3V" },
{ ESM_S_VOLTS, 0, "Video +2.5V" },
{ ESM_S_PWRSUP, 0, "Power Supply 3" },
{ ESM_S_FANRPM, 0, "Fan 4" },
{ ESM_S_FANRPM, 0, "Power Supply Fan" }, /* 50 */
{ ESM_S_FANRPM, 0, "Power Supply Fan" },
{ ESM_S_FANRPM, 0, "Power Supply Fan" },
{ ESM_S_ACSWITCH, 0, "A/C Power Switch" },
{ ESM_S_UNKNOWN, 0, "PS Over Temp" }
};
struct esm_sensor_map esm_sensors_backplane[] = {
{ ESM_S_UNKNOWN, 0, "Backplane" },
{ ESM_S_UNKNOWN, 0, "Backplane Control" },
{ ESM_S_TEMP, 0, "Backplane Top" },
{ ESM_S_TEMP, 0, "Backplane Bottom" },
{ ESM_S_TEMP, 0, "Backplane Control Panel" },
{ ESM_S_VOLTS, 0, "Backplane Battery" },
{ ESM_S_VOLTS, 0, "Backplane +5V" },
{ ESM_S_VOLTS, 0, "Backplane +12V" },
{ ESM_S_VOLTS, 0, "Backplane Board" },
{ ESM_S_INTRUSION, 0, "Backplane Intrusion" },
{ ESM_S_UNKNOWN, 0, "Backplane Fan Control" },
{ ESM_S_FANRPM, 0, "Backplane Fan 1" },
{ ESM_S_FANRPM, 0, "Backplane Fan 2" },
{ ESM_S_FANRPM, 0, "Backplane Fan 3" },
{ ESM_S_SCSICONN, 0, "Backplane SCSI A Connected" },
{ ESM_S_VOLTS, 0, "Backplane SCSI A External" },
{ ESM_S_VOLTS, 0, "Backplane SCSI A Internal" },
{ ESM_S_SCSICONN, 0, "Backplane SCSI B Connected" },
{ ESM_S_VOLTS, 0, "Backplane SCSI B External" },
{ ESM_S_VOLTS, 0, "Backplane SCSI B Internal" },
{ ESM_S_DRIVES, 0, "Drive" },
{ ESM_S_DRIVES, 4, "Drive" },
{ ESM_S_DRIVE, 0, "Drive 0" },
{ ESM_S_DRIVE, 0, "Drive 1" },
{ ESM_S_DRIVE, 0, "Drive 2" },
{ ESM_S_DRIVE, 0, "Drive 3" },
{ ESM_S_DRIVE, 0, "Drive 4" },
{ ESM_S_DRIVE, 0, "Drive 5" },
{ ESM_S_DRIVE, 0, "Drive 6" },
{ ESM_S_DRIVE, 0, "Drive 7" },
{ ESM_S_UNKNOWN, 0, "Backplane Control 2" },
{ ESM_S_VOLTS, 0, "Backplane +3.3V" },
};
struct esm_sensor_map esm_sensors_powerunit[] = {
{ ESM_S_UNKNOWN, 0, "Power Unit" },
{ ESM_S_VOLTSx10, 0, "Power Supply 1 +5V" },
{ ESM_S_VOLTSx10, 0, "Power Supply 1 +12V" },
{ ESM_S_VOLTSx10, 0, "Power Supply 1 +3.3V" },
{ ESM_S_VOLTSx10, 0, "Power Supply 1 -5V" },
{ ESM_S_VOLTSx10, 0, "Power Supply 1 -12V" },
{ ESM_S_VOLTSx10, 0, "Power Supply 2 +5V" },
{ ESM_S_VOLTSx10, 0, "Power Supply 2 +12V" },
{ ESM_S_VOLTSx10, 0, "Power Supply 2 +3.3V" },
{ ESM_S_VOLTSx10, 0, "Power Supply 2 -5V" },
{ ESM_S_VOLTSx10, 0, "Power Supply 2 -12V" },
{ ESM_S_VOLTSx10, 0, "Power Supply 3 +5V" },
{ ESM_S_VOLTSx10, 0, "Power Supply 3 +12V" },
{ ESM_S_VOLTSx10, 0, "Power Supply 3 +3.3V" },
{ ESM_S_VOLTSx10, 0, "Power Supply 3 -5V" },
{ ESM_S_VOLTSx10, 0, "Power Supply 3 -12V" },
{ ESM_S_VOLTSx10, 0, "System Power Supply +5V" },
{ ESM_S_VOLTSx10, 0, "System Power Supply +12V" },
{ ESM_S_VOLTSx10, 0, "System Power Supply +3.3V" },
{ ESM_S_VOLTSx10, 0, "System Power Supply -5V" },
{ ESM_S_VOLTSx10, 0, "System Power Supply -12V" },
{ ESM_S_VOLTSx10, 0, "System Power Supply +5V aux" },
{ ESM_S_AMPS, 0, "Power Supply 1 +5V" },
{ ESM_S_AMPS, 0, "Power Supply 1 +12V" },
{ ESM_S_AMPS, 0, "Power Supply 1 +3.3V" },
{ ESM_S_AMPS, 0, "Power Supply 2 +5V" },
{ ESM_S_AMPS, 0, "Power Supply 2 +12V" },
{ ESM_S_AMPS, 0, "Power Supply 2 +3.3V" },
{ ESM_S_AMPS, 0, "Power Supply 3 +5V" },
{ ESM_S_AMPS, 0, "Power Supply 3 +12V" },
{ ESM_S_AMPS, 0, "Power Supply 3 +3.3V" },
{ ESM_S_FANRPM, 0, "Power Supply 1 Fan" },
{ ESM_S_FANRPM, 0, "Power Supply 2 Fan" },
{ ESM_S_FANRPM, 0, "Power Supply 3 Fan" },
{ ESM_S_PWRSUP, 0, "Power Supply 1" },
{ ESM_S_PWRSUP, 0, "Power Supply 2" },
{ ESM_S_PWRSUP, 0, "Power Supply 3" },
{ ESM_S_UNKNOWN, 0, "PSPB Fan Control" },
{ ESM_S_FANRPM, 0, "Fan 1" },
{ ESM_S_FANRPM, 0, "Fan 2" },
{ ESM_S_FANRPM, 0, "Fan 3" },
{ ESM_S_FANRPM, 0, "Fan 4" },
{ ESM_S_FANRPM, 0, "Fan 5" },
{ ESM_S_FANRPM, 0, "Fan 6" },
{ ESM_S_UNKNOWN, 0, "Fan Enclosure" },
};
void
esm_devmap(struct esm_softc *sc, struct esm_devmap *devmap)
{
struct esm_sensor_map *sensor_map;
const char *name = NULL, *fname = NULL;
int mapsize;
switch (devmap->dev_major) {
case ESM2_DEV_ESM2:
sensor_map = esm_sensors_esm2;
switch (devmap->dev_minor) {
case ESM2_DEV_ESM2_2300:
name = "PowerEdge 2300";
mapsize = 23;
break;
case ESM2_DEV_ESM2_4300:
name = "PowerEdge 4300";
mapsize = 27;
break;
case ESM2_DEV_ESM2_6300:
name = "PowerEdge 6300";
mapsize = 27;
break;
case ESM2_DEV_ESM2_6400:
name = "PowerEdge 6400";
mapsize = 44;
break;
case ESM2_DEV_ESM2_2550:
name = "PowerEdge 2550";
mapsize = 48;
break;
case ESM2_DEV_ESM2_4350:
name = "PowerEdge 4350";
mapsize = 27;
break;
case ESM2_DEV_ESM2_6350:
name = "PowerEdge 6350";
mapsize = 27;
break;
case ESM2_DEV_ESM2_6450:
name = "PowerEdge 6450";
mapsize = 44;
break;
case ESM2_DEV_ESM2_2400:
name = "PowerEdge 2400";
mapsize = 30;
break;
case ESM2_DEV_ESM2_4400:
name = "PowerEdge 4400";
mapsize = 44;
break;
case ESM2_DEV_ESM2_2500:
name = "PowerEdge 2500";
mapsize = 55;
break;
case ESM2_DEV_ESM2_2450:
name = "PowerEdge 2450";
mapsize = 27;
break;
case ESM2_DEV_ESM2_2400EX:
name = "PowerEdge 2400";
mapsize = 27;
break;
case ESM2_DEV_ESM2_2450EX:
name = "PowerEdge 2450";
mapsize = 44;
break;
default:
return;
}
fname = "Embedded Server Management";
break;
case ESM2_DEV_DRACII:
fname = "Dell Remote Assistance Card II";
break;
case ESM2_DEV_FRONT_PANEL:
fname = "Front Panel";
break;
case ESM2_DEV_BACKPLANE2:
sensor_map = esm_sensors_backplane;
mapsize = 22;
fname = "Primary System Backplane";
break;
case ESM2_DEV_POWERUNIT2:
sensor_map = esm_sensors_powerunit;
mapsize = sizeof(esm_sensors_powerunit) /
sizeof(esm_sensors_powerunit[0]);
fname = "Power Unit";
break;
case ESM2_DEV_ENCL2_BACKPLANE:
case ESM2_DEV_ENCL1_BACKPLANE:
fname = "Enclosure Backplane";
break;
case ESM2_DEV_ENCL2_POWERUNIT:
case ESM2_DEV_ENCL1_POWERUNIT:
fname = "Enclosure Powerunit";
break;
case ESM2_DEV_HPPCI: /* nfi what this is */
fname = "HPPCI";
break;
case ESM2_DEV_BACKPLANE3:
sensor_map = esm_sensors_backplane;
mapsize = sizeof(esm_sensors_backplane) /
sizeof(esm_sensors_backplane[0]);
fname = "Primary System Backplane";
break;
default:
return;
}
printf("%s: %s%s%s %d.%d\n", DEVNAME(sc),
name ? name : "", name ? " " : "", fname,
devmap->rev_major, devmap->rev_minor);
esm_make_sensors(sc, devmap, sensor_map, mapsize);
}
void
esm_make_sensors(struct esm_softc *sc, struct esm_devmap *devmap,
struct esm_sensor_map *sensor_map, int mapsize)
{
struct esm_smb_req req;
struct esm_smb_resp resp;
struct esm_smb_resp_val *val = &resp.resp_val;
struct esm_sensor *es;
struct ksensor *s;
int nsensors, i, j;
const char *psulabels[] = {
"AC", "SW", "OK", "ON", "FFAN", "OTMP"
};
memset(&req, 0, sizeof(req));
req.h_cmd = ESM2_CMD_SMB_XMIT_RECV;
req.h_dev = devmap->index;
req.h_txlen = sizeof(req.req_val);
req.h_rxlen = sizeof(resp.resp_val);
req.req_val.v_cmd = ESM2_SMB_SENSOR_VALUE;
for (i = 0; i < mapsize; i++) {
req.req_val.v_sensor = i;
if (esm_smb_cmd(sc, &req, &resp, 1, 0) != 0)
continue;
DPRINTFN(1, "%s: dev: 0x%02x sensor: %d (%s) "
"reading: 0x%04x status: 0x%02x cksum: 0x%02x\n",
DEVNAME(sc), devmap->index, i, sensor_map[i].name,
val->v_reading, val->v_status, val->v_checksum);
switch (sensor_map[i].type) {
case ESM_S_PWRSUP:
if (val->v_status == 0x00)
continue;
break;
default:
if (!(val->v_status & ESM2_VS_VALID))
continue;
break;
}
es = malloc(sizeof(struct esm_sensor), M_DEVBUF, M_NOWAIT);
if (es == NULL)
return;
memset(es, 0, sizeof(struct esm_sensor));
es->es_dev = devmap->index;
es->es_id = i;
es->es_type = sensor_map[i].type;
switch (es->es_type) {
case ESM_S_DRIVES:
/*
* this esm sensor represents 4 kernel sensors, so we
* go through these hoops to deal with it.
*/
nsensors = 4;
s = malloc(sizeof(struct ksensor) * nsensors, M_DEVBUF,
M_NOWAIT);
if (s == NULL) {
free(es, M_DEVBUF);
return;
}
memset(s, 0, sizeof(struct ksensor) * nsensors);
for (j = 0; j < nsensors; j++) {
snprintf(s[j].desc, sizeof(s[j].desc), "%s %d",
sensor_map[i].name, sensor_map[i].arg + j);
}
break;
case ESM_S_PWRSUP:
/*
* the esm pwrsup sensor has a bitfield for its value,
* this expands it out to 6 separate indicators
*/
nsensors = 6;
s = malloc(sizeof(struct ksensor) * nsensors, M_DEVBUF,
M_NOWAIT);
if (s == NULL) {
free(es, M_DEVBUF);
return;
}
memset(s, 0, sizeof(struct ksensor) * nsensors);
for (j = 0; j < nsensors; j++) {
snprintf(s[j].desc, sizeof(s[j].desc), "%s %s",
sensor_map[i].name, psulabels[j]);
}
break;
case ESM_S_TEMP:
case ESM_S_FANRPM:
case ESM_S_AMPS:
case ESM_S_VOLTS:
case ESM_S_VOLTSx10:
if (esm_thresholds(sc, devmap, es) != 0) {
free(es, M_DEVBUF);
continue;
}
/* FALLTHROUGH */
default:
nsensors = 1;
s = malloc(sizeof(struct ksensor), M_DEVBUF, M_NOWAIT);
if (s == NULL) {
free(es, M_DEVBUF);
return;
}
memset(s, 0, sizeof(struct ksensor));
strlcpy(s->desc, sensor_map[i].name, sizeof(s->desc));
break;
}
for (j = 0; j < nsensors; j++) {
s[j].type = esm_typemap[es->es_type];
sensor_attach(&sc->sc_sensordev, &s[j]);
}
es->es_sensor = s;
TAILQ_INSERT_TAIL(&sc->sc_sensors, es, es_entry);
}
}
int
esm_thresholds(struct esm_softc *sc, struct esm_devmap *devmap,
struct esm_sensor *es)
{
struct esm_smb_req req;
struct esm_smb_resp resp;
struct esm_smb_resp_thr *thr = &resp.resp_thr;
memset(&req, 0, sizeof(req));
req.h_cmd = ESM2_CMD_SMB_XMIT_RECV;
req.h_dev = devmap->index;
req.h_txlen = sizeof(req.req_thr);
req.h_rxlen = sizeof(resp.resp_thr);
req.req_thr.t_cmd = ESM2_SMB_SENSOR_THRESHOLDS;
req.req_thr.t_sensor = es->es_id;
if (esm_smb_cmd(sc, &req, &resp, 1, 0) != 0)
return (1);
DPRINTFN(2, "%s: dev: %d sensor: %d lo fail: %d hi fail: %d "
"lo warn: %d hi warn: %d hysterisis: %d checksum: 0x%02x\n",
DEVNAME(sc), devmap->index, es->es_id, thr->t_lo_fail,
thr->t_hi_fail, thr->t_lo_warn, thr->t_hi_warn, thr->t_hysterisis,
thr->t_checksum);
es->es_thresholds.th_lo_crit = thr->t_lo_fail;
es->es_thresholds.th_lo_warn = thr->t_lo_warn;
es->es_thresholds.th_hi_warn = thr->t_hi_warn;
es->es_thresholds.th_hi_crit = thr->t_hi_fail;
return (0);
}
int
esm_bmc_ready(struct esm_softc *sc, int port, u_int8_t mask, u_int8_t val,
int wait)
{
unsigned int count = wait ? 0 : 0xfffff;
do {
if ((EREAD(sc, port) & mask) == val)
return (0);
} while (count++ < 0xfffff);
return (1);
}
int
esm_cmd(struct esm_softc *sc, void *cmd, size_t cmdlen, void *resp,
size_t resplen, int wait, int step)
{
u_int8_t *tx = (u_int8_t *)cmd;
u_int8_t *rx = (u_int8_t *)resp;
int i;
switch (step) {
case 0:
case 1:
/* Wait for card ready */
if (esm_bmc_ready(sc, ESM2_CTRL_REG, ESM2_TC_READY,
0, wait) != 0)
return (1); /* busy */
/* Write command data to port */
ECTRLWR(sc, ESM2_TC_CLR_WPTR);
for (i = 0; i < cmdlen; i++) {
DPRINTFN(2, "write: %.2x\n", *tx);
EDATAWR(sc, *tx);
tx++;
}
/* Ring doorbell... */
ECTRLWR(sc, ESM2_TC_H2ECDB);
/* FALLTHROUGH */
case 2:
/* ...and wait */
if (esm_bmc_ready(sc, ESM2_CTRL_REG, ESM2_TC_EC2HDB,
ESM2_TC_EC2HDB, wait) != 0)
return (2);
/* Set host busy semaphore and clear doorbell */
ECTRLWR(sc, ESM2_TC_HOSTBUSY);
ECTRLWR(sc, ESM2_TC_EC2HDB);
/* Read response data from port */
ECTRLWR(sc, ESM2_TC_CLR_RPTR);
for (i = 0; i < resplen; i++) {
*rx = EDATARD(sc);
DPRINTFN(2, "read = %.2x\n", *rx);
rx++;
}
/* release semaphore */
ECTRLWR(sc, ESM2_TC_HOSTBUSY);
break;
}
return (0);
}
int
esm_smb_cmd(struct esm_softc *sc, struct esm_smb_req *req,
struct esm_smb_resp *resp, int wait, int step)
{
int err;
memset(resp, 0, sizeof(struct esm_smb_resp));
err = esm_cmd(sc, req, sizeof(req->hdr) + req->h_txlen, resp,
sizeof(resp->hdr) + req->h_rxlen, wait, step);
if (err)
return (err);
if (resp->h_status != 0 || resp->h_i2csts != 0) {
DPRINTFN(3, "%s: dev: 0x%02x error status: 0x%02x "
"i2csts: 0x%02x procsts: 0x%02x tx: 0x%02x rx: 0x%02x\n",
__func__, req->h_dev, resp->h_status, resp->h_i2csts,
resp->h_procsts, resp->h_rx, resp->h_tx);
return (1);
}
return (0);
}
int64_t
esm_val2temp(u_int16_t value)
{
return (((int64_t)value * 100000) + 273150000);
}
int64_t
esm_val2volts(u_int16_t value)
{
return ((int64_t)value * 1000);
}
int64_t
esm_val2amps(u_int16_t value)
{
return ((int64_t)value * 100000);
}