/* * Copyright (c) 2003 Julien Bordet * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * * $OpenBSD: it.c,v 1.22 2007/03/22 16:55:31 deraadt Exp $ * $DragonFly: src/sys/dev/powermng/it/it.c,v 1.1 2007/10/02 13:37:38 hasso Exp $ */ #include #include #include #include #include #include #include #include #include #include "itvar.h" #if defined(ITDEBUG) #define DPRINTF(x) do { printf x; } while (0) #else #define DPRINTF(x) #endif /* * IT87-compatible chips can typically measure voltages up to 4.096 V. * To measure higher voltages the input is attenuated with (external) * resistors. Negative voltages are measured using a reference * voltage. So we have to convert the sensor values back to real * voltages by applying the appropriate resistor factor. */ #define RFACT_NONE 10000 #define RFACT(x, y) (RFACT_NONE * ((x) + (y)) / (y)) int it_probe(struct device *); int it_attach(struct device *); int it_detach(struct device *); u_int8_t it_readreg(struct it_softc *, int); void it_writereg(struct it_softc *, int, int); void it_setup_volt(struct it_softc *, int, int); void it_setup_temp(struct it_softc *, int, int); void it_setup_fan(struct it_softc *, int, int); void it_generic_stemp(struct it_softc *, struct ksensor *); void it_generic_svolt(struct it_softc *, struct ksensor *); void it_generic_fanrpm(struct it_softc *, struct ksensor *); void it_refresh_sensor_data(struct it_softc *); void it_refresh(void *); extern struct cfdriver it_cd; static device_method_t it_methods[] = { /* Methods from the device interface */ DEVMETHOD(device_probe, it_probe), DEVMETHOD(device_attach, it_attach), DEVMETHOD(device_detach, it_detach), /* Terminate method list */ { 0, 0 } }; static driver_t it_driver = { "it", it_methods, sizeof (struct it_softc) }; static devclass_t it_devclass; DRIVER_MODULE(it, isa, it_driver, it_devclass, NULL, NULL); const int it_vrfact[] = { RFACT_NONE, RFACT_NONE, RFACT_NONE, RFACT(68, 100), RFACT(30, 10), RFACT(21, 10), RFACT(83, 20), RFACT(68, 100), RFACT_NONE }; int it_probe(struct device *dev) { struct resource *iores; int iorid = 0; bus_space_tag_t iot; bus_space_handle_t ioh; u_int8_t cr; iores = bus_alloc_resource(dev, SYS_RES_IOPORT, &iorid, 0ul, ~0ul, 8, RF_ACTIVE); if (iores == NULL) { DPRINTF(("%s: can't map i/o space\n", __func__)); return 1; } iot = rman_get_bustag(iores); ioh = rman_get_bushandle(iores); /* Check Vendor ID */ bus_space_write_1(iot, ioh, ITC_ADDR, ITD_CHIPID); cr = bus_space_read_1(iot, ioh, ITC_DATA); bus_release_resource(dev, SYS_RES_IOPORT, iorid, iores); DPRINTF(("it: vendor id 0x%x\n", cr)); if (cr != IT_ID_IT87) return 1; return 0; } int it_attach(struct device *dev) { struct it_softc *sc = device_get_softc(dev); int i; u_int8_t cr; sc->sc_dev = dev; sc->sc_iores = bus_alloc_resource(dev, SYS_RES_IOPORT, &sc->sc_iorid, 0ul, ~0ul, 8, RF_ACTIVE); if (sc->sc_iores == NULL) { device_printf(dev, "can't map i/o space\n"); return 1; } sc->sc_iot = rman_get_bustag(sc->sc_iores); sc->sc_ioh = rman_get_bushandle(sc->sc_iores); sc->numsensors = IT_NUM_SENSORS; it_setup_fan(sc, 0, 3); it_setup_volt(sc, 3, 9); it_setup_temp(sc, 12, 3); if (sensor_task_register(sc, it_refresh, 5)) { device_printf(sc->sc_dev, "unable to register update task\n"); return 1; } /* Activate monitoring */ cr = it_readreg(sc, ITD_CONFIG); cr |= 0x01 | 0x08; it_writereg(sc, ITD_CONFIG, cr); /* Initialize sensors */ strlcpy(sc->sensordev.xname, device_get_nameunit(sc->sc_dev), sizeof(sc->sensordev.xname)); for (i = 0; i < sc->numsensors; ++i) sensor_attach(&sc->sensordev, &sc->sensors[i]); sensordev_install(&sc->sensordev); return 0; } int it_detach(struct device *dev) { struct it_softc *sc = device_get_softc(dev); int error; sensordev_deinstall(&sc->sensordev); sensor_task_unregister(sc); error = bus_release_resource(dev, SYS_RES_IOPORT, sc->sc_iorid, sc->sc_iores); if (error) return error; return 0; } u_int8_t it_readreg(struct it_softc *sc, int reg) { bus_space_write_1(sc->sc_iot, sc->sc_ioh, ITC_ADDR, reg); return (bus_space_read_1(sc->sc_iot, sc->sc_ioh, ITC_DATA)); } void it_writereg(struct it_softc *sc, int reg, int val) { bus_space_write_1(sc->sc_iot, sc->sc_ioh, ITC_ADDR, reg); bus_space_write_1(sc->sc_iot, sc->sc_ioh, ITC_DATA, val); } void it_setup_volt(struct it_softc *sc, int start, int n) { int i; for (i = 0; i < n; ++i) { sc->sensors[start + i].type = SENSOR_VOLTS_DC; } ksnprintf(sc->sensors[start + 0].desc, sizeof(sc->sensors[0].desc), "VCORE_A"); ksnprintf(sc->sensors[start + 1].desc, sizeof(sc->sensors[1].desc), "VCORE_B"); ksnprintf(sc->sensors[start + 2].desc, sizeof(sc->sensors[2].desc), "+3.3V"); ksnprintf(sc->sensors[start + 3].desc, sizeof(sc->sensors[3].desc), "+5V"); ksnprintf(sc->sensors[start + 4].desc, sizeof(sc->sensors[4].desc), "+12V"); ksnprintf(sc->sensors[start + 5].desc, sizeof(sc->sensors[5].desc), "Unused"); ksnprintf(sc->sensors[start + 6].desc, sizeof(sc->sensors[6].desc), "-12V"); ksnprintf(sc->sensors[start + 7].desc, sizeof(sc->sensors[7].desc), "+5VSB"); ksnprintf(sc->sensors[start + 8].desc, sizeof(sc->sensors[8].desc), "VBAT"); } void it_setup_temp(struct it_softc *sc, int start, int n) { int i; for (i = 0; i < n; ++i) sc->sensors[start + i].type = SENSOR_TEMP; } void it_setup_fan(struct it_softc *sc, int start, int n) { int i; for (i = 0; i < n; ++i) sc->sensors[start + i].type = SENSOR_FANRPM; } void it_generic_stemp(struct it_softc *sc, struct ksensor *sensors) { int i, sdata; for (i = 0; i < 3; i++) { sdata = it_readreg(sc, ITD_SENSORTEMPBASE + i); /* Convert temperature to Fahrenheit degres */ sensors[i].value = sdata * 1000000 + 273150000; } } void it_generic_svolt(struct it_softc *sc, struct ksensor *sensors) { int i, sdata; for (i = 0; i < 9; i++) { sdata = it_readreg(sc, ITD_SENSORVOLTBASE + i); DPRINTF(("sdata[volt%d] 0x%x\n", i, sdata)); /* voltage returned as (mV >> 4) */ sensors[i].value = (sdata << 4); /* these two values are negative and formula is different */ if (i == 5 || i == 6) sensors[i].value = ((sdata << 4) - IT_VREF); /* rfact is (factor * 10^4) */ sensors[i].value *= it_vrfact[i]; /* division by 10 gets us back to uVDC */ sensors[i].value /= 10; if (i == 5 || i == 6) sensors[i].value += IT_VREF * 1000; } } void it_generic_fanrpm(struct it_softc *sc, struct ksensor *sensors) { int i, sdata, divisor, odivisor, ndivisor; odivisor = ndivisor = divisor = it_readreg(sc, ITD_FAN); for (i = 0; i < 3; i++, divisor >>= 3) { sensors[i].flags &= ~SENSOR_FINVALID; if ((sdata = it_readreg(sc, ITD_SENSORFANBASE + i)) == 0xff) { sensors[i].flags |= SENSOR_FINVALID; if (i == 2) ndivisor ^= 0x40; else { ndivisor &= ~(7 << (i * 3)); ndivisor |= ((divisor + 1) & 7) << (i * 3); } } else if (sdata == 0) { sensors[i].value = 0; } else { if (i == 2) divisor = divisor & 1 ? 3 : 1; sensors[i].value = 1350000 / (sdata << (divisor & 7)); } } if (ndivisor != odivisor) it_writereg(sc, ITD_FAN, ndivisor); } /* * pre: last read occurred >= 1.5 seconds ago * post: sensors[] current data are the latest from the chip */ void it_refresh_sensor_data(struct it_softc *sc) { /* Refresh our stored data for every sensor */ it_generic_stemp(sc, &sc->sensors[12]); it_generic_svolt(sc, &sc->sensors[3]); it_generic_fanrpm(sc, &sc->sensors[0]); } void it_refresh(void *arg) { struct it_softc *sc = (struct it_softc *)arg; it_refresh_sensor_data(sc); }