xref: /dflybsd-src/sys/dev/powermng/aps/aps.c (revision 5d302545124b16bb6e9f48d720cba81afba1adb3)
10d06db5aSConstantine A. Murenin /*	$OpenBSD: aps.c,v 1.19 2009/05/24 16:40:18 jsg Exp $	*/
20d06db5aSConstantine A. Murenin /*
30d06db5aSConstantine A. Murenin  * Copyright (c) 2005 Jonathan Gray <jsg@openbsd.org>
40d06db5aSConstantine A. Murenin  * Copyright (c) 2008 Can Erkin Acar <canacar@openbsd.org>
523e32507SConstantine A. Murenin  * Copyright (c) 2010 Constantine A. Murenin <cnst++@dragonflybsd.org>
60d06db5aSConstantine A. Murenin  *
70d06db5aSConstantine A. Murenin  * Permission to use, copy, modify, and distribute this software for any
80d06db5aSConstantine A. Murenin  * purpose with or without fee is hereby granted, provided that the above
90d06db5aSConstantine A. Murenin  * copyright notice and this permission notice appear in all copies.
100d06db5aSConstantine A. Murenin  *
110d06db5aSConstantine A. Murenin  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
120d06db5aSConstantine A. Murenin  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
130d06db5aSConstantine A. Murenin  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
140d06db5aSConstantine A. Murenin  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
150d06db5aSConstantine A. Murenin  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
160d06db5aSConstantine A. Murenin  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
170d06db5aSConstantine A. Murenin  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
180d06db5aSConstantine A. Murenin  */
190d06db5aSConstantine A. Murenin 
200d06db5aSConstantine A. Murenin /*
210d06db5aSConstantine A. Murenin  * A driver for the ThinkPad Active Protection System based on notes from
220d06db5aSConstantine A. Murenin  * http://www.almaden.ibm.com/cs/people/marksmith/tpaps.html
230d06db5aSConstantine A. Murenin  */
240d06db5aSConstantine A. Murenin 
250d06db5aSConstantine A. Murenin #include <sys/param.h>
260d06db5aSConstantine A. Murenin #include <sys/systm.h>
2723e32507SConstantine A. Murenin #include <sys/bus.h>
28017d4aa0SConstantine A. Murenin #include <sys/kernel.h>
29017d4aa0SConstantine A. Murenin #include <sys/module.h>
3023e32507SConstantine A. Murenin #include <sys/rman.h>
31017d4aa0SConstantine A. Murenin #include <sys/sensors.h>
3223e32507SConstantine A. Murenin 
3323e32507SConstantine A. Murenin #include <bus/isa/isavar.h>
340d06db5aSConstantine A. Murenin 
350d06db5aSConstantine A. Murenin #if defined(APSDEBUG)
3623e32507SConstantine A. Murenin #define DPRINTF(x)		do { kprintf x; } while (0)
370d06db5aSConstantine A. Murenin #else
380d06db5aSConstantine A. Murenin #define DPRINTF(x)
390d06db5aSConstantine A. Murenin #endif
400d06db5aSConstantine A. Murenin 
410d06db5aSConstantine A. Murenin 
420d06db5aSConstantine A. Murenin /*
430d06db5aSConstantine A. Murenin  * EC interface on Thinkpad Laptops, from Linux HDAPS driver notes.
440d06db5aSConstantine A. Murenin  * From Renesans H8S/2140B Group Hardware Manual
450d06db5aSConstantine A. Murenin  * http://documentation.renesas.com/eng/products/mpumcu/rej09b0300_2140bhm.pdf
460d06db5aSConstantine A. Murenin  *
470d06db5aSConstantine A. Murenin  * EC uses LPC Channel 3 registers TWR0..15
480d06db5aSConstantine A. Murenin  */
490d06db5aSConstantine A. Murenin 
500d06db5aSConstantine A. Murenin /* STR3 status register */
510d06db5aSConstantine A. Murenin #define APS_STR3		0x04
520d06db5aSConstantine A. Murenin 
530d06db5aSConstantine A. Murenin #define APS_STR3_IBF3B	0x80	/* Input buffer full (host->slave) */
540d06db5aSConstantine A. Murenin #define APS_STR3_OBF3B	0x40	/* Output buffer full (slave->host)*/
550d06db5aSConstantine A. Murenin #define APS_STR3_MWMF	0x20	/* Master write mode */
560d06db5aSConstantine A. Murenin #define APS_STR3_SWMF	0x10	/* Slave write mode */
570d06db5aSConstantine A. Murenin 
580d06db5aSConstantine A. Murenin 
590d06db5aSConstantine A. Murenin /* Base address of TWR registers */
600d06db5aSConstantine A. Murenin #define APS_TWR_BASE		0x10
610d06db5aSConstantine A. Murenin #define APS_TWR_RET		0x1f
620d06db5aSConstantine A. Murenin 
630d06db5aSConstantine A. Murenin /* TWR registers */
640d06db5aSConstantine A. Murenin #define APS_CMD			0x00
650d06db5aSConstantine A. Murenin #define APS_ARG1		0x01
660d06db5aSConstantine A. Murenin #define APS_ARG2		0x02
670d06db5aSConstantine A. Murenin #define APS_ARG3		0x03
680d06db5aSConstantine A. Murenin #define APS_RET			0x0f
690d06db5aSConstantine A. Murenin 
700d06db5aSConstantine A. Murenin /* Sensor values */
710d06db5aSConstantine A. Murenin #define APS_STATE		0x01
720d06db5aSConstantine A. Murenin #define	APS_XACCEL		0x02
730d06db5aSConstantine A. Murenin #define APS_YACCEL		0x04
740d06db5aSConstantine A. Murenin #define APS_TEMP		0x06
750d06db5aSConstantine A. Murenin #define	APS_XVAR		0x07
760d06db5aSConstantine A. Murenin #define APS_YVAR		0x09
770d06db5aSConstantine A. Murenin #define APS_TEMP2		0x0b
780d06db5aSConstantine A. Murenin #define APS_UNKNOWN		0x0c
790d06db5aSConstantine A. Murenin #define APS_INPUT		0x0d
800d06db5aSConstantine A. Murenin 
810d06db5aSConstantine A. Murenin /* write masks for I/O, send command + 0-3 arguments*/
820d06db5aSConstantine A. Murenin #define APS_WRITE_0		0x0001
830d06db5aSConstantine A. Murenin #define APS_WRITE_1		0x0003
840d06db5aSConstantine A. Murenin #define APS_WRITE_2		0x0007
850d06db5aSConstantine A. Murenin #define APS_WRITE_3		0x000f
860d06db5aSConstantine A. Murenin 
870d06db5aSConstantine A. Murenin /* read masks for I/O, read 0-3 values (skip command byte) */
880d06db5aSConstantine A. Murenin #define APS_READ_0		0x0000
890d06db5aSConstantine A. Murenin #define APS_READ_1		0x0002
900d06db5aSConstantine A. Murenin #define APS_READ_2		0x0006
910d06db5aSConstantine A. Murenin #define APS_READ_3		0x000e
920d06db5aSConstantine A. Murenin 
930d06db5aSConstantine A. Murenin #define APS_READ_RET		0x8000
940d06db5aSConstantine A. Murenin #define APS_READ_ALL		0xffff
950d06db5aSConstantine A. Murenin 
960d06db5aSConstantine A. Murenin /* Bit definitions for APS_INPUT value */
970d06db5aSConstantine A. Murenin #define APS_INPUT_KB		(1 << 5)
980d06db5aSConstantine A. Murenin #define APS_INPUT_MS		(1 << 6)
990d06db5aSConstantine A. Murenin #define APS_INPUT_LIDOPEN	(1 << 7)
1000d06db5aSConstantine A. Murenin 
10123e32507SConstantine A. Murenin #define APS_ADDR_BASE		0x1600
1020d06db5aSConstantine A. Murenin #define APS_ADDR_SIZE		0x1f
1030d06db5aSConstantine A. Murenin 
10423e32507SConstantine A. Murenin struct aps_sensor_rec {
1050d06db5aSConstantine A. Murenin 	u_int8_t	state;
1060d06db5aSConstantine A. Murenin 	u_int16_t	x_accel;
1070d06db5aSConstantine A. Murenin 	u_int16_t	y_accel;
1080d06db5aSConstantine A. Murenin 	u_int8_t	temp1;
1090d06db5aSConstantine A. Murenin 	u_int16_t	x_var;
1100d06db5aSConstantine A. Murenin 	u_int16_t	y_var;
1110d06db5aSConstantine A. Murenin 	u_int8_t	temp2;
1120d06db5aSConstantine A. Murenin 	u_int8_t	unk;
1130d06db5aSConstantine A. Murenin 	u_int8_t	input;
1140d06db5aSConstantine A. Murenin };
1150d06db5aSConstantine A. Murenin 
1160d06db5aSConstantine A. Murenin #define APS_NUM_SENSORS		9
1170d06db5aSConstantine A. Murenin 
1180d06db5aSConstantine A. Murenin #define APS_SENSOR_XACCEL	0
1190d06db5aSConstantine A. Murenin #define APS_SENSOR_YACCEL	1
1200d06db5aSConstantine A. Murenin #define APS_SENSOR_XVAR		2
1210d06db5aSConstantine A. Murenin #define APS_SENSOR_YVAR		3
1220d06db5aSConstantine A. Murenin #define APS_SENSOR_TEMP1	4
1230d06db5aSConstantine A. Murenin #define APS_SENSOR_TEMP2	5
1240d06db5aSConstantine A. Murenin #define APS_SENSOR_KBACT	6
1250d06db5aSConstantine A. Murenin #define APS_SENSOR_MSACT	7
1260d06db5aSConstantine A. Murenin #define APS_SENSOR_LIDOPEN	8
1270d06db5aSConstantine A. Murenin 
1280d06db5aSConstantine A. Murenin struct aps_softc {
129*5d302545SFrançois Tigeot 	device_t		sc_dev;
1300d06db5aSConstantine A. Murenin 
13123e32507SConstantine A. Murenin 	struct resource		*sc_iores;
13223e32507SConstantine A. Murenin 	int			sc_iorid;
1330d06db5aSConstantine A. Murenin 
1340d06db5aSConstantine A. Murenin 	struct ksensor		sensors[APS_NUM_SENSORS];
1350d06db5aSConstantine A. Murenin 	struct ksensordev	sensordev;
1360d06db5aSConstantine A. Murenin 
13723e32507SConstantine A. Murenin 	struct aps_sensor_rec	aps_data;
1380d06db5aSConstantine A. Murenin };
1390d06db5aSConstantine A. Murenin 
140*5d302545SFrançois Tigeot static void	aps_identify(driver_t *, device_t);
141*5d302545SFrançois Tigeot static int	aps_probe(device_t);
142*5d302545SFrançois Tigeot static int	aps_attach(device_t);
143*5d302545SFrançois Tigeot static int	aps_detach(device_t);
1440d06db5aSConstantine A. Murenin 
145*5d302545SFrançois Tigeot static int	aps_resume(device_t);
146*5d302545SFrançois Tigeot static int	aps_suspend(device_t);
1470d06db5aSConstantine A. Murenin 
14823e32507SConstantine A. Murenin static int	aps_init(struct resource *);
14923e32507SConstantine A. Murenin static int	aps_read_data(struct aps_softc *);
15023e32507SConstantine A. Murenin static void	aps_refresh_sensor_data(struct aps_softc *);
15123e32507SConstantine A. Murenin static void	aps_refresh(void *);
15223e32507SConstantine A. Murenin static int	aps_do_io(struct resource *, unsigned char *, int, int);
15323e32507SConstantine A. Murenin 
15423e32507SConstantine A. Murenin static device_method_t aps_methods[] = {
155017d4aa0SConstantine A. Murenin 	DEVMETHOD(device_identify,	aps_identify),
15623e32507SConstantine A. Murenin 	DEVMETHOD(device_probe,		aps_probe),
15723e32507SConstantine A. Murenin 	DEVMETHOD(device_attach,	aps_attach),
15823e32507SConstantine A. Murenin 	DEVMETHOD(device_detach,	aps_detach),
15923e32507SConstantine A. Murenin 
16023e32507SConstantine A. Murenin 	DEVMETHOD(device_resume,	aps_resume),
16123e32507SConstantine A. Murenin 	DEVMETHOD(device_suspend,	aps_suspend),
16223e32507SConstantine A. Murenin 	{ NULL, NULL }
1630d06db5aSConstantine A. Murenin };
1640d06db5aSConstantine A. Murenin 
16523e32507SConstantine A. Murenin static driver_t aps_driver = {
16623e32507SConstantine A. Murenin 	"aps",
16723e32507SConstantine A. Murenin 	aps_methods,
16823e32507SConstantine A. Murenin 	sizeof(struct aps_softc)
1690d06db5aSConstantine A. Murenin };
1700d06db5aSConstantine A. Murenin 
17123e32507SConstantine A. Murenin static devclass_t aps_devclass;
17223e32507SConstantine A. Murenin 
17323e32507SConstantine A. Murenin DRIVER_MODULE(aps, isa, aps_driver, aps_devclass, NULL, NULL);
1740d06db5aSConstantine A. Murenin 
1750d06db5aSConstantine A. Murenin 
1760d06db5aSConstantine A. Murenin 
1770d06db5aSConstantine A. Murenin /* properly communicate with the controller, writing a set of memory
1780d06db5aSConstantine A. Murenin  * locations and reading back another set  */
17923e32507SConstantine A. Murenin static int
aps_do_io(struct resource * iores,unsigned char * buf,int wmask,int rmask)18023e32507SConstantine A. Murenin aps_do_io(struct resource *iores, unsigned char *buf, int wmask, int rmask)
1810d06db5aSConstantine A. Murenin {
18223e32507SConstantine A. Murenin 	bus_space_tag_t iot = rman_get_bustag(iores);
18323e32507SConstantine A. Murenin 	bus_space_handle_t ioh = rman_get_bushandle(iores);
1840d06db5aSConstantine A. Murenin 	int bp, stat, n;
1850d06db5aSConstantine A. Murenin 
1860d06db5aSConstantine A. Murenin 	DPRINTF(("aps_do_io: CMD: 0x%02x, wmask: 0x%04x, rmask: 0x%04x\n",
1870d06db5aSConstantine A. Murenin 	       buf[0], wmask, rmask));
1880d06db5aSConstantine A. Murenin 
1890d06db5aSConstantine A. Murenin 	/* write init byte using arbitration */
1900d06db5aSConstantine A. Murenin 	for (n = 0; n < 100; n++) {
1910d06db5aSConstantine A. Murenin 		stat = bus_space_read_1(iot, ioh, APS_STR3);
1920d06db5aSConstantine A. Murenin 		if (stat & (APS_STR3_OBF3B | APS_STR3_SWMF)) {
1930d06db5aSConstantine A. Murenin 			bus_space_read_1(iot, ioh, APS_TWR_RET);
1940d06db5aSConstantine A. Murenin 			continue;
1950d06db5aSConstantine A. Murenin 		}
1960d06db5aSConstantine A. Murenin 		bus_space_write_1(iot, ioh, APS_TWR_BASE, buf[0]);
1970d06db5aSConstantine A. Murenin 		stat = bus_space_read_1(iot, ioh, APS_STR3);
1980d06db5aSConstantine A. Murenin 		if (stat & (APS_STR3_MWMF))
1990d06db5aSConstantine A. Murenin 			break;
200333f47f2SConstantine A. Murenin 		DRIVERSLEEP(1);
2010d06db5aSConstantine A. Murenin 	}
2020d06db5aSConstantine A. Murenin 
2030d06db5aSConstantine A. Murenin 	if (n == 100) {
2040d06db5aSConstantine A. Murenin 		DPRINTF(("aps_do_io: Failed to get bus\n"));
2050d06db5aSConstantine A. Murenin 		return (1);
2060d06db5aSConstantine A. Murenin 	}
2070d06db5aSConstantine A. Murenin 
2080d06db5aSConstantine A. Murenin 	/* write data bytes, init already sent */
2090d06db5aSConstantine A. Murenin 	/* make sure last bye is always written as this will trigger slave */
2100d06db5aSConstantine A. Murenin 	wmask |= APS_READ_RET;
2110d06db5aSConstantine A. Murenin 	buf[APS_RET] = 0x01;
2120d06db5aSConstantine A. Murenin 
2130d06db5aSConstantine A. Murenin 	for (n = 1, bp = 2; n < 16; bp <<= 1, n++) {
2140d06db5aSConstantine A. Murenin 		if (wmask & bp) {
2150d06db5aSConstantine A. Murenin 			bus_space_write_1(iot, ioh, APS_TWR_BASE + n, buf[n]);
2160d06db5aSConstantine A. Murenin 			DPRINTF(("aps_do_io:  write %2d 0x%02x\n", n, buf[n]));
2170d06db5aSConstantine A. Murenin 		}
2180d06db5aSConstantine A. Murenin 	}
2190d06db5aSConstantine A. Murenin 
2200d06db5aSConstantine A. Murenin 	for (n = 0; n < 100; n++) {
2210d06db5aSConstantine A. Murenin 		stat = bus_space_read_1(iot, ioh, APS_STR3);
2220d06db5aSConstantine A. Murenin 		if (stat & (APS_STR3_OBF3B))
2230d06db5aSConstantine A. Murenin 			break;
224333f47f2SConstantine A. Murenin 		DRIVERSLEEP(500);
2250d06db5aSConstantine A. Murenin 	}
2260d06db5aSConstantine A. Murenin 
2270d06db5aSConstantine A. Murenin 	if (n == 100) {
2280d06db5aSConstantine A. Murenin 		DPRINTF(("aps_do_io: timeout waiting response\n"));
2290d06db5aSConstantine A. Murenin 		return (1);
2300d06db5aSConstantine A. Murenin 	}
2310d06db5aSConstantine A. Murenin 	/* wait for data available */
2320d06db5aSConstantine A. Murenin 	/* make sure to read the final byte to clear status */
2330d06db5aSConstantine A. Murenin 	rmask |= APS_READ_RET;
2340d06db5aSConstantine A. Murenin 
2350d06db5aSConstantine A. Murenin 	/* read cmd and data bytes */
2360d06db5aSConstantine A. Murenin 	for (n = 0, bp = 1; n < 16; bp <<= 1, n++) {
2370d06db5aSConstantine A. Murenin 		if (rmask & bp) {
2380d06db5aSConstantine A. Murenin 			buf[n] = bus_space_read_1(iot, ioh, APS_TWR_BASE + n);
2390d06db5aSConstantine A. Murenin 			DPRINTF(("aps_do_io:  read %2d 0x%02x\n", n, buf[n]));
2400d06db5aSConstantine A. Murenin 		}
2410d06db5aSConstantine A. Murenin 	}
2420d06db5aSConstantine A. Murenin 
2430d06db5aSConstantine A. Murenin 	return (0);
2440d06db5aSConstantine A. Murenin }
2450d06db5aSConstantine A. Murenin 
246017d4aa0SConstantine A. Murenin /* for hints, see /sys/bus/isa/isahint.c */
247017d4aa0SConstantine A. Murenin 
248017d4aa0SConstantine A. Murenin static void
aps_identify(driver_t * driver,device_t parent)249*5d302545SFrançois Tigeot aps_identify(driver_t *driver, device_t parent)
250017d4aa0SConstantine A. Murenin {
251*5d302545SFrançois Tigeot 	device_t child;
252017d4aa0SConstantine A. Murenin 
253017d4aa0SConstantine A. Murenin 	child = device_find_child(parent, driver->name, -1);
254017d4aa0SConstantine A. Murenin 	if (child != NULL) {
255017d4aa0SConstantine A. Murenin 		if (isa_get_portsize(child) == 0) {
256017d4aa0SConstantine A. Murenin 			// aps(4) must have been compiled into the kernel
257017d4aa0SConstantine A. Murenin 			if (bootverbose)
258017d4aa0SConstantine A. Murenin 				kprintf("%s: will specify the port\n",
259017d4aa0SConstantine A. Murenin 				    __func__);
260017d4aa0SConstantine A. Murenin 		} else if (isa_get_port(child) != APS_ADDR_BASE)
261017d4aa0SConstantine A. Murenin 			kprintf("%s: will overwrite specified port\n",
262017d4aa0SConstantine A. Murenin 			    __func__);
263017d4aa0SConstantine A. Murenin 		else {
264017d4aa0SConstantine A. Murenin 			if (isa_get_portsize(child) == APS_ADDR_SIZE) {
265017d4aa0SConstantine A. Murenin 				// aps.ko must have been reloaded
266017d4aa0SConstantine A. Murenin 				kprintf("%s: already have been invoked\n",
267017d4aa0SConstantine A. Murenin 				    __func__);
268017d4aa0SConstantine A. Murenin 				return;
269017d4aa0SConstantine A. Murenin 			} else
270017d4aa0SConstantine A. Murenin 				kprintf("%s: will amend the portsize\n",
271017d4aa0SConstantine A. Murenin 				    __func__);
272017d4aa0SConstantine A. Murenin 		}
273017d4aa0SConstantine A. Murenin 	} else {
274017d4aa0SConstantine A. Murenin 		// first invocation of `kldload aps.ko`
275017d4aa0SConstantine A. Murenin 		kprintf("%s: creating a new %s\n",
276017d4aa0SConstantine A. Murenin 		    __func__, driver->name);
277017d4aa0SConstantine A. Murenin 		child = BUS_ADD_CHILD(parent, parent, ISA_ORDER_PNP,
278017d4aa0SConstantine A. Murenin 		    driver->name, -1);
279017d4aa0SConstantine A. Murenin 		if (child == NULL) {
280017d4aa0SConstantine A. Murenin 			kprintf("%s: cannot add child\n", __func__);
281017d4aa0SConstantine A. Murenin 			return;
282017d4aa0SConstantine A. Murenin 		}
283017d4aa0SConstantine A. Murenin 	}
284017d4aa0SConstantine A. Murenin 	if (bus_set_resource(child, SYS_RES_IOPORT, 0,
285b47b3275SSepherosa Ziehau 		APS_ADDR_BASE, APS_ADDR_SIZE, -1))
286017d4aa0SConstantine A. Murenin 		kprintf("%s: cannot set resource\n", __func__);
287017d4aa0SConstantine A. Murenin }
288017d4aa0SConstantine A. Murenin 
28923e32507SConstantine A. Murenin static int
aps_probe(device_t dev)290*5d302545SFrançois Tigeot aps_probe(device_t dev)
2910d06db5aSConstantine A. Murenin {
29223e32507SConstantine A. Murenin 	struct resource *iores;
29323e32507SConstantine A. Murenin 	int iorid = 0;
2940d06db5aSConstantine A. Murenin 	u_int8_t cr;
2950d06db5aSConstantine A. Murenin 	unsigned char iobuf[16];
2960d06db5aSConstantine A. Murenin 
29723e32507SConstantine A. Murenin #if defined(APSDEBUG) || defined(KLD_MODULE)
298017d4aa0SConstantine A. Murenin 	device_printf(dev, "%s: port 0x%x\n", __func__, isa_get_port(dev));
29923e32507SConstantine A. Murenin #endif
3000d06db5aSConstantine A. Murenin 
30123e32507SConstantine A. Murenin 	if (device_get_unit(dev) != 0)
30223e32507SConstantine A. Murenin 		return ENXIO;
30323e32507SConstantine A. Murenin 
304017d4aa0SConstantine A. Murenin 	iores = bus_alloc_resource_any(dev, SYS_RES_IOPORT, &iorid, RF_ACTIVE);
30523e32507SConstantine A. Murenin 	if (iores == NULL) {
3060d06db5aSConstantine A. Murenin 		DPRINTF(("aps: can't map i/o space\n"));
30723e32507SConstantine A. Murenin 		return ENXIO;
3080d06db5aSConstantine A. Murenin 	}
3090d06db5aSConstantine A. Murenin 
3100d06db5aSConstantine A. Murenin 
3110d06db5aSConstantine A. Murenin 	/* See if this machine has APS */
3120d06db5aSConstantine A. Murenin 
3130d06db5aSConstantine A. Murenin 	/* get APS mode */
3140d06db5aSConstantine A. Murenin 	iobuf[APS_CMD] = 0x13;
31523e32507SConstantine A. Murenin 	if (aps_do_io(iores, iobuf, APS_WRITE_0, APS_READ_1)) {
31623e32507SConstantine A. Murenin 		bus_release_resource(dev, SYS_RES_IOPORT, iorid, iores);
31723e32507SConstantine A. Murenin 		return ENXIO;
3180d06db5aSConstantine A. Murenin 	}
3190d06db5aSConstantine A. Murenin 
3200d06db5aSConstantine A. Murenin 	/*
3210d06db5aSConstantine A. Murenin 	 * Observed values from Linux driver:
3220d06db5aSConstantine A. Murenin 	 * 0x01: T42
3230d06db5aSConstantine A. Murenin 	 * 0x02: chip already initialised
3240d06db5aSConstantine A. Murenin 	 * 0x03: T41
3250d06db5aSConstantine A. Murenin 	 * 0x05: T61
3260d06db5aSConstantine A. Murenin 	 */
3270d06db5aSConstantine A. Murenin 
3280d06db5aSConstantine A. Murenin 	cr = iobuf[APS_ARG1];
3290d06db5aSConstantine A. Murenin 
33023e32507SConstantine A. Murenin 	bus_release_resource(dev, SYS_RES_IOPORT, iorid, iores);
3310d06db5aSConstantine A. Murenin 	DPRINTF(("aps: state register 0x%x\n", cr));
3320d06db5aSConstantine A. Murenin 
3330d06db5aSConstantine A. Murenin 	if (iobuf[APS_RET] != 0 || cr < 1 || cr > 5) {
33423e32507SConstantine A. Murenin 		DPRINTF(("aps: unsupported state %d\n", cr));
33523e32507SConstantine A. Murenin 		return ENXIO;
33623e32507SConstantine A. Murenin 	}
33723e32507SConstantine A. Murenin 	device_set_desc(dev, "ThinkPad Active Protection System");
33823e32507SConstantine A. Murenin 	return 0;
3390d06db5aSConstantine A. Murenin }
3400d06db5aSConstantine A. Murenin 
34123e32507SConstantine A. Murenin static int
aps_attach(device_t dev)342*5d302545SFrançois Tigeot aps_attach(device_t dev)
3430d06db5aSConstantine A. Murenin {
344017d4aa0SConstantine A. Murenin 	struct aps_softc *sc = device_get_softc(dev);
3450d06db5aSConstantine A. Murenin 
34623e32507SConstantine A. Murenin 	sc->sc_dev = dev;
347017d4aa0SConstantine A. Murenin 	sc->sc_iores = bus_alloc_resource_any(dev, SYS_RES_IOPORT,
348017d4aa0SConstantine A. Murenin 	    &sc->sc_iorid, RF_ACTIVE);
34923e32507SConstantine A. Murenin 	if (sc->sc_iores == NULL) {
35023e32507SConstantine A. Murenin 		device_printf(dev, "can't map i/o space\n");
35123e32507SConstantine A. Murenin 		return ENXIO;
3520d06db5aSConstantine A. Murenin 	}
3530d06db5aSConstantine A. Murenin 
35423e32507SConstantine A. Murenin 	if (aps_init(sc->sc_iores)) {
35523e32507SConstantine A. Murenin 		device_printf(dev, "failed to initialise\n");
35623e32507SConstantine A. Murenin 		bus_release_resource(dev, SYS_RES_IOPORT, sc->sc_iorid, sc->sc_iores);
35723e32507SConstantine A. Murenin 		return ENXIO;
35823e32507SConstantine A. Murenin 	}
3590d06db5aSConstantine A. Murenin 
3600d06db5aSConstantine A. Murenin 	sc->sensors[APS_SENSOR_XACCEL].type = SENSOR_INTEGER;
36123e32507SConstantine A. Murenin 	ksnprintf(sc->sensors[APS_SENSOR_XACCEL].desc,
3620d06db5aSConstantine A. Murenin 	    sizeof(sc->sensors[APS_SENSOR_XACCEL].desc), "X_ACCEL");
3630d06db5aSConstantine A. Murenin 
3640d06db5aSConstantine A. Murenin 	sc->sensors[APS_SENSOR_YACCEL].type = SENSOR_INTEGER;
36523e32507SConstantine A. Murenin 	ksnprintf(sc->sensors[APS_SENSOR_YACCEL].desc,
3660d06db5aSConstantine A. Murenin 	    sizeof(sc->sensors[APS_SENSOR_YACCEL].desc), "Y_ACCEL");
3670d06db5aSConstantine A. Murenin 
3680d06db5aSConstantine A. Murenin 	sc->sensors[APS_SENSOR_TEMP1].type = SENSOR_TEMP;
3690d06db5aSConstantine A. Murenin 	sc->sensors[APS_SENSOR_TEMP2].type = SENSOR_TEMP;
3700d06db5aSConstantine A. Murenin 
3710d06db5aSConstantine A. Murenin 	sc->sensors[APS_SENSOR_XVAR].type = SENSOR_INTEGER;
37223e32507SConstantine A. Murenin 	ksnprintf(sc->sensors[APS_SENSOR_XVAR].desc,
3730d06db5aSConstantine A. Murenin 	    sizeof(sc->sensors[APS_SENSOR_XVAR].desc), "X_VAR");
3740d06db5aSConstantine A. Murenin 
3750d06db5aSConstantine A. Murenin 	sc->sensors[APS_SENSOR_YVAR].type = SENSOR_INTEGER;
37623e32507SConstantine A. Murenin 	ksnprintf(sc->sensors[APS_SENSOR_YVAR].desc,
3770d06db5aSConstantine A. Murenin 	    sizeof(sc->sensors[APS_SENSOR_YVAR].desc), "Y_VAR");
3780d06db5aSConstantine A. Murenin 
3790d06db5aSConstantine A. Murenin 	sc->sensors[APS_SENSOR_KBACT].type = SENSOR_INDICATOR;
38023e32507SConstantine A. Murenin 	ksnprintf(sc->sensors[APS_SENSOR_KBACT].desc,
3810d06db5aSConstantine A. Murenin 	    sizeof(sc->sensors[APS_SENSOR_KBACT].desc), "Keyboard Active");
3820d06db5aSConstantine A. Murenin 
3830d06db5aSConstantine A. Murenin 	sc->sensors[APS_SENSOR_MSACT].type = SENSOR_INDICATOR;
38423e32507SConstantine A. Murenin 	ksnprintf(sc->sensors[APS_SENSOR_MSACT].desc,
3850d06db5aSConstantine A. Murenin 	    sizeof(sc->sensors[APS_SENSOR_MSACT].desc), "Mouse Active");
3860d06db5aSConstantine A. Murenin 
3870d06db5aSConstantine A. Murenin 	sc->sensors[APS_SENSOR_LIDOPEN].type = SENSOR_INDICATOR;
38823e32507SConstantine A. Murenin 	ksnprintf(sc->sensors[APS_SENSOR_LIDOPEN].desc,
3890d06db5aSConstantine A. Murenin 	    sizeof(sc->sensors[APS_SENSOR_LIDOPEN].desc), "Lid Open");
3900d06db5aSConstantine A. Murenin 
3910d06db5aSConstantine A. Murenin 	/* stop hiding and report to the authorities */
39223e32507SConstantine A. Murenin 	strlcpy(sc->sensordev.xname, device_get_nameunit(dev),
3930d06db5aSConstantine A. Murenin 	    sizeof(sc->sensordev.xname));
3942b05a037SConstantine A. Murenin 	for (int i = 0; i < APS_NUM_SENSORS ; i++)
3950d06db5aSConstantine A. Murenin 		sensor_attach(&sc->sensordev, &sc->sensors[i]);
39623e32507SConstantine A. Murenin 
39723e32507SConstantine A. Murenin 	/* Refresh sensor data every 1 second */
39823e32507SConstantine A. Murenin 	/* XXX: a more frequent refresh might be appropriate */
3991bedd63aSSepherosa Ziehau 	sensor_task_register(sc, aps_refresh, 1);
40023e32507SConstantine A. Murenin 
4010d06db5aSConstantine A. Murenin 	sensordev_install(&sc->sensordev);
40223e32507SConstantine A. Murenin 	return 0;
4030d06db5aSConstantine A. Murenin }
4040d06db5aSConstantine A. Murenin 
40523e32507SConstantine A. Murenin static int
aps_detach(device_t dev)406*5d302545SFrançois Tigeot aps_detach(device_t dev)
40723e32507SConstantine A. Murenin {
40823e32507SConstantine A. Murenin 	struct aps_softc *sc = device_get_softc(dev);
40923e32507SConstantine A. Murenin 
41023e32507SConstantine A. Murenin 	sensordev_deinstall(&sc->sensordev);
41123e32507SConstantine A. Murenin 	sensor_task_unregister(sc);
41223e32507SConstantine A. Murenin 	return bus_release_resource(dev, SYS_RES_IOPORT,
41323e32507SConstantine A. Murenin 	    sc->sc_iorid, sc->sc_iores);
41423e32507SConstantine A. Murenin }
41523e32507SConstantine A. Murenin 
41623e32507SConstantine A. Murenin static int
aps_init(struct resource * iores)41723e32507SConstantine A. Murenin aps_init(struct resource *iores)
4180d06db5aSConstantine A. Murenin {
4190d06db5aSConstantine A. Murenin 	unsigned char iobuf[16];
4200d06db5aSConstantine A. Murenin 
4210d06db5aSConstantine A. Murenin 	/* command 0x17/0x81: check EC */
4220d06db5aSConstantine A. Murenin 	iobuf[APS_CMD] = 0x17;
4230d06db5aSConstantine A. Murenin 	iobuf[APS_ARG1] = 0x81;
4240d06db5aSConstantine A. Murenin 
42523e32507SConstantine A. Murenin 	if (aps_do_io(iores, iobuf, APS_WRITE_1, APS_READ_3))
4260d06db5aSConstantine A. Murenin 		return (1);
4270d06db5aSConstantine A. Murenin 
4280d06db5aSConstantine A. Murenin 	if (iobuf[APS_RET] != 0 ||iobuf[APS_ARG3] != 0)
4290d06db5aSConstantine A. Murenin 		return (1);
4300d06db5aSConstantine A. Murenin 
4310d06db5aSConstantine A. Murenin 	/* Test values from the Linux driver */
4320d06db5aSConstantine A. Murenin 	if ((iobuf[APS_ARG1] != 0 || iobuf[APS_ARG2] != 0x60) &&
4330d06db5aSConstantine A. Murenin 	    (iobuf[APS_ARG1] != 1 || iobuf[APS_ARG2] != 0))
4340d06db5aSConstantine A. Murenin 		return (1);
4350d06db5aSConstantine A. Murenin 
4360d06db5aSConstantine A. Murenin 	/* command 0x14: set power */
4370d06db5aSConstantine A. Murenin 	iobuf[APS_CMD] = 0x14;
4380d06db5aSConstantine A. Murenin 	iobuf[APS_ARG1] = 0x01;
4390d06db5aSConstantine A. Murenin 
44023e32507SConstantine A. Murenin 	if (aps_do_io(iores, iobuf, APS_WRITE_1, APS_READ_0))
4410d06db5aSConstantine A. Murenin 		return (1);
4420d06db5aSConstantine A. Murenin 
4430d06db5aSConstantine A. Murenin 	if (iobuf[APS_RET] != 0)
4440d06db5aSConstantine A. Murenin 		return (1);
4450d06db5aSConstantine A. Murenin 
4460d06db5aSConstantine A. Murenin 	/* command 0x10: set config (sample rate and order) */
4470d06db5aSConstantine A. Murenin 	iobuf[APS_CMD] = 0x10;
4480d06db5aSConstantine A. Murenin 	iobuf[APS_ARG1] = 0xc8;
4490d06db5aSConstantine A. Murenin 	iobuf[APS_ARG2] = 0x00;
4500d06db5aSConstantine A. Murenin 	iobuf[APS_ARG3] = 0x02;
4510d06db5aSConstantine A. Murenin 
45223e32507SConstantine A. Murenin 	if (aps_do_io(iores, iobuf, APS_WRITE_3, APS_READ_0))
4530d06db5aSConstantine A. Murenin 		return (1);
4540d06db5aSConstantine A. Murenin 
4550d06db5aSConstantine A. Murenin 	if (iobuf[APS_RET] != 0)
4560d06db5aSConstantine A. Murenin 		return (1);
4570d06db5aSConstantine A. Murenin 
4580d06db5aSConstantine A. Murenin 	/* command 0x11: refresh data */
4590d06db5aSConstantine A. Murenin 	iobuf[APS_CMD] = 0x11;
46023e32507SConstantine A. Murenin 	if (aps_do_io(iores, iobuf, APS_WRITE_0, APS_READ_1))
4610d06db5aSConstantine A. Murenin 		return (1);
4620d06db5aSConstantine A. Murenin 	if (iobuf[APS_ARG1] != 0)
4630d06db5aSConstantine A. Murenin 		return (1);
4640d06db5aSConstantine A. Murenin 
4650d06db5aSConstantine A. Murenin 	return (0);
4660d06db5aSConstantine A. Murenin }
4670d06db5aSConstantine A. Murenin 
46823e32507SConstantine A. Murenin static int
aps_read_data(struct aps_softc * sc)4690d06db5aSConstantine A. Murenin aps_read_data(struct aps_softc *sc)
4700d06db5aSConstantine A. Murenin {
4710d06db5aSConstantine A. Murenin 	unsigned char iobuf[16];
4720d06db5aSConstantine A. Murenin 
4730d06db5aSConstantine A. Murenin 	/* command 0x11: refresh data */
4740d06db5aSConstantine A. Murenin 	iobuf[APS_CMD] = 0x11;
47523e32507SConstantine A. Murenin 	if (aps_do_io(sc->sc_iores, iobuf, APS_WRITE_0, APS_READ_ALL))
4760d06db5aSConstantine A. Murenin 		return (1);
4770d06db5aSConstantine A. Murenin 
4780d06db5aSConstantine A. Murenin 	sc->aps_data.state = iobuf[APS_STATE];
4790d06db5aSConstantine A. Murenin 	sc->aps_data.x_accel = iobuf[APS_XACCEL] + 256 * iobuf[APS_XACCEL + 1];
4800d06db5aSConstantine A. Murenin 	sc->aps_data.y_accel = iobuf[APS_YACCEL] + 256 * iobuf[APS_YACCEL + 1];
4810d06db5aSConstantine A. Murenin 	sc->aps_data.temp1 = iobuf[APS_TEMP];
4820d06db5aSConstantine A. Murenin 	sc->aps_data.x_var = iobuf[APS_XVAR] + 256 * iobuf[APS_XVAR + 1];
4830d06db5aSConstantine A. Murenin 	sc->aps_data.y_var = iobuf[APS_YVAR] + 256 * iobuf[APS_YVAR + 1];
4840d06db5aSConstantine A. Murenin 	sc->aps_data.temp2 = iobuf[APS_TEMP2];
4850d06db5aSConstantine A. Murenin 	sc->aps_data.input = iobuf[APS_INPUT];
4860d06db5aSConstantine A. Murenin 
4870d06db5aSConstantine A. Murenin 	return (0);
4880d06db5aSConstantine A. Murenin }
4890d06db5aSConstantine A. Murenin 
49023e32507SConstantine A. Murenin static void
aps_refresh_sensor_data(struct aps_softc * sc)4910d06db5aSConstantine A. Murenin aps_refresh_sensor_data(struct aps_softc *sc)
4920d06db5aSConstantine A. Murenin {
4930d06db5aSConstantine A. Murenin 	int64_t temp;
4940d06db5aSConstantine A. Murenin 
49523e32507SConstantine A. Murenin 	if (aps_read_data(sc)) {
4962b05a037SConstantine A. Murenin 		for (int i = 0; i < APS_NUM_SENSORS; i++)
49723e32507SConstantine A. Murenin 			sc->sensors[i].flags |= SENSOR_FINVALID;
4980d06db5aSConstantine A. Murenin 		return;
4990d06db5aSConstantine A. Murenin 	}
5000d06db5aSConstantine A. Murenin 
5010d06db5aSConstantine A. Murenin 	sc->sensors[APS_SENSOR_XACCEL].value = sc->aps_data.x_accel;
5020d06db5aSConstantine A. Murenin 	sc->sensors[APS_SENSOR_YACCEL].value = sc->aps_data.y_accel;
5030d06db5aSConstantine A. Murenin 
5040d06db5aSConstantine A. Murenin 	/* convert to micro (mu) degrees */
5050d06db5aSConstantine A. Murenin 	temp = sc->aps_data.temp1 * 1000000;
5060d06db5aSConstantine A. Murenin 	/* convert to kelvin */
5070d06db5aSConstantine A. Murenin 	temp += 273150000;
5080d06db5aSConstantine A. Murenin 	sc->sensors[APS_SENSOR_TEMP1].value = temp;
5090d06db5aSConstantine A. Murenin 
5100d06db5aSConstantine A. Murenin 	/* convert to micro (mu) degrees */
5110d06db5aSConstantine A. Murenin 	temp = sc->aps_data.temp2 * 1000000;
5120d06db5aSConstantine A. Murenin 	/* convert to kelvin */
5130d06db5aSConstantine A. Murenin 	temp += 273150000;
5140d06db5aSConstantine A. Murenin 	sc->sensors[APS_SENSOR_TEMP2].value = temp;
5150d06db5aSConstantine A. Murenin 
5160d06db5aSConstantine A. Murenin 	sc->sensors[APS_SENSOR_XVAR].value = sc->aps_data.x_var;
5170d06db5aSConstantine A. Murenin 	sc->sensors[APS_SENSOR_YVAR].value = sc->aps_data.y_var;
5180d06db5aSConstantine A. Murenin 	sc->sensors[APS_SENSOR_KBACT].value =
5190d06db5aSConstantine A. Murenin 	    (sc->aps_data.input &  APS_INPUT_KB) ? 1 : 0;
5200d06db5aSConstantine A. Murenin 	sc->sensors[APS_SENSOR_MSACT].value =
5210d06db5aSConstantine A. Murenin 	    (sc->aps_data.input & APS_INPUT_MS) ? 1 : 0;
5220d06db5aSConstantine A. Murenin 	sc->sensors[APS_SENSOR_LIDOPEN].value =
5230d06db5aSConstantine A. Murenin 	    (sc->aps_data.input & APS_INPUT_LIDOPEN) ? 1 : 0;
52423e32507SConstantine A. Murenin 
5252b05a037SConstantine A. Murenin 	for (int i = 0; i < APS_NUM_SENSORS; i++)
52623e32507SConstantine A. Murenin 		sc->sensors[i].flags &= ~SENSOR_FINVALID;
5270d06db5aSConstantine A. Murenin }
5280d06db5aSConstantine A. Murenin 
52923e32507SConstantine A. Murenin static void
aps_refresh(void * arg)5300d06db5aSConstantine A. Murenin aps_refresh(void *arg)
5310d06db5aSConstantine A. Murenin {
5320d06db5aSConstantine A. Murenin 	struct aps_softc *sc = (struct aps_softc *)arg;
5330d06db5aSConstantine A. Murenin 
5340d06db5aSConstantine A. Murenin 	aps_refresh_sensor_data(sc);
5350d06db5aSConstantine A. Murenin }
5360d06db5aSConstantine A. Murenin 
53723e32507SConstantine A. Murenin static int
aps_resume(device_t dev)538*5d302545SFrançois Tigeot aps_resume(device_t dev)
5390d06db5aSConstantine A. Murenin {
54023e32507SConstantine A. Murenin 	struct aps_softc *sc = device_get_softc(dev);
5410d06db5aSConstantine A. Murenin 	unsigned char iobuf[16];
5420d06db5aSConstantine A. Murenin 
5430d06db5aSConstantine A. Murenin 	/*
5440d06db5aSConstantine A. Murenin 	 * Redo the init sequence on resume, because APS is
5450d06db5aSConstantine A. Murenin 	 * as forgetful as it is deaf.
5460d06db5aSConstantine A. Murenin 	 */
5470d06db5aSConstantine A. Murenin 
5480d06db5aSConstantine A. Murenin 	/* get APS mode */
5490d06db5aSConstantine A. Murenin 	iobuf[APS_CMD] = 0x13;
55023e32507SConstantine A. Murenin 	if (aps_do_io(sc->sc_iores, iobuf, APS_WRITE_0, APS_READ_1)
55123e32507SConstantine A. Murenin 	    || aps_init(sc->sc_iores)) {
55223e32507SConstantine A. Murenin 		device_printf(sc->sc_dev, "failed to wake up\n");
55323e32507SConstantine A. Murenin 		return EIO;
55423e32507SConstantine A. Murenin 	}
55523e32507SConstantine A. Murenin 
55623e32507SConstantine A. Murenin 	sensor_task_register(sc, aps_refresh, 1);
55723e32507SConstantine A. Murenin 	return 0;
55823e32507SConstantine A. Murenin }
55923e32507SConstantine A. Murenin 
56023e32507SConstantine A. Murenin static int
aps_suspend(device_t dev)561*5d302545SFrançois Tigeot aps_suspend(device_t dev)
56223e32507SConstantine A. Murenin {
56323e32507SConstantine A. Murenin 	struct aps_softc *sc = device_get_softc(dev);
56423e32507SConstantine A. Murenin 
56523e32507SConstantine A. Murenin 	for (int i = 0; i < APS_NUM_SENSORS; i++)
56623e32507SConstantine A. Murenin 		sc->sensors[i].flags |= SENSOR_FINVALID;
56723e32507SConstantine A. Murenin 	sensor_task_unregister(sc);
56823e32507SConstantine A. Murenin 	return 0;
5690d06db5aSConstantine A. Murenin }
570