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