summaryrefslogtreecommitdiff
path: root/sys/arch/loongson
diff options
context:
space:
mode:
authorOtto Moerbeek <otto@cvs.openbsd.org>2010-02-23 21:04:17 +0000
committerOtto Moerbeek <otto@cvs.openbsd.org>2010-02-23 21:04:17 +0000
commitaab571f6ba429ac0a816e6d586e80a99f56cbe0e (patch)
tree3d52bb51b8d3d236f9e871a5b3408f7b1eac29bf /sys/arch/loongson
parentdc07dcdb13d83e0bd90779224df9e92d727cb04e (diff)
ykbec(4), a driver to read fan, temperature and battery status using
the kb3310b embedded controller chip on the yeeloong. With help & ok miod@
Diffstat (limited to 'sys/arch/loongson')
-rw-r--r--sys/arch/loongson/conf/GENERIC3
-rw-r--r--sys/arch/loongson/conf/files.loongson7
-rw-r--r--sys/arch/loongson/dev/kb3310.c298
-rw-r--r--sys/arch/loongson/loongson/yeeloong_machdep.c4
4 files changed, 309 insertions, 3 deletions
diff --git a/sys/arch/loongson/conf/GENERIC b/sys/arch/loongson/conf/GENERIC
index 00e522f1ecc..a1e49082624 100644
--- a/sys/arch/loongson/conf/GENERIC
+++ b/sys/arch/loongson/conf/GENERIC
@@ -1,4 +1,4 @@
-# $OpenBSD: GENERIC,v 1.12 2010/02/19 15:14:19 miod Exp $
+# $OpenBSD: GENERIC,v 1.13 2010/02/23 21:04:16 otto Exp $
#
# For further information on compiling OpenBSD kernels, see the config(8)
# man page.
@@ -44,6 +44,7 @@ pckbd* at pckbc? # Yeeloong only
wskbd* at pckbd? mux 1 # Yeeloong only
pmsi* at pckbc? # Yeeloong only
wsmouse* at pmsi? mux 0 # Yeeloong only
+ykbec0 at isa? port 0x381 # Yeeloong only
com0 at isa? port 0x2f8 irq 3 # Fuloong 2F only
pciide* at pci?
wd* at pciide? flags 0x0000
diff --git a/sys/arch/loongson/conf/files.loongson b/sys/arch/loongson/conf/files.loongson
index e154a67395c..6e020c959c2 100644
--- a/sys/arch/loongson/conf/files.loongson
+++ b/sys/arch/loongson/conf/files.loongson
@@ -1,4 +1,4 @@
-# $OpenBSD: files.loongson,v 1.4 2010/02/19 00:21:43 miod Exp $
+# $OpenBSD: files.loongson,v 1.5 2010/02/23 21:04:16 otto Exp $
# Standard stanzas config(8) can't run without
maxpartitions 16
@@ -73,6 +73,11 @@ attach mcclock at isa with mcclock_isa
file arch/loongson/dev/mcclock.c mcclock
file arch/loongson/dev/mcclock_isa.c mcclock_isa
+# Lemote Yeeloong KB3310B Embedded Controller
+device ykbec
+attach ykbec at isa
+file arch/loongson/dev/kb3310.c ykbec
+
# Silicon Motion SM502 master device
device voyager {}: gpiobus
attach voyager at pci
diff --git a/sys/arch/loongson/dev/kb3310.c b/sys/arch/loongson/dev/kb3310.c
new file mode 100644
index 00000000000..84ba918be34
--- /dev/null
+++ b/sys/arch/loongson/dev/kb3310.c
@@ -0,0 +1,298 @@
+/* $OpenBSD: kb3310.c,v 1.1 2010/02/23 21:04:16 otto Exp $ */
+/*
+ * Copyright (c) 2010 Otto Moerbeek <otto@drijf.net>
+ *
+ * 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.
+ */
+
+#include <sys/param.h>
+#include <sys/kernel.h>
+#include <sys/systm.h>
+#include <sys/device.h>
+#include <sys/sensors.h>
+
+#include <machine/bus.h>
+#include <dev/isa/isavar.h>
+
+struct cfdriver ykbec_cd = {
+ NULL, "ykbec", DV_DULL,
+};
+
+#define IO_YKBEC 0x381
+#define IO_YKBECSIZE 0x3
+
+#define KB3310_NUM_SENSORS 10
+
+struct ykbec_softc {
+ struct device sc_dev;
+ bus_space_tag_t sc_iot;
+ bus_space_handle_t sc_ioh;
+ struct ksensor sc_sensor[KB3310_NUM_SENSORS];
+ struct ksensordev sc_sensordev;
+
+};
+
+int ykbec_match(struct device *, void *, void *);
+void ykbec_attach(struct device *, struct device *, void *);
+void ykbec_refresh(void *arg);
+
+const struct cfattach ykbec_ca = {
+ sizeof(struct ykbec_softc), ykbec_match, ykbec_attach
+};
+
+void ykbec_write(struct ykbec_softc *, u_int, u_int);
+u_int ykbec_read(struct ykbec_softc *, u_int);
+u_int ykbec_read16(struct ykbec_softc *, u_int);
+
+int
+ykbec_match(struct device *parent, void *match, void *aux)
+{
+ struct isa_attach_args *ia = aux;
+ bus_space_handle_t ioh;
+
+ if ((ia->ia_iobase != IOBASEUNK && ia->ia_iobase != IO_YKBEC) ||
+ /* (ia->ia_iosize != 0 && ia->ia_iosize != IO_YKBECSIZE) || XXX isa.c */
+ ia->ia_maddr != MADDRUNK || ia->ia_msize != 0 ||
+ ia->ia_irq != IRQUNK || ia->ia_drq != DRQUNK)
+ return (0);
+
+ if (bus_space_map(ia->ia_iot, IO_YKBEC, IO_YKBECSIZE, 0, &ioh))
+ return (0);
+
+ bus_space_unmap(ia->ia_iot, ioh, IO_YKBECSIZE);
+
+ ia->ia_iobase = IO_YKBEC;
+ ia->ia_iosize = IO_YKBECSIZE;
+
+ return (1);
+}
+
+
+void
+ykbec_attach( struct device *parent, struct device *self, void *aux)
+{
+ struct isa_attach_args *ia = aux;
+ struct ykbec_softc *sc = (struct ykbec_softc *)self;
+
+ sc->sc_iot = ia->ia_iot;
+ if (bus_space_map(sc->sc_iot, ia->ia_iobase, ia->ia_iosize, 0,
+ &sc->sc_ioh)) {
+ printf(": couldn't map I/O space");
+ return;
+ }
+
+ /* Initialize sensor data. */
+ strlcpy(sc->sc_sensordev.xname, sc->sc_dev.dv_xname,
+ sizeof(sc->sc_sensordev.xname));
+ if (sensor_task_register(sc, ykbec_refresh, 5) == NULL) {
+ printf(", unable to register update task\n");
+ return;
+ }
+ sc->sc_sensor[0].type = SENSOR_FANRPM;
+ sensor_attach(&sc->sc_sensordev, &sc->sc_sensor[0]);
+
+ sc->sc_sensor[1].type = SENSOR_TEMP;
+ strlcpy(sc->sc_sensor[1].desc, "Internal temperature",
+ sizeof(sc->sc_sensor[1].desc));
+ sensor_attach(&sc->sc_sensordev, &sc->sc_sensor[1]);
+
+ sc->sc_sensor[2].type = SENSOR_AMPHOUR;
+ strlcpy(sc->sc_sensor[2].desc, "Battery design capacity",
+ sizeof(sc->sc_sensor[2].desc));
+ sensor_attach(&sc->sc_sensordev, &sc->sc_sensor[2]);
+
+ sc->sc_sensor[3].type = SENSOR_AMPHOUR;
+ strlcpy(sc->sc_sensor[3].desc, "Battery full charge capacity",
+ sizeof(sc->sc_sensor[3].desc));
+ sensor_attach(&sc->sc_sensordev, &sc->sc_sensor[3]);
+
+ sc->sc_sensor[4].type = SENSOR_VOLTS_DC;
+ strlcpy(sc->sc_sensor[4].desc, "Battery design voltage",
+ sizeof(sc->sc_sensor[4].desc));
+ sensor_attach(&sc->sc_sensordev, &sc->sc_sensor[4]);
+
+ sc->sc_sensor[5].type = SENSOR_AMPS;
+ strlcpy(sc->sc_sensor[5].desc, "Battery current",
+ sizeof(sc->sc_sensor[5].desc));
+ sensor_attach(&sc->sc_sensordev, &sc->sc_sensor[5]);
+
+ sc->sc_sensor[6].type = SENSOR_VOLTS_DC;
+ strlcpy(sc->sc_sensor[6].desc, "Battery voltage",
+ sizeof(sc->sc_sensor[6].desc));
+ sensor_attach(&sc->sc_sensordev, &sc->sc_sensor[6]);
+
+ sc->sc_sensor[7].type = SENSOR_TEMP;
+ strlcpy(sc->sc_sensor[7].desc, "Battery temperature",
+ sizeof(sc->sc_sensor[7].desc));
+ sensor_attach(&sc->sc_sensordev, &sc->sc_sensor[7]);
+
+ sc->sc_sensor[8].type = SENSOR_PERCENT;
+ strlcpy(sc->sc_sensor[8].desc, "Battery capacity",
+ sizeof(sc->sc_sensor[8].desc));
+ sensor_attach(&sc->sc_sensordev, &sc->sc_sensor[8]);
+
+ sc->sc_sensor[9].type = SENSOR_INTEGER;
+ sensor_attach(&sc->sc_sensordev, &sc->sc_sensor[9]);
+
+ sensordev_install(&sc->sc_sensordev);
+
+ printf("\n");
+}
+
+void
+ykbec_write(struct ykbec_softc *mcsc, u_int reg, u_int datum)
+{
+ struct ykbec_softc *sc = (struct ykbec_softc *)mcsc;
+ bus_space_tag_t iot = sc->sc_iot;
+ bus_space_handle_t ioh = sc->sc_ioh;
+
+ bus_space_write_1(iot, ioh, 0, (reg >> 8) & 0xff);
+ bus_space_write_1(iot, ioh, 1, (reg >> 0) & 0xff);
+ bus_space_write_1(iot, ioh, 2, datum);
+}
+
+u_int
+ykbec_read(struct ykbec_softc *mcsc, u_int reg)
+{
+ struct ykbec_softc *sc = (struct ykbec_softc *)mcsc;
+ bus_space_tag_t iot = sc->sc_iot;
+ bus_space_handle_t ioh = sc->sc_ioh;
+
+ bus_space_write_1(iot, ioh, 0, (reg >> 8) & 0xff);
+ bus_space_write_1(iot, ioh, 1, (reg >> 0) & 0xff);
+ return bus_space_read_1(iot, ioh, 2);
+}
+
+u_int
+ykbec_read16(struct ykbec_softc *mcsc, u_int reg)
+{
+ u_int val;
+
+ val = ykbec_read(mcsc, reg);
+ return (val << 8) | ykbec_read(mcsc, reg + 1);
+}
+
+#define KB3310_FAN_SPEED_DIVIDER 480000
+
+#define ECTEMP_CURRENT_REG 0xf458
+#define REG_FAN_SPEED_HIGH 0xfe22
+#define REG_FAN_SPEED_LOW 0xfe23
+
+#define REG_DESIGN_CAP_HIGH 0xf77d
+#define REG_DESIGN_CAP_LOW 0xf77e
+#define REG_FULLCHG_CAP_HIGH 0xf780
+#define REG_FULLCHG_CAP_LOW 0xf781
+
+#define REG_DESIGN_VOL_HIGH 0xf782
+#define REG_DESIGN_VOL_LOW 0xf783
+#define REG_CURRENT_HIGH 0xf784
+#define REG_CURRENT_LOW 0xf785
+#define REG_VOLTAGE_HIGH 0xf786
+#define REG_VOLTAGE_LOW 0xf787
+#define REG_TEMPERATURE_HIGH 0xf788
+#define REG_TEMPERATURE_LOW 0xf789
+#define REG_RELATIVE_CAT_HIGH 0xf492
+#define REG_RELATIVE_CAT_LOW 0xf493
+#define REG_BAT_VENDOR 0xf4c4
+#define REG_BAT_CELL_COUNT 0xf4c6
+#define REG_BAT_CHARGE 0xf4a2
+#define REG_POWER_FLAG 0xf440
+
+#define REG_BAT_STATUS 0xf4b0
+#define BAT_STATUS_BAT_EXISTS (1<<0)
+#define BAT_STATUS_BAT_FULL (1<<1)
+#define BAT_STATUS_BAT_DESTROY (1<<2)
+#define BAT_STATUS_BAT_LOW (1<<5)
+
+#define REG_CHARGE_STATUS 0xf4b1
+#define CHARGE_STATUS_PRECHARGE (1<<1)
+#define CHARGE_STATUS_OVERHEAT (1<<2)
+
+#define REG_BAT_STATE 0xf482
+#define BAT_STATE_DISCHARGING (1<<0)
+#define BAT_STATE_CHARGING (1<<1)
+
+const char *REG_BAT_CHARGE_state[] = {
+ "AC-power", "Battery discharging", "Battery charging"
+};
+
+#define STATUS(a, i) ((i) >= nitems(a) ? "unknown" : (a)[i])
+
+void
+ykbec_refresh(void *arg)
+{
+ struct ykbec_softc *sc = (struct ykbec_softc *)arg;
+ u_int val;
+ int current;
+
+ val = ykbec_read16(sc, REG_FAN_SPEED_HIGH);
+ if (val != 0)
+ val = KB3310_FAN_SPEED_DIVIDER / val;
+ else
+ val = UINT_MAX;
+ sc->sc_sensor[0].value = val;
+
+ val = ykbec_read(sc, ECTEMP_CURRENT_REG);
+ sc->sc_sensor[1].value = val * 1000000 + 273150000;
+
+ sc->sc_sensor[2].value = ykbec_read16(sc, REG_DESIGN_CAP_HIGH) * 1000;
+ sc->sc_sensor[3].value = ykbec_read16(sc, REG_FULLCHG_CAP_HIGH) * 1000;
+ sc->sc_sensor[4].value = ykbec_read16(sc, REG_DESIGN_VOL_HIGH) * 1000;
+ current = ykbec_read16(sc, REG_CURRENT_HIGH);
+ if (current & 0x8000)
+ sc->sc_sensor[5].value = (0xffff - current) * 1000;
+ else
+ sc->sc_sensor[5].value = current * 1000;
+ sc->sc_sensor[6].value = ykbec_read16(sc, REG_VOLTAGE_HIGH) * 1000;
+
+ val = ykbec_read16(sc, REG_TEMPERATURE_HIGH);
+ sc->sc_sensor[7].value = val * 1000000 + 273150000;
+
+ sc->sc_sensor[8].value = ykbec_read16(sc, REG_RELATIVE_CAT_HIGH) * 1000;
+
+ val = ykbec_read(sc, REG_BAT_CHARGE);
+ strlcpy(sc->sc_sensor[9].desc, STATUS(REG_BAT_CHARGE_state, val),
+ sizeof(sc->sc_sensor[9].desc));
+
+ val = (val << 8) | ykbec_read(sc, REG_BAT_STATUS);
+ if (!(val & BAT_STATUS_BAT_EXISTS))
+ strlcat(sc->sc_sensor[9].desc, ",not available",
+ sizeof(sc->sc_sensor[9].desc));
+ if (val & BAT_STATUS_BAT_FULL)
+ strlcat(sc->sc_sensor[9].desc, ",full",
+ sizeof(sc->sc_sensor[9].desc));
+ if (val & BAT_STATUS_BAT_DESTROY)
+ strlcat(sc->sc_sensor[9].desc, ",bad",
+ sizeof(sc->sc_sensor[9].desc));
+ if (val & BAT_STATUS_BAT_LOW)
+ strlcat(sc->sc_sensor[9].desc, ",low",
+ sizeof(sc->sc_sensor[9].desc));
+
+ val = (val << 8) | ykbec_read(sc, REG_CHARGE_STATUS);
+ if (val & CHARGE_STATUS_PRECHARGE)
+ strlcat(sc->sc_sensor[9].desc, ",precharge",
+ sizeof(sc->sc_sensor[9].desc));
+ if (val & CHARGE_STATUS_OVERHEAT)
+ strlcat(sc->sc_sensor[9].desc, ",overheat",
+ sizeof(sc->sc_sensor[9].desc));
+#if 0
+ val = ykbec_read(sc, REG_BAT_STATE);
+ if (val & BAT_STATE_CHARGING)
+ strlcat(sc->sc_sensor[9].desc, ",charging",
+ sizeof(sc->sc_sensor[9].desc));
+ if (val & BAT_STATE_DISCHARGING)
+ strlcat(sc->sc_sensor[9].desc, ",discharging",
+ sizeof(sc->sc_sensor[9].desc));
+#endif
+ sc->sc_sensor[9].value = val;
+}
diff --git a/sys/arch/loongson/loongson/yeeloong_machdep.c b/sys/arch/loongson/loongson/yeeloong_machdep.c
index f8407469b63..6b658684bd0 100644
--- a/sys/arch/loongson/loongson/yeeloong_machdep.c
+++ b/sys/arch/loongson/loongson/yeeloong_machdep.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: yeeloong_machdep.c,v 1.6 2010/02/16 21:31:36 miod Exp $ */
+/* $OpenBSD: yeeloong_machdep.c,v 1.7 2010/02/23 21:04:16 otto Exp $ */
/*
* Copyright (c) 2009, 2010 Miodrag Vallat.
@@ -103,6 +103,8 @@ const struct legacy_io_range yeeloong_legacy_ranges[] = {
{ 0x1f0, 0x1f0 + 7 },
{ 0x376, 0x376 },
{ 0x3f6, 0x3f6 },
+ /* kb3110b embedded controller */
+ { 0x381, 0x383 },
{ 0 }
};