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