xref: /openbsd/sys/dev/isa/aps.c (revision 471aeecf)
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