1 /* $OpenBSD: aps.c,v 1.19 2009/05/24 16:40:18 jsg Exp $ */
3 * Copyright (c) 2005 Jonathan Gray <jsg@openbsd.org>
4 * Copyright (c) 2008 Can Erkin Acar <canacar@openbsd.org>
5 * Copyright (c) 2010 Constantine A. Murenin <cnst++@dragonflybsd.org>
7 * Permission to use, copy, modify, and distribute this software for any
8 * purpose with or without fee is hereby granted, provided that the above
9 * copyright notice and this permission notice appear in all copies.
11 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
12 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
13 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
14 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
15 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
16 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
17 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
21 * A driver for the ThinkPad Active Protection System based on notes from
22 * http://www.almaden.ibm.com/cs/people/marksmith/tpaps.html
25 #include <sys/param.h>
26 #include <sys/systm.h>
28 #include <sys/sensors.h>
32 #include <bus/isa/isavar.h>
35 #define DPRINTF(x) do { kprintf x; } while (0)
42 * EC interface on Thinkpad Laptops, from Linux HDAPS driver notes.
43 * From Renesans H8S/2140B Group Hardware Manual
44 * http://documentation.renesas.com/eng/products/mpumcu/rej09b0300_2140bhm.pdf
46 * EC uses LPC Channel 3 registers TWR0..15
49 /* STR3 status register */
52 #define APS_STR3_IBF3B 0x80 /* Input buffer full (host->slave) */
53 #define APS_STR3_OBF3B 0x40 /* Output buffer full (slave->host)*/
54 #define APS_STR3_MWMF 0x20 /* Master write mode */
55 #define APS_STR3_SWMF 0x10 /* Slave write mode */
58 /* Base address of TWR registers */
59 #define APS_TWR_BASE 0x10
60 #define APS_TWR_RET 0x1f
70 #define APS_STATE 0x01
71 #define APS_XACCEL 0x02
72 #define APS_YACCEL 0x04
76 #define APS_TEMP2 0x0b
77 #define APS_UNKNOWN 0x0c
78 #define APS_INPUT 0x0d
80 /* write masks for I/O, send command + 0-3 arguments*/
81 #define APS_WRITE_0 0x0001
82 #define APS_WRITE_1 0x0003
83 #define APS_WRITE_2 0x0007
84 #define APS_WRITE_3 0x000f
86 /* read masks for I/O, read 0-3 values (skip command byte) */
87 #define APS_READ_0 0x0000
88 #define APS_READ_1 0x0002
89 #define APS_READ_2 0x0006
90 #define APS_READ_3 0x000e
92 #define APS_READ_RET 0x8000
93 #define APS_READ_ALL 0xffff
95 /* Bit definitions for APS_INPUT value */
96 #define APS_INPUT_KB (1 << 5)
97 #define APS_INPUT_MS (1 << 6)
98 #define APS_INPUT_LIDOPEN (1 << 7)
100 #define APS_ADDR_BASE 0x1600
101 #define APS_ADDR_SIZE 0x1f
103 struct aps_sensor_rec {
115 #define APS_NUM_SENSORS 9
117 #define APS_SENSOR_XACCEL 0
118 #define APS_SENSOR_YACCEL 1
119 #define APS_SENSOR_XVAR 2
120 #define APS_SENSOR_YVAR 3
121 #define APS_SENSOR_TEMP1 4
122 #define APS_SENSOR_TEMP2 5
123 #define APS_SENSOR_KBACT 6
124 #define APS_SENSOR_MSACT 7
125 #define APS_SENSOR_LIDOPEN 8
128 struct device *sc_dev;
130 struct resource *sc_iores;
133 struct ksensor sensors[APS_NUM_SENSORS];
134 struct ksensordev sensordev;
136 struct aps_sensor_rec aps_data;
139 static int aps_probe(struct device *);
140 static int aps_attach(struct device *);
141 static int aps_detach(struct device *);
143 static int aps_resume(struct device *);
144 static int aps_suspend(struct device *);
146 static int aps_init(struct resource *);
147 static int aps_read_data(struct aps_softc *);
148 static void aps_refresh_sensor_data(struct aps_softc *);
149 static void aps_refresh(void *);
150 static int aps_do_io(struct resource *, unsigned char *, int, int);
152 static device_method_t aps_methods[] = {
153 DEVMETHOD(device_probe, aps_probe),
154 DEVMETHOD(device_attach, aps_attach),
155 DEVMETHOD(device_detach, aps_detach),
157 DEVMETHOD(device_resume, aps_resume),
158 DEVMETHOD(device_suspend, aps_suspend),
162 static driver_t aps_driver = {
165 sizeof(struct aps_softc)
168 static devclass_t aps_devclass;
170 DRIVER_MODULE(aps, isa, aps_driver, aps_devclass, NULL, NULL);
174 /* properly communicate with the controller, writing a set of memory
175 * locations and reading back another set */
177 aps_do_io(struct resource *iores, unsigned char *buf, int wmask, int rmask)
179 bus_space_tag_t iot = rman_get_bustag(iores);
180 bus_space_handle_t ioh = rman_get_bushandle(iores);
183 DPRINTF(("aps_do_io: CMD: 0x%02x, wmask: 0x%04x, rmask: 0x%04x\n",
184 buf[0], wmask, rmask));
186 /* write init byte using arbitration */
187 for (n = 0; n < 100; n++) {
188 stat = bus_space_read_1(iot, ioh, APS_STR3);
189 if (stat & (APS_STR3_OBF3B | APS_STR3_SWMF)) {
190 bus_space_read_1(iot, ioh, APS_TWR_RET);
193 bus_space_write_1(iot, ioh, APS_TWR_BASE, buf[0]);
194 stat = bus_space_read_1(iot, ioh, APS_STR3);
195 if (stat & (APS_STR3_MWMF))
197 /* XXX: OpenBSD has an intended delay of 1 us */
198 tsleep(iores, 0, __func__, hz / 100);
202 DPRINTF(("aps_do_io: Failed to get bus\n"));
206 /* write data bytes, init already sent */
207 /* make sure last bye is always written as this will trigger slave */
208 wmask |= APS_READ_RET;
211 for (n = 1, bp = 2; n < 16; bp <<= 1, n++) {
213 bus_space_write_1(iot, ioh, APS_TWR_BASE + n, buf[n]);
214 DPRINTF(("aps_do_io: write %2d 0x%02x\n", n, buf[n]));
218 for (n = 0; n < 100; n++) {
219 stat = bus_space_read_1(iot, ioh, APS_STR3);
220 if (stat & (APS_STR3_OBF3B))
222 /* XXX: OpenBSD has an intended delay of 500 us */
223 tsleep(iores, 0, __func__, hz / 100);
227 DPRINTF(("aps_do_io: timeout waiting response\n"));
230 /* wait for data available */
231 /* make sure to read the final byte to clear status */
232 rmask |= APS_READ_RET;
234 /* read cmd and data bytes */
235 for (n = 0, bp = 1; n < 16; bp <<= 1, n++) {
237 buf[n] = bus_space_read_1(iot, ioh, APS_TWR_BASE + n);
238 DPRINTF(("aps_do_io: read %2d 0x%02x\n", n, buf[n]));
246 aps_probe(struct device *dev)
248 struct resource *iores;
251 unsigned char iobuf[16];
253 #if defined(APSDEBUG) || defined(KLD_MODULE)
254 device_printf(dev, "%s: 0x%x\n", __func__, isa_get_port(dev));
257 if (device_get_unit(dev) != 0)
260 #ifdef KLD_MODULE /* XXX: isa modules need more work */
261 iores = bus_alloc_resource(dev, SYS_RES_IOPORT, &iorid,
262 APS_ADDR_BASE, APS_ADDR_BASE + APS_ADDR_SIZE - 1, APS_ADDR_SIZE,
265 iores = bus_alloc_resource(dev, SYS_RES_IOPORT, &iorid,
266 0ul, ~0ul, APS_ADDR_SIZE, RF_ACTIVE);
269 DPRINTF(("aps: can't map i/o space\n"));
274 /* See if this machine has APS */
277 iobuf[APS_CMD] = 0x13;
278 if (aps_do_io(iores, iobuf, APS_WRITE_0, APS_READ_1)) {
279 bus_release_resource(dev, SYS_RES_IOPORT, iorid, iores);
284 * Observed values from Linux driver:
286 * 0x02: chip already initialised
291 cr = iobuf[APS_ARG1];
293 bus_release_resource(dev, SYS_RES_IOPORT, iorid, iores);
294 DPRINTF(("aps: state register 0x%x\n", cr));
296 if (iobuf[APS_RET] != 0 || cr < 1 || cr > 5) {
297 DPRINTF(("aps: unsupported state %d\n", cr));
300 device_set_desc(dev, "ThinkPad Active Protection System");
305 aps_attach(struct device *dev)
307 struct aps_softc *sc;
310 sc = device_get_softc(dev);
313 #ifdef KLD_MODULE /* XXX: isa modules need more work */
314 device_printf(dev, "%s: 0x%x\n", __func__, isa_get_port(dev));
315 sc->sc_iores = bus_alloc_resource(dev, SYS_RES_IOPORT, &sc->sc_iorid,
316 APS_ADDR_BASE, APS_ADDR_BASE + APS_ADDR_SIZE - 1, APS_ADDR_SIZE,
319 sc->sc_iores = bus_alloc_resource(dev, SYS_RES_IOPORT, &sc->sc_iorid,
320 0ul, ~0ul, APS_ADDR_SIZE, RF_ACTIVE);
322 if (sc->sc_iores == NULL) {
323 device_printf(dev, "can't map i/o space\n");
327 if (aps_init(sc->sc_iores)) {
328 device_printf(dev, "failed to initialise\n");
329 bus_release_resource(dev, SYS_RES_IOPORT, sc->sc_iorid, sc->sc_iores);
333 sc->sensors[APS_SENSOR_XACCEL].type = SENSOR_INTEGER;
334 ksnprintf(sc->sensors[APS_SENSOR_XACCEL].desc,
335 sizeof(sc->sensors[APS_SENSOR_XACCEL].desc), "X_ACCEL");
337 sc->sensors[APS_SENSOR_YACCEL].type = SENSOR_INTEGER;
338 ksnprintf(sc->sensors[APS_SENSOR_YACCEL].desc,
339 sizeof(sc->sensors[APS_SENSOR_YACCEL].desc), "Y_ACCEL");
341 sc->sensors[APS_SENSOR_TEMP1].type = SENSOR_TEMP;
342 sc->sensors[APS_SENSOR_TEMP2].type = SENSOR_TEMP;
344 sc->sensors[APS_SENSOR_XVAR].type = SENSOR_INTEGER;
345 ksnprintf(sc->sensors[APS_SENSOR_XVAR].desc,
346 sizeof(sc->sensors[APS_SENSOR_XVAR].desc), "X_VAR");
348 sc->sensors[APS_SENSOR_YVAR].type = SENSOR_INTEGER;
349 ksnprintf(sc->sensors[APS_SENSOR_YVAR].desc,
350 sizeof(sc->sensors[APS_SENSOR_YVAR].desc), "Y_VAR");
352 sc->sensors[APS_SENSOR_KBACT].type = SENSOR_INDICATOR;
353 ksnprintf(sc->sensors[APS_SENSOR_KBACT].desc,
354 sizeof(sc->sensors[APS_SENSOR_KBACT].desc), "Keyboard Active");
356 sc->sensors[APS_SENSOR_MSACT].type = SENSOR_INDICATOR;
357 ksnprintf(sc->sensors[APS_SENSOR_MSACT].desc,
358 sizeof(sc->sensors[APS_SENSOR_MSACT].desc), "Mouse Active");
360 sc->sensors[APS_SENSOR_LIDOPEN].type = SENSOR_INDICATOR;
361 ksnprintf(sc->sensors[APS_SENSOR_LIDOPEN].desc,
362 sizeof(sc->sensors[APS_SENSOR_LIDOPEN].desc), "Lid Open");
364 /* stop hiding and report to the authorities */
365 strlcpy(sc->sensordev.xname, device_get_nameunit(dev),
366 sizeof(sc->sensordev.xname));
367 for (i = 0; i < APS_NUM_SENSORS ; i++)
368 sensor_attach(&sc->sensordev, &sc->sensors[i]);
370 /* Refresh sensor data every 1 second */
371 /* XXX: a more frequent refresh might be appropriate */
372 if (sensor_task_register(sc, aps_refresh, 1)) {
373 device_printf(dev, "unable to register update task\n");
374 bus_release_resource(dev, SYS_RES_IOPORT, sc->sc_iorid, sc->sc_iores);
378 sensordev_install(&sc->sensordev);
383 aps_detach(struct device *dev)
385 struct aps_softc *sc = device_get_softc(dev);
387 sensordev_deinstall(&sc->sensordev);
388 sensor_task_unregister(sc);
389 return bus_release_resource(dev, SYS_RES_IOPORT,
390 sc->sc_iorid, sc->sc_iores);
394 aps_init(struct resource *iores)
396 unsigned char iobuf[16];
398 /* command 0x17/0x81: check EC */
399 iobuf[APS_CMD] = 0x17;
400 iobuf[APS_ARG1] = 0x81;
402 if (aps_do_io(iores, iobuf, APS_WRITE_1, APS_READ_3))
405 if (iobuf[APS_RET] != 0 ||iobuf[APS_ARG3] != 0)
408 /* Test values from the Linux driver */
409 if ((iobuf[APS_ARG1] != 0 || iobuf[APS_ARG2] != 0x60) &&
410 (iobuf[APS_ARG1] != 1 || iobuf[APS_ARG2] != 0))
413 /* command 0x14: set power */
414 iobuf[APS_CMD] = 0x14;
415 iobuf[APS_ARG1] = 0x01;
417 if (aps_do_io(iores, iobuf, APS_WRITE_1, APS_READ_0))
420 if (iobuf[APS_RET] != 0)
423 /* command 0x10: set config (sample rate and order) */
424 iobuf[APS_CMD] = 0x10;
425 iobuf[APS_ARG1] = 0xc8;
426 iobuf[APS_ARG2] = 0x00;
427 iobuf[APS_ARG3] = 0x02;
429 if (aps_do_io(iores, iobuf, APS_WRITE_3, APS_READ_0))
432 if (iobuf[APS_RET] != 0)
435 /* command 0x11: refresh data */
436 iobuf[APS_CMD] = 0x11;
437 if (aps_do_io(iores, iobuf, APS_WRITE_0, APS_READ_1))
439 if (iobuf[APS_ARG1] != 0)
446 aps_read_data(struct aps_softc *sc)
448 unsigned char iobuf[16];
450 /* command 0x11: refresh data */
451 iobuf[APS_CMD] = 0x11;
452 if (aps_do_io(sc->sc_iores, iobuf, APS_WRITE_0, APS_READ_ALL))
455 sc->aps_data.state = iobuf[APS_STATE];
456 sc->aps_data.x_accel = iobuf[APS_XACCEL] + 256 * iobuf[APS_XACCEL + 1];
457 sc->aps_data.y_accel = iobuf[APS_YACCEL] + 256 * iobuf[APS_YACCEL + 1];
458 sc->aps_data.temp1 = iobuf[APS_TEMP];
459 sc->aps_data.x_var = iobuf[APS_XVAR] + 256 * iobuf[APS_XVAR + 1];
460 sc->aps_data.y_var = iobuf[APS_YVAR] + 256 * iobuf[APS_YVAR + 1];
461 sc->aps_data.temp2 = iobuf[APS_TEMP2];
462 sc->aps_data.input = iobuf[APS_INPUT];
468 aps_refresh_sensor_data(struct aps_softc *sc)
473 if (aps_read_data(sc)) {
474 for (i = 0; i < APS_NUM_SENSORS; i++)
475 sc->sensors[i].flags |= SENSOR_FINVALID;
479 sc->sensors[APS_SENSOR_XACCEL].value = sc->aps_data.x_accel;
480 sc->sensors[APS_SENSOR_YACCEL].value = sc->aps_data.y_accel;
482 /* convert to micro (mu) degrees */
483 temp = sc->aps_data.temp1 * 1000000;
484 /* convert to kelvin */
486 sc->sensors[APS_SENSOR_TEMP1].value = temp;
488 /* convert to micro (mu) degrees */
489 temp = sc->aps_data.temp2 * 1000000;
490 /* convert to kelvin */
492 sc->sensors[APS_SENSOR_TEMP2].value = temp;
494 sc->sensors[APS_SENSOR_XVAR].value = sc->aps_data.x_var;
495 sc->sensors[APS_SENSOR_YVAR].value = sc->aps_data.y_var;
496 sc->sensors[APS_SENSOR_KBACT].value =
497 (sc->aps_data.input & APS_INPUT_KB) ? 1 : 0;
498 sc->sensors[APS_SENSOR_MSACT].value =
499 (sc->aps_data.input & APS_INPUT_MS) ? 1 : 0;
500 sc->sensors[APS_SENSOR_LIDOPEN].value =
501 (sc->aps_data.input & APS_INPUT_LIDOPEN) ? 1 : 0;
503 for (i = 0; i < APS_NUM_SENSORS; i++)
504 sc->sensors[i].flags &= ~SENSOR_FINVALID;
508 aps_refresh(void *arg)
510 struct aps_softc *sc = (struct aps_softc *)arg;
512 aps_refresh_sensor_data(sc);
516 aps_resume(struct device *dev)
518 struct aps_softc *sc = device_get_softc(dev);
519 unsigned char iobuf[16];
522 * Redo the init sequence on resume, because APS is
523 * as forgetful as it is deaf.
527 iobuf[APS_CMD] = 0x13;
528 if (aps_do_io(sc->sc_iores, iobuf, APS_WRITE_0, APS_READ_1)
529 || aps_init(sc->sc_iores)) {
530 device_printf(sc->sc_dev, "failed to wake up\n");
534 sensor_task_register(sc, aps_refresh, 1);
539 aps_suspend(struct device *dev)
541 struct aps_softc *sc = device_get_softc(dev);
543 for (int i = 0; i < APS_NUM_SENSORS; i++)
544 sc->sensors[i].flags |= SENSOR_FINVALID;
545 sensor_task_unregister(sc);