summaryrefslogtreecommitdiff
path: root/sys/arch/loongson/dev/kb3310.c
diff options
context:
space:
mode:
authorOtto Moerbeek <otto@cvs.openbsd.org>2010-02-28 08:30:28 +0000
committerOtto Moerbeek <otto@cvs.openbsd.org>2010-02-28 08:30:28 +0000
commitc518dd2600807e5276d21b9657745e6d5f4a9deb (patch)
tree959188edada0f5d7d75c046e952d1dd1c6507700 /sys/arch/loongson/dev/kb3310.c
parent79999529fca020d754c68793ff897c9230212875 (diff)
Basic apm(4), providing battery/power status and events. apm(4) is the
generic part, ykbec(4) provides the device specific parts. Other battery/power status drivers can easily hook to adb(4). With help from miod@; ok matthieu@ miod@ jasper@
Diffstat (limited to 'sys/arch/loongson/dev/kb3310.c')
-rw-r--r--sys/arch/loongson/dev/kb3310.c223
1 files changed, 147 insertions, 76 deletions
diff --git a/sys/arch/loongson/dev/kb3310.c b/sys/arch/loongson/dev/kb3310.c
index 600a5c24cac..38d1842dbbb 100644
--- a/sys/arch/loongson/dev/kb3310.c
+++ b/sys/arch/loongson/dev/kb3310.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: kb3310.c,v 1.4 2010/02/24 18:29:39 otto Exp $ */
+/* $OpenBSD: kb3310.c,v 1.5 2010/02/28 08:30:27 otto Exp $ */
/*
* Copyright (c) 2010 Otto Moerbeek <otto@drijf.net>
*
@@ -21,9 +21,12 @@
#include <sys/device.h>
#include <sys/sensors.h>
+#include <machine/apmvar.h>
#include <machine/bus.h>
#include <dev/isa/isavar.h>
+#include "apm.h"
+
struct cfdriver ykbec_cd = {
NULL, "ykbec", DV_DULL,
};
@@ -31,13 +34,40 @@ struct cfdriver ykbec_cd = {
#define IO_YKBEC 0x381
#define IO_YKBECSIZE 0x3
-#define KB3310_NUM_SENSORS 12
+static const struct {
+ const char *desc;
+ int type;
+} ykbec_table[] = {
+#define YKBEC_FAN 0
+ { NULL, SENSOR_FANRPM },
+#define YKBEC_ITEMP 1
+ { "Internal temperature", SENSOR_TEMP },
+#define YKBEC_DCAP 2
+ { "Battery design capacity", SENSOR_AMPHOUR },
+#define YKBEC_FCAP 3
+ { "Battery full charge capacity", SENSOR_AMPHOUR },
+#define YKBEC_DVOLT 4
+ { "Battery design voltage", SENSOR_VOLTS_DC },
+#define YKBEC_BCURRENT 5
+ { "Battery current", SENSOR_AMPS },
+#define YKBEC_BVOLT 6
+ { "Battery voltage", SENSOR_VOLTS_DC },
+#define YKBEC_BTEMP 7
+ { "Battery temperature", SENSOR_TEMP },
+#define YKBEC_CAP 8
+ { "Battery capacity", SENSOR_PERCENT },
+#define YKBEC_CHARGING 9
+ { "Battery charging", SENSOR_INDICATOR },
+#define YKBEC_AC 10
+ { "AC-Power", SENSOR_INDICATOR }
+#define YKBEC_NSENSORS 11
+};
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 ksensor sc_sensor[YKBEC_NSENSORS];
struct ksensordev sc_sensordev;
};
@@ -54,6 +84,21 @@ 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);
+#if NAPM > 0
+int ykbec_apminfo(struct apm_power_info *);
+struct apm_power_info ykbec_apmdata;
+const char *ykbec_batstate[] = {
+ "high",
+ "low",
+ "critical",
+ "charging",
+ "unknown"
+};
+#define BATTERY_STRING(x) ((x) < nitems(ykbec_batstate) ? \
+ ykbec_batstate[x] : ykbec_batstate[4])
+#endif
+
+
int
ykbec_match(struct device *parent, void *match, void *aux)
{
@@ -83,6 +128,7 @@ ykbec_attach( struct device *parent, struct device *self, void *aux)
{
struct isa_attach_args *ia = aux;
struct ykbec_softc *sc = (struct ykbec_softc *)self;
+ int i;
sc->sc_iot = ia->ia_iot;
if (bus_space_map(sc->sc_iot, ia->ia_iobase, ia->ia_iosize, 0,
@@ -98,66 +144,20 @@ ykbec_attach( struct device *parent, struct device *self, void *aux)
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_INDICATOR;
- strlcpy(sc->sc_sensor[9].desc, "Battery charging",
- sizeof(sc->sc_sensor[9].desc));
- sensor_attach(&sc->sc_sensordev, &sc->sc_sensor[9]);
-
- sc->sc_sensor[10].type = SENSOR_INDICATOR;
- strlcpy(sc->sc_sensor[10].desc, "AC-Power",
- sizeof(sc->sc_sensor[10].desc));
- sensor_attach(&sc->sc_sensordev, &sc->sc_sensor[10]);
-
- sc->sc_sensor[11].type = SENSOR_INTEGER;
- strlcpy(sc->sc_sensor[11].desc, "Battery low-level status",
- sizeof(sc->sc_sensor[11].desc));
- sensor_attach(&sc->sc_sensordev, &sc->sc_sensor[11]);
+
+ for (i = 0; i < YKBEC_NSENSORS; i++) {
+ sc->sc_sensor[i].type = ykbec_table[i].type;
+ if (ykbec_table[i].desc)
+ strlcpy(sc->sc_sensor[i].desc, ykbec_table[i].desc,
+ sizeof(sc->sc_sensor[i].desc));
+ sensor_attach(&sc->sc_sensordev, &sc->sc_sensor[i]);
+ }
sensordev_install(&sc->sc_sensordev);
+#if NAPM > 0
+ apm_setinfohook(ykbec_apminfo);
+#endif
printf("\n");
}
@@ -247,33 +247,43 @@ ykbec_refresh(void *arg)
{
struct ykbec_softc *sc = (struct ykbec_softc *)arg;
u_int val, bat_charge, bat_status, charge_status, bat_state, power_flag;
+ u_int cap_pct, fullcap;
int current;
+#if NAPM > 0
+ struct apm_power_info old;
+#endif
val = ykbec_read16(sc, REG_FAN_SPEED_HIGH) & 0xfffff;
- if (val != 0)
+ if (val != 0) {
val = KB3310_FAN_SPEED_DIVIDER / val;
- else
- val = UINT_MAX;
- sc->sc_sensor[0].value = val;
+ sc->sc_sensor[YKBEC_FAN].value = val;
+ sc->sc_sensor[YKBEC_FAN].flags &= ~SENSOR_FINVALID;
+ } else
+ sc->sc_sensor[YKBEC_FAN].flags |= SENSOR_FINVALID;
val = ykbec_read(sc, ECTEMP_CURRENT_REG);
- sc->sc_sensor[1].value = val * 1000000 + 273150000;
+ sc->sc_sensor[YKBEC_ITEMP].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;
+ sc->sc_sensor[YKBEC_DCAP].value = ykbec_read16(sc, REG_DESIGN_CAP_HIGH)
+ * 1000;
+ fullcap = ykbec_read16(sc, REG_FULLCHG_CAP_HIGH);
+ sc->sc_sensor[YKBEC_FCAP].value = fullcap * 1000;
+ sc->sc_sensor[YKBEC_DVOLT].value = ykbec_read16(sc, REG_DESIGN_VOL_HIGH)
+ * 1000;
current = ykbec_read16(sc, REG_CURRENT_HIGH);
/* sign extend short -> int, int -> int64 will be done next statement */
current |= -(current & 0x8000);
- sc->sc_sensor[5].value = current * -1000;
+ sc->sc_sensor[YKBEC_BCURRENT].value = -1000 * current;
- sc->sc_sensor[6].value = ykbec_read16(sc, REG_VOLTAGE_HIGH) * 1000;
+ sc->sc_sensor[YKBEC_BVOLT].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[YKBEC_BTEMP].value = val * 1000000 + 273150000;
- sc->sc_sensor[8].value = ykbec_read16(sc, REG_RELATIVE_CAT_HIGH) * 1000;
+ cap_pct = ykbec_read16(sc, REG_RELATIVE_CAT_HIGH);
+ sc->sc_sensor[YKBEC_CAP].value = cap_pct * 1000;
bat_charge = ykbec_read(sc, REG_BAT_CHARGE);
bat_status = ykbec_read(sc, REG_BAT_STATUS);
@@ -281,8 +291,69 @@ ykbec_refresh(void *arg)
bat_state = ykbec_read(sc, REG_BAT_STATE);
power_flag = ykbec_read(sc, REG_POWER_FLAG);
- sc->sc_sensor[9].value = (bat_state & BAT_STATE_CHARGING) ? 1 : 0;
- sc->sc_sensor[10].value = (power_flag & POWER_FLAG_ADAPTER_IN) ? 1 : 0;
- sc->sc_sensor[11].value = (bat_state << 24) | (charge_status << 16) |
- (bat_status << 8) | bat_charge;
+ sc->sc_sensor[YKBEC_CHARGING].value = (bat_state & BAT_STATE_CHARGING) ?
+ 1 : 0;
+ sc->sc_sensor[YKBEC_AC].value = (power_flag & POWER_FLAG_ADAPTER_IN) ?
+ 1 : 0;
+
+#if NAPM > 0
+ bcopy(&ykbec_apmdata, &old, sizeof(old));
+ ykbec_apmdata.battery_life = cap_pct;
+ ykbec_apmdata.ac_state = (power_flag & POWER_FLAG_ADAPTER_IN) ?
+ APM_AC_ON : APM_AC_OFF;
+ if ((bat_status & BAT_STATUS_BAT_EXISTS) == 0) {
+ ykbec_apmdata.battery_state = APM_BATTERY_ABSENT;
+ ykbec_apmdata.minutes_left = 0;
+ ykbec_apmdata.battery_life = 0;
+ } else {
+ /* if charging, return the minutes until full */
+ if (bat_state & BAT_STATE_CHARGING) {
+ ykbec_apmdata.battery_state = APM_BATT_CHARGING;
+ if (current > 0) {
+ fullcap = (100 - cap_pct) * 60 * fullcap / 100;
+ ykbec_apmdata.minutes_left = fullcap / current;
+ } else
+ ykbec_apmdata.minutes_left = 0;
+ } else {
+ /* arbitrary */
+ if (cap_pct > 60)
+ ykbec_apmdata.battery_state = APM_BATT_HIGH;
+ else if (cap_pct < 10)
+ ykbec_apmdata.battery_state = APM_BATT_CRITICAL;
+ else
+ ykbec_apmdata.battery_state = APM_BATT_LOW;
+
+ current = -current;
+ /* Yeeloong draw is about 1A */
+ if (current <= 0)
+ current = 1000;
+ /* at 5?%, the Yeeloong shuts down */
+ if (cap_pct <= 5)
+ cap_pct = 0;
+ else
+ cap_pct -= 5;
+ fullcap = cap_pct * 60 * fullcap / 100;
+ ykbec_apmdata.minutes_left = fullcap / current;
+ }
+
+ }
+ if (old.ac_state != ykbec_apmdata.ac_state)
+ apm_record_event(APM_POWER_CHANGE, "AC power",
+ ykbec_apmdata.ac_state ? "restored" : "lost");
+ if (old.battery_state != ykbec_apmdata.battery_state)
+ apm_record_event(APM_POWER_CHANGE, "battery",
+ BATTERY_STRING(ykbec_apmdata.battery_state));
+#endif
+}
+
+
+#if NAPM > 0
+
+int
+ykbec_apminfo(struct apm_power_info *info)
+{
+ bcopy(&ykbec_apmdata, info, sizeof(struct apm_power_info));
+ return 0;
}
+
+#endif