#include <sys/param.h>
#include <sys/systm.h>
#include <sys/device.h>
#include <sys/sensors.h>
#include <dev/i2c/i2cvar.h>
#define ASC7621_PECI 0x40
#define ASC7621_PECI_MASK 0x10
#define ASC7621_LEGACY 0x36
#define ASC7621_LEGACY_MASK 0x10
#define ASC7621_TEMP1H 0x25
#define ASC7621_TEMP1L 0x10
#define ASC7621_TEMP2H 0x26
#define ASC7621_TEMP2L 0x15
#define ASC7621_TEMP3H 0x27
#define ASC7621_TEMP3L 0x16
#define ASC7621_TEMP4H 0x33
#define ASC7621_TEMP4L 0x17
#define ASC7621_TEMP_NA 0x80
#define ASC7621_IN1_VH 0x20
#define ASC7621_IN1_VL 0x13
#define ASC7621_IN2_VH 0x21
#define ASC7621_IN2_VL 0x18
#define ASC7621_IN3_VH 0x22
#define ASC7621_IN3_VL 0x11
#define ASC7621_IN4_VH 0x23
#define ASC7621_IN4_VL 0x12
#define ASC7621_IN5_VH 0x24
#define ASC7621_IN5_VL 0x14
#define ASC7621_TACH1H 0x29
#define ASC7621_TACH1L 0x28
#define ASC7621_TACH2H 0x2b
#define ASC7621_TACH2L 0x2a
#define ASC7621_TACH3H 0x2d
#define ASC7621_TACH3L 0x2c
#define ASC7621_TACH4H 0x2f
#define ASC7621_TACH4L 0x2e
#define ADL_TEMP1 0
#define ADL_TEMP2 1
#define ADL_TEMP3 2
#define ADL_TEMP4 3
#define ADL_IN1_V 4
#define ADL_IN2_V 5
#define ADL_IN3_V 6
#define ADL_IN4_V 7
#define ADL_IN5_V 8
#define ADL_TACH1 9
#define ADL_TACH2 10
#define ADL_TACH3 11
#define ADL_TACH4 12
#define ADL_NUM_SENSORS 13
struct {
char sensor;
u_int8_t hreg;
u_int8_t lreg;
char *name;
u_short mVscale;
u_short tempscale;
} adl_worklist[] = {
{ ADL_TEMP1, ASC7621_TEMP1H, ASC7621_TEMP1L, "CPU", 0, 1 },
{ ADL_TEMP2, ASC7621_TEMP2H, ASC7621_TEMP2L, "CPU", 0, 1 },
{ ADL_TEMP3, ASC7621_TEMP3H, ASC7621_TEMP3L, "Internal", 0, 1 },
{ ADL_TEMP4, ASC7621_TEMP4H, ASC7621_TEMP4L, "External", 0, 1 },
{ ADL_IN1_V, ASC7621_IN1_VH, ASC7621_IN1_VL, "+1.5V", 2500, 0 },
{ ADL_IN2_V, ASC7621_IN2_VH, ASC7621_IN2_VL, "Vccp", 2250, 0 },
{ ADL_IN3_V, ASC7621_IN3_VH, ASC7621_IN3_VL, "+3.3V", 3300, 0 },
{ ADL_IN4_V, ASC7621_IN4_VH, ASC7621_IN4_VL, "+5V", 5000, 0 },
{ ADL_IN5_V, ASC7621_IN5_VH, ASC7621_IN5_VL, "+12V", 12000, 0 },
{ ADL_TACH1, ASC7621_TACH1L, ASC7621_TACH1H, "", 0, 0 },
{ ADL_TACH2, ASC7621_TACH2L, ASC7621_TACH2H, "", 0, 0 },
{ ADL_TACH3, ASC7621_TACH3L, ASC7621_TACH3H, "", 0, 0 },
{ ADL_TACH4, ASC7621_TACH4L, ASC7621_TACH4H, "", 0, 0 }
};
struct adl_softc {
struct device sc_dev;
i2c_tag_t sc_tag;
i2c_addr_t sc_addr;
u_int8_t sc_conf;
struct ksensor sc_sensor[ADL_NUM_SENSORS];
struct ksensordev sc_sensordev;
};
#if 0
static int peci_enabled;
static int legacy_mode;
#endif
int adl_match(struct device *, void *, void *);
void adl_attach(struct device *, struct device *, void *);
void adl_refresh(void *);
const struct cfattach adl_ca = {
sizeof(struct adl_softc), adl_match, adl_attach
};
struct cfdriver adl_cd = {
NULL, "adl", DV_DULL
};
int
adl_match(struct device *parent, void *match, void *aux)
{
struct i2c_attach_args *ia = aux;
if (strcmp(ia->ia_name, "asc7621") == 0)
return (1);
return (0);
}
void
adl_attach(struct device *parent, struct device *self, void *aux)
{
struct adl_softc *sc = (struct adl_softc *)self;
struct i2c_attach_args *ia = aux;
u_int8_t cmd, data;
int i;
sc->sc_tag = ia->ia_tag;
sc->sc_addr = ia->ia_addr;
printf(": %s", ia->ia_name);
strlcpy(sc->sc_sensordev.xname, sc->sc_dev.dv_xname,
sizeof(sc->sc_sensordev.xname));
cmd = ASC7621_PECI;
(void)iic_exec(sc->sc_tag, I2C_OP_READ_WITH_STOP, sc->sc_addr,
&cmd, sizeof(cmd), &data, sizeof(data), 0);
if (data & ASC7621_PECI_MASK)
printf(", PECI enabled\n");
#if 0
cmd = ASC7621_LEGACY;
if (iic_exec(sc->sc_tag, I2C_OP_READ_WITH_STOP, sc->sc_addr,
&cmd, sizeof(cmd), &data, sizeof(data), 0)) {
printf(", unable to read PECI configuration register");
}
if (data & ASC7621_LEGACY_MASK)
legacy_mode = 1;
#endif
if (sensor_task_register(sc, adl_refresh, 5) == NULL) {
printf(", unable to register update task\n");
return;
}
for (i = 0; i < ADL_NUM_SENSORS; i++) {
if (adl_worklist[i].tempscale)
sc->sc_sensor[i].type = SENSOR_TEMP;
else if (adl_worklist[i].mVscale)
sc->sc_sensor[i].type = SENSOR_VOLTS_DC;
else
sc->sc_sensor[i].type = SENSOR_FANRPM;
strlcpy(sc->sc_sensor[i].desc, adl_worklist[i].name,
sizeof(sc->sc_sensor[i].desc));
sensor_attach(&sc->sc_sensordev, &sc->sc_sensor[i]);
}
sensordev_install(&sc->sc_sensordev);
printf("\n");
}
void
adl_refresh(void *arg)
{
struct adl_softc *sc = arg;
int64_t temp, volt;
u_int8_t hdata, ldata, hreg, lreg;
u_int16_t fan;
int i;
iic_acquire_bus(sc->sc_tag, 0);
for (i = 0; i < sizeof adl_worklist / sizeof(adl_worklist[0]); i++) {
hreg = adl_worklist[i].hreg;
if (iic_exec(sc->sc_tag, I2C_OP_READ_WITH_STOP,
sc->sc_addr, &hreg, sizeof hreg, &hdata, sizeof hdata, 0)) {
sc->sc_sensor[i].flags |= SENSOR_FINVALID;
continue;
}
lreg = adl_worklist[i].lreg;
if (iic_exec(sc->sc_tag, I2C_OP_READ_WITH_STOP,
sc->sc_addr, &lreg, sizeof lreg, &ldata, sizeof ldata, 0)) {
sc->sc_sensor[i].flags |= SENSOR_FINVALID;
continue;
}
sc->sc_sensor[i].flags &= ~SENSOR_FINVALID;
if (adl_worklist[i].tempscale) {
if (hdata == ASC7621_TEMP_NA)
sc->sc_sensor[i].flags |= SENSOR_FINVALID;
else {
temp = ((hdata << 8 | ldata)) >> (16 - 10);
temp = temp * 250000 + 273150000;
sc->sc_sensor[i].value = temp;
}
} else if (adl_worklist[i].mVscale) {
volt = ((hdata << 8 | ldata)) >> (16 - 10);
volt = volt * adl_worklist[i].mVscale / (192 << 2);
sc->sc_sensor[i].value = volt * 1000;
} else {
fan = hdata + (ldata << 8);
if (fan == 0 || fan == 0xffff)
sc->sc_sensor[i].flags |= SENSOR_FINVALID;
else
sc->sc_sensor[i].value = (90000 * 60) / fan;
}
}
iic_release_bus(sc->sc_tag, 0);
}