/* $OpenBSD: viasio.c,v 1.10 2007/06/14 19:13:37 grange Exp $ */ /* * Copyright (c) 2005 Alexander Yurchenko * * 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 #include #include #include #include #include #include #include #include #include #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 */ #define VIASIO_CFFLAGS_WDG_ENABLE 0x0002 /* enable WDG 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 ksensor sc_hm_sensors[VT1211_HM_NSENSORS]; struct ksensordev sc_sensordev; struct timeout sc_hm_timo; /* Watchdog timer */ bus_space_handle_t sc_wdg_ioh; }; 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 *); void viasio_wdg_init(struct viasio_softc *); int viasio_wdg_cb(void *, int); 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", reg); /* Initialize logical devices */ viasio_hm_init(sc); viasio_wdg_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)); /* Temperature reading 1 */ sc->sc_hm_sensors[VT1211_HMS_TEMP1].type = SENSOR_TEMP; /* 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; } 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)); /* FAN reading 1, 2 */ sc->sc_hm_sensors[VT1211_HMS_FAN1].type = SENSOR_FANRPM; sc->sc_hm_sensors[VT1211_HMS_FAN2].type = SENSOR_FANRPM; /* Start sensors */ strlcpy(sc->sc_sensordev.xname, sc->sc_dev.dv_xname, sizeof(sc->sc_sensordev.xname)); for (i = 0; i < VT1211_HM_NSENSORS; i++) sensor_attach(&sc->sc_sensordev, &sc->sc_hm_sensors[i]); sensordev_install(&sc->sc_sensordev); 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 = vt1211_hm_vrfact[i - 1]; 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 = vt1211_hm_vrfact[5]; 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); } void viasio_wdg_init(struct viasio_softc *sc) { u_int8_t reg0, reg1; u_int16_t iobase; printf(", WDG"); /* Select WDG logical device */ viasio_conf_write(sc->sc_iot, sc->sc_ioh, VT1211_LDN, VT1211_LDN_WDG); /* * 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_WDG_ACT); DPRINTF((": ACT 0x%02x", reg0)); if ((reg0 & VT1211_WDG_ACT_EN) == 0) { if ((sc->sc_dev.dv_cfdata->cf_flags & VIASIO_CFFLAGS_WDG_ENABLE) != 0) { reg0 |= VT1211_WDG_ACT_EN; viasio_conf_write(sc->sc_iot, sc->sc_ioh, VT1211_WDG_ACT, reg0); reg0 = viasio_conf_read(sc->sc_iot, sc->sc_ioh, VT1211_WDG_ACT); DPRINTF((", new ACT 0x%02x", reg0)); if ((reg0 & VT1211_WDG_ACT_EN) == 0) { printf(" failed to activate"); return; } } else { printf(" not activated"); return; } } /* Read WDG I/O space address */ reg0 = viasio_conf_read(sc->sc_iot, sc->sc_ioh, VT1211_WDG_ADDR_LSB); reg1 = viasio_conf_read(sc->sc_iot, sc->sc_ioh, VT1211_WDG_ADDR_MSB); iobase = (reg1 << 8) | reg0; DPRINTF((", addr 0x%04x", iobase)); /* Map WDG I/O space */ if (bus_space_map(sc->sc_iot, iobase, VT1211_WDG_IOSIZE, 0, &sc->sc_wdg_ioh)) { printf(" can't map I/O space"); return; } /* Register new watchdog */ wdog_register(sc, viasio_wdg_cb); } int viasio_wdg_cb(void *arg, int period) { struct viasio_softc *sc = arg; int mins; mins = (period + 59) / 60; if (mins > 255) mins = 255; bus_space_write_1(sc->sc_iot, sc->sc_wdg_ioh, VT1211_WDG_TIMEOUT, mins); DPRINTF(("viasio_wdg_cb: %d mins\n", mins)); return (mins * 60); }