summaryrefslogtreecommitdiff
path: root/sys/dev
diff options
context:
space:
mode:
authorCan Erkin Acar <canacar@cvs.openbsd.org>2008-06-27 06:08:44 +0000
committerCan Erkin Acar <canacar@cvs.openbsd.org>2008-06-27 06:08:44 +0000
commit3b91a06c33092d681c3bfb8c0bf4c91bc72bdd80 (patch)
treea2a595f85a71a5017fec9275abe3228480190312 /sys/dev
parent39edd764cbe0f389e80c11e104079e123f8a641e (diff)
Access the embedded controller according to the documentation referenced
from the hdaps linux driver, and update model detection. Now works on newer thinkpad models. Suggestions from jsg@, tested by many.
Diffstat (limited to 'sys/dev')
-rw-r--r--sys/dev/isa/aps.c343
1 files changed, 220 insertions, 123 deletions
diff --git a/sys/dev/isa/aps.c b/sys/dev/isa/aps.c
index 043b082618f..ca9a9a410db 100644
--- a/sys/dev/isa/aps.c
+++ b/sys/dev/isa/aps.c
@@ -1,6 +1,7 @@
-/* $OpenBSD: aps.c,v 1.16 2008/05/22 19:23:04 mk Exp $ */
+/* $OpenBSD: aps.c,v 1.17 2008/06/27 06:08:43 canacar Exp $ */
/*
* Copyright (c) 2005 Jonathan Gray <jsg@openbsd.org>
+ * Copyright (c) 2008 Can Erkin Acar <canacar@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
@@ -37,23 +38,62 @@
#define DPRINTF(x)
#endif
-#define APS_ACCEL_STATE 0x04
-#define APS_INIT 0x10
-#define APS_STATE 0x11
-#define APS_XACCEL 0x12
-#define APS_YACCEL 0x14
-#define APS_TEMP 0x16
-#define APS_XVAR 0x17
-#define APS_YVAR 0x19
-#define APS_TEMP2 0x1b
-#define APS_UNKNOWN 0x1c
-#define APS_INPUT 0x1d
-#define APS_CMD 0x1f
-#define APS_STATE_NEWDATA 0x50
-
-#define APS_CMD_START 0x01
+/*
+ * EC interface on Thinkpad Laptops, from Linux HDAPS driver notes.
+ * From Renesans H8S/2140B Group Hardware Manual
+ * http://documentation.renesas.com/eng/products/mpumcu/rej09b0300_2140bhm.pdf
+ *
+ * EC uses LPC Channel 3 registers TWR0..15
+ */
+/* STR3 status register */
+#define APS_STR3 0x04
+
+#define APS_STR3_IBF3B 0x80 /* Input buffer full (host->slave) */
+#define APS_STR3_OBF3B 0x40 /* Output buffer full (slave->host)*/
+#define APS_STR3_MWMF 0x20 /* Master write mode */
+#define APS_STR3_SWMF 0x10 /* Slave write mode */
+
+
+/* Base address of TWR registers */
+#define APS_TWR_BASE 0x10
+#define APS_TWR_RET 0x1f
+
+/* TWR registers */
+#define APS_CMD 0x00
+#define APS_ARG1 0x01
+#define APS_ARG2 0x02
+#define APS_ARG3 0x03
+#define APS_RET 0x0f
+
+/* Sensor values */
+#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
+
+/* write masks for I/O, send command + 0-3 arguments*/
+#define APS_WRITE_0 0x0001
+#define APS_WRITE_1 0x0003
+#define APS_WRITE_2 0x0007
+#define APS_WRITE_3 0x000f
+
+/* read masks for I/O, read 0-3 values (skip command byte) */
+#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
+
+/* Bit definitions for APS_INPUT value */
#define APS_INPUT_KB (1 << 5)
#define APS_INPUT_MS (1 << 6)
#define APS_INPUT_LIDOPEN (1 << 7)
@@ -101,11 +141,12 @@ int aps_match(struct device *, void *, void *);
void aps_attach(struct device *, struct device *, void *);
int aps_init(bus_space_tag_t, bus_space_handle_t);
-u_int8_t aps_mem_read_1(bus_space_tag_t, bus_space_handle_t, int, u_int8_t);
int aps_read_data(struct aps_softc *);
-void aps_refresh_sensor_data(struct aps_softc *sc);
+void aps_refresh_sensor_data(struct aps_softc *);
void aps_refresh(void *);
void aps_power(int, void *);
+int aps_do_io(bus_space_tag_t, bus_space_handle_t,
+ unsigned char *, int, int);
struct cfattach aps_ca = {
sizeof(struct aps_softc),
@@ -119,15 +160,87 @@ struct cfdriver aps_cd = {
struct timeout aps_timeout;
+
+
+/* properly communicate with the controller, writing a set of memory
+ * locations and reading back another set */
+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));
+
+ /* write init byte using arbitration */
+ 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);
+ }
+
+ /* write data bytes, init already sent */
+ /* make sure last bye is always written as this will trigger slave */
+ 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);
+ }
+ /* wait for data available */
+ /* make sure to read the final byte to clear status */
+ rmask |= APS_READ_RET;
+
+ /* read cmd and data bytes */
+ 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)
{
bus_space_tag_t iot;
bus_space_handle_t ioh;
struct isa_attach_args *ia = aux;
- int iobase, i;
+ int iobase;
u_int8_t cr;
+ char iobuf[16];
+
iot = ia->ia_iot;
iobase = ia->ipa_io[0].base;
@@ -136,16 +249,12 @@ aps_match(struct device *parent, void *match, void *aux)
return (0);
}
- /* See if this machine has APS */
- bus_space_write_1(iot, ioh, APS_INIT, 0x13);
- bus_space_write_1(iot, ioh, APS_CMD, 0x01);
- /* ask again as the X40 is slightly deaf in one ear */
- bus_space_read_1(iot, ioh, APS_CMD);
- bus_space_write_1(iot, ioh, APS_INIT, 0x13);
- bus_space_write_1(iot, ioh, APS_CMD, 0x01);
+ /* See if this machine has APS */
- if (!aps_mem_read_1(iot, ioh, APS_CMD, 0x00)) {
+ /* get APS mode */
+ 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);
}
@@ -155,17 +264,15 @@ aps_match(struct device *parent, void *match, void *aux)
* 0x01: T42
* 0x02: chip already initialised
* 0x03: T41
+ * 0x05: T61
*/
- for (i = 0; i < 10; i++) {
- cr = bus_space_read_1(iot, ioh, APS_STATE);
- if (cr > 0 && cr < 6)
- break;
- delay(5 * 1000);
- }
-
+
+ cr = iobuf[APS_ARG1];
+
bus_space_unmap(iot, ioh, APS_ADDR_SIZE);
DPRINTF(("aps: state register 0x%x\n", cr));
- if (cr < 1 || cr > 5) {
+
+ if (iobuf[APS_RET] != 0 || cr < 1 || cr > 5) {
DPRINTF(("aps0: unsupported state %d\n", cr));
return (0);
}
@@ -200,7 +307,7 @@ aps_attach(struct device *parent, struct device *self, void *aux)
printf("\n");
- if (!aps_init(iot, ioh))
+ if (aps_init(iot, ioh))
goto out;
sc->sensors[APS_SENSOR_XACCEL].type = SENSOR_INTEGER;
@@ -249,61 +356,60 @@ aps_attach(struct device *parent, struct device *self, void *aux)
timeout_add(&aps_timeout, (5 * hz) / 10);
return;
out:
- printf("%s: failed to initialise\n", sc->sc_dev.dv_xname);
+ printf("%s: failed to initialize\n", sc->sc_dev.dv_xname);
return;
}
int
aps_init(bus_space_tag_t iot, bus_space_handle_t ioh)
{
- bus_space_write_1(iot, ioh, APS_INIT, 0x17);
- bus_space_write_1(iot, ioh, APS_STATE, 0x81);
- bus_space_write_1(iot, ioh, APS_CMD, 0x01);
- if (!aps_mem_read_1(iot, ioh, APS_CMD, 0x00))
- return (0);
- if (!aps_mem_read_1(iot, ioh, APS_STATE, 0x00))
- return (0);
- if (!aps_mem_read_1(iot, ioh, APS_XACCEL, 0x60))
- return (0);
- if (!aps_mem_read_1(iot, ioh, APS_XACCEL + 1, 0x00))
- return (0);
- bus_space_write_1(iot, ioh, APS_INIT, 0x14);
- bus_space_write_1(iot, ioh, APS_STATE, 0x01);
- bus_space_write_1(iot, ioh, APS_CMD, 0x01);
- if (!aps_mem_read_1(iot, ioh, APS_CMD, 0x00))
- return (0);
- bus_space_write_1(iot, ioh, APS_INIT, 0x10);
- bus_space_write_1(iot, ioh, APS_STATE, 0xc8);
- bus_space_write_1(iot, ioh, APS_XACCEL, 0x00);
- bus_space_write_1(iot, ioh, APS_XACCEL + 1, 0x02);
- bus_space_write_1(iot, ioh, APS_CMD, 0x01);
- if (!aps_mem_read_1(iot, ioh, APS_CMD, 0x00))
- return (0);
- /* refresh data */
- bus_space_write_1(iot, ioh, APS_INIT, 0x11);
- bus_space_write_1(iot, ioh, APS_CMD, 0x01);
- if (!aps_mem_read_1(iot, ioh, APS_ACCEL_STATE, 0x50))
- return (0);
- if (!aps_mem_read_1(iot, ioh, APS_STATE, 0x00))
- return (0);
+ unsigned char iobuf[16];
- return (1);
-}
-u_int8_t
-aps_mem_read_1(bus_space_tag_t iot, bus_space_handle_t ioh, int reg,
- u_int8_t val)
-{
- int i;
- u_int8_t cr;
- /* should take no longer than 50 microseconds */
- for (i = 0; i < 10; i++) {
- cr = bus_space_read_1(iot, ioh, reg);
- if (cr == val)
- return (1);
- delay(5 * 1000);
- }
- DPRINTF(("aps: reg 0x%x not val 0x%x!\n", reg, val));
+ /* command 0x17/0x81: check EC */
+ 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 (1);
+
+ /* Test values from the Linux driver */
+ if ((iobuf[APS_ARG1] != 0 || iobuf[APS_ARG2] != 0x60) &&
+ (iobuf[APS_ARG1] != 1 || iobuf[APS_ARG2] != 0))
+ return (1);
+
+ /* command 0x14: set power */
+ iobuf[APS_CMD] = 0x14;
+ iobuf[APS_ARG1] = 0x01;
+
+ if (aps_do_io(iot, ioh, iobuf, APS_WRITE_1, APS_READ_0))
+ return (1);
+
+ if (iobuf[APS_RET] != 0)
+ return (1);
+
+ /* command 0x10: set config (sample rate and order) */
+ 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 (1);
+
+ if (iobuf[APS_RET] != 0)
+ return (1);
+
+ /* command 0x11: refresh data */
+ iobuf[APS_CMD] = 0x11;
+ if (aps_do_io(iot, ioh, iobuf, APS_WRITE_0, APS_READ_1))
+ return (1);
+ if (iobuf[APS_ARG1] != 0)
+ return (1);
+
return (0);
}
@@ -312,39 +418,33 @@ 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];
+
+ /* command 0x11: refresh data */
+ 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];
- sc->aps_data.state = bus_space_read_1(iot, ioh, APS_STATE);
- sc->aps_data.x_accel = bus_space_read_2(iot, ioh, APS_XACCEL);
- sc->aps_data.y_accel = bus_space_read_2(iot, ioh, APS_YACCEL);
- sc->aps_data.temp1 = bus_space_read_1(iot, ioh, APS_TEMP);
- sc->aps_data.x_var = bus_space_read_2(iot, ioh, APS_XVAR);
- sc->aps_data.y_var = bus_space_read_2(iot, ioh, APS_YVAR);
- sc->aps_data.temp2 = bus_space_read_1(iot, ioh, APS_TEMP2);
- sc->aps_data.input = bus_space_read_1(iot, ioh, APS_INPUT);
-
- return (1);
+ return (0);
}
void
aps_refresh_sensor_data(struct aps_softc *sc)
{
- bus_space_tag_t iot = sc->aps_iot;
- bus_space_handle_t ioh = sc->aps_ioh;
int64_t temp;
int i;
- /* ask for new data */
- bus_space_write_1(iot, ioh, APS_INIT, 0x11);
- bus_space_write_1(iot, ioh, APS_CMD, 0x01);
- if (!aps_mem_read_1(iot, ioh, APS_ACCEL_STATE, 0x50))
+ if (aps_read_data(sc))
return;
- aps_read_data(sc);
- bus_space_write_1(iot, ioh, APS_INIT, 0x11);
- bus_space_write_1(iot, ioh, APS_CMD, 0x01);
-
- /* tell accelerometer we're done reading from it */
- bus_space_read_1(iot, ioh, APS_CMD);
- bus_space_read_1(iot, ioh, APS_ACCEL_STATE);
for (i = 0; i < APS_NUM_SENSORS; i++) {
sc->sensors[i].flags &= ~SENSOR_FINVALID;
@@ -390,25 +490,22 @@ aps_power(int why, void *arg)
struct aps_softc *sc = (struct aps_softc *)arg;
bus_space_tag_t iot = sc->aps_iot;
bus_space_handle_t ioh = sc->aps_ioh;
+ unsigned char iobuf[16];
if (why != PWR_RESUME) {
timeout_del(&aps_timeout);
- } else {
- /*
- * Redo the init sequence on resume, because APS is
- * as forgetful as it is deaf.
- */
- bus_space_write_1(iot, ioh, APS_INIT, 0x13);
- bus_space_write_1(iot, ioh, APS_CMD, 0x01);
- bus_space_read_1(iot, ioh, APS_CMD);
- bus_space_write_1(iot, ioh, APS_INIT, 0x13);
- bus_space_write_1(iot, ioh, APS_CMD, 0x01);
-
- if (aps_mem_read_1(iot, ioh, APS_CMD, 0x00) &&
- aps_init(iot, ioh))
- timeout_add(&aps_timeout, (5 * hz) / 10);
- else
- printf("aps: failed to wake up\n");
+ return;
}
-}
+ /*
+ * Redo the init sequence on resume, because APS is
+ * as forgetful as it is deaf.
+ */
+ /* get APS mode */
+ iobuf[APS_CMD] = 0x13;
+ if (aps_do_io(iot, ioh, iobuf, APS_WRITE_0, APS_READ_1)
+ || aps_init(iot, ioh))
+ printf("aps: failed to wake up\n");
+ else
+ timeout_add(&aps_timeout, (5 * hz) / 10);
+}