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