diff options
Diffstat (limited to 'sys/dev/isa/viasio.c')
-rw-r--r-- | sys/dev/isa/viasio.c | 433 |
1 files changed, 433 insertions, 0 deletions
diff --git a/sys/dev/isa/viasio.c b/sys/dev/isa/viasio.c new file mode 100644 index 00000000000..bd71575067b --- /dev/null +++ b/sys/dev/isa/viasio.c @@ -0,0 +1,433 @@ +/* $OpenBSD: viasio.c,v 1.1 2005/07/28 20:12:13 grange Exp $ */ +/* + * Copyright (c) 2005 Alexander Yurchenko <grange@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. + */ + +/* + * VIA VT1211 LPC Super I/O driver. + */ + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/device.h> +#include <sys/kernel.h> +#include <sys/sensors.h> +#include <sys/timeout.h> + +#include <machine/bus.h> + +#include <dev/isa/isareg.h> +#include <dev/isa/isavar.h> + +#include <dev/isa/viasioreg.h> + +#ifdef VIASIO_DEBUG +#define DPRINTF(x) printf x +#else +#define DPRINTF(x) +#endif + +/* autoconf flags */ +#define VIASIO_CFFLAGS_HM_ENABLE 0x0001 /* enable HM if disabled */ + +struct viasio_softc { + struct device sc_dev; + + bus_space_tag_t sc_iot; + bus_space_handle_t sc_ioh; + + /* Hardware monitor */ + bus_space_handle_t sc_hm_ioh; + int sc_hm_clock; + struct sensor sc_hm_sensors[VT1211_HM_NSENSORS]; + struct timeout sc_hm_timo; +}; + +int viasio_probe(struct device *, void *, void *); +void viasio_attach(struct device *, struct device *, void *); + +void viasio_hm_init(struct viasio_softc *); +void viasio_hm_refresh(void *); + +struct cfattach viasio_ca = { + sizeof(struct viasio_softc), + viasio_probe, + viasio_attach +}; + +struct cfdriver viasio_cd = { + NULL, "viasio", DV_DULL +}; + +static __inline void +viasio_conf_enable(bus_space_tag_t iot, bus_space_handle_t ioh) +{ + bus_space_write_1(iot, ioh, VT1211_INDEX, VT1211_CONF_EN_MAGIC); + bus_space_write_1(iot, ioh, VT1211_INDEX, VT1211_CONF_EN_MAGIC); +} + +static __inline void +viasio_conf_disable(bus_space_tag_t iot, bus_space_handle_t ioh) +{ + bus_space_write_1(iot, ioh, VT1211_INDEX, VT1211_CONF_DS_MAGIC); +} + +static __inline u_int8_t +viasio_conf_read(bus_space_tag_t iot, bus_space_handle_t ioh, u_int8_t index) +{ + bus_space_write_1(iot, ioh, VT1211_INDEX, index); + return (bus_space_read_1(iot, ioh, VT1211_DATA)); +} + +static __inline void +viasio_conf_write(bus_space_tag_t iot, bus_space_handle_t ioh, u_int8_t index, + u_int8_t data) +{ + bus_space_write_1(iot, ioh, VT1211_INDEX, index); + bus_space_write_1(iot, ioh, VT1211_DATA, data); +} + +static __inline int64_t +viasio_raw2temp(int raw) +{ + int tblsize = sizeof(vt1211_hm_temptbl) / sizeof(vt1211_hm_temptbl[0]); + int i; + int raw1, raw2; + int64_t temp = -1, temp1, temp2; + + if (raw < vt1211_hm_temptbl[0].raw || + raw > vt1211_hm_temptbl[tblsize - 1].raw) + return (-1); + + for (i = 0; i < tblsize - 1; i++) { + raw1 = vt1211_hm_temptbl[i].raw; + temp1 = vt1211_hm_temptbl[i].temp; + raw2 = vt1211_hm_temptbl[i + 1].raw; + temp2 = vt1211_hm_temptbl[i + 1].temp; + + if (raw >= raw1 && raw <= raw2) { + /* linear interpolation */ + temp = temp1 + ((raw - raw1) * (temp2 - temp1)) / + (raw2 - raw1); + break; + } + } + + return (temp); +} + +int +viasio_probe(struct device *parent, void *match, void *aux) +{ + struct isa_attach_args *ia = aux; + bus_space_tag_t iot; + bus_space_handle_t ioh; + u_int8_t reg; + + /* Match by device ID */ + iot = ia->ia_iot; + if (bus_space_map(iot, ia->ipa_io[0].base, VT1211_IOSIZE, 0, &ioh)) + return (0); + viasio_conf_enable(iot, ioh); + reg = viasio_conf_read(iot, ioh, VT1211_ID); + DPRINTF(("viasio_probe: id 0x%02x\n", reg)); + viasio_conf_disable(iot, ioh); + bus_space_unmap(iot, ioh, VT1211_IOSIZE); + if (reg == VT1211_ID_VT1211) { + ia->ipa_nio = 1; + ia->ipa_io[0].length = VT1211_IOSIZE; + ia->ipa_nmem = 0; + ia->ipa_nirq = 0; + ia->ipa_ndrq = 0; + return (1); + } + + return (0); +} + +void +viasio_attach(struct device *parent, struct device *self, void *aux) +{ + struct viasio_softc *sc = (void *)self; + struct isa_attach_args *ia = aux; + u_int8_t reg; + + /* Map ISA I/O space */ + sc->sc_iot = ia->ia_iot; + if (bus_space_map(sc->sc_iot, ia->ipa_io[0].base, + VT1211_IOSIZE, 0, &sc->sc_ioh)) { + printf(": can't map I/O space\n"); + return; + } + + /* Enter configuration mode */ + viasio_conf_enable(sc->sc_iot, sc->sc_ioh); + + /* Read device revision */ + reg = viasio_conf_read(sc->sc_iot, sc->sc_ioh, VT1211_REV); + printf(": VT1211 rev 0x%02x\n", reg); + + /* Initialize logical devices */ + printf("%s", sc->sc_dev.dv_xname); + viasio_hm_init(sc); + printf("\n"); + + /* Escape from configuration mode */ + viasio_conf_disable(sc->sc_iot, sc->sc_ioh); +} + +void +viasio_hm_init(struct viasio_softc *sc) +{ + u_int8_t reg0, reg1; + u_int16_t iobase; + int i; + + printf(": HM"); + + /* Select HM logical device */ + viasio_conf_write(sc->sc_iot, sc->sc_ioh, VT1211_LDN, VT1211_LDN_HM); + + /* + * Check if logical device is activated by firmware. If not + * try to activate it only if requested. + */ + reg0 = viasio_conf_read(sc->sc_iot, sc->sc_ioh, VT1211_HM_ACT); + DPRINTF((": ACT 0x%02x", reg0)); + if ((reg0 & VT1211_HM_ACT_EN) == 0) { + if ((sc->sc_dev.dv_cfdata->cf_flags & + VIASIO_CFFLAGS_HM_ENABLE) != 0) { + reg0 |= VT1211_HM_ACT_EN; + viasio_conf_write(sc->sc_iot, sc->sc_ioh, + VT1211_HM_ACT, reg0); + reg0 = viasio_conf_read(sc->sc_iot, sc->sc_ioh, + VT1211_HM_ACT); + DPRINTF((", new ACT 0x%02x", reg0)); + if ((reg0 & VT1211_HM_ACT_EN) == 0) { + printf(": failed to activate"); + return; + } + } else { + printf(": not activated"); + return; + } + } + + /* Read HM I/O space address */ + reg0 = viasio_conf_read(sc->sc_iot, sc->sc_ioh, VT1211_HM_ADDR_LSB); + reg1 = viasio_conf_read(sc->sc_iot, sc->sc_ioh, VT1211_HM_ADDR_MSB); + iobase = (reg1 << 8) | reg0; + DPRINTF((", addr 0x%04x", iobase)); + + /* Map HM I/O space */ + if (bus_space_map(sc->sc_iot, iobase, VT1211_HM_IOSIZE, 0, + &sc->sc_hm_ioh)) { + printf(": can't map I/O space"); + return; + } + + /* + * Check if hardware monitoring is enabled by firmware. If not + * try to enable it only if requested. + */ + reg0 = bus_space_read_1(sc->sc_iot, sc->sc_hm_ioh, VT1211_HM_CONF); + DPRINTF((", CONF 0x%02x", reg0)); + if ((reg0 & VT1211_HM_CONF_START) == 0) { + if ((sc->sc_dev.dv_cfdata->cf_flags & + VIASIO_CFFLAGS_HM_ENABLE) != 0) { + reg0 |= VT1211_HM_CONF_START; + bus_space_write_1(sc->sc_iot, sc->sc_hm_ioh, + VT1211_HM_CONF, reg0); + reg0 = bus_space_read_1(sc->sc_iot, sc->sc_hm_ioh, + VT1211_HM_CONF); + DPRINTF((", new CONF 0x%02x", reg0)); + if ((reg0 & VT1211_HM_CONF_START) == 0) { + printf(": failed to enable monitoring"); + return; + } + } else { + printf(": monitoring not enabled"); + return; + } + } + + /* Read PWM clock frequency */ + reg0 = bus_space_read_1(sc->sc_iot, sc->sc_hm_ioh, VT1211_HM_PWMCS); + sc->sc_hm_clock = vt1211_hm_clock[reg0 & 0x07]; + DPRINTF((", PWMCS 0x%02x, %dHz", reg0, sc->sc_hm_clock)); + + /* Initialize sensors */ + for (i = 0; i < VT1211_HM_NSENSORS; i++) + strlcpy(sc->sc_hm_sensors[i].device, sc->sc_dev.dv_xname, + sizeof(sc->sc_hm_sensors[i].device)); + + /* Temperature reading 1 */ + sc->sc_hm_sensors[VT1211_HMS_TEMP1].type = SENSOR_TEMP; + strlcpy(sc->sc_hm_sensors[VT1211_HMS_TEMP1].desc, "TEMP1", + sizeof(sc->sc_hm_sensors[VT1211_HMS_TEMP1].desc)); + + /* Universal channels (UCH) 1-5 */ + reg0 = bus_space_read_1(sc->sc_iot, sc->sc_hm_ioh, VT1211_HM_UCHCONF); + DPRINTF((", UCHCONF 0x%02x", reg0)); + for (i = 1; i <= 5; i++) { + /* UCH can be configured either as thermal or voltage input */ + if (VT1211_HM_UCHCONF_ISTEMP(reg0, i)) { + sc->sc_hm_sensors[VT1211_HMS_UCH1 + i - 1].type = + SENSOR_TEMP; + } else { + sc->sc_hm_sensors[VT1211_HMS_UCH1 + i - 1].type = + SENSOR_VOLTS_DC; + sc->sc_hm_sensors[VT1211_HMS_UCH1 + i - 1].rfact = + vt1211_hm_vrfact[i - 1]; + } + snprintf(sc->sc_hm_sensors[VT1211_HMS_UCH1 + i - 1].desc, + sizeof(sc->sc_hm_sensors[VT1211_HMS_UCH1 + i - 1].desc), + "UCH%d", i); + } + + /* Internal +3.3V */ + sc->sc_hm_sensors[VT1211_HMS_33V].type = SENSOR_VOLTS_DC; + strlcpy(sc->sc_hm_sensors[VT1211_HMS_33V].desc, "+3.3V", + sizeof(sc->sc_hm_sensors[VT1211_HMS_33V].desc)); + sc->sc_hm_sensors[VT1211_HMS_33V].rfact = vt1211_hm_vrfact[5]; + + /* FAN reading 1, 2 */ + sc->sc_hm_sensors[VT1211_HMS_FAN1].type = SENSOR_FANRPM; + strlcpy(sc->sc_hm_sensors[VT1211_HMS_FAN1].desc, "FAN1", + sizeof(sc->sc_hm_sensors[VT1211_HMS_FAN1].desc)); + sc->sc_hm_sensors[VT1211_HMS_FAN2].type = SENSOR_FANRPM; + strlcpy(sc->sc_hm_sensors[VT1211_HMS_FAN2].desc, "FAN2", + sizeof(sc->sc_hm_sensors[VT1211_HMS_FAN2].desc)); + + /* Start sensors */ + for (i = 0; i < VT1211_HM_NSENSORS; i++) + SENSOR_ADD(&sc->sc_hm_sensors[i]); + timeout_set(&sc->sc_hm_timo, viasio_hm_refresh, sc); + timeout_add(&sc->sc_hm_timo, hz); +} + +void +viasio_hm_refresh(void *arg) +{ + struct viasio_softc *sc = arg; + u_int8_t reg0, reg1; + int64_t val, rfact; + int i; + + /* TEMP1 is a 10-bit thermal input */ + reg0 = bus_space_read_1(sc->sc_iot, sc->sc_hm_ioh, VT1211_HM_TEMP1); + reg1 = bus_space_read_1(sc->sc_iot, sc->sc_hm_ioh, VT1211_HM_TCONF1); + reg1 = VT1211_HM_TCONF1_TEMP1(reg1); + val = (reg0 << 2) | reg1; + + /* Convert to uK */ + /* XXX: conversion function is guessed */ + val = viasio_raw2temp(val); + if (val == -1) { + sc->sc_hm_sensors[VT1211_HMS_TEMP1].flags |= SENSOR_FINVALID; + } else { + sc->sc_hm_sensors[VT1211_HMS_TEMP1].flags &= ~SENSOR_FINVALID; + sc->sc_hm_sensors[VT1211_HMS_TEMP1].value = val; + } + + /* Universal channels 1-5 */ + for (i = 1; i <= 5; i++) { + if (sc->sc_hm_sensors[VT1211_HMS_UCH1 + i - 1].type == + SENSOR_TEMP) { + /* UCH is a 10-bit thermal input */ + reg0 = bus_space_read_1(sc->sc_iot, sc->sc_hm_ioh, + VT1211_HM_UCH1 + i - 1); + if (i == 1) { + reg1 = bus_space_read_1(sc->sc_iot, + sc->sc_hm_ioh, VT1211_HM_VID4); + reg1 = VT1211_HM_VID4_UCH1(reg1); + } else { + reg1 = bus_space_read_1(sc->sc_iot, + sc->sc_hm_ioh, VT1211_HM_ETR); + reg1 = VT1211_HM_ETR_UCH(reg1, i); + } + val = (reg0 << 2) | reg1; + + /* Convert to uK */ + /* XXX: conversion function is guessed */ + val = viasio_raw2temp(val); + if (val == -1) { + sc->sc_hm_sensors[VT1211_HMS_UCH1 + + i - 1].flags |= SENSOR_FINVALID; + } else { + sc->sc_hm_sensors[VT1211_HMS_UCH1 + + i - 1].flags &= ~SENSOR_FINVALID; + sc->sc_hm_sensors[VT1211_HMS_UCH1 + + i - 1].value = val; + } + } else { + /* UCH is a voltage input */ + reg0 = bus_space_read_1(sc->sc_iot, sc->sc_hm_ioh, + VT1211_HM_UCH1 + i - 1); + val = reg0; + + /* Convert to uV */ + /* XXX: conversion function is guessed */ + rfact = sc->sc_hm_sensors[VT1211_HMS_UCH1 + + i - 1].rfact; + sc->sc_hm_sensors[VT1211_HMS_UCH1 + i - 1].value = + ((val * 100000000000ULL) / (rfact * 958)); + } + } + + /* Read internal +3.3V */ + reg0 = bus_space_read_1(sc->sc_iot, sc->sc_hm_ioh, VT1211_HM_33V); + val = reg0; + + /* Convert to uV */ + /* XXX: conversion function is guessed */ + rfact = sc->sc_hm_sensors[VT1211_HMS_33V].rfact; + sc->sc_hm_sensors[VT1211_HMS_33V].value = ((val * 100000000000ULL) / + (rfact * 958)); + + /* Read FAN1 clock counter and divisor */ + reg0 = bus_space_read_1(sc->sc_iot, sc->sc_hm_ioh, VT1211_HM_FAN1); + reg1 = bus_space_read_1(sc->sc_iot, sc->sc_hm_ioh, VT1211_HM_FSCTL); + reg1 = VT1211_HM_FSCTL_DIV1(reg1); + val = reg0 << reg1; + + /* Convert to RPM */ + /* XXX: conversion function is guessed */ + if (val != 0) { + sc->sc_hm_sensors[VT1211_HMS_FAN1].value = + (sc->sc_hm_clock * 60 / 2) / val; + sc->sc_hm_sensors[VT1211_HMS_FAN1].flags &= ~SENSOR_FINVALID; + } else { + sc->sc_hm_sensors[VT1211_HMS_FAN1].flags |= SENSOR_FINVALID; + } + + /* Read FAN2 clock counter and divisor */ + reg0 = bus_space_read_1(sc->sc_iot, sc->sc_hm_ioh, VT1211_HM_FAN2); + reg1 = bus_space_read_1(sc->sc_iot, sc->sc_hm_ioh, VT1211_HM_FSCTL); + reg1 = VT1211_HM_FSCTL_DIV2(reg1); + val = reg0 << reg1; + + /* Convert to RPM */ + /* XXX: conversion function is guessed */ + if (val != 0) { + sc->sc_hm_sensors[VT1211_HMS_FAN2].value = + (sc->sc_hm_clock * 60 / 2) / val; + sc->sc_hm_sensors[VT1211_HMS_FAN2].flags &= ~SENSOR_FINVALID; + } else { + sc->sc_hm_sensors[VT1211_HMS_FAN2].flags |= SENSOR_FINVALID; + } + + timeout_add(&sc->sc_hm_timo, hz); +} |