#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 <sys/event.h>
#include <dev/isa/isavar.h>
#ifdef __i386__
#include "apm.h"
#include <machine/acpiapm.h>
#include <machine/biosvar.h>
#include <machine/apmvar.h>
#endif
#if defined(APSDEBUG)
#define DPRINTF(x) do { printf x; } while (0)
#else
#define DPRINTF(x)
#endif
#define APS_STR3 0x04
#define APS_STR3_IBF3B 0x80
#define APS_STR3_OBF3B 0x40
#define APS_STR3_MWMF 0x20
#define APS_STR3_SWMF 0x10
#define APS_TWR_BASE 0x10
#define APS_TWR_RET 0x1f
#define APS_CMD 0x00
#define APS_ARG1 0x01
#define APS_ARG2 0x02
#define APS_ARG3 0x03
#define APS_RET 0x0f
#define APS_STATE 0x01
#define APS_XACCEL 0x02
#define APS_YACCEL 0x04
#define APS_TEMP 0x06
#define APS_XVAR 0x07
#define APS_YVAR 0x09
#define APS_TEMP2 0x0b
#define APS_UNKNOWN 0x0c
#define APS_INPUT 0x0d
#define APS_WRITE_0 0x0001
#define APS_WRITE_1 0x0003
#define APS_WRITE_2 0x0007
#define APS_WRITE_3 0x000f
#define APS_READ_0 0x0000
#define APS_READ_1 0x0002
#define APS_READ_2 0x0006
#define APS_READ_3 0x000e
#define APS_READ_RET 0x8000
#define APS_READ_ALL 0xffff
#define APS_INPUT_KB (1 << 5)
#define APS_INPUT_MS (1 << 6)
#define APS_INPUT_LIDOPEN (1 << 7)
#define APS_ADDR_SIZE 0x1f
struct sensor_rec {
u_int8_t state;
u_int16_t x_accel;
u_int16_t y_accel;
u_int8_t temp1;
u_int16_t x_var;
u_int16_t y_var;
u_int8_t temp2;
u_int8_t unk;
u_int8_t input;
};
#define APS_NUM_SENSORS 9
#define APS_SENSOR_XACCEL 0
#define APS_SENSOR_YACCEL 1
#define APS_SENSOR_XVAR 2
#define APS_SENSOR_YVAR 3
#define APS_SENSOR_TEMP1 4
#define APS_SENSOR_TEMP2 5
#define APS_SENSOR_KBACT 6
#define APS_SENSOR_MSACT 7
#define APS_SENSOR_LIDOPEN 8
struct aps_softc {
struct device sc_dev;
bus_space_tag_t aps_iot;
bus_space_handle_t aps_ioh;
struct ksensor sensors[APS_NUM_SENSORS];
struct ksensordev sensordev;
void (*refresh_sensor_data)(struct aps_softc *);
struct sensor_rec aps_data;
};
int aps_match(struct device *, void *, void *);
void aps_attach(struct device *, struct device *, void *);
int aps_activate(struct device *, int);
int aps_init(bus_space_tag_t, bus_space_handle_t);
int aps_read_data(struct aps_softc *);
void aps_refresh_sensor_data(struct aps_softc *);
void aps_refresh(void *);
int aps_do_io(bus_space_tag_t, bus_space_handle_t,
unsigned char *, int, int);
const struct cfattach aps_ca = {
sizeof(struct aps_softc),
aps_match, aps_attach, NULL, aps_activate
};
struct cfdriver aps_cd = {
NULL, "aps", DV_DULL
};
struct timeout aps_timeout;
int
aps_do_io(bus_space_tag_t iot, bus_space_handle_t ioh,
unsigned char *buf, int wmask, int rmask)
{
int bp, stat, n;
DPRINTF(("aps_do_io: CMD: 0x%02x, wmask: 0x%04x, rmask: 0x%04x\n",
buf[0], wmask, rmask));
for (n = 0; n < 100; n++) {
stat = bus_space_read_1(iot, ioh, APS_STR3);
if (stat & (APS_STR3_OBF3B | APS_STR3_SWMF)) {
bus_space_read_1(iot, ioh, APS_TWR_RET);
continue;
}
bus_space_write_1(iot, ioh, APS_TWR_BASE, buf[0]);
stat = bus_space_read_1(iot, ioh, APS_STR3);
if (stat & (APS_STR3_MWMF))
break;
delay(1);
}
if (n == 100) {
DPRINTF(("aps_do_io: Failed to get bus\n"));
return (1);
}
wmask |= APS_READ_RET;
buf[APS_RET] = 0x01;
for (n = 1, bp = 2; n < 16; bp <<= 1, n++) {
if (wmask & bp) {
bus_space_write_1(iot, ioh, APS_TWR_BASE + n, buf[n]);
DPRINTF(("aps_do_io: write %2d 0x%02x\n", n, buf[n]));
}
}
for (n = 0; n < 100; n++) {
stat = bus_space_read_1(iot, ioh, APS_STR3);
if (stat & (APS_STR3_OBF3B))
break;
delay(5 * 100);
}
if (n == 100) {
DPRINTF(("aps_do_io: timeout waiting response\n"));
return (1);
}
rmask |= APS_READ_RET;
for (n = 0, bp = 1; n < 16; bp <<= 1, n++) {
if (rmask & bp) {
buf[n] = bus_space_read_1(iot, ioh, APS_TWR_BASE + n);
DPRINTF(("aps_do_io: read %2d 0x%02x\n", n, buf[n]));
}
}
return (0);
}
int
aps_match(struct device *parent, void *match, void *aux)
{
struct isa_attach_args *ia = aux;
bus_space_tag_t iot = ia->ia_iot;
bus_space_handle_t ioh;
int iobase = ia->ipa_io[0].base;
u_int8_t cr;
unsigned char iobuf[16];
if (bus_space_map(iot, iobase, APS_ADDR_SIZE, 0, &ioh)) {
DPRINTF(("aps: can't map i/o space\n"));
return (0);
}
iobuf[APS_CMD] = 0x13;
if (aps_do_io(iot, ioh, iobuf, APS_WRITE_0, APS_READ_1)) {
bus_space_unmap(iot, ioh, APS_ADDR_SIZE);
return (0);
}
cr = iobuf[APS_ARG1];
DPRINTF(("aps: state register 0x%x\n", cr));
if (aps_init(iot, ioh)) {
bus_space_unmap(iot, ioh, APS_ADDR_SIZE);
return (0);
}
bus_space_unmap(iot, ioh, APS_ADDR_SIZE);
if (iobuf[APS_RET] != 0 || cr < 1 || cr > 5) {
DPRINTF(("aps0: unsupported state %d\n", cr));
return (0);
}
ia->ipa_nio = 1;
ia->ipa_io[0].length = APS_ADDR_SIZE;
ia->ipa_nmem = 0;
ia->ipa_nirq = 0;
ia->ipa_ndrq = 0;
return (1);
}
void
aps_attach(struct device *parent, struct device *self, void *aux)
{
struct aps_softc *sc = (void *)self;
int iobase, i;
bus_space_tag_t iot;
bus_space_handle_t ioh;
struct isa_attach_args *ia = aux;
iobase = ia->ipa_io[0].base;
iot = sc->aps_iot = ia->ia_iot;
if (bus_space_map(iot, iobase, APS_ADDR_SIZE, 0, &sc->aps_ioh)) {
printf(": can't map i/o space\n");
return;
}
ioh = sc->aps_ioh;
printf("\n");
sc->sensors[APS_SENSOR_XACCEL].type = SENSOR_INTEGER;
snprintf(sc->sensors[APS_SENSOR_XACCEL].desc,
sizeof(sc->sensors[APS_SENSOR_XACCEL].desc), "X_ACCEL");
sc->sensors[APS_SENSOR_YACCEL].type = SENSOR_INTEGER;
snprintf(sc->sensors[APS_SENSOR_YACCEL].desc,
sizeof(sc->sensors[APS_SENSOR_YACCEL].desc), "Y_ACCEL");
sc->sensors[APS_SENSOR_TEMP1].type = SENSOR_TEMP;
sc->sensors[APS_SENSOR_TEMP2].type = SENSOR_TEMP;
sc->sensors[APS_SENSOR_XVAR].type = SENSOR_INTEGER;
snprintf(sc->sensors[APS_SENSOR_XVAR].desc,
sizeof(sc->sensors[APS_SENSOR_XVAR].desc), "X_VAR");
sc->sensors[APS_SENSOR_YVAR].type = SENSOR_INTEGER;
snprintf(sc->sensors[APS_SENSOR_YVAR].desc,
sizeof(sc->sensors[APS_SENSOR_YVAR].desc), "Y_VAR");
sc->sensors[APS_SENSOR_KBACT].type = SENSOR_INDICATOR;
snprintf(sc->sensors[APS_SENSOR_KBACT].desc,
sizeof(sc->sensors[APS_SENSOR_KBACT].desc), "Keyboard Active");
sc->sensors[APS_SENSOR_MSACT].type = SENSOR_INDICATOR;
snprintf(sc->sensors[APS_SENSOR_MSACT].desc,
sizeof(sc->sensors[APS_SENSOR_MSACT].desc), "Mouse Active");
sc->sensors[APS_SENSOR_LIDOPEN].type = SENSOR_INDICATOR;
snprintf(sc->sensors[APS_SENSOR_LIDOPEN].desc,
sizeof(sc->sensors[APS_SENSOR_LIDOPEN].desc), "Lid Open");
strlcpy(sc->sensordev.xname, sc->sc_dev.dv_xname,
sizeof(sc->sensordev.xname));
for (i = 0; i < APS_NUM_SENSORS ; i++) {
sensor_attach(&sc->sensordev, &sc->sensors[i]);
}
sensordev_install(&sc->sensordev);
timeout_set(&aps_timeout, aps_refresh, sc);
timeout_add_msec(&aps_timeout, 500);
}
int
aps_init(bus_space_tag_t iot, bus_space_handle_t ioh)
{
unsigned char iobuf[16];
iobuf[APS_CMD] = 0x17;
iobuf[APS_ARG1] = 0x81;
if (aps_do_io(iot, ioh, iobuf, APS_WRITE_1, APS_READ_3))
return (1);
if (iobuf[APS_RET] != 0 ||iobuf[APS_ARG3] != 0)
return (2);
if ((iobuf[APS_ARG1] != 0 || iobuf[APS_ARG2] != 0x60) &&
(iobuf[APS_ARG1] != 1 || iobuf[APS_ARG2] != 0))
return (3);
iobuf[APS_CMD] = 0x14;
iobuf[APS_ARG1] = 0x01;
if (aps_do_io(iot, ioh, iobuf, APS_WRITE_1, APS_READ_0))
return (4);
if (iobuf[APS_RET] != 0)
return (5);
iobuf[APS_CMD] = 0x10;
iobuf[APS_ARG1] = 0xc8;
iobuf[APS_ARG2] = 0x00;
iobuf[APS_ARG3] = 0x02;
if (aps_do_io(iot, ioh, iobuf, APS_WRITE_3, APS_READ_0))
return (6);
if (iobuf[APS_RET] != 0)
return (7);
iobuf[APS_CMD] = 0x11;
if (aps_do_io(iot, ioh, iobuf, APS_WRITE_0, APS_READ_1))
return (8);
return (0);
}
int
aps_read_data(struct aps_softc *sc)
{
bus_space_tag_t iot = sc->aps_iot;
bus_space_handle_t ioh = sc->aps_ioh;
unsigned char iobuf[16];
iobuf[APS_CMD] = 0x11;
if (aps_do_io(iot, ioh, iobuf, APS_WRITE_0, APS_READ_ALL))
return (1);
sc->aps_data.state = iobuf[APS_STATE];
sc->aps_data.x_accel = iobuf[APS_XACCEL] + 256 * iobuf[APS_XACCEL + 1];
sc->aps_data.y_accel = iobuf[APS_YACCEL] + 256 * iobuf[APS_YACCEL + 1];
sc->aps_data.temp1 = iobuf[APS_TEMP];
sc->aps_data.x_var = iobuf[APS_XVAR] + 256 * iobuf[APS_XVAR + 1];
sc->aps_data.y_var = iobuf[APS_YVAR] + 256 * iobuf[APS_YVAR + 1];
sc->aps_data.temp2 = iobuf[APS_TEMP2];
sc->aps_data.input = iobuf[APS_INPUT];
return (0);
}
void
aps_refresh_sensor_data(struct aps_softc *sc)
{
int64_t temp;
int i;
#if NAPM > 0
extern int lid_action;
extern int apm_lidclose;
#endif
if (aps_read_data(sc))
return;
for (i = 0; i < APS_NUM_SENSORS; i++) {
sc->sensors[i].flags &= ~SENSOR_FINVALID;
}
sc->sensors[APS_SENSOR_XACCEL].value = sc->aps_data.x_accel;
sc->sensors[APS_SENSOR_YACCEL].value = sc->aps_data.y_accel;
temp = sc->aps_data.temp1 * 1000000;
temp += 273150000;
sc->sensors[APS_SENSOR_TEMP1].value = temp;
temp = sc->aps_data.temp2 * 1000000;
temp += 273150000;
sc->sensors[APS_SENSOR_TEMP2].value = temp;
sc->sensors[APS_SENSOR_XVAR].value = sc->aps_data.x_var;
sc->sensors[APS_SENSOR_YVAR].value = sc->aps_data.y_var;
sc->sensors[APS_SENSOR_KBACT].value =
(sc->aps_data.input & APS_INPUT_KB) ? 1 : 0;
sc->sensors[APS_SENSOR_MSACT].value =
(sc->aps_data.input & APS_INPUT_MS) ? 1 : 0;
#if NAPM > 0
if (lid_action &&
(sc->sensors[APS_SENSOR_LIDOPEN].value == 1) &&
(sc->aps_data.input & APS_INPUT_LIDOPEN) == 0)
apm_lidclose = 1;
#endif
sc->sensors[APS_SENSOR_LIDOPEN].value =
(sc->aps_data.input & APS_INPUT_LIDOPEN) ? 1 : 0;
}
void
aps_refresh(void *arg)
{
struct aps_softc *sc = (struct aps_softc *)arg;
aps_refresh_sensor_data(sc);
timeout_add_msec(&aps_timeout, 500);
}
int
aps_activate(struct device *self, int act)
{
struct aps_softc *sc = (struct aps_softc *)self;
bus_space_tag_t iot = sc->aps_iot;
bus_space_handle_t ioh = sc->aps_ioh;
unsigned char iobuf[16];
if (!timeout_initialized(&aps_timeout))
return (0);
switch (act) {
case DVACT_SUSPEND:
timeout_del(&aps_timeout);
break;
case DVACT_RESUME:
iobuf[APS_CMD] = 0x13;
aps_do_io(iot, ioh, iobuf, APS_WRITE_0, APS_READ_1);
aps_init(iot, ioh);
timeout_add_msec(&aps_timeout, 500);
break;
}
return (0);
}