xref: /openbsd/sys/dev/isa/aps.c (revision 471aeecf)
1*471aeecfSnaddy /*	$OpenBSD: aps.c,v 1.28 2022/04/06 18:59:28 naddy Exp $	*/
24569cca6Sjsg /*
34569cca6Sjsg  * Copyright (c) 2005 Jonathan Gray <jsg@openbsd.org>
4c96de550Scanacar  * Copyright (c) 2008 Can Erkin Acar <canacar@openbsd.org>
54569cca6Sjsg  *
64569cca6Sjsg  * Permission to use, copy, modify, and distribute this software for any
74569cca6Sjsg  * purpose with or without fee is hereby granted, provided that the above
84569cca6Sjsg  * copyright notice and this permission notice appear in all copies.
94569cca6Sjsg  *
104569cca6Sjsg  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
114569cca6Sjsg  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
124569cca6Sjsg  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
134569cca6Sjsg  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
144569cca6Sjsg  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
154569cca6Sjsg  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
164569cca6Sjsg  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
174569cca6Sjsg  */
184569cca6Sjsg 
194569cca6Sjsg /*
204569cca6Sjsg  * A driver for the ThinkPad Active Protection System based on notes from
2167ee430bSjsg  * http://www.almaden.ibm.com/cs/people/marksmith/tpaps.html
224569cca6Sjsg  */
234569cca6Sjsg 
244569cca6Sjsg #include <sys/param.h>
254569cca6Sjsg #include <sys/systm.h>
264569cca6Sjsg #include <sys/device.h>
274569cca6Sjsg #include <sys/kernel.h>
284569cca6Sjsg #include <sys/sensors.h>
294569cca6Sjsg #include <sys/timeout.h>
304569cca6Sjsg #include <machine/bus.h>
31037e02e3Sderaadt #include <sys/event.h>
324569cca6Sjsg 
334569cca6Sjsg #include <dev/isa/isavar.h>
344569cca6Sjsg 
35037e02e3Sderaadt #ifdef __i386__
36037e02e3Sderaadt #include "apm.h"
37037e02e3Sderaadt #include <machine/acpiapm.h>
38037e02e3Sderaadt #include <machine/biosvar.h>
39037e02e3Sderaadt #include <machine/apmvar.h>
40037e02e3Sderaadt #endif
41037e02e3Sderaadt 
424569cca6Sjsg #if defined(APSDEBUG)
434569cca6Sjsg #define DPRINTF(x)		do { printf x; } while (0)
444569cca6Sjsg #else
454569cca6Sjsg #define DPRINTF(x)
464569cca6Sjsg #endif
474569cca6Sjsg 
48093389c9Sjsg 
49c96de550Scanacar /*
50c96de550Scanacar  * EC interface on Thinkpad Laptops, from Linux HDAPS driver notes.
514b1a56afSjsg  * From Renesas H8S/2140B Group Hardware Manual
52c96de550Scanacar  * http://documentation.renesas.com/eng/products/mpumcu/rej09b0300_2140bhm.pdf
53c96de550Scanacar  *
54c96de550Scanacar  * EC uses LPC Channel 3 registers TWR0..15
55c96de550Scanacar  */
56093389c9Sjsg 
57c96de550Scanacar /* STR3 status register */
58c96de550Scanacar #define APS_STR3		0x04
59093389c9Sjsg 
60c96de550Scanacar #define APS_STR3_IBF3B	0x80	/* Input buffer full (host->slave) */
61c96de550Scanacar #define APS_STR3_OBF3B	0x40	/* Output buffer full (slave->host)*/
62c96de550Scanacar #define APS_STR3_MWMF	0x20	/* Master write mode */
63c96de550Scanacar #define APS_STR3_SWMF	0x10	/* Slave write mode */
64c96de550Scanacar 
65c96de550Scanacar 
66c96de550Scanacar /* Base address of TWR registers */
67c96de550Scanacar #define APS_TWR_BASE		0x10
68c96de550Scanacar #define APS_TWR_RET		0x1f
69c96de550Scanacar 
70c96de550Scanacar /* TWR registers */
71c96de550Scanacar #define APS_CMD			0x00
72c96de550Scanacar #define APS_ARG1		0x01
73c96de550Scanacar #define APS_ARG2		0x02
74c96de550Scanacar #define APS_ARG3		0x03
75c96de550Scanacar #define APS_RET			0x0f
76c96de550Scanacar 
77c96de550Scanacar /* Sensor values */
78c96de550Scanacar #define APS_STATE		0x01
79c96de550Scanacar #define	APS_XACCEL		0x02
80c96de550Scanacar #define APS_YACCEL		0x04
81c96de550Scanacar #define APS_TEMP		0x06
82c96de550Scanacar #define	APS_XVAR		0x07
83c96de550Scanacar #define APS_YVAR		0x09
84c96de550Scanacar #define APS_TEMP2		0x0b
85c96de550Scanacar #define APS_UNKNOWN		0x0c
86c96de550Scanacar #define APS_INPUT		0x0d
87c96de550Scanacar 
88c96de550Scanacar /* write masks for I/O, send command + 0-3 arguments*/
89c96de550Scanacar #define APS_WRITE_0		0x0001
90c96de550Scanacar #define APS_WRITE_1		0x0003
91c96de550Scanacar #define APS_WRITE_2		0x0007
92c96de550Scanacar #define APS_WRITE_3		0x000f
93c96de550Scanacar 
94c96de550Scanacar /* read masks for I/O, read 0-3 values (skip command byte) */
95c96de550Scanacar #define APS_READ_0		0x0000
96c96de550Scanacar #define APS_READ_1		0x0002
97c96de550Scanacar #define APS_READ_2		0x0006
98c96de550Scanacar #define APS_READ_3		0x000e
99c96de550Scanacar 
100c96de550Scanacar #define APS_READ_RET		0x8000
101c96de550Scanacar #define APS_READ_ALL		0xffff
102c96de550Scanacar 
103c96de550Scanacar /* Bit definitions for APS_INPUT value */
104093389c9Sjsg #define APS_INPUT_KB		(1 << 5)
105093389c9Sjsg #define APS_INPUT_MS		(1 << 6)
106093389c9Sjsg #define APS_INPUT_LIDOPEN	(1 << 7)
107093389c9Sjsg 
108093389c9Sjsg #define APS_ADDR_SIZE		0x1f
109093389c9Sjsg 
110093389c9Sjsg struct sensor_rec {
111093389c9Sjsg 	u_int8_t	state;
112093389c9Sjsg 	u_int16_t	x_accel;
113093389c9Sjsg 	u_int16_t	y_accel;
114093389c9Sjsg 	u_int8_t	temp1;
115093389c9Sjsg 	u_int16_t	x_var;
116093389c9Sjsg 	u_int16_t	y_var;
117093389c9Sjsg 	u_int8_t	temp2;
118093389c9Sjsg 	u_int8_t	unk;
119093389c9Sjsg 	u_int8_t	input;
120093389c9Sjsg };
121093389c9Sjsg 
122093389c9Sjsg #define APS_NUM_SENSORS		9
123093389c9Sjsg 
124093389c9Sjsg #define APS_SENSOR_XACCEL	0
125093389c9Sjsg #define APS_SENSOR_YACCEL	1
126093389c9Sjsg #define APS_SENSOR_XVAR		2
127093389c9Sjsg #define APS_SENSOR_YVAR		3
128093389c9Sjsg #define APS_SENSOR_TEMP1	4
129093389c9Sjsg #define APS_SENSOR_TEMP2	5
130093389c9Sjsg #define APS_SENSOR_KBACT	6
131093389c9Sjsg #define APS_SENSOR_MSACT	7
132093389c9Sjsg #define APS_SENSOR_LIDOPEN	8
133093389c9Sjsg 
134093389c9Sjsg struct aps_softc {
135093389c9Sjsg 	struct device sc_dev;
136093389c9Sjsg 
137093389c9Sjsg 	bus_space_tag_t aps_iot;
138093389c9Sjsg 	bus_space_handle_t aps_ioh;
139093389c9Sjsg 
140275cbf62Sderaadt 	struct ksensor sensors[APS_NUM_SENSORS];
141275cbf62Sderaadt 	struct ksensordev sensordev;
142093389c9Sjsg 	void (*refresh_sensor_data)(struct aps_softc *);
143093389c9Sjsg 
144093389c9Sjsg 	struct sensor_rec aps_data;
145093389c9Sjsg };
146093389c9Sjsg 
1474569cca6Sjsg int	 aps_match(struct device *, void *, void *);
1484569cca6Sjsg void	 aps_attach(struct device *, struct device *, void *);
149e612a1baSderaadt int	 aps_activate(struct device *, int);
1504569cca6Sjsg 
151721d5653Sdjm int	 aps_init(bus_space_tag_t, bus_space_handle_t);
1524569cca6Sjsg int	 aps_read_data(struct aps_softc *);
153c96de550Scanacar void	 aps_refresh_sensor_data(struct aps_softc *);
1544569cca6Sjsg void	 aps_refresh(void *);
155c96de550Scanacar int	 aps_do_io(bus_space_tag_t, bus_space_handle_t,
156c96de550Scanacar 		   unsigned char *, int, int);
1574569cca6Sjsg 
158*471aeecfSnaddy const struct cfattach aps_ca = {
1594569cca6Sjsg 	sizeof(struct aps_softc),
160e612a1baSderaadt 	aps_match, aps_attach, NULL, aps_activate
1614569cca6Sjsg };
1624569cca6Sjsg 
1634569cca6Sjsg struct cfdriver aps_cd = {
1644569cca6Sjsg 	NULL, "aps", DV_DULL
1654569cca6Sjsg };
1664569cca6Sjsg 
1674569cca6Sjsg struct timeout aps_timeout;
1684569cca6Sjsg 
169c96de550Scanacar 
170c96de550Scanacar 
171c96de550Scanacar /* properly communicate with the controller, writing a set of memory
172c96de550Scanacar  * locations and reading back another set  */
173c96de550Scanacar int
aps_do_io(bus_space_tag_t iot,bus_space_handle_t ioh,unsigned char * buf,int wmask,int rmask)174c96de550Scanacar aps_do_io(bus_space_tag_t iot, bus_space_handle_t ioh,
175c96de550Scanacar 	  unsigned char *buf, int wmask, int rmask)
176c96de550Scanacar {
177c96de550Scanacar 	int bp, stat, n;
178c96de550Scanacar 
179c96de550Scanacar 	DPRINTF(("aps_do_io: CMD: 0x%02x, wmask: 0x%04x, rmask: 0x%04x\n",
180c96de550Scanacar 	       buf[0], wmask, rmask));
181c96de550Scanacar 
182c96de550Scanacar 	/* write init byte using arbitration */
183c96de550Scanacar 	for (n = 0; n < 100; n++) {
184c96de550Scanacar 		stat = bus_space_read_1(iot, ioh, APS_STR3);
185c96de550Scanacar 		if (stat & (APS_STR3_OBF3B | APS_STR3_SWMF)) {
186c96de550Scanacar 			bus_space_read_1(iot, ioh, APS_TWR_RET);
187c96de550Scanacar 			continue;
188c96de550Scanacar 		}
189c96de550Scanacar 		bus_space_write_1(iot, ioh, APS_TWR_BASE, buf[0]);
190c96de550Scanacar 		stat = bus_space_read_1(iot, ioh, APS_STR3);
191c96de550Scanacar 		if (stat & (APS_STR3_MWMF))
192c96de550Scanacar 			break;
193c96de550Scanacar 		delay(1);
194c96de550Scanacar 	}
195c96de550Scanacar 
196c96de550Scanacar 	if (n == 100) {
197c96de550Scanacar 		DPRINTF(("aps_do_io: Failed to get bus\n"));
198c96de550Scanacar 		return (1);
199c96de550Scanacar 	}
200c96de550Scanacar 
201c96de550Scanacar 	/* write data bytes, init already sent */
202c96de550Scanacar 	/* make sure last bye is always written as this will trigger slave */
203c96de550Scanacar 	wmask |= APS_READ_RET;
204c96de550Scanacar 	buf[APS_RET] = 0x01;
205c96de550Scanacar 
206c96de550Scanacar 	for (n = 1, bp = 2; n < 16; bp <<= 1, n++) {
207c96de550Scanacar 		if (wmask & bp) {
208c96de550Scanacar 			bus_space_write_1(iot, ioh, APS_TWR_BASE + n, buf[n]);
209c96de550Scanacar 			DPRINTF(("aps_do_io:  write %2d 0x%02x\n", n, buf[n]));
210c96de550Scanacar 		}
211c96de550Scanacar 	}
212c96de550Scanacar 
213c96de550Scanacar 	for (n = 0; n < 100; n++) {
214c96de550Scanacar 		stat = bus_space_read_1(iot, ioh, APS_STR3);
215c96de550Scanacar 		if (stat & (APS_STR3_OBF3B))
216c96de550Scanacar 			break;
217c96de550Scanacar 		delay(5 * 100);
218c96de550Scanacar 	}
219c96de550Scanacar 
220c96de550Scanacar 	if (n == 100) {
221c96de550Scanacar 		DPRINTF(("aps_do_io: timeout waiting response\n"));
222c96de550Scanacar 		return (1);
223c96de550Scanacar 	}
224c96de550Scanacar 	/* wait for data available */
225c96de550Scanacar 	/* make sure to read the final byte to clear status */
226c96de550Scanacar 	rmask |= APS_READ_RET;
227c96de550Scanacar 
228c96de550Scanacar 	/* read cmd and data bytes */
229c96de550Scanacar 	for (n = 0, bp = 1; n < 16; bp <<= 1, n++) {
230c96de550Scanacar 		if (rmask & bp) {
231c96de550Scanacar 			buf[n] = bus_space_read_1(iot, ioh, APS_TWR_BASE + n);
232c96de550Scanacar 			DPRINTF(("aps_do_io:  read %2d 0x%02x\n", n, buf[n]));
233c96de550Scanacar 		}
234c96de550Scanacar 	}
235c96de550Scanacar 
236c96de550Scanacar 	return (0);
237c96de550Scanacar }
238c96de550Scanacar 
2394569cca6Sjsg int
aps_match(struct device * parent,void * match,void * aux)2404569cca6Sjsg aps_match(struct device *parent, void *match, void *aux)
2414569cca6Sjsg {
2424569cca6Sjsg 	struct isa_attach_args *ia = aux;
24388eb0740Sderaadt 	bus_space_tag_t iot = ia->ia_iot;
24488eb0740Sderaadt 	bus_space_handle_t ioh;
24588eb0740Sderaadt 	int iobase = ia->ipa_io[0].base;
2464569cca6Sjsg 	u_int8_t cr;
2470caa36ecSjsg 	unsigned char iobuf[16];
248c96de550Scanacar 
2494569cca6Sjsg 	if (bus_space_map(iot, iobase, APS_ADDR_SIZE, 0, &ioh)) {
2504569cca6Sjsg 		DPRINTF(("aps: can't map i/o space\n"));
2514569cca6Sjsg 		return (0);
2524569cca6Sjsg 	}
253c96de550Scanacar 	/* get APS mode */
254c96de550Scanacar 	iobuf[APS_CMD] = 0x13;
255c96de550Scanacar 	if (aps_do_io(iot, ioh, iobuf, APS_WRITE_0, APS_READ_1)) {
2564569cca6Sjsg 		bus_space_unmap(iot, ioh, APS_ADDR_SIZE);
2574569cca6Sjsg 		return (0);
2584569cca6Sjsg 	}
2594569cca6Sjsg 
2604569cca6Sjsg 	/*
2614569cca6Sjsg 	 * Observed values from Linux driver:
2624569cca6Sjsg 	 * 0x01: T42
2634569cca6Sjsg 	 * 0x02: chip already initialised
2644569cca6Sjsg 	 * 0x03: T41
265c96de550Scanacar 	 * 0x05: T61
2664569cca6Sjsg 	 */
267c96de550Scanacar 
268c96de550Scanacar 	cr = iobuf[APS_ARG1];
26988eb0740Sderaadt 	DPRINTF(("aps: state register 0x%x\n", cr));
27088eb0740Sderaadt 
27188eb0740Sderaadt 	if (aps_init(iot, ioh)) {
27288eb0740Sderaadt 		bus_space_unmap(iot, ioh, APS_ADDR_SIZE);
27388eb0740Sderaadt 		return (0);
27488eb0740Sderaadt 	}
2754569cca6Sjsg 
2764569cca6Sjsg 	bus_space_unmap(iot, ioh, APS_ADDR_SIZE);
277c96de550Scanacar 
278c96de550Scanacar 	if (iobuf[APS_RET] != 0 || cr < 1 || cr > 5) {
2794569cca6Sjsg 		DPRINTF(("aps0: unsupported state %d\n", cr));
2804569cca6Sjsg 		return (0);
2814569cca6Sjsg 	}
2824569cca6Sjsg 
2834569cca6Sjsg 	ia->ipa_nio = 1;
2844569cca6Sjsg 	ia->ipa_io[0].length = APS_ADDR_SIZE;
2854569cca6Sjsg 	ia->ipa_nmem = 0;
2864569cca6Sjsg 	ia->ipa_nirq = 0;
2874569cca6Sjsg 	ia->ipa_ndrq = 0;
2884569cca6Sjsg 	return (1);
2894569cca6Sjsg }
2904569cca6Sjsg 
2914569cca6Sjsg void
aps_attach(struct device * parent,struct device * self,void * aux)2924569cca6Sjsg aps_attach(struct device *parent, struct device *self, void *aux)
2934569cca6Sjsg {
2944569cca6Sjsg 	struct aps_softc *sc = (void *)self;
2954569cca6Sjsg 	int iobase, i;
2964569cca6Sjsg 	bus_space_tag_t iot;
2974569cca6Sjsg 	bus_space_handle_t ioh;
2984569cca6Sjsg 	struct isa_attach_args *ia = aux;
2994569cca6Sjsg 
3004569cca6Sjsg 	iobase = ia->ipa_io[0].base;
3014569cca6Sjsg 	iot = sc->aps_iot = ia->ia_iot;
3024569cca6Sjsg 
3034569cca6Sjsg 	if (bus_space_map(iot, iobase, APS_ADDR_SIZE, 0, &sc->aps_ioh)) {
3044569cca6Sjsg 		printf(": can't map i/o space\n");
3054569cca6Sjsg 		return;
3064569cca6Sjsg 	}
3074569cca6Sjsg 
3084569cca6Sjsg 	ioh = sc->aps_ioh;
3094569cca6Sjsg 
3104569cca6Sjsg 	printf("\n");
3114569cca6Sjsg 
3124569cca6Sjsg 	sc->sensors[APS_SENSOR_XACCEL].type = SENSOR_INTEGER;
3134569cca6Sjsg 	snprintf(sc->sensors[APS_SENSOR_XACCEL].desc,
3144569cca6Sjsg 	    sizeof(sc->sensors[APS_SENSOR_XACCEL].desc), "X_ACCEL");
3154569cca6Sjsg 
3164569cca6Sjsg 	sc->sensors[APS_SENSOR_YACCEL].type = SENSOR_INTEGER;
3174569cca6Sjsg 	snprintf(sc->sensors[APS_SENSOR_YACCEL].desc,
3184569cca6Sjsg 	    sizeof(sc->sensors[APS_SENSOR_YACCEL].desc), "Y_ACCEL");
3194569cca6Sjsg 
3204569cca6Sjsg 	sc->sensors[APS_SENSOR_TEMP1].type = SENSOR_TEMP;
3214569cca6Sjsg 	sc->sensors[APS_SENSOR_TEMP2].type = SENSOR_TEMP;
3224569cca6Sjsg 
3234569cca6Sjsg 	sc->sensors[APS_SENSOR_XVAR].type = SENSOR_INTEGER;
3244569cca6Sjsg 	snprintf(sc->sensors[APS_SENSOR_XVAR].desc,
3254569cca6Sjsg 	    sizeof(sc->sensors[APS_SENSOR_XVAR].desc), "X_VAR");
3264569cca6Sjsg 
3274569cca6Sjsg 	sc->sensors[APS_SENSOR_YVAR].type = SENSOR_INTEGER;
3284569cca6Sjsg 	snprintf(sc->sensors[APS_SENSOR_YVAR].desc,
3294569cca6Sjsg 	    sizeof(sc->sensors[APS_SENSOR_YVAR].desc), "Y_VAR");
3304569cca6Sjsg 
331569d7da4Sjsg 	sc->sensors[APS_SENSOR_KBACT].type = SENSOR_INDICATOR;
3324569cca6Sjsg 	snprintf(sc->sensors[APS_SENSOR_KBACT].desc,
333aafcf214Sderaadt 	    sizeof(sc->sensors[APS_SENSOR_KBACT].desc), "Keyboard Active");
3344569cca6Sjsg 
335569d7da4Sjsg 	sc->sensors[APS_SENSOR_MSACT].type = SENSOR_INDICATOR;
3364569cca6Sjsg 	snprintf(sc->sensors[APS_SENSOR_MSACT].desc,
337aafcf214Sderaadt 	    sizeof(sc->sensors[APS_SENSOR_MSACT].desc), "Mouse Active");
3384569cca6Sjsg 
339569d7da4Sjsg 	sc->sensors[APS_SENSOR_LIDOPEN].type = SENSOR_INDICATOR;
3404569cca6Sjsg 	snprintf(sc->sensors[APS_SENSOR_LIDOPEN].desc,
341aafcf214Sderaadt 	    sizeof(sc->sensors[APS_SENSOR_LIDOPEN].desc), "Lid Open");
3424569cca6Sjsg 
3434569cca6Sjsg 	/* stop hiding and report to the authorities */
34427515a6bSderaadt 	strlcpy(sc->sensordev.xname, sc->sc_dev.dv_xname,
34527515a6bSderaadt 	    sizeof(sc->sensordev.xname));
34652a6f82eSjsg 	for (i = 0; i < APS_NUM_SENSORS ; i++) {
34727515a6bSderaadt 		sensor_attach(&sc->sensordev, &sc->sensors[i]);
3484569cca6Sjsg 	}
34927515a6bSderaadt 	sensordev_install(&sc->sensordev);
3504569cca6Sjsg 
3514569cca6Sjsg 	/* Refresh sensor data every 0.5 seconds */
3524569cca6Sjsg 	timeout_set(&aps_timeout, aps_refresh, sc);
353325ff358Smk 	timeout_add_msec(&aps_timeout, 500);
3544569cca6Sjsg }
3554569cca6Sjsg 
356721d5653Sdjm int
aps_init(bus_space_tag_t iot,bus_space_handle_t ioh)357721d5653Sdjm aps_init(bus_space_tag_t iot, bus_space_handle_t ioh)
358721d5653Sdjm {
359c96de550Scanacar 	unsigned char iobuf[16];
360721d5653Sdjm 
361721d5653Sdjm 
362c96de550Scanacar 	/* command 0x17/0x81: check EC */
363c96de550Scanacar 	iobuf[APS_CMD] = 0x17;
364c96de550Scanacar 	iobuf[APS_ARG1] = 0x81;
365c96de550Scanacar 
366c96de550Scanacar 	if (aps_do_io(iot, ioh, iobuf, APS_WRITE_1, APS_READ_3))
3674569cca6Sjsg 		return (1);
368c96de550Scanacar 
369c96de550Scanacar 	if (iobuf[APS_RET] != 0 ||iobuf[APS_ARG3] != 0)
37088eb0740Sderaadt 		return (2);
371c96de550Scanacar 
372c96de550Scanacar 	/* Test values from the Linux driver */
373c96de550Scanacar 	if ((iobuf[APS_ARG1] != 0 || iobuf[APS_ARG2] != 0x60) &&
374c96de550Scanacar 	    (iobuf[APS_ARG1] != 1 || iobuf[APS_ARG2] != 0))
37588eb0740Sderaadt 		return (3);
376c96de550Scanacar 
377c96de550Scanacar 	/* command 0x14: set power */
378c96de550Scanacar 	iobuf[APS_CMD] = 0x14;
379c96de550Scanacar 	iobuf[APS_ARG1] = 0x01;
380c96de550Scanacar 
381c96de550Scanacar 	if (aps_do_io(iot, ioh, iobuf, APS_WRITE_1, APS_READ_0))
38288eb0740Sderaadt 		return (4);
383c96de550Scanacar 
384c96de550Scanacar 	if (iobuf[APS_RET] != 0)
38588eb0740Sderaadt 		return (5);
386c96de550Scanacar 
387c96de550Scanacar 	/* command 0x10: set config (sample rate and order) */
388c96de550Scanacar 	iobuf[APS_CMD] = 0x10;
389c96de550Scanacar 	iobuf[APS_ARG1] = 0xc8;
390c96de550Scanacar 	iobuf[APS_ARG2] = 0x00;
391c96de550Scanacar 	iobuf[APS_ARG3] = 0x02;
392c96de550Scanacar 
393c96de550Scanacar 	if (aps_do_io(iot, ioh, iobuf, APS_WRITE_3, APS_READ_0))
39488eb0740Sderaadt 		return (6);
395c96de550Scanacar 
396c96de550Scanacar 	if (iobuf[APS_RET] != 0)
39788eb0740Sderaadt 		return (7);
398c96de550Scanacar 
399c96de550Scanacar 	/* command 0x11: refresh data */
400c96de550Scanacar 	iobuf[APS_CMD] = 0x11;
401c96de550Scanacar 	if (aps_do_io(iot, ioh, iobuf, APS_WRITE_0, APS_READ_1))
40288eb0740Sderaadt 		return (8);
403c96de550Scanacar 
4044569cca6Sjsg 	return (0);
4054569cca6Sjsg }
4064569cca6Sjsg 
4074569cca6Sjsg int
aps_read_data(struct aps_softc * sc)4084569cca6Sjsg aps_read_data(struct aps_softc *sc)
4094569cca6Sjsg {
4104569cca6Sjsg 	bus_space_tag_t iot = sc->aps_iot;
4114569cca6Sjsg 	bus_space_handle_t ioh = sc->aps_ioh;
412c96de550Scanacar 	unsigned char iobuf[16];
4134569cca6Sjsg 
414c96de550Scanacar 	/* command 0x11: refresh data */
415c96de550Scanacar 	iobuf[APS_CMD] = 0x11;
416c96de550Scanacar 	if (aps_do_io(iot, ioh, iobuf, APS_WRITE_0, APS_READ_ALL))
4174569cca6Sjsg 		return (1);
418c96de550Scanacar 
419c96de550Scanacar 	sc->aps_data.state = iobuf[APS_STATE];
420c96de550Scanacar 	sc->aps_data.x_accel = iobuf[APS_XACCEL] + 256 * iobuf[APS_XACCEL + 1];
421c96de550Scanacar 	sc->aps_data.y_accel = iobuf[APS_YACCEL] + 256 * iobuf[APS_YACCEL + 1];
422c96de550Scanacar 	sc->aps_data.temp1 = iobuf[APS_TEMP];
423c96de550Scanacar 	sc->aps_data.x_var = iobuf[APS_XVAR] + 256 * iobuf[APS_XVAR + 1];
424c96de550Scanacar 	sc->aps_data.y_var = iobuf[APS_YVAR] + 256 * iobuf[APS_YVAR + 1];
425c96de550Scanacar 	sc->aps_data.temp2 = iobuf[APS_TEMP2];
426c96de550Scanacar 	sc->aps_data.input = iobuf[APS_INPUT];
427c96de550Scanacar 
428c96de550Scanacar 	return (0);
4294569cca6Sjsg }
4304569cca6Sjsg 
4314569cca6Sjsg void
aps_refresh_sensor_data(struct aps_softc * sc)4324569cca6Sjsg aps_refresh_sensor_data(struct aps_softc *sc)
4334569cca6Sjsg {
4344569cca6Sjsg 	int64_t temp;
4354569cca6Sjsg 	int i;
436037e02e3Sderaadt #if NAPM > 0
4372d357aedSnatano 	extern int lid_action;
438037e02e3Sderaadt 	extern int apm_lidclose;
439037e02e3Sderaadt #endif
4404569cca6Sjsg 
441c96de550Scanacar 	if (aps_read_data(sc))
4424569cca6Sjsg 		return;
4434569cca6Sjsg 
4444569cca6Sjsg 	for (i = 0; i < APS_NUM_SENSORS; i++) {
4454569cca6Sjsg 		sc->sensors[i].flags &= ~SENSOR_FINVALID;
4464569cca6Sjsg 	}
4474569cca6Sjsg 
4484569cca6Sjsg 	sc->sensors[APS_SENSOR_XACCEL].value = sc->aps_data.x_accel;
4494569cca6Sjsg 	sc->sensors[APS_SENSOR_YACCEL].value = sc->aps_data.y_accel;
4504569cca6Sjsg 
4514569cca6Sjsg 	/* convert to micro (mu) degrees */
4524569cca6Sjsg 	temp = sc->aps_data.temp1 * 1000000;
4534569cca6Sjsg 	/* convert to kelvin */
4544569cca6Sjsg 	temp += 273150000;
4554569cca6Sjsg 	sc->sensors[APS_SENSOR_TEMP1].value = temp;
4564569cca6Sjsg 
4574569cca6Sjsg 	/* convert to micro (mu) degrees */
4584569cca6Sjsg 	temp = sc->aps_data.temp2 * 1000000;
4594569cca6Sjsg 	/* convert to kelvin */
4604569cca6Sjsg 	temp += 273150000;
4614569cca6Sjsg 	sc->sensors[APS_SENSOR_TEMP2].value = temp;
4624569cca6Sjsg 
4634569cca6Sjsg 	sc->sensors[APS_SENSOR_XVAR].value = sc->aps_data.x_var;
4644569cca6Sjsg 	sc->sensors[APS_SENSOR_YVAR].value = sc->aps_data.y_var;
4654569cca6Sjsg 	sc->sensors[APS_SENSOR_KBACT].value =
4664569cca6Sjsg 	    (sc->aps_data.input &  APS_INPUT_KB) ? 1 : 0;
4674569cca6Sjsg 	sc->sensors[APS_SENSOR_MSACT].value =
4684569cca6Sjsg 	    (sc->aps_data.input & APS_INPUT_MS) ? 1 : 0;
469037e02e3Sderaadt #if NAPM > 0
4702d357aedSnatano 	if (lid_action &&
471037e02e3Sderaadt 	    (sc->sensors[APS_SENSOR_LIDOPEN].value == 1) &&
472633b34e4Sderaadt 	    (sc->aps_data.input & APS_INPUT_LIDOPEN) == 0)
473037e02e3Sderaadt 		/* Inform APM that the lid has closed */
474037e02e3Sderaadt 		apm_lidclose = 1;
475037e02e3Sderaadt #endif
4764569cca6Sjsg 	sc->sensors[APS_SENSOR_LIDOPEN].value =
4774569cca6Sjsg 	    (sc->aps_data.input & APS_INPUT_LIDOPEN) ? 1 : 0;
4784569cca6Sjsg }
4794569cca6Sjsg 
4804569cca6Sjsg void
aps_refresh(void * arg)4814569cca6Sjsg aps_refresh(void *arg)
4824569cca6Sjsg {
4834569cca6Sjsg 	struct aps_softc *sc = (struct aps_softc *)arg;
4844569cca6Sjsg 
4854569cca6Sjsg 	aps_refresh_sensor_data(sc);
486325ff358Smk 	timeout_add_msec(&aps_timeout, 500);
4874569cca6Sjsg }
488721d5653Sdjm 
489e612a1baSderaadt int
aps_activate(struct device * self,int act)490e612a1baSderaadt aps_activate(struct device *self, int act)
491721d5653Sdjm {
492e612a1baSderaadt 	struct aps_softc *sc = (struct aps_softc *)self;
493721d5653Sdjm 	bus_space_tag_t iot = sc->aps_iot;
494721d5653Sdjm 	bus_space_handle_t ioh = sc->aps_ioh;
495c96de550Scanacar 	unsigned char iobuf[16];
496721d5653Sdjm 
49788eb0740Sderaadt 	/* check if we bombed during attach */
49888eb0740Sderaadt 	if (!timeout_initialized(&aps_timeout))
49988eb0740Sderaadt 		return (0);
50088eb0740Sderaadt 
501e612a1baSderaadt 	switch (act) {
502e612a1baSderaadt 	case DVACT_SUSPEND:
503721d5653Sdjm 		timeout_del(&aps_timeout);
504e612a1baSderaadt 		break;
505e612a1baSderaadt 	case DVACT_RESUME:
506721d5653Sdjm 		/*
507721d5653Sdjm 		 * Redo the init sequence on resume, because APS is
508721d5653Sdjm 		 * as forgetful as it is deaf.
509721d5653Sdjm 		 */
510721d5653Sdjm 
511c96de550Scanacar 		/* get APS mode */
512c96de550Scanacar 		iobuf[APS_CMD] = 0x13;
51388eb0740Sderaadt 		aps_do_io(iot, ioh, iobuf, APS_WRITE_0, APS_READ_1);
51488eb0740Sderaadt 
51588eb0740Sderaadt 		aps_init(iot, ioh);
516325ff358Smk 		timeout_add_msec(&aps_timeout, 500);
517e612a1baSderaadt 		break;
518e612a1baSderaadt 	}
519e612a1baSderaadt 	return (0);
520e612a1baSderaadt }
521