Line data Source code
1 : /* $OpenBSD: aps.c,v 1.26 2017/03/02 10:38:10 natano Exp $ */
2 : /*
3 : * Copyright (c) 2005 Jonathan Gray <jsg@openbsd.org>
4 : * Copyright (c) 2008 Can Erkin Acar <canacar@openbsd.org>
5 : *
6 : * Permission to use, copy, modify, and distribute this software for any
7 : * purpose with or without fee is hereby granted, provided that the above
8 : * copyright notice and this permission notice appear in all copies.
9 : *
10 : * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11 : * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12 : * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13 : * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14 : * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15 : * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16 : * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17 : */
18 :
19 : /*
20 : * A driver for the ThinkPad Active Protection System based on notes from
21 : * http://www.almaden.ibm.com/cs/people/marksmith/tpaps.html
22 : */
23 :
24 : #include <sys/param.h>
25 : #include <sys/systm.h>
26 : #include <sys/device.h>
27 : #include <sys/kernel.h>
28 : #include <sys/sensors.h>
29 : #include <sys/timeout.h>
30 : #include <machine/bus.h>
31 : #include <sys/event.h>
32 :
33 : #include <dev/isa/isavar.h>
34 :
35 : #ifdef __i386__
36 : #include "apm.h"
37 : #include <machine/acpiapm.h>
38 : #include <machine/biosvar.h>
39 : #include <machine/apmvar.h>
40 : #endif
41 :
42 : #if defined(APSDEBUG)
43 : #define DPRINTF(x) do { printf x; } while (0)
44 : #else
45 : #define DPRINTF(x)
46 : #endif
47 :
48 :
49 : /*
50 : * EC interface on Thinkpad Laptops, from Linux HDAPS driver notes.
51 : * From Renesans H8S/2140B Group Hardware Manual
52 : * http://documentation.renesas.com/eng/products/mpumcu/rej09b0300_2140bhm.pdf
53 : *
54 : * EC uses LPC Channel 3 registers TWR0..15
55 : */
56 :
57 : /* STR3 status register */
58 : #define APS_STR3 0x04
59 :
60 : #define APS_STR3_IBF3B 0x80 /* Input buffer full (host->slave) */
61 : #define APS_STR3_OBF3B 0x40 /* Output buffer full (slave->host)*/
62 : #define APS_STR3_MWMF 0x20 /* Master write mode */
63 : #define APS_STR3_SWMF 0x10 /* Slave write mode */
64 :
65 :
66 : /* Base address of TWR registers */
67 : #define APS_TWR_BASE 0x10
68 : #define APS_TWR_RET 0x1f
69 :
70 : /* TWR registers */
71 : #define APS_CMD 0x00
72 : #define APS_ARG1 0x01
73 : #define APS_ARG2 0x02
74 : #define APS_ARG3 0x03
75 : #define APS_RET 0x0f
76 :
77 : /* Sensor values */
78 : #define APS_STATE 0x01
79 : #define APS_XACCEL 0x02
80 : #define APS_YACCEL 0x04
81 : #define APS_TEMP 0x06
82 : #define APS_XVAR 0x07
83 : #define APS_YVAR 0x09
84 : #define APS_TEMP2 0x0b
85 : #define APS_UNKNOWN 0x0c
86 : #define APS_INPUT 0x0d
87 :
88 : /* write masks for I/O, send command + 0-3 arguments*/
89 : #define APS_WRITE_0 0x0001
90 : #define APS_WRITE_1 0x0003
91 : #define APS_WRITE_2 0x0007
92 : #define APS_WRITE_3 0x000f
93 :
94 : /* read masks for I/O, read 0-3 values (skip command byte) */
95 : #define APS_READ_0 0x0000
96 : #define APS_READ_1 0x0002
97 : #define APS_READ_2 0x0006
98 : #define APS_READ_3 0x000e
99 :
100 : #define APS_READ_RET 0x8000
101 : #define APS_READ_ALL 0xffff
102 :
103 : /* Bit definitions for APS_INPUT value */
104 : #define APS_INPUT_KB (1 << 5)
105 : #define APS_INPUT_MS (1 << 6)
106 : #define APS_INPUT_LIDOPEN (1 << 7)
107 :
108 : #define APS_ADDR_SIZE 0x1f
109 :
110 : struct sensor_rec {
111 : u_int8_t state;
112 : u_int16_t x_accel;
113 : u_int16_t y_accel;
114 : u_int8_t temp1;
115 : u_int16_t x_var;
116 : u_int16_t y_var;
117 : u_int8_t temp2;
118 : u_int8_t unk;
119 : u_int8_t input;
120 : };
121 :
122 : #define APS_NUM_SENSORS 9
123 :
124 : #define APS_SENSOR_XACCEL 0
125 : #define APS_SENSOR_YACCEL 1
126 : #define APS_SENSOR_XVAR 2
127 : #define APS_SENSOR_YVAR 3
128 : #define APS_SENSOR_TEMP1 4
129 : #define APS_SENSOR_TEMP2 5
130 : #define APS_SENSOR_KBACT 6
131 : #define APS_SENSOR_MSACT 7
132 : #define APS_SENSOR_LIDOPEN 8
133 :
134 : struct aps_softc {
135 : struct device sc_dev;
136 :
137 : bus_space_tag_t aps_iot;
138 : bus_space_handle_t aps_ioh;
139 :
140 : struct ksensor sensors[APS_NUM_SENSORS];
141 : struct ksensordev sensordev;
142 : void (*refresh_sensor_data)(struct aps_softc *);
143 :
144 : struct sensor_rec aps_data;
145 : };
146 :
147 : int aps_match(struct device *, void *, void *);
148 : void aps_attach(struct device *, struct device *, void *);
149 : int aps_activate(struct device *, int);
150 :
151 : int aps_init(bus_space_tag_t, bus_space_handle_t);
152 : int aps_read_data(struct aps_softc *);
153 : void aps_refresh_sensor_data(struct aps_softc *);
154 : void aps_refresh(void *);
155 : int aps_do_io(bus_space_tag_t, bus_space_handle_t,
156 : unsigned char *, int, int);
157 :
158 : struct cfattach aps_ca = {
159 : sizeof(struct aps_softc),
160 : aps_match, aps_attach, NULL, aps_activate
161 : };
162 :
163 : struct cfdriver aps_cd = {
164 : NULL, "aps", DV_DULL
165 : };
166 :
167 : struct timeout aps_timeout;
168 :
169 :
170 :
171 : /* properly communicate with the controller, writing a set of memory
172 : * locations and reading back another set */
173 : int
174 0 : aps_do_io(bus_space_tag_t iot, bus_space_handle_t ioh,
175 : unsigned char *buf, int wmask, int rmask)
176 : {
177 : int bp, stat, n;
178 :
179 : DPRINTF(("aps_do_io: CMD: 0x%02x, wmask: 0x%04x, rmask: 0x%04x\n",
180 : buf[0], wmask, rmask));
181 :
182 : /* write init byte using arbitration */
183 0 : for (n = 0; n < 100; n++) {
184 0 : stat = bus_space_read_1(iot, ioh, APS_STR3);
185 0 : if (stat & (APS_STR3_OBF3B | APS_STR3_SWMF)) {
186 0 : bus_space_read_1(iot, ioh, APS_TWR_RET);
187 0 : continue;
188 : }
189 0 : bus_space_write_1(iot, ioh, APS_TWR_BASE, buf[0]);
190 0 : stat = bus_space_read_1(iot, ioh, APS_STR3);
191 0 : if (stat & (APS_STR3_MWMF))
192 : break;
193 0 : delay(1);
194 0 : }
195 :
196 0 : if (n == 100) {
197 : DPRINTF(("aps_do_io: Failed to get bus\n"));
198 0 : return (1);
199 : }
200 :
201 : /* write data bytes, init already sent */
202 : /* make sure last bye is always written as this will trigger slave */
203 0 : wmask |= APS_READ_RET;
204 0 : buf[APS_RET] = 0x01;
205 :
206 0 : for (n = 1, bp = 2; n < 16; bp <<= 1, n++) {
207 0 : if (wmask & bp) {
208 0 : bus_space_write_1(iot, ioh, APS_TWR_BASE + n, buf[n]);
209 : DPRINTF(("aps_do_io: write %2d 0x%02x\n", n, buf[n]));
210 0 : }
211 : }
212 :
213 0 : for (n = 0; n < 100; n++) {
214 0 : stat = bus_space_read_1(iot, ioh, APS_STR3);
215 0 : if (stat & (APS_STR3_OBF3B))
216 : break;
217 0 : delay(5 * 100);
218 : }
219 :
220 0 : if (n == 100) {
221 : DPRINTF(("aps_do_io: timeout waiting response\n"));
222 0 : return (1);
223 : }
224 : /* wait for data available */
225 : /* make sure to read the final byte to clear status */
226 0 : rmask |= APS_READ_RET;
227 :
228 : /* read cmd and data bytes */
229 0 : for (n = 0, bp = 1; n < 16; bp <<= 1, n++) {
230 0 : if (rmask & bp) {
231 0 : buf[n] = bus_space_read_1(iot, ioh, APS_TWR_BASE + n);
232 : DPRINTF(("aps_do_io: read %2d 0x%02x\n", n, buf[n]));
233 0 : }
234 : }
235 :
236 0 : return (0);
237 0 : }
238 :
239 : int
240 0 : aps_match(struct device *parent, void *match, void *aux)
241 : {
242 0 : struct isa_attach_args *ia = aux;
243 0 : bus_space_tag_t iot = ia->ia_iot;
244 0 : bus_space_handle_t ioh;
245 0 : int iobase = ia->ipa_io[0].base;
246 : u_int8_t cr;
247 0 : unsigned char iobuf[16];
248 :
249 0 : if (bus_space_map(iot, iobase, APS_ADDR_SIZE, 0, &ioh)) {
250 : DPRINTF(("aps: can't map i/o space\n"));
251 0 : return (0);
252 : }
253 : /* get APS mode */
254 0 : iobuf[APS_CMD] = 0x13;
255 0 : if (aps_do_io(iot, ioh, iobuf, APS_WRITE_0, APS_READ_1)) {
256 0 : bus_space_unmap(iot, ioh, APS_ADDR_SIZE);
257 0 : return (0);
258 : }
259 :
260 : /*
261 : * Observed values from Linux driver:
262 : * 0x01: T42
263 : * 0x02: chip already initialised
264 : * 0x03: T41
265 : * 0x05: T61
266 : */
267 :
268 0 : cr = iobuf[APS_ARG1];
269 : DPRINTF(("aps: state register 0x%x\n", cr));
270 :
271 0 : if (aps_init(iot, ioh)) {
272 : bus_space_unmap(iot, ioh, APS_ADDR_SIZE);
273 0 : return (0);
274 : }
275 :
276 : bus_space_unmap(iot, ioh, APS_ADDR_SIZE);
277 :
278 0 : if (iobuf[APS_RET] != 0 || cr < 1 || cr > 5) {
279 : DPRINTF(("aps0: unsupported state %d\n", cr));
280 0 : return (0);
281 : }
282 :
283 0 : ia->ipa_nio = 1;
284 0 : ia->ipa_io[0].length = APS_ADDR_SIZE;
285 0 : ia->ipa_nmem = 0;
286 0 : ia->ipa_nirq = 0;
287 0 : ia->ipa_ndrq = 0;
288 0 : return (1);
289 0 : }
290 :
291 : void
292 0 : aps_attach(struct device *parent, struct device *self, void *aux)
293 : {
294 0 : struct aps_softc *sc = (void *)self;
295 : int iobase, i;
296 : bus_space_tag_t iot;
297 : bus_space_handle_t ioh;
298 0 : struct isa_attach_args *ia = aux;
299 :
300 0 : iobase = ia->ipa_io[0].base;
301 0 : iot = sc->aps_iot = ia->ia_iot;
302 :
303 0 : if (bus_space_map(iot, iobase, APS_ADDR_SIZE, 0, &sc->aps_ioh)) {
304 0 : printf(": can't map i/o space\n");
305 0 : return;
306 : }
307 :
308 : ioh = sc->aps_ioh;
309 :
310 0 : printf("\n");
311 :
312 0 : sc->sensors[APS_SENSOR_XACCEL].type = SENSOR_INTEGER;
313 0 : snprintf(sc->sensors[APS_SENSOR_XACCEL].desc,
314 : sizeof(sc->sensors[APS_SENSOR_XACCEL].desc), "X_ACCEL");
315 :
316 0 : sc->sensors[APS_SENSOR_YACCEL].type = SENSOR_INTEGER;
317 0 : snprintf(sc->sensors[APS_SENSOR_YACCEL].desc,
318 : sizeof(sc->sensors[APS_SENSOR_YACCEL].desc), "Y_ACCEL");
319 :
320 0 : sc->sensors[APS_SENSOR_TEMP1].type = SENSOR_TEMP;
321 0 : sc->sensors[APS_SENSOR_TEMP2].type = SENSOR_TEMP;
322 :
323 0 : sc->sensors[APS_SENSOR_XVAR].type = SENSOR_INTEGER;
324 0 : snprintf(sc->sensors[APS_SENSOR_XVAR].desc,
325 : sizeof(sc->sensors[APS_SENSOR_XVAR].desc), "X_VAR");
326 :
327 0 : sc->sensors[APS_SENSOR_YVAR].type = SENSOR_INTEGER;
328 0 : snprintf(sc->sensors[APS_SENSOR_YVAR].desc,
329 : sizeof(sc->sensors[APS_SENSOR_YVAR].desc), "Y_VAR");
330 :
331 0 : sc->sensors[APS_SENSOR_KBACT].type = SENSOR_INDICATOR;
332 0 : snprintf(sc->sensors[APS_SENSOR_KBACT].desc,
333 : sizeof(sc->sensors[APS_SENSOR_KBACT].desc), "Keyboard Active");
334 :
335 0 : sc->sensors[APS_SENSOR_MSACT].type = SENSOR_INDICATOR;
336 0 : snprintf(sc->sensors[APS_SENSOR_MSACT].desc,
337 : sizeof(sc->sensors[APS_SENSOR_MSACT].desc), "Mouse Active");
338 :
339 0 : sc->sensors[APS_SENSOR_LIDOPEN].type = SENSOR_INDICATOR;
340 0 : snprintf(sc->sensors[APS_SENSOR_LIDOPEN].desc,
341 : sizeof(sc->sensors[APS_SENSOR_LIDOPEN].desc), "Lid Open");
342 :
343 : /* stop hiding and report to the authorities */
344 0 : strlcpy(sc->sensordev.xname, sc->sc_dev.dv_xname,
345 : sizeof(sc->sensordev.xname));
346 0 : for (i = 0; i < APS_NUM_SENSORS ; i++) {
347 0 : sensor_attach(&sc->sensordev, &sc->sensors[i]);
348 : }
349 0 : sensordev_install(&sc->sensordev);
350 :
351 : /* Refresh sensor data every 0.5 seconds */
352 0 : timeout_set(&aps_timeout, aps_refresh, sc);
353 0 : timeout_add_msec(&aps_timeout, 500);
354 0 : }
355 :
356 : int
357 0 : aps_init(bus_space_tag_t iot, bus_space_handle_t ioh)
358 : {
359 0 : unsigned char iobuf[16];
360 :
361 :
362 : /* command 0x17/0x81: check EC */
363 0 : iobuf[APS_CMD] = 0x17;
364 0 : iobuf[APS_ARG1] = 0x81;
365 :
366 0 : if (aps_do_io(iot, ioh, iobuf, APS_WRITE_1, APS_READ_3))
367 0 : return (1);
368 :
369 0 : if (iobuf[APS_RET] != 0 ||iobuf[APS_ARG3] != 0)
370 0 : return (2);
371 :
372 : /* Test values from the Linux driver */
373 0 : if ((iobuf[APS_ARG1] != 0 || iobuf[APS_ARG2] != 0x60) &&
374 0 : (iobuf[APS_ARG1] != 1 || iobuf[APS_ARG2] != 0))
375 0 : return (3);
376 :
377 : /* command 0x14: set power */
378 0 : iobuf[APS_CMD] = 0x14;
379 0 : iobuf[APS_ARG1] = 0x01;
380 :
381 0 : if (aps_do_io(iot, ioh, iobuf, APS_WRITE_1, APS_READ_0))
382 0 : return (4);
383 :
384 0 : if (iobuf[APS_RET] != 0)
385 0 : return (5);
386 :
387 : /* command 0x10: set config (sample rate and order) */
388 0 : iobuf[APS_CMD] = 0x10;
389 0 : iobuf[APS_ARG1] = 0xc8;
390 0 : iobuf[APS_ARG2] = 0x00;
391 0 : iobuf[APS_ARG3] = 0x02;
392 :
393 0 : if (aps_do_io(iot, ioh, iobuf, APS_WRITE_3, APS_READ_0))
394 0 : return (6);
395 :
396 0 : if (iobuf[APS_RET] != 0)
397 0 : return (7);
398 :
399 : /* command 0x11: refresh data */
400 0 : iobuf[APS_CMD] = 0x11;
401 0 : if (aps_do_io(iot, ioh, iobuf, APS_WRITE_0, APS_READ_1))
402 0 : return (8);
403 :
404 0 : return (0);
405 0 : }
406 :
407 : int
408 0 : aps_read_data(struct aps_softc *sc)
409 : {
410 0 : bus_space_tag_t iot = sc->aps_iot;
411 0 : bus_space_handle_t ioh = sc->aps_ioh;
412 0 : unsigned char iobuf[16];
413 :
414 : /* command 0x11: refresh data */
415 0 : iobuf[APS_CMD] = 0x11;
416 0 : if (aps_do_io(iot, ioh, iobuf, APS_WRITE_0, APS_READ_ALL))
417 0 : return (1);
418 :
419 0 : sc->aps_data.state = iobuf[APS_STATE];
420 0 : sc->aps_data.x_accel = iobuf[APS_XACCEL] + 256 * iobuf[APS_XACCEL + 1];
421 0 : sc->aps_data.y_accel = iobuf[APS_YACCEL] + 256 * iobuf[APS_YACCEL + 1];
422 0 : sc->aps_data.temp1 = iobuf[APS_TEMP];
423 0 : sc->aps_data.x_var = iobuf[APS_XVAR] + 256 * iobuf[APS_XVAR + 1];
424 0 : sc->aps_data.y_var = iobuf[APS_YVAR] + 256 * iobuf[APS_YVAR + 1];
425 0 : sc->aps_data.temp2 = iobuf[APS_TEMP2];
426 0 : sc->aps_data.input = iobuf[APS_INPUT];
427 :
428 0 : return (0);
429 0 : }
430 :
431 : void
432 0 : aps_refresh_sensor_data(struct aps_softc *sc)
433 : {
434 : int64_t temp;
435 : int i;
436 : #if NAPM > 0
437 : extern int lid_action;
438 : extern int apm_lidclose;
439 : #endif
440 :
441 0 : if (aps_read_data(sc))
442 0 : return;
443 :
444 0 : for (i = 0; i < APS_NUM_SENSORS; i++) {
445 0 : sc->sensors[i].flags &= ~SENSOR_FINVALID;
446 : }
447 :
448 0 : sc->sensors[APS_SENSOR_XACCEL].value = sc->aps_data.x_accel;
449 0 : sc->sensors[APS_SENSOR_YACCEL].value = sc->aps_data.y_accel;
450 :
451 : /* convert to micro (mu) degrees */
452 0 : temp = sc->aps_data.temp1 * 1000000;
453 : /* convert to kelvin */
454 0 : temp += 273150000;
455 0 : sc->sensors[APS_SENSOR_TEMP1].value = temp;
456 :
457 : /* convert to micro (mu) degrees */
458 0 : temp = sc->aps_data.temp2 * 1000000;
459 : /* convert to kelvin */
460 0 : temp += 273150000;
461 0 : sc->sensors[APS_SENSOR_TEMP2].value = temp;
462 :
463 0 : sc->sensors[APS_SENSOR_XVAR].value = sc->aps_data.x_var;
464 0 : sc->sensors[APS_SENSOR_YVAR].value = sc->aps_data.y_var;
465 0 : sc->sensors[APS_SENSOR_KBACT].value =
466 0 : (sc->aps_data.input & APS_INPUT_KB) ? 1 : 0;
467 0 : sc->sensors[APS_SENSOR_MSACT].value =
468 0 : (sc->aps_data.input & APS_INPUT_MS) ? 1 : 0;
469 : #if NAPM > 0
470 : if (lid_action &&
471 : (sc->sensors[APS_SENSOR_LIDOPEN].value == 1) &&
472 : (sc->aps_data.input & APS_INPUT_LIDOPEN) == 0)
473 : /* Inform APM that the lid has closed */
474 : apm_lidclose = 1;
475 : #endif
476 0 : sc->sensors[APS_SENSOR_LIDOPEN].value =
477 0 : (sc->aps_data.input & APS_INPUT_LIDOPEN) ? 1 : 0;
478 0 : }
479 :
480 : void
481 0 : aps_refresh(void *arg)
482 : {
483 0 : struct aps_softc *sc = (struct aps_softc *)arg;
484 :
485 0 : aps_refresh_sensor_data(sc);
486 0 : timeout_add_msec(&aps_timeout, 500);
487 0 : }
488 :
489 : int
490 0 : aps_activate(struct device *self, int act)
491 : {
492 0 : struct aps_softc *sc = (struct aps_softc *)self;
493 0 : bus_space_tag_t iot = sc->aps_iot;
494 0 : bus_space_handle_t ioh = sc->aps_ioh;
495 0 : unsigned char iobuf[16];
496 :
497 : /* check if we bombed during attach */
498 0 : if (!timeout_initialized(&aps_timeout))
499 0 : return (0);
500 :
501 0 : switch (act) {
502 : case DVACT_SUSPEND:
503 0 : timeout_del(&aps_timeout);
504 0 : break;
505 : case DVACT_RESUME:
506 : /*
507 : * Redo the init sequence on resume, because APS is
508 : * as forgetful as it is deaf.
509 : */
510 :
511 : /* get APS mode */
512 0 : iobuf[APS_CMD] = 0x13;
513 0 : aps_do_io(iot, ioh, iobuf, APS_WRITE_0, APS_READ_1);
514 :
515 0 : aps_init(iot, ioh);
516 0 : timeout_add_msec(&aps_timeout, 500);
517 0 : break;
518 : }
519 0 : return (0);
520 0 : }
|