NetBSD-Bugs archive
[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index][Old Index]
port-amd64/41698: Import of changes to sys/dev/isa/aps.c from OpenBSD
>Number: 41698
>Category: port-amd64
>Synopsis: aps driver does not work on newer ThinkPads
>Confidential: no
>Severity: non-critical
>Priority: low
>Responsible: port-amd64-maintainer
>State: open
>Class: sw-bug
>Submitter-Id: net
>Arrival-Date: Sat Jul 11 02:30:00 +0000 2009
>Originator: Sverre Froyen
>Release: NetBSD 5.99.15
>Organization:
Viewmark
>Environment:
System: NetBSD abbor.fesk.com 5.99.15 NetBSD 5.99.15 (ABBOR) #29: Fri Jul 10
17:48:52 MDT 2009
root%abbor.fesk.com@localhost:/usr/src/objdir/sys/arch/amd64/compile.amd64/ABBOR
amd64
Architecture: x86_64
Machine: amd64
>Description:
aps driver does not work on recent ThinkPads.
>How-To-Repeat:
Boot NetBSD on a ThinkPad T500. Notice that aps0 is not
initialized and that the sensor values are missing from
envstat.
>Fix:
Port OpenBSD changes to the driver. My changes, below, should
be considered a hack and not be imported without review. In
particular, I have not tested the suspend/resume part of the
code. That said, it makes the driver work for me and the sensor
values appear correct.
Index: sys/dev/isa/aps.c
===================================================================
RCS file: /cvsroot/src/sys/dev/isa/aps.c,v
retrieving revision 1.8
diff -u -r1.8 aps.c
--- sys/dev/isa/aps.c 4 Apr 2008 09:41:40 -0000 1.8
+++ sys/dev/isa/aps.c 11 Jul 2009 02:11:10 -0000
@@ -1,8 +1,9 @@
/* $NetBSD: aps.c,v 1.8 2008/04/04 09:41:40 xtraeme Exp $ */
/* $OpenBSD: aps.c,v 1.15 2007/05/19 19:14:11 tedu Exp $ */
-
+/* $OpenBSD: aps.c,v 1.17 2008/06/27 06:08:43 canacar Exp $ */
/*
* Copyright (c) 2005 Jonathan Gray <jsg%openbsd.org@localhost>
+ * Copyright (c) 2008 Can Erkin Acar <canacar%openbsd.org@localhost>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
@@ -44,23 +45,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
+/*
+ * 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_CMD_START 0x01
+#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)
@@ -108,25 +148,96 @@
static int aps_detach(device_t, int);
static int aps_init(struct aps_softc *);
-static uint8_t aps_mem_read_1(bus_space_tag_t, bus_space_handle_t,
- int, uint8_t);
-static void aps_refresh_sensor_data(struct aps_softc *sc);
+static int aps_read_data(struct aps_softc *);
+static void aps_refresh_sensor_data(struct aps_softc *);
static void aps_refresh(void *);
static bool aps_suspend(device_t PMF_FN_PROTO);
static bool aps_resume(device_t PMF_FN_PROTO);
+static int aps_do_io(bus_space_tag_t, bus_space_handle_t,
+ unsigned char *, int, int);
CFATTACH_DECL_NEW(aps, sizeof(struct aps_softc),
aps_match, aps_attach, aps_detach, NULL);
+/* properly communicate with the controller, writing a set of memory
+ * locations and reading back another set */
+static 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);
+}
+
static int
aps_match(device_t parent, cfdata_t match, void *aux)
{
struct isa_attach_args *ia = aux;
bus_space_tag_t iot = ia->ia_iot;
bus_space_handle_t ioh;
- int iobase, i;
+ int iobase;
uint8_t cr;
+ unsigned char iobuf[16];
+
/* Must supply an address */
if (ia->ia_nio < 1)
return 0;
@@ -144,16 +255,12 @@
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;
}
@@ -163,17 +270,15 @@
* 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;
}
@@ -205,10 +310,8 @@
aprint_naive("\n");
aprint_normal("\n");
- if (!aps_init(sc)) {
- aprint_error_dev(self, "failed to initialise\n");
+ if (aps_init(sc))
goto out;
- }
/* Initialize sensors */
#define INITDATA(idx, unit, string) \
@@ -261,43 +364,60 @@
out:
bus_space_unmap(sc->sc_iot, sc->sc_ioh, APS_ADDR_SIZE);
+ aprint_error_dev(self, "failed to initialize\n");
}
static int
aps_init(struct aps_softc *sc)
{
- bus_space_write_1(sc->sc_iot, sc->sc_ioh, APS_INIT, 0x17);
- bus_space_write_1(sc->sc_iot, sc->sc_ioh, APS_STATE, 0x81);
- bus_space_write_1(sc->sc_iot, sc->sc_ioh, APS_CMD, 0x01);
- if (!aps_mem_read_1(sc->sc_iot, sc->sc_ioh, APS_CMD, 0x00))
- return 0;
- if (!aps_mem_read_1(sc->sc_iot, sc->sc_ioh, APS_STATE, 0x00))
- return 0;
- if (!aps_mem_read_1(sc->sc_iot, sc->sc_ioh, APS_XACCEL, 0x60))
- return 0;
- if (!aps_mem_read_1(sc->sc_iot, sc->sc_ioh, APS_XACCEL + 1, 0x00))
- return 0;
- bus_space_write_1(sc->sc_iot, sc->sc_ioh, APS_INIT, 0x14);
- bus_space_write_1(sc->sc_iot, sc->sc_ioh, APS_STATE, 0x01);
- bus_space_write_1(sc->sc_iot, sc->sc_ioh, APS_CMD, 0x01);
- if (!aps_mem_read_1(sc->sc_iot, sc->sc_ioh, APS_CMD, 0x00))
- return 0;
- bus_space_write_1(sc->sc_iot, sc->sc_ioh, APS_INIT, 0x10);
- bus_space_write_1(sc->sc_iot, sc->sc_ioh, APS_STATE, 0xc8);
- bus_space_write_1(sc->sc_iot, sc->sc_ioh, APS_XACCEL, 0x00);
- bus_space_write_1(sc->sc_iot, sc->sc_ioh, APS_XACCEL + 1, 0x02);
- bus_space_write_1(sc->sc_iot, sc->sc_ioh, APS_CMD, 0x01);
- if (!aps_mem_read_1(sc->sc_iot, sc->sc_ioh, APS_CMD, 0x00))
- return 0;
- /* refresh data */
- bus_space_write_1(sc->sc_iot, sc->sc_ioh, APS_INIT, 0x11);
- bus_space_write_1(sc->sc_iot, sc->sc_ioh, APS_CMD, 0x01);
- if (!aps_mem_read_1(sc->sc_iot, sc->sc_ioh, APS_ACCEL_STATE, 0x50))
- return 0;
- if (!aps_mem_read_1(sc->sc_iot, sc->sc_ioh, APS_STATE, 0x00))
- return 0;
+ unsigned char iobuf[16];
- return 1;
+
+ /* command 0x17/0x81: check EC */
+ iobuf[APS_CMD] = 0x17;
+ iobuf[APS_ARG1] = 0x81;
+
+ if (aps_do_io(sc->sc_iot, sc->sc_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(sc->sc_iot, sc->sc_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(sc->sc_iot, sc->sc_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(sc->sc_iot, sc->sc_ioh, iobuf, APS_WRITE_0, APS_READ_1))
+ return (1);
+ if (iobuf[APS_ARG1] != 0)
+ return (1);
+
+ return (0);
}
static int
@@ -313,58 +433,44 @@
return 0;
}
-static uint8_t
-aps_mem_read_1(bus_space_tag_t iot, bus_space_handle_t ioh, int reg,
- uint8_t val)
+static int
+aps_read_data(struct aps_softc *sc)
{
- int i;
- uint8_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);
- }
+ unsigned char iobuf[16];
+
+ /* command 0x11: refresh data */
+ iobuf[APS_CMD] = 0x11;
+ if (aps_do_io(sc->sc_iot, sc->sc_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];
- DPRINTF(("aps: reg 0x%x not val 0x%x!\n", reg, val));
- return 0;
+ return (0);
}
static void
aps_refresh_sensor_data(struct aps_softc *sc)
{
int64_t temp;
+#if 0
+ int i;
+#endif
- /* ask for new data */
- bus_space_write_1(sc->sc_iot, sc->sc_ioh, APS_INIT, 0x11);
- bus_space_write_1(sc->sc_iot, sc->sc_ioh, APS_CMD, 0x01);
- if (!aps_mem_read_1(sc->sc_iot, sc->sc_ioh, APS_ACCEL_STATE, 0x50))
+ if (aps_read_data(sc))
return;
- sc->aps_data.state =
- bus_space_read_1(sc->sc_iot, sc->sc_ioh, APS_STATE);
- sc->aps_data.x_accel =
- bus_space_read_2(sc->sc_iot, sc->sc_ioh, APS_XACCEL);
- sc->aps_data.y_accel =
- bus_space_read_2(sc->sc_iot, sc->sc_ioh, APS_YACCEL);
- sc->aps_data.temp1 =
- bus_space_read_1(sc->sc_iot, sc->sc_ioh, APS_TEMP);
- sc->aps_data.x_var =
- bus_space_read_2(sc->sc_iot, sc->sc_ioh, APS_XVAR);
- sc->aps_data.y_var =
- bus_space_read_2(sc->sc_iot, sc->sc_ioh, APS_YVAR);
- sc->aps_data.temp2 =
- bus_space_read_1(sc->sc_iot, sc->sc_ioh, APS_TEMP2);
- sc->aps_data.input =
- bus_space_read_1(sc->sc_iot, sc->sc_ioh, APS_INPUT);
-
- bus_space_write_1(sc->sc_iot, sc->sc_ioh, APS_INIT, 0x11);
- bus_space_write_1(sc->sc_iot, sc->sc_ioh, APS_CMD, 0x01);
-
- /* tell accelerometer we're done reading from it */
- bus_space_read_1(sc->sc_iot, sc->sc_ioh, APS_CMD);
- bus_space_read_1(sc->sc_iot, sc->sc_ioh, APS_ACCEL_STATE);
+#if 0
+ for (i = 0; i < APS_NUM_SENSORS; i++) {
+ sc->sensors[i].flags &= ~SENSOR_FINVALID;
+ }
+#endif
sc->sc_sensor[APS_SENSOR_XACCEL].value_cur = sc->aps_data.x_accel;
sc->sc_sensor[APS_SENSOR_YACCEL].value_cur = sc->aps_data.y_accel;
@@ -414,22 +520,20 @@
aps_resume(device_t dv PMF_FN_ARGS)
{
struct aps_softc *sc = device_private(dv);
+ unsigned char iobuf[16];
/*
* Redo the init sequence on resume, because APS is
* as forgetful as it is deaf.
*/
- bus_space_write_1(sc->sc_iot, sc->sc_ioh, APS_INIT, 0x13);
- bus_space_write_1(sc->sc_iot, sc->sc_ioh, APS_CMD, 0x01);
- bus_space_read_1(sc->sc_iot, sc->sc_ioh, APS_CMD);
- bus_space_write_1(sc->sc_iot, sc->sc_ioh, APS_INIT, 0x13);
- bus_space_write_1(sc->sc_iot, sc->sc_ioh, APS_CMD, 0x01);
- if (aps_mem_read_1(sc->sc_iot, sc->sc_ioh, APS_CMD, 0x00) &&
- aps_init(sc))
- callout_schedule(&sc->sc_callout, (hz) / 2);
- else
+ /* get APS mode */
+ iobuf[APS_CMD] = 0x13;
+ if (aps_do_io(sc->sc_iot, sc->sc_ioh, iobuf, APS_WRITE_0, APS_READ_1)
+ || aps_init(sc))
aprint_error_dev(dv, "failed to wake up\n");
+ else
+ callout_schedule(&sc->sc_callout, (hz) / 2);
return true;
}
Home |
Main Index |
Thread Index |
Old Index