1 /* $OpenBSD: aps.c,v 1.28 2022/04/06 18:59:28 naddy 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 Renesas 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 const 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
aps_do_io(bus_space_tag_t iot,bus_space_handle_t ioh,unsigned char * buf,int wmask,int rmask)174 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 for (n = 0; n < 100; n++) {
184 stat = bus_space_read_1(iot, ioh, APS_STR3);
185 if (stat & (APS_STR3_OBF3B | APS_STR3_SWMF)) {
186 bus_space_read_1(iot, ioh, APS_TWR_RET);
187 continue;
188 }
189 bus_space_write_1(iot, ioh, APS_TWR_BASE, buf[0]);
190 stat = bus_space_read_1(iot, ioh, APS_STR3);
191 if (stat & (APS_STR3_MWMF))
192 break;
193 delay(1);
194 }
195
196 if (n == 100) {
197 DPRINTF(("aps_do_io: Failed to get bus\n"));
198 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 wmask |= APS_READ_RET;
204 buf[APS_RET] = 0x01;
205
206 for (n = 1, bp = 2; n < 16; bp <<= 1, n++) {
207 if (wmask & bp) {
208 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 }
211 }
212
213 for (n = 0; n < 100; n++) {
214 stat = bus_space_read_1(iot, ioh, APS_STR3);
215 if (stat & (APS_STR3_OBF3B))
216 break;
217 delay(5 * 100);
218 }
219
220 if (n == 100) {
221 DPRINTF(("aps_do_io: timeout waiting response\n"));
222 return (1);
223 }
224 /* wait for data available */
225 /* make sure to read the final byte to clear status */
226 rmask |= APS_READ_RET;
227
228 /* read cmd and data bytes */
229 for (n = 0, bp = 1; n < 16; bp <<= 1, n++) {
230 if (rmask & bp) {
231 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 }
234 }
235
236 return (0);
237 }
238
239 int
aps_match(struct device * parent,void * match,void * aux)240 aps_match(struct device *parent, void *match, void *aux)
241 {
242 struct isa_attach_args *ia = aux;
243 bus_space_tag_t iot = ia->ia_iot;
244 bus_space_handle_t ioh;
245 int iobase = ia->ipa_io[0].base;
246 u_int8_t cr;
247 unsigned char iobuf[16];
248
249 if (bus_space_map(iot, iobase, APS_ADDR_SIZE, 0, &ioh)) {
250 DPRINTF(("aps: can't map i/o space\n"));
251 return (0);
252 }
253 /* get APS mode */
254 iobuf[APS_CMD] = 0x13;
255 if (aps_do_io(iot, ioh, iobuf, APS_WRITE_0, APS_READ_1)) {
256 bus_space_unmap(iot, ioh, APS_ADDR_SIZE);
257 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 cr = iobuf[APS_ARG1];
269 DPRINTF(("aps: state register 0x%x\n", cr));
270
271 if (aps_init(iot, ioh)) {
272 bus_space_unmap(iot, ioh, APS_ADDR_SIZE);
273 return (0);
274 }
275
276 bus_space_unmap(iot, ioh, APS_ADDR_SIZE);
277
278 if (iobuf[APS_RET] != 0 || cr < 1 || cr > 5) {
279 DPRINTF(("aps0: unsupported state %d\n", cr));
280 return (0);
281 }
282
283 ia->ipa_nio = 1;
284 ia->ipa_io[0].length = APS_ADDR_SIZE;
285 ia->ipa_nmem = 0;
286 ia->ipa_nirq = 0;
287 ia->ipa_ndrq = 0;
288 return (1);
289 }
290
291 void
aps_attach(struct device * parent,struct device * self,void * aux)292 aps_attach(struct device *parent, struct device *self, void *aux)
293 {
294 struct aps_softc *sc = (void *)self;
295 int iobase, i;
296 bus_space_tag_t iot;
297 bus_space_handle_t ioh;
298 struct isa_attach_args *ia = aux;
299
300 iobase = ia->ipa_io[0].base;
301 iot = sc->aps_iot = ia->ia_iot;
302
303 if (bus_space_map(iot, iobase, APS_ADDR_SIZE, 0, &sc->aps_ioh)) {
304 printf(": can't map i/o space\n");
305 return;
306 }
307
308 ioh = sc->aps_ioh;
309
310 printf("\n");
311
312 sc->sensors[APS_SENSOR_XACCEL].type = SENSOR_INTEGER;
313 snprintf(sc->sensors[APS_SENSOR_XACCEL].desc,
314 sizeof(sc->sensors[APS_SENSOR_XACCEL].desc), "X_ACCEL");
315
316 sc->sensors[APS_SENSOR_YACCEL].type = SENSOR_INTEGER;
317 snprintf(sc->sensors[APS_SENSOR_YACCEL].desc,
318 sizeof(sc->sensors[APS_SENSOR_YACCEL].desc), "Y_ACCEL");
319
320 sc->sensors[APS_SENSOR_TEMP1].type = SENSOR_TEMP;
321 sc->sensors[APS_SENSOR_TEMP2].type = SENSOR_TEMP;
322
323 sc->sensors[APS_SENSOR_XVAR].type = SENSOR_INTEGER;
324 snprintf(sc->sensors[APS_SENSOR_XVAR].desc,
325 sizeof(sc->sensors[APS_SENSOR_XVAR].desc), "X_VAR");
326
327 sc->sensors[APS_SENSOR_YVAR].type = SENSOR_INTEGER;
328 snprintf(sc->sensors[APS_SENSOR_YVAR].desc,
329 sizeof(sc->sensors[APS_SENSOR_YVAR].desc), "Y_VAR");
330
331 sc->sensors[APS_SENSOR_KBACT].type = SENSOR_INDICATOR;
332 snprintf(sc->sensors[APS_SENSOR_KBACT].desc,
333 sizeof(sc->sensors[APS_SENSOR_KBACT].desc), "Keyboard Active");
334
335 sc->sensors[APS_SENSOR_MSACT].type = SENSOR_INDICATOR;
336 snprintf(sc->sensors[APS_SENSOR_MSACT].desc,
337 sizeof(sc->sensors[APS_SENSOR_MSACT].desc), "Mouse Active");
338
339 sc->sensors[APS_SENSOR_LIDOPEN].type = SENSOR_INDICATOR;
340 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 strlcpy(sc->sensordev.xname, sc->sc_dev.dv_xname,
345 sizeof(sc->sensordev.xname));
346 for (i = 0; i < APS_NUM_SENSORS ; i++) {
347 sensor_attach(&sc->sensordev, &sc->sensors[i]);
348 }
349 sensordev_install(&sc->sensordev);
350
351 /* Refresh sensor data every 0.5 seconds */
352 timeout_set(&aps_timeout, aps_refresh, sc);
353 timeout_add_msec(&aps_timeout, 500);
354 }
355
356 int
aps_init(bus_space_tag_t iot,bus_space_handle_t ioh)357 aps_init(bus_space_tag_t iot, bus_space_handle_t ioh)
358 {
359 unsigned char iobuf[16];
360
361
362 /* command 0x17/0x81: check EC */
363 iobuf[APS_CMD] = 0x17;
364 iobuf[APS_ARG1] = 0x81;
365
366 if (aps_do_io(iot, ioh, iobuf, APS_WRITE_1, APS_READ_3))
367 return (1);
368
369 if (iobuf[APS_RET] != 0 ||iobuf[APS_ARG3] != 0)
370 return (2);
371
372 /* Test values from the Linux driver */
373 if ((iobuf[APS_ARG1] != 0 || iobuf[APS_ARG2] != 0x60) &&
374 (iobuf[APS_ARG1] != 1 || iobuf[APS_ARG2] != 0))
375 return (3);
376
377 /* command 0x14: set power */
378 iobuf[APS_CMD] = 0x14;
379 iobuf[APS_ARG1] = 0x01;
380
381 if (aps_do_io(iot, ioh, iobuf, APS_WRITE_1, APS_READ_0))
382 return (4);
383
384 if (iobuf[APS_RET] != 0)
385 return (5);
386
387 /* command 0x10: set config (sample rate and order) */
388 iobuf[APS_CMD] = 0x10;
389 iobuf[APS_ARG1] = 0xc8;
390 iobuf[APS_ARG2] = 0x00;
391 iobuf[APS_ARG3] = 0x02;
392
393 if (aps_do_io(iot, ioh, iobuf, APS_WRITE_3, APS_READ_0))
394 return (6);
395
396 if (iobuf[APS_RET] != 0)
397 return (7);
398
399 /* command 0x11: refresh data */
400 iobuf[APS_CMD] = 0x11;
401 if (aps_do_io(iot, ioh, iobuf, APS_WRITE_0, APS_READ_1))
402 return (8);
403
404 return (0);
405 }
406
407 int
aps_read_data(struct aps_softc * sc)408 aps_read_data(struct aps_softc *sc)
409 {
410 bus_space_tag_t iot = sc->aps_iot;
411 bus_space_handle_t ioh = sc->aps_ioh;
412 unsigned char iobuf[16];
413
414 /* command 0x11: refresh data */
415 iobuf[APS_CMD] = 0x11;
416 if (aps_do_io(iot, ioh, iobuf, APS_WRITE_0, APS_READ_ALL))
417 return (1);
418
419 sc->aps_data.state = iobuf[APS_STATE];
420 sc->aps_data.x_accel = iobuf[APS_XACCEL] + 256 * iobuf[APS_XACCEL + 1];
421 sc->aps_data.y_accel = iobuf[APS_YACCEL] + 256 * iobuf[APS_YACCEL + 1];
422 sc->aps_data.temp1 = iobuf[APS_TEMP];
423 sc->aps_data.x_var = iobuf[APS_XVAR] + 256 * iobuf[APS_XVAR + 1];
424 sc->aps_data.y_var = iobuf[APS_YVAR] + 256 * iobuf[APS_YVAR + 1];
425 sc->aps_data.temp2 = iobuf[APS_TEMP2];
426 sc->aps_data.input = iobuf[APS_INPUT];
427
428 return (0);
429 }
430
431 void
aps_refresh_sensor_data(struct aps_softc * sc)432 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 if (aps_read_data(sc))
442 return;
443
444 for (i = 0; i < APS_NUM_SENSORS; i++) {
445 sc->sensors[i].flags &= ~SENSOR_FINVALID;
446 }
447
448 sc->sensors[APS_SENSOR_XACCEL].value = sc->aps_data.x_accel;
449 sc->sensors[APS_SENSOR_YACCEL].value = sc->aps_data.y_accel;
450
451 /* convert to micro (mu) degrees */
452 temp = sc->aps_data.temp1 * 1000000;
453 /* convert to kelvin */
454 temp += 273150000;
455 sc->sensors[APS_SENSOR_TEMP1].value = temp;
456
457 /* convert to micro (mu) degrees */
458 temp = sc->aps_data.temp2 * 1000000;
459 /* convert to kelvin */
460 temp += 273150000;
461 sc->sensors[APS_SENSOR_TEMP2].value = temp;
462
463 sc->sensors[APS_SENSOR_XVAR].value = sc->aps_data.x_var;
464 sc->sensors[APS_SENSOR_YVAR].value = sc->aps_data.y_var;
465 sc->sensors[APS_SENSOR_KBACT].value =
466 (sc->aps_data.input & APS_INPUT_KB) ? 1 : 0;
467 sc->sensors[APS_SENSOR_MSACT].value =
468 (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 sc->sensors[APS_SENSOR_LIDOPEN].value =
477 (sc->aps_data.input & APS_INPUT_LIDOPEN) ? 1 : 0;
478 }
479
480 void
aps_refresh(void * arg)481 aps_refresh(void *arg)
482 {
483 struct aps_softc *sc = (struct aps_softc *)arg;
484
485 aps_refresh_sensor_data(sc);
486 timeout_add_msec(&aps_timeout, 500);
487 }
488
489 int
aps_activate(struct device * self,int act)490 aps_activate(struct device *self, int act)
491 {
492 struct aps_softc *sc = (struct aps_softc *)self;
493 bus_space_tag_t iot = sc->aps_iot;
494 bus_space_handle_t ioh = sc->aps_ioh;
495 unsigned char iobuf[16];
496
497 /* check if we bombed during attach */
498 if (!timeout_initialized(&aps_timeout))
499 return (0);
500
501 switch (act) {
502 case DVACT_SUSPEND:
503 timeout_del(&aps_timeout);
504 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 iobuf[APS_CMD] = 0x13;
513 aps_do_io(iot, ioh, iobuf, APS_WRITE_0, APS_READ_1);
514
515 aps_init(iot, ioh);
516 timeout_add_msec(&aps_timeout, 500);
517 break;
518 }
519 return (0);
520 }
521