summaryrefslogtreecommitdiff
path: root/sys/dev/isa/viasio.c
diff options
context:
space:
mode:
Diffstat (limited to 'sys/dev/isa/viasio.c')
-rw-r--r--sys/dev/isa/viasio.c433
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);
+}