xref: /dflybsd-src/sys/dev/powermng/aps/aps.c (revision 1c4f2fa48567f20b32ef2963ca51c6d456c4b58b)
1 /*	$OpenBSD: aps.c,v 1.19 2009/05/24 16:40:18 jsg Exp $	*/
2 /*
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>
6  *
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.
10  *
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.
18  */
19 
20 /*
21  * A driver for the ThinkPad Active Protection System based on notes from
22  * http://www.almaden.ibm.com/cs/people/marksmith/tpaps.html
23  */
24 
25 #include <sys/param.h>
26 #include <sys/systm.h>
27 #include <sys/bus.h>
28 #include <sys/sensors.h>
29 
30 #include <sys/rman.h>
31 
32 #include <bus/isa/isavar.h>
33 
34 #if defined(APSDEBUG)
35 #define DPRINTF(x)		do { kprintf x; } while (0)
36 #else
37 #define DPRINTF(x)
38 #endif
39 
40 
41 /*
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
45  *
46  * EC uses LPC Channel 3 registers TWR0..15
47  */
48 
49 /* STR3 status register */
50 #define APS_STR3		0x04
51 
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 */
56 
57 
58 /* Base address of TWR registers */
59 #define APS_TWR_BASE		0x10
60 #define APS_TWR_RET		0x1f
61 
62 /* TWR registers */
63 #define APS_CMD			0x00
64 #define APS_ARG1		0x01
65 #define APS_ARG2		0x02
66 #define APS_ARG3		0x03
67 #define APS_RET			0x0f
68 
69 /* Sensor values */
70 #define APS_STATE		0x01
71 #define	APS_XACCEL		0x02
72 #define APS_YACCEL		0x04
73 #define APS_TEMP		0x06
74 #define	APS_XVAR		0x07
75 #define APS_YVAR		0x09
76 #define APS_TEMP2		0x0b
77 #define APS_UNKNOWN		0x0c
78 #define APS_INPUT		0x0d
79 
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
85 
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
91 
92 #define APS_READ_RET		0x8000
93 #define APS_READ_ALL		0xffff
94 
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)
99 
100 #define APS_ADDR_BASE		0x1600
101 #define APS_ADDR_SIZE		0x1f
102 
103 struct aps_sensor_rec {
104 	u_int8_t	state;
105 	u_int16_t	x_accel;
106 	u_int16_t	y_accel;
107 	u_int8_t	temp1;
108 	u_int16_t	x_var;
109 	u_int16_t	y_var;
110 	u_int8_t	temp2;
111 	u_int8_t	unk;
112 	u_int8_t	input;
113 };
114 
115 #define APS_NUM_SENSORS		9
116 
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
126 
127 struct aps_softc {
128 	struct device		*sc_dev;
129 
130 	struct resource		*sc_iores;
131 	int			sc_iorid;
132 
133 	struct ksensor		sensors[APS_NUM_SENSORS];
134 	struct ksensordev	sensordev;
135 
136 	struct aps_sensor_rec	aps_data;
137 };
138 
139 static int	aps_probe(struct device *);
140 static int	aps_attach(struct device *);
141 static int	aps_detach(struct device *);
142 
143 static int	aps_resume(struct device *);
144 static int	aps_suspend(struct device *);
145 
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);
151 
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),
156 
157 	DEVMETHOD(device_resume,	aps_resume),
158 	DEVMETHOD(device_suspend,	aps_suspend),
159 	{ NULL, NULL }
160 };
161 
162 static driver_t aps_driver = {
163 	"aps",
164 	aps_methods,
165 	sizeof(struct aps_softc)
166 };
167 
168 static devclass_t aps_devclass;
169 
170 DRIVER_MODULE(aps, isa, aps_driver, aps_devclass, NULL, NULL);
171 
172 
173 
174 /* properly communicate with the controller, writing a set of memory
175  * locations and reading back another set  */
176 static int
177 aps_do_io(struct resource *iores, unsigned char *buf, int wmask, int rmask)
178 {
179 	bus_space_tag_t iot = rman_get_bustag(iores);
180 	bus_space_handle_t ioh = rman_get_bushandle(iores);
181 	int bp, stat, n;
182 
183 	DPRINTF(("aps_do_io: CMD: 0x%02x, wmask: 0x%04x, rmask: 0x%04x\n",
184 	       buf[0], wmask, rmask));
185 
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);
191 			continue;
192 		}
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))
196 			break;
197 		/* XXX: OpenBSD has an intended delay of 1 us */
198 		tsleep(iores, 0, __func__, hz / 100);
199 	}
200 
201 	if (n == 100) {
202 		DPRINTF(("aps_do_io: Failed to get bus\n"));
203 		return (1);
204 	}
205 
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;
209 	buf[APS_RET] = 0x01;
210 
211 	for (n = 1, bp = 2; n < 16; bp <<= 1, n++) {
212 		if (wmask & bp) {
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]));
215 		}
216 	}
217 
218 	for (n = 0; n < 100; n++) {
219 		stat = bus_space_read_1(iot, ioh, APS_STR3);
220 		if (stat & (APS_STR3_OBF3B))
221 			break;
222 		/* XXX: OpenBSD has an intended delay of 500 us */
223 		tsleep(iores, 0, __func__, hz / 100);
224 	}
225 
226 	if (n == 100) {
227 		DPRINTF(("aps_do_io: timeout waiting response\n"));
228 		return (1);
229 	}
230 	/* wait for data available */
231 	/* make sure to read the final byte to clear status */
232 	rmask |= APS_READ_RET;
233 
234 	/* read cmd and data bytes */
235 	for (n = 0, bp = 1; n < 16; bp <<= 1, n++) {
236 		if (rmask & bp) {
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]));
239 		}
240 	}
241 
242 	return (0);
243 }
244 
245 static int
246 aps_probe(struct device *dev)
247 {
248 	struct resource *iores;
249 	int iorid = 0;
250 	u_int8_t cr;
251 	unsigned char iobuf[16];
252 
253 #if defined(APSDEBUG) || defined(KLD_MODULE)
254 	device_printf(dev, "%s: 0x%x\n", __func__, isa_get_port(dev));
255 #endif
256 
257 	if (device_get_unit(dev) != 0)
258 		return ENXIO;
259 
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,
263 	    RF_ACTIVE);
264 #else
265 	iores = bus_alloc_resource(dev, SYS_RES_IOPORT, &iorid,
266 	    0ul, ~0ul, APS_ADDR_SIZE, RF_ACTIVE);
267 #endif
268 	if (iores == NULL) {
269 		DPRINTF(("aps: can't map i/o space\n"));
270 		return ENXIO;
271 	}
272 
273 
274 	/* See if this machine has APS */
275 
276 	/* get APS mode */
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);
280 		return ENXIO;
281 	}
282 
283 	/*
284 	 * Observed values from Linux driver:
285 	 * 0x01: T42
286 	 * 0x02: chip already initialised
287 	 * 0x03: T41
288 	 * 0x05: T61
289 	 */
290 
291 	cr = iobuf[APS_ARG1];
292 
293 	bus_release_resource(dev, SYS_RES_IOPORT, iorid, iores);
294 	DPRINTF(("aps: state register 0x%x\n", cr));
295 
296 	if (iobuf[APS_RET] != 0 || cr < 1 || cr > 5) {
297 		DPRINTF(("aps: unsupported state %d\n", cr));
298 		return ENXIO;
299 	}
300 	device_set_desc(dev, "ThinkPad Active Protection System");
301 	return 0;
302 }
303 
304 static int
305 aps_attach(struct device *dev)
306 {
307 	struct aps_softc *sc;
308 
309 	sc = device_get_softc(dev);
310 	sc->sc_dev = dev;
311 
312 #ifdef KLD_MODULE	/* XXX: isa modules need more work */
313 	device_printf(dev, "%s: 0x%x\n", __func__, isa_get_port(dev));
314 	sc->sc_iores = bus_alloc_resource(dev, SYS_RES_IOPORT, &sc->sc_iorid,
315 	    APS_ADDR_BASE, APS_ADDR_BASE + APS_ADDR_SIZE - 1, APS_ADDR_SIZE,
316 	    RF_ACTIVE);
317 #else
318 	sc->sc_iores = bus_alloc_resource(dev, SYS_RES_IOPORT, &sc->sc_iorid,
319 	    0ul, ~0ul, APS_ADDR_SIZE, RF_ACTIVE);
320 #endif
321 	if (sc->sc_iores == NULL) {
322 		device_printf(dev, "can't map i/o space\n");
323 		return ENXIO;
324 	}
325 
326 	if (aps_init(sc->sc_iores)) {
327 		device_printf(dev, "failed to initialise\n");
328 		bus_release_resource(dev, SYS_RES_IOPORT, sc->sc_iorid, sc->sc_iores);
329 		return ENXIO;
330 	}
331 
332 	sc->sensors[APS_SENSOR_XACCEL].type = SENSOR_INTEGER;
333 	ksnprintf(sc->sensors[APS_SENSOR_XACCEL].desc,
334 	    sizeof(sc->sensors[APS_SENSOR_XACCEL].desc), "X_ACCEL");
335 
336 	sc->sensors[APS_SENSOR_YACCEL].type = SENSOR_INTEGER;
337 	ksnprintf(sc->sensors[APS_SENSOR_YACCEL].desc,
338 	    sizeof(sc->sensors[APS_SENSOR_YACCEL].desc), "Y_ACCEL");
339 
340 	sc->sensors[APS_SENSOR_TEMP1].type = SENSOR_TEMP;
341 	sc->sensors[APS_SENSOR_TEMP2].type = SENSOR_TEMP;
342 
343 	sc->sensors[APS_SENSOR_XVAR].type = SENSOR_INTEGER;
344 	ksnprintf(sc->sensors[APS_SENSOR_XVAR].desc,
345 	    sizeof(sc->sensors[APS_SENSOR_XVAR].desc), "X_VAR");
346 
347 	sc->sensors[APS_SENSOR_YVAR].type = SENSOR_INTEGER;
348 	ksnprintf(sc->sensors[APS_SENSOR_YVAR].desc,
349 	    sizeof(sc->sensors[APS_SENSOR_YVAR].desc), "Y_VAR");
350 
351 	sc->sensors[APS_SENSOR_KBACT].type = SENSOR_INDICATOR;
352 	ksnprintf(sc->sensors[APS_SENSOR_KBACT].desc,
353 	    sizeof(sc->sensors[APS_SENSOR_KBACT].desc), "Keyboard Active");
354 
355 	sc->sensors[APS_SENSOR_MSACT].type = SENSOR_INDICATOR;
356 	ksnprintf(sc->sensors[APS_SENSOR_MSACT].desc,
357 	    sizeof(sc->sensors[APS_SENSOR_MSACT].desc), "Mouse Active");
358 
359 	sc->sensors[APS_SENSOR_LIDOPEN].type = SENSOR_INDICATOR;
360 	ksnprintf(sc->sensors[APS_SENSOR_LIDOPEN].desc,
361 	    sizeof(sc->sensors[APS_SENSOR_LIDOPEN].desc), "Lid Open");
362 
363 	/* stop hiding and report to the authorities */
364 	strlcpy(sc->sensordev.xname, device_get_nameunit(dev),
365 	    sizeof(sc->sensordev.xname));
366 	for (int i = 0; i < APS_NUM_SENSORS ; i++)
367 		sensor_attach(&sc->sensordev, &sc->sensors[i]);
368 
369 	/* Refresh sensor data every 1 second */
370 	/* XXX: a more frequent refresh might be appropriate */
371 	if (sensor_task_register(sc, aps_refresh, 1)) {
372 		device_printf(dev, "unable to register update task\n");
373 		bus_release_resource(dev, SYS_RES_IOPORT, sc->sc_iorid, sc->sc_iores);
374 		return ENXIO;
375 	}
376 
377 	sensordev_install(&sc->sensordev);
378 	return 0;
379 }
380 
381 static int
382 aps_detach(struct device *dev)
383 {
384 	struct aps_softc *sc = device_get_softc(dev);
385 
386 	sensordev_deinstall(&sc->sensordev);
387 	sensor_task_unregister(sc);
388 	return bus_release_resource(dev, SYS_RES_IOPORT,
389 	    sc->sc_iorid, sc->sc_iores);
390 }
391 
392 static int
393 aps_init(struct resource *iores)
394 {
395 	unsigned char iobuf[16];
396 
397 	/* command 0x17/0x81: check EC */
398 	iobuf[APS_CMD] = 0x17;
399 	iobuf[APS_ARG1] = 0x81;
400 
401 	if (aps_do_io(iores, iobuf, APS_WRITE_1, APS_READ_3))
402 		return (1);
403 
404 	if (iobuf[APS_RET] != 0 ||iobuf[APS_ARG3] != 0)
405 		return (1);
406 
407 	/* Test values from the Linux driver */
408 	if ((iobuf[APS_ARG1] != 0 || iobuf[APS_ARG2] != 0x60) &&
409 	    (iobuf[APS_ARG1] != 1 || iobuf[APS_ARG2] != 0))
410 		return (1);
411 
412 	/* command 0x14: set power */
413 	iobuf[APS_CMD] = 0x14;
414 	iobuf[APS_ARG1] = 0x01;
415 
416 	if (aps_do_io(iores, iobuf, APS_WRITE_1, APS_READ_0))
417 		return (1);
418 
419 	if (iobuf[APS_RET] != 0)
420 		return (1);
421 
422 	/* command 0x10: set config (sample rate and order) */
423 	iobuf[APS_CMD] = 0x10;
424 	iobuf[APS_ARG1] = 0xc8;
425 	iobuf[APS_ARG2] = 0x00;
426 	iobuf[APS_ARG3] = 0x02;
427 
428 	if (aps_do_io(iores, iobuf, APS_WRITE_3, APS_READ_0))
429 		return (1);
430 
431 	if (iobuf[APS_RET] != 0)
432 		return (1);
433 
434 	/* command 0x11: refresh data */
435 	iobuf[APS_CMD] = 0x11;
436 	if (aps_do_io(iores, iobuf, APS_WRITE_0, APS_READ_1))
437 		return (1);
438 	if (iobuf[APS_ARG1] != 0)
439 		return (1);
440 
441 	return (0);
442 }
443 
444 static int
445 aps_read_data(struct aps_softc *sc)
446 {
447 	unsigned char iobuf[16];
448 
449 	/* command 0x11: refresh data */
450 	iobuf[APS_CMD] = 0x11;
451 	if (aps_do_io(sc->sc_iores, iobuf, APS_WRITE_0, APS_READ_ALL))
452 		return (1);
453 
454 	sc->aps_data.state = iobuf[APS_STATE];
455 	sc->aps_data.x_accel = iobuf[APS_XACCEL] + 256 * iobuf[APS_XACCEL + 1];
456 	sc->aps_data.y_accel = iobuf[APS_YACCEL] + 256 * iobuf[APS_YACCEL + 1];
457 	sc->aps_data.temp1 = iobuf[APS_TEMP];
458 	sc->aps_data.x_var = iobuf[APS_XVAR] + 256 * iobuf[APS_XVAR + 1];
459 	sc->aps_data.y_var = iobuf[APS_YVAR] + 256 * iobuf[APS_YVAR + 1];
460 	sc->aps_data.temp2 = iobuf[APS_TEMP2];
461 	sc->aps_data.input = iobuf[APS_INPUT];
462 
463 	return (0);
464 }
465 
466 static void
467 aps_refresh_sensor_data(struct aps_softc *sc)
468 {
469 	int64_t temp;
470 
471 	if (aps_read_data(sc)) {
472 		for (int i = 0; i < APS_NUM_SENSORS; i++)
473 			sc->sensors[i].flags |= SENSOR_FINVALID;
474 		return;
475 	}
476 
477 	sc->sensors[APS_SENSOR_XACCEL].value = sc->aps_data.x_accel;
478 	sc->sensors[APS_SENSOR_YACCEL].value = sc->aps_data.y_accel;
479 
480 	/* convert to micro (mu) degrees */
481 	temp = sc->aps_data.temp1 * 1000000;
482 	/* convert to kelvin */
483 	temp += 273150000;
484 	sc->sensors[APS_SENSOR_TEMP1].value = temp;
485 
486 	/* convert to micro (mu) degrees */
487 	temp = sc->aps_data.temp2 * 1000000;
488 	/* convert to kelvin */
489 	temp += 273150000;
490 	sc->sensors[APS_SENSOR_TEMP2].value = temp;
491 
492 	sc->sensors[APS_SENSOR_XVAR].value = sc->aps_data.x_var;
493 	sc->sensors[APS_SENSOR_YVAR].value = sc->aps_data.y_var;
494 	sc->sensors[APS_SENSOR_KBACT].value =
495 	    (sc->aps_data.input &  APS_INPUT_KB) ? 1 : 0;
496 	sc->sensors[APS_SENSOR_MSACT].value =
497 	    (sc->aps_data.input & APS_INPUT_MS) ? 1 : 0;
498 	sc->sensors[APS_SENSOR_LIDOPEN].value =
499 	    (sc->aps_data.input & APS_INPUT_LIDOPEN) ? 1 : 0;
500 
501 	for (int i = 0; i < APS_NUM_SENSORS; i++)
502 		sc->sensors[i].flags &= ~SENSOR_FINVALID;
503 }
504 
505 static void
506 aps_refresh(void *arg)
507 {
508 	struct aps_softc *sc = (struct aps_softc *)arg;
509 
510 	aps_refresh_sensor_data(sc);
511 }
512 
513 static int
514 aps_resume(struct device *dev)
515 {
516 	struct aps_softc *sc = device_get_softc(dev);
517 	unsigned char iobuf[16];
518 
519 	/*
520 	 * Redo the init sequence on resume, because APS is
521 	 * as forgetful as it is deaf.
522 	 */
523 
524 	/* get APS mode */
525 	iobuf[APS_CMD] = 0x13;
526 	if (aps_do_io(sc->sc_iores, iobuf, APS_WRITE_0, APS_READ_1)
527 	    || aps_init(sc->sc_iores)) {
528 		device_printf(sc->sc_dev, "failed to wake up\n");
529 		return EIO;
530 	}
531 
532 	sensor_task_register(sc, aps_refresh, 1);
533 	return 0;
534 }
535 
536 static int
537 aps_suspend(struct device *dev)
538 {
539 	struct aps_softc *sc = device_get_softc(dev);
540 
541 	for (int i = 0; i < APS_NUM_SENSORS; i++)
542 		sc->sensors[i].flags |= SENSOR_FINVALID;
543 	sensor_task_unregister(sc);
544 	return 0;
545 }
546