#include <sys/param.h>
#include <sys/systm.h>
#include <sys/device.h>
#include <sys/sensors.h>
#include <machine/bus.h>
#include <dev/isa/isavar.h>
#include <dev/isa/itvar.h>
#if defined(ITDEBUG)
#define DPRINTF(x) do { printf x; } while (0)
#else
#define DPRINTF(x)
#endif
int it_match(struct device *, void *, void *);
void it_attach(struct device *, struct device *, void *);
int it_activate(struct device *, int);
u_int8_t it_readreg(bus_space_tag_t, bus_space_handle_t, int);
void it_writereg(bus_space_tag_t, bus_space_handle_t, int, u_int8_t);
void it_enter(bus_space_tag_t, bus_space_handle_t, int);
void it_exit(bus_space_tag_t, bus_space_handle_t);
u_int8_t it_ec_readreg(struct it_softc *, int);
void it_ec_writereg(struct it_softc *, int, u_int8_t);
void it_ec_refresh(void *arg);
int it_wdog_cb(void *, int);
#define RFACT_NONE 10000
#define RFACT(x, y) (RFACT_NONE * ((x) + (y)) / (y))
const struct {
enum sensor_type type;
const char *desc;
} it_sensors[IT_EC_NUMSENSORS] = {
#define IT_TEMP_BASE 0
#define IT_TEMP_COUNT 3
{ SENSOR_TEMP, NULL },
{ SENSOR_TEMP, NULL },
{ SENSOR_TEMP, NULL },
#define IT_FAN_BASE 3
#define IT_FAN_COUNT 5
{ SENSOR_FANRPM, NULL },
{ SENSOR_FANRPM, NULL },
{ SENSOR_FANRPM, NULL },
{ SENSOR_FANRPM, NULL },
{ SENSOR_FANRPM, NULL },
#define IT_VOLT_BASE 8
#define IT_VOLT_COUNT 9
{ SENSOR_VOLTS_DC, "VCORE_A" },
{ SENSOR_VOLTS_DC, "VCORE_B" },
{ SENSOR_VOLTS_DC, "+3.3V" },
{ SENSOR_VOLTS_DC, "+5V" },
{ SENSOR_VOLTS_DC, "+12V" },
{ SENSOR_VOLTS_DC, "-12V" },
{ SENSOR_VOLTS_DC, "-5V" },
{ SENSOR_VOLTS_DC, "+5VSB" },
{ SENSOR_VOLTS_DC, "VBAT" }
};
const int it_vrfact[IT_VOLT_COUNT] = {
RFACT_NONE,
RFACT_NONE,
RFACT_NONE,
RFACT(68, 100),
RFACT(30, 10),
RFACT(83, 20),
RFACT(21, 10),
RFACT(68, 100),
RFACT_NONE
};
const int it_fan_regs[] = {
IT_EC_FAN_TAC1, IT_EC_FAN_TAC2, IT_EC_FAN_TAC3,
IT_EC_FAN_TAC4_LSB, IT_EC_FAN_TAC5_LSB
};
const int it_fan_ext_regs[] = {
IT_EC_FAN_EXT_TAC1, IT_EC_FAN_EXT_TAC2, IT_EC_FAN_EXT_TAC3,
IT_EC_FAN_TAC4_MSB, IT_EC_FAN_TAC5_MSB
};
LIST_HEAD(, it_softc) it_softc_list = LIST_HEAD_INITIALIZER(it_softc_list);
int
it_match(struct device *parent, void *match, void *aux)
{
struct isa_attach_args *ia = aux;
struct it_softc *sc;
bus_space_handle_t ioh;
int ec_iobase, found = 0;
u_int16_t cr;
if (ia->ipa_io[0].base != IO_IT1 && ia->ipa_io[0].base != IO_IT2)
return (0);
if (bus_space_map(ia->ia_iot, ia->ipa_io[0].base, 2, 0, &ioh) != 0) {
DPRINTF(("it_match: can't map i/o space"));
return (0);
}
it_enter(ia->ia_iot, ioh, ia->ipa_io[0].base);
bus_space_write_1(ia->ia_iot, ioh, IT_IO_ADDR, 0xaa);
cr = it_readreg(ia->ia_iot, ioh, IT_CHIPID1) << 8;
cr |= it_readreg(ia->ia_iot, ioh, IT_CHIPID2);
switch (cr) {
case IT_ID_8705:
case IT_ID_8712:
case IT_ID_8716:
case IT_ID_8718:
case IT_ID_8720:
case IT_ID_8721:
case IT_ID_8726:
case IT_ID_8728:
case IT_ID_8772:
it_writereg(ia->ia_iot, ioh, IT_LDN, IT_EC_LDN);
ec_iobase = it_readreg(ia->ia_iot, ioh, IT_EC_MSB) << 8;
ec_iobase |= it_readreg(ia->ia_iot, ioh, IT_EC_LSB);
LIST_FOREACH(sc, &it_softc_list, sc_list)
if (sc->sc_ec_iobase == ec_iobase)
break;
if (sc == NULL) {
ia->ipa_nio = 1;
ia->ipa_io[0].length = 2;
ia->ipa_nmem = ia->ipa_nirq = ia->ipa_ndrq = 0;
found++;
}
break;
}
it_exit(ia->ia_iot, ioh);
bus_space_unmap(ia->ia_iot, ioh, 2);
return (found);
}
void
it_attach(struct device *parent, struct device *self, void *aux)
{
struct it_softc *sc = (void *)self;
struct isa_attach_args *ia = aux;
int i;
sc->sc_iot = ia->ia_iot;
sc->sc_iobase = ia->ipa_io[0].base;
if (bus_space_map(sc->sc_iot, sc->sc_iobase, 2, 0, &sc->sc_ioh) != 0) {
printf(": can't map i/o space\n");
return;
}
it_enter(sc->sc_iot, sc->sc_ioh, sc->sc_iobase);
sc->sc_chipid = it_readreg(sc->sc_iot, sc->sc_ioh, IT_CHIPID1) << 8;
sc->sc_chipid |= it_readreg(sc->sc_iot, sc->sc_ioh, IT_CHIPID2);
sc->sc_chiprev = it_readreg(sc->sc_iot, sc->sc_ioh, IT_CHIPREV) & 0x0f;
it_writereg(sc->sc_iot, sc->sc_ioh, IT_LDN, IT_EC_LDN);
sc->sc_ec_iobase = it_readreg(sc->sc_iot, sc->sc_ioh, IT_EC_MSB) << 8;
sc->sc_ec_iobase |= it_readreg(sc->sc_iot, sc->sc_ioh, IT_EC_LSB);
if (sc->sc_chipid != IT_ID_8705) {
it_writereg(sc->sc_iot, sc->sc_ioh, IT_LDN, IT_WDT_LDN);
it_writereg(sc->sc_iot, sc->sc_ioh, IT_WDT_CSR, 0x00);
it_writereg(sc->sc_iot, sc->sc_ioh, IT_WDT_TCR, 0x00);
wdog_register(it_wdog_cb, sc);
}
it_exit(sc->sc_iot, sc->sc_ioh);
LIST_INSERT_HEAD(&it_softc_list, sc, sc_list);
printf(": IT%xF rev %X", sc->sc_chipid, sc->sc_chiprev);
if (sc->sc_ec_iobase == 0) {
printf(", EC disabled\n");
return;
}
printf(", EC port 0x%x\n", sc->sc_ec_iobase);
sc->sc_ec_iot = ia->ia_iot;
if (bus_space_map(sc->sc_ec_iot, sc->sc_ec_iobase, 8, 0,
&sc->sc_ec_ioh) != 0) {
printf("%s: can't map EC i/o space\n", sc->sc_dev.dv_xname);
return;
}
for (i = 0; i < IT_EC_NUMSENSORS; i++) {
sc->sc_sensors[i].type = it_sensors[i].type;
if (it_sensors[i].desc != NULL)
strlcpy(sc->sc_sensors[i].desc, it_sensors[i].desc,
sizeof(sc->sc_sensors[i].desc));
}
if (sensor_task_register(sc, it_ec_refresh, IT_EC_INTERVAL) == NULL) {
printf("%s: unable to register update task\n",
sc->sc_dev.dv_xname);
bus_space_unmap(sc->sc_ec_iot, sc->sc_ec_ioh, 8);
return;
}
if (sc->sc_chipid != IT_ID_8705 && sc->sc_chipid != IT_ID_8712)
it_ec_writereg(sc, IT_EC_FAN_ECER,
it_ec_readreg(sc, IT_EC_FAN_ECER) | 0x07);
it_ec_writereg(sc, IT_EC_CFG,
it_ec_readreg(sc, IT_EC_CFG) | IT_EC_CFG_START | IT_EC_CFG_INTCLR);
strlcpy(sc->sc_sensordev.xname, sc->sc_dev.dv_xname,
sizeof(sc->sc_sensordev.xname));
for (i = 0; i < IT_EC_NUMSENSORS; i++)
sensor_attach(&sc->sc_sensordev, &sc->sc_sensors[i]);
sensordev_install(&sc->sc_sensordev);
}
int
it_activate(struct device *self, int act)
{
switch (act) {
case DVACT_POWERDOWN:
wdog_shutdown(self);
break;
}
return (0);
}
u_int8_t
it_readreg(bus_space_tag_t iot, bus_space_handle_t ioh, int r)
{
bus_space_write_1(iot, ioh, IT_IO_ADDR, r);
return (bus_space_read_1(iot, ioh, IT_IO_DATA));
}
void
it_writereg(bus_space_tag_t iot, bus_space_handle_t ioh, int r, u_int8_t v)
{
bus_space_write_1(iot, ioh, IT_IO_ADDR, r);
bus_space_write_1(iot, ioh, IT_IO_DATA, v);
}
void
it_enter(bus_space_tag_t iot, bus_space_handle_t ioh, int iobase)
{
bus_space_write_1(iot, ioh, IT_IO_ADDR, 0x87);
bus_space_write_1(iot, ioh, IT_IO_ADDR, 0x01);
bus_space_write_1(iot, ioh, IT_IO_ADDR, 0x55);
if (iobase == IO_IT1)
bus_space_write_1(iot, ioh, IT_IO_ADDR, 0x55);
else
bus_space_write_1(iot, ioh, IT_IO_ADDR, 0xaa);
}
void
it_exit(bus_space_tag_t iot, bus_space_handle_t ioh)
{
bus_space_write_1(iot, ioh, IT_IO_ADDR, IT_CCR);
bus_space_write_1(iot, ioh, IT_IO_DATA, 0x02);
}
u_int8_t
it_ec_readreg(struct it_softc *sc, int r)
{
bus_space_write_1(sc->sc_ec_iot, sc->sc_ec_ioh, IT_EC_ADDR, r);
return (bus_space_read_1(sc->sc_ec_iot, sc->sc_ec_ioh, IT_EC_DATA));
}
void
it_ec_writereg(struct it_softc *sc, int r, u_int8_t v)
{
bus_space_write_1(sc->sc_ec_iot, sc->sc_ec_ioh, IT_EC_ADDR, r);
bus_space_write_1(sc->sc_ec_iot, sc->sc_ec_ioh, IT_EC_DATA, v);
}
void
it_ec_refresh(void *arg)
{
struct it_softc *sc = arg;
int i, sdata, divisor, odivisor, ndivisor;
u_int8_t cr, ecr;
cr = it_ec_readreg(sc, IT_EC_ADC_TEMPER);
for (i = 0; i < IT_TEMP_COUNT; i++) {
sc->sc_sensors[IT_TEMP_BASE + i].flags &=
SENSOR_FINVALID;
if (!(cr & (1 << i)) && !(cr & (1 << (i + 3)))) {
sc->sc_sensors[IT_TEMP_BASE + i].flags |=
SENSOR_FINVALID;
continue;
}
sdata = it_ec_readreg(sc, IT_EC_TEMPBASE + i);
sc->sc_sensors[IT_TEMP_BASE + i].value =
sdata * 1000000 + 273150000;
}
cr = it_ec_readreg(sc, IT_EC_ADC_VINER);
for (i = 0; i < IT_VOLT_COUNT; i++) {
sc->sc_sensors[IT_VOLT_BASE + i].flags &=
SENSOR_FINVALID;
if ((i < 8) && !(cr & (1 << i))) {
sc->sc_sensors[IT_VOLT_BASE + i].flags |=
SENSOR_FINVALID;
continue;
}
sdata = it_ec_readreg(sc, IT_EC_VOLTBASE + i);
sc->sc_sensors[IT_VOLT_BASE + i].value = sdata << 4;
if (i == 5 || i == 6)
sc->sc_sensors[IT_VOLT_BASE + i].value -= IT_EC_VREF;
sc->sc_sensors[IT_VOLT_BASE + i].value *= it_vrfact[i];
sc->sc_sensors[IT_VOLT_BASE + i].value /= 10;
if (i == 5 || i == 6)
sc->sc_sensors[IT_VOLT_BASE + i].value +=
IT_EC_VREF * 1000;
}
cr = it_ec_readreg(sc, IT_EC_FAN_MCR);
if (sc->sc_chipid != IT_ID_8705 && sc->sc_chipid != IT_ID_8712) {
ecr = it_ec_readreg(sc, IT_EC_FAN_ECER);
for (i = 0; i < IT_FAN_COUNT; i++) {
sc->sc_sensors[IT_FAN_BASE + i].flags &=
~SENSOR_FINVALID;
if (i < 3 && !(cr & (1 << (i + 4)))) {
sc->sc_sensors[IT_FAN_BASE + i].flags |=
SENSOR_FINVALID;
continue;
} else if (i > 2 && !(ecr & (1 << (i + 1)))) {
sc->sc_sensors[IT_FAN_BASE + i].flags |=
SENSOR_FINVALID;
continue;
}
sdata = it_ec_readreg(sc, it_fan_regs[i]);
sdata |= it_ec_readreg(sc, it_fan_ext_regs[i]) << 8;
if (sdata == 0 || sdata == 0xffff)
sc->sc_sensors[IT_FAN_BASE + i].value = 0;
else
sc->sc_sensors[IT_FAN_BASE + i].value =
675000 / sdata;
}
} else {
odivisor = ndivisor = divisor =
it_ec_readreg(sc, IT_EC_FAN_DIV);
for (i = 0; i < IT_FAN_COUNT; i++) {
if (i > 2 || !(cr & (1 << (i + 4)))) {
sc->sc_sensors[IT_FAN_BASE + i].flags |=
SENSOR_FINVALID;
continue;
}
sc->sc_sensors[IT_FAN_BASE + i].flags &=
~SENSOR_FINVALID;
sdata = it_ec_readreg(sc, it_fan_regs[i]);
if (sdata == 0xff) {
sc->sc_sensors[IT_FAN_BASE + i].value = 0;
if (i == 2)
ndivisor ^= 0x40;
else {
ndivisor &= ~(7 << (i * 3));
ndivisor |= ((divisor + 1) & 7) <<
(i * 3);
}
} else if (sdata != 0) {
if (i == 2)
divisor = divisor & 1 ? 3 : 1;
sc->sc_sensors[IT_FAN_BASE + i].value =
1350000 / (sdata << (divisor & 7));
} else
sc->sc_sensors[IT_FAN_BASE + i].value = 0;
}
if (ndivisor != odivisor)
it_ec_writereg(sc, IT_EC_FAN_DIV, ndivisor);
}
}
int
it_wdog_cb(void *arg, int period)
{
struct it_softc *sc = arg;
int minutes = 0;
it_enter(sc->sc_iot, sc->sc_ioh, sc->sc_iobase);
it_writereg(sc->sc_iot, sc->sc_ioh, IT_LDN, IT_WDT_LDN);
it_writereg(sc->sc_iot, sc->sc_ioh, IT_WDT_TCR, 0x00);
if (period > 1000)
period = 1000;
else if (period < 0)
period = 0;
if (period > 0) {
if (sc->sc_chipid == IT_ID_8712 && sc->sc_chiprev < 0x8 &&
period > 0xff) {
if (period % 60 >= 30)
period += 60;
period /= 60;
minutes++;
}
it_writereg(sc->sc_iot, sc->sc_ioh, IT_WDT_TMO_LSB,
period & 0xff);
if (minutes) {
it_writereg(sc->sc_iot, sc->sc_ioh, IT_WDT_TCR,
IT_WDT_TCR_KRST | IT_WDT_TCR_PWROK);
period *= 60;
} else {
it_writereg(sc->sc_iot, sc->sc_ioh, IT_WDT_TMO_MSB,
period >> 8);
it_writereg(sc->sc_iot, sc->sc_ioh, IT_WDT_TCR,
IT_WDT_TCR_SECS | IT_WDT_TCR_KRST |
IT_WDT_TCR_PWROK);
}
}
it_exit(sc->sc_iot, sc->sc_ioh);
return (period);
}
const struct cfattach it_ca = {
sizeof(struct it_softc),
it_match,
it_attach,
NULL,
it_activate
};
struct cfdriver it_cd = {
NULL, "it", DV_DULL
};