xref: /dragonfly/sys/dev/powermng/aps/aps.c (revision 0ca59c34)
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/kernel.h>
29 #include <sys/module.h>
30 #include <sys/rman.h>
31 #include <sys/sensors.h>
32 
33 #include <bus/isa/isavar.h>
34 
35 #if defined(APSDEBUG)
36 #define DPRINTF(x)		do { kprintf x; } while (0)
37 #else
38 #define DPRINTF(x)
39 #endif
40 
41 
42 /*
43  * EC interface on Thinkpad Laptops, from Linux HDAPS driver notes.
44  * From Renesans H8S/2140B Group Hardware Manual
45  * http://documentation.renesas.com/eng/products/mpumcu/rej09b0300_2140bhm.pdf
46  *
47  * EC uses LPC Channel 3 registers TWR0..15
48  */
49 
50 /* STR3 status register */
51 #define APS_STR3		0x04
52 
53 #define APS_STR3_IBF3B	0x80	/* Input buffer full (host->slave) */
54 #define APS_STR3_OBF3B	0x40	/* Output buffer full (slave->host)*/
55 #define APS_STR3_MWMF	0x20	/* Master write mode */
56 #define APS_STR3_SWMF	0x10	/* Slave write mode */
57 
58 
59 /* Base address of TWR registers */
60 #define APS_TWR_BASE		0x10
61 #define APS_TWR_RET		0x1f
62 
63 /* TWR registers */
64 #define APS_CMD			0x00
65 #define APS_ARG1		0x01
66 #define APS_ARG2		0x02
67 #define APS_ARG3		0x03
68 #define APS_RET			0x0f
69 
70 /* Sensor values */
71 #define APS_STATE		0x01
72 #define	APS_XACCEL		0x02
73 #define APS_YACCEL		0x04
74 #define APS_TEMP		0x06
75 #define	APS_XVAR		0x07
76 #define APS_YVAR		0x09
77 #define APS_TEMP2		0x0b
78 #define APS_UNKNOWN		0x0c
79 #define APS_INPUT		0x0d
80 
81 /* write masks for I/O, send command + 0-3 arguments*/
82 #define APS_WRITE_0		0x0001
83 #define APS_WRITE_1		0x0003
84 #define APS_WRITE_2		0x0007
85 #define APS_WRITE_3		0x000f
86 
87 /* read masks for I/O, read 0-3 values (skip command byte) */
88 #define APS_READ_0		0x0000
89 #define APS_READ_1		0x0002
90 #define APS_READ_2		0x0006
91 #define APS_READ_3		0x000e
92 
93 #define APS_READ_RET		0x8000
94 #define APS_READ_ALL		0xffff
95 
96 /* Bit definitions for APS_INPUT value */
97 #define APS_INPUT_KB		(1 << 5)
98 #define APS_INPUT_MS		(1 << 6)
99 #define APS_INPUT_LIDOPEN	(1 << 7)
100 
101 #define APS_ADDR_BASE		0x1600
102 #define APS_ADDR_SIZE		0x1f
103 
104 struct aps_sensor_rec {
105 	u_int8_t	state;
106 	u_int16_t	x_accel;
107 	u_int16_t	y_accel;
108 	u_int8_t	temp1;
109 	u_int16_t	x_var;
110 	u_int16_t	y_var;
111 	u_int8_t	temp2;
112 	u_int8_t	unk;
113 	u_int8_t	input;
114 };
115 
116 #define APS_NUM_SENSORS		9
117 
118 #define APS_SENSOR_XACCEL	0
119 #define APS_SENSOR_YACCEL	1
120 #define APS_SENSOR_XVAR		2
121 #define APS_SENSOR_YVAR		3
122 #define APS_SENSOR_TEMP1	4
123 #define APS_SENSOR_TEMP2	5
124 #define APS_SENSOR_KBACT	6
125 #define APS_SENSOR_MSACT	7
126 #define APS_SENSOR_LIDOPEN	8
127 
128 struct aps_softc {
129 	struct device		*sc_dev;
130 
131 	struct resource		*sc_iores;
132 	int			sc_iorid;
133 
134 	struct ksensor		sensors[APS_NUM_SENSORS];
135 	struct ksensordev	sensordev;
136 
137 	struct aps_sensor_rec	aps_data;
138 };
139 
140 static void	aps_identify(driver_t *, struct device *);
141 static int	aps_probe(struct device *);
142 static int	aps_attach(struct device *);
143 static int	aps_detach(struct device *);
144 
145 static int	aps_resume(struct device *);
146 static int	aps_suspend(struct device *);
147 
148 static int	aps_init(struct resource *);
149 static int	aps_read_data(struct aps_softc *);
150 static void	aps_refresh_sensor_data(struct aps_softc *);
151 static void	aps_refresh(void *);
152 static int	aps_do_io(struct resource *, unsigned char *, int, int);
153 
154 static device_method_t aps_methods[] = {
155 	DEVMETHOD(device_identify,	aps_identify),
156 	DEVMETHOD(device_probe,		aps_probe),
157 	DEVMETHOD(device_attach,	aps_attach),
158 	DEVMETHOD(device_detach,	aps_detach),
159 
160 	DEVMETHOD(device_resume,	aps_resume),
161 	DEVMETHOD(device_suspend,	aps_suspend),
162 	{ NULL, NULL }
163 };
164 
165 static driver_t aps_driver = {
166 	"aps",
167 	aps_methods,
168 	sizeof(struct aps_softc)
169 };
170 
171 static devclass_t aps_devclass;
172 
173 DRIVER_MODULE(aps, isa, aps_driver, aps_devclass, NULL, NULL);
174 
175 
176 
177 /* properly communicate with the controller, writing a set of memory
178  * locations and reading back another set  */
179 static int
180 aps_do_io(struct resource *iores, unsigned char *buf, int wmask, int rmask)
181 {
182 	bus_space_tag_t iot = rman_get_bustag(iores);
183 	bus_space_handle_t ioh = rman_get_bushandle(iores);
184 	int bp, stat, n;
185 
186 	DPRINTF(("aps_do_io: CMD: 0x%02x, wmask: 0x%04x, rmask: 0x%04x\n",
187 	       buf[0], wmask, rmask));
188 
189 	/* write init byte using arbitration */
190 	for (n = 0; n < 100; n++) {
191 		stat = bus_space_read_1(iot, ioh, APS_STR3);
192 		if (stat & (APS_STR3_OBF3B | APS_STR3_SWMF)) {
193 			bus_space_read_1(iot, ioh, APS_TWR_RET);
194 			continue;
195 		}
196 		bus_space_write_1(iot, ioh, APS_TWR_BASE, buf[0]);
197 		stat = bus_space_read_1(iot, ioh, APS_STR3);
198 		if (stat & (APS_STR3_MWMF))
199 			break;
200 		DRIVERSLEEP(1);
201 	}
202 
203 	if (n == 100) {
204 		DPRINTF(("aps_do_io: Failed to get bus\n"));
205 		return (1);
206 	}
207 
208 	/* write data bytes, init already sent */
209 	/* make sure last bye is always written as this will trigger slave */
210 	wmask |= APS_READ_RET;
211 	buf[APS_RET] = 0x01;
212 
213 	for (n = 1, bp = 2; n < 16; bp <<= 1, n++) {
214 		if (wmask & bp) {
215 			bus_space_write_1(iot, ioh, APS_TWR_BASE + n, buf[n]);
216 			DPRINTF(("aps_do_io:  write %2d 0x%02x\n", n, buf[n]));
217 		}
218 	}
219 
220 	for (n = 0; n < 100; n++) {
221 		stat = bus_space_read_1(iot, ioh, APS_STR3);
222 		if (stat & (APS_STR3_OBF3B))
223 			break;
224 		DRIVERSLEEP(500);
225 	}
226 
227 	if (n == 100) {
228 		DPRINTF(("aps_do_io: timeout waiting response\n"));
229 		return (1);
230 	}
231 	/* wait for data available */
232 	/* make sure to read the final byte to clear status */
233 	rmask |= APS_READ_RET;
234 
235 	/* read cmd and data bytes */
236 	for (n = 0, bp = 1; n < 16; bp <<= 1, n++) {
237 		if (rmask & bp) {
238 			buf[n] = bus_space_read_1(iot, ioh, APS_TWR_BASE + n);
239 			DPRINTF(("aps_do_io:  read %2d 0x%02x\n", n, buf[n]));
240 		}
241 	}
242 
243 	return (0);
244 }
245 
246 /* for hints, see /sys/bus/isa/isahint.c */
247 
248 static void
249 aps_identify(driver_t *driver, struct device *parent)
250 {
251 	struct device *child;
252 
253 	child = device_find_child(parent, driver->name, -1);
254 	if (child != NULL) {
255 		if (isa_get_portsize(child) == 0) {
256 			// aps(4) must have been compiled into the kernel
257 			if (bootverbose)
258 				kprintf("%s: will specify the port\n",
259 				    __func__);
260 		} else if (isa_get_port(child) != APS_ADDR_BASE)
261 			kprintf("%s: will overwrite specified port\n",
262 			    __func__);
263 		else {
264 			if (isa_get_portsize(child) == APS_ADDR_SIZE) {
265 				// aps.ko must have been reloaded
266 				kprintf("%s: already have been invoked\n",
267 				    __func__);
268 				return;
269 			} else
270 				kprintf("%s: will amend the portsize\n",
271 				    __func__);
272 		}
273 	} else {
274 		// first invocation of `kldload aps.ko`
275 		kprintf("%s: creating a new %s\n",
276 		    __func__, driver->name);
277 		child = BUS_ADD_CHILD(parent, parent, ISA_ORDER_PNP,
278 		    driver->name, -1);
279 		if (child == NULL) {
280 			kprintf("%s: cannot add child\n", __func__);
281 			return;
282 		}
283 	}
284 	if (bus_set_resource(child, SYS_RES_IOPORT, 0,
285 		APS_ADDR_BASE, APS_ADDR_SIZE, -1))
286 		kprintf("%s: cannot set resource\n", __func__);
287 }
288 
289 static int
290 aps_probe(struct device *dev)
291 {
292 	struct resource *iores;
293 	int iorid = 0;
294 	u_int8_t cr;
295 	unsigned char iobuf[16];
296 
297 #if defined(APSDEBUG) || defined(KLD_MODULE)
298 	device_printf(dev, "%s: port 0x%x\n", __func__, isa_get_port(dev));
299 #endif
300 
301 	if (device_get_unit(dev) != 0)
302 		return ENXIO;
303 
304 	iores = bus_alloc_resource_any(dev, SYS_RES_IOPORT, &iorid, RF_ACTIVE);
305 	if (iores == NULL) {
306 		DPRINTF(("aps: can't map i/o space\n"));
307 		return ENXIO;
308 	}
309 
310 
311 	/* See if this machine has APS */
312 
313 	/* get APS mode */
314 	iobuf[APS_CMD] = 0x13;
315 	if (aps_do_io(iores, iobuf, APS_WRITE_0, APS_READ_1)) {
316 		bus_release_resource(dev, SYS_RES_IOPORT, iorid, iores);
317 		return ENXIO;
318 	}
319 
320 	/*
321 	 * Observed values from Linux driver:
322 	 * 0x01: T42
323 	 * 0x02: chip already initialised
324 	 * 0x03: T41
325 	 * 0x05: T61
326 	 */
327 
328 	cr = iobuf[APS_ARG1];
329 
330 	bus_release_resource(dev, SYS_RES_IOPORT, iorid, iores);
331 	DPRINTF(("aps: state register 0x%x\n", cr));
332 
333 	if (iobuf[APS_RET] != 0 || cr < 1 || cr > 5) {
334 		DPRINTF(("aps: unsupported state %d\n", cr));
335 		return ENXIO;
336 	}
337 	device_set_desc(dev, "ThinkPad Active Protection System");
338 	return 0;
339 }
340 
341 static int
342 aps_attach(struct device *dev)
343 {
344 	struct aps_softc *sc = device_get_softc(dev);
345 
346 	sc->sc_dev = dev;
347 	sc->sc_iores = bus_alloc_resource_any(dev, SYS_RES_IOPORT,
348 	    &sc->sc_iorid, RF_ACTIVE);
349 	if (sc->sc_iores == NULL) {
350 		device_printf(dev, "can't map i/o space\n");
351 		return ENXIO;
352 	}
353 
354 	if (aps_init(sc->sc_iores)) {
355 		device_printf(dev, "failed to initialise\n");
356 		bus_release_resource(dev, SYS_RES_IOPORT, sc->sc_iorid, sc->sc_iores);
357 		return ENXIO;
358 	}
359 
360 	sc->sensors[APS_SENSOR_XACCEL].type = SENSOR_INTEGER;
361 	ksnprintf(sc->sensors[APS_SENSOR_XACCEL].desc,
362 	    sizeof(sc->sensors[APS_SENSOR_XACCEL].desc), "X_ACCEL");
363 
364 	sc->sensors[APS_SENSOR_YACCEL].type = SENSOR_INTEGER;
365 	ksnprintf(sc->sensors[APS_SENSOR_YACCEL].desc,
366 	    sizeof(sc->sensors[APS_SENSOR_YACCEL].desc), "Y_ACCEL");
367 
368 	sc->sensors[APS_SENSOR_TEMP1].type = SENSOR_TEMP;
369 	sc->sensors[APS_SENSOR_TEMP2].type = SENSOR_TEMP;
370 
371 	sc->sensors[APS_SENSOR_XVAR].type = SENSOR_INTEGER;
372 	ksnprintf(sc->sensors[APS_SENSOR_XVAR].desc,
373 	    sizeof(sc->sensors[APS_SENSOR_XVAR].desc), "X_VAR");
374 
375 	sc->sensors[APS_SENSOR_YVAR].type = SENSOR_INTEGER;
376 	ksnprintf(sc->sensors[APS_SENSOR_YVAR].desc,
377 	    sizeof(sc->sensors[APS_SENSOR_YVAR].desc), "Y_VAR");
378 
379 	sc->sensors[APS_SENSOR_KBACT].type = SENSOR_INDICATOR;
380 	ksnprintf(sc->sensors[APS_SENSOR_KBACT].desc,
381 	    sizeof(sc->sensors[APS_SENSOR_KBACT].desc), "Keyboard Active");
382 
383 	sc->sensors[APS_SENSOR_MSACT].type = SENSOR_INDICATOR;
384 	ksnprintf(sc->sensors[APS_SENSOR_MSACT].desc,
385 	    sizeof(sc->sensors[APS_SENSOR_MSACT].desc), "Mouse Active");
386 
387 	sc->sensors[APS_SENSOR_LIDOPEN].type = SENSOR_INDICATOR;
388 	ksnprintf(sc->sensors[APS_SENSOR_LIDOPEN].desc,
389 	    sizeof(sc->sensors[APS_SENSOR_LIDOPEN].desc), "Lid Open");
390 
391 	/* stop hiding and report to the authorities */
392 	strlcpy(sc->sensordev.xname, device_get_nameunit(dev),
393 	    sizeof(sc->sensordev.xname));
394 	for (int i = 0; i < APS_NUM_SENSORS ; i++)
395 		sensor_attach(&sc->sensordev, &sc->sensors[i]);
396 
397 	/* Refresh sensor data every 1 second */
398 	/* XXX: a more frequent refresh might be appropriate */
399 	sensor_task_register(sc, aps_refresh, 1);
400 
401 	sensordev_install(&sc->sensordev);
402 	return 0;
403 }
404 
405 static int
406 aps_detach(struct device *dev)
407 {
408 	struct aps_softc *sc = device_get_softc(dev);
409 
410 	sensordev_deinstall(&sc->sensordev);
411 	sensor_task_unregister(sc);
412 	return bus_release_resource(dev, SYS_RES_IOPORT,
413 	    sc->sc_iorid, sc->sc_iores);
414 }
415 
416 static int
417 aps_init(struct resource *iores)
418 {
419 	unsigned char iobuf[16];
420 
421 	/* command 0x17/0x81: check EC */
422 	iobuf[APS_CMD] = 0x17;
423 	iobuf[APS_ARG1] = 0x81;
424 
425 	if (aps_do_io(iores, iobuf, APS_WRITE_1, APS_READ_3))
426 		return (1);
427 
428 	if (iobuf[APS_RET] != 0 ||iobuf[APS_ARG3] != 0)
429 		return (1);
430 
431 	/* Test values from the Linux driver */
432 	if ((iobuf[APS_ARG1] != 0 || iobuf[APS_ARG2] != 0x60) &&
433 	    (iobuf[APS_ARG1] != 1 || iobuf[APS_ARG2] != 0))
434 		return (1);
435 
436 	/* command 0x14: set power */
437 	iobuf[APS_CMD] = 0x14;
438 	iobuf[APS_ARG1] = 0x01;
439 
440 	if (aps_do_io(iores, iobuf, APS_WRITE_1, APS_READ_0))
441 		return (1);
442 
443 	if (iobuf[APS_RET] != 0)
444 		return (1);
445 
446 	/* command 0x10: set config (sample rate and order) */
447 	iobuf[APS_CMD] = 0x10;
448 	iobuf[APS_ARG1] = 0xc8;
449 	iobuf[APS_ARG2] = 0x00;
450 	iobuf[APS_ARG3] = 0x02;
451 
452 	if (aps_do_io(iores, iobuf, APS_WRITE_3, APS_READ_0))
453 		return (1);
454 
455 	if (iobuf[APS_RET] != 0)
456 		return (1);
457 
458 	/* command 0x11: refresh data */
459 	iobuf[APS_CMD] = 0x11;
460 	if (aps_do_io(iores, iobuf, APS_WRITE_0, APS_READ_1))
461 		return (1);
462 	if (iobuf[APS_ARG1] != 0)
463 		return (1);
464 
465 	return (0);
466 }
467 
468 static int
469 aps_read_data(struct aps_softc *sc)
470 {
471 	unsigned char iobuf[16];
472 
473 	/* command 0x11: refresh data */
474 	iobuf[APS_CMD] = 0x11;
475 	if (aps_do_io(sc->sc_iores, iobuf, APS_WRITE_0, APS_READ_ALL))
476 		return (1);
477 
478 	sc->aps_data.state = iobuf[APS_STATE];
479 	sc->aps_data.x_accel = iobuf[APS_XACCEL] + 256 * iobuf[APS_XACCEL + 1];
480 	sc->aps_data.y_accel = iobuf[APS_YACCEL] + 256 * iobuf[APS_YACCEL + 1];
481 	sc->aps_data.temp1 = iobuf[APS_TEMP];
482 	sc->aps_data.x_var = iobuf[APS_XVAR] + 256 * iobuf[APS_XVAR + 1];
483 	sc->aps_data.y_var = iobuf[APS_YVAR] + 256 * iobuf[APS_YVAR + 1];
484 	sc->aps_data.temp2 = iobuf[APS_TEMP2];
485 	sc->aps_data.input = iobuf[APS_INPUT];
486 
487 	return (0);
488 }
489 
490 static void
491 aps_refresh_sensor_data(struct aps_softc *sc)
492 {
493 	int64_t temp;
494 
495 	if (aps_read_data(sc)) {
496 		for (int i = 0; i < APS_NUM_SENSORS; i++)
497 			sc->sensors[i].flags |= SENSOR_FINVALID;
498 		return;
499 	}
500 
501 	sc->sensors[APS_SENSOR_XACCEL].value = sc->aps_data.x_accel;
502 	sc->sensors[APS_SENSOR_YACCEL].value = sc->aps_data.y_accel;
503 
504 	/* convert to micro (mu) degrees */
505 	temp = sc->aps_data.temp1 * 1000000;
506 	/* convert to kelvin */
507 	temp += 273150000;
508 	sc->sensors[APS_SENSOR_TEMP1].value = temp;
509 
510 	/* convert to micro (mu) degrees */
511 	temp = sc->aps_data.temp2 * 1000000;
512 	/* convert to kelvin */
513 	temp += 273150000;
514 	sc->sensors[APS_SENSOR_TEMP2].value = temp;
515 
516 	sc->sensors[APS_SENSOR_XVAR].value = sc->aps_data.x_var;
517 	sc->sensors[APS_SENSOR_YVAR].value = sc->aps_data.y_var;
518 	sc->sensors[APS_SENSOR_KBACT].value =
519 	    (sc->aps_data.input &  APS_INPUT_KB) ? 1 : 0;
520 	sc->sensors[APS_SENSOR_MSACT].value =
521 	    (sc->aps_data.input & APS_INPUT_MS) ? 1 : 0;
522 	sc->sensors[APS_SENSOR_LIDOPEN].value =
523 	    (sc->aps_data.input & APS_INPUT_LIDOPEN) ? 1 : 0;
524 
525 	for (int i = 0; i < APS_NUM_SENSORS; i++)
526 		sc->sensors[i].flags &= ~SENSOR_FINVALID;
527 }
528 
529 static void
530 aps_refresh(void *arg)
531 {
532 	struct aps_softc *sc = (struct aps_softc *)arg;
533 
534 	aps_refresh_sensor_data(sc);
535 }
536 
537 static int
538 aps_resume(struct device *dev)
539 {
540 	struct aps_softc *sc = device_get_softc(dev);
541 	unsigned char iobuf[16];
542 
543 	/*
544 	 * Redo the init sequence on resume, because APS is
545 	 * as forgetful as it is deaf.
546 	 */
547 
548 	/* get APS mode */
549 	iobuf[APS_CMD] = 0x13;
550 	if (aps_do_io(sc->sc_iores, iobuf, APS_WRITE_0, APS_READ_1)
551 	    || aps_init(sc->sc_iores)) {
552 		device_printf(sc->sc_dev, "failed to wake up\n");
553 		return EIO;
554 	}
555 
556 	sensor_task_register(sc, aps_refresh, 1);
557 	return 0;
558 }
559 
560 static int
561 aps_suspend(struct device *dev)
562 {
563 	struct aps_softc *sc = device_get_softc(dev);
564 
565 	for (int i = 0; i < APS_NUM_SENSORS; i++)
566 		sc->sensors[i].flags |= SENSOR_FINVALID;
567 	sensor_task_unregister(sc);
568 	return 0;
569 }
570