aps.c revision 1.13 1 1.13 jmcneill /* $NetBSD: aps.c,v 1.13 2011/01/18 16:45:11 jmcneill Exp $ */
2 1.1 xtraeme /* $OpenBSD: aps.c,v 1.15 2007/05/19 19:14:11 tedu Exp $ */
3 1.11 jmcneill /* $OpenBSD: aps.c,v 1.17 2008/06/27 06:08:43 canacar Exp $ */
4 1.1 xtraeme /*
5 1.1 xtraeme * Copyright (c) 2005 Jonathan Gray <jsg (at) openbsd.org>
6 1.11 jmcneill * Copyright (c) 2008 Can Erkin Acar <canacar (at) openbsd.org>
7 1.1 xtraeme *
8 1.1 xtraeme * Permission to use, copy, modify, and distribute this software for any
9 1.1 xtraeme * purpose with or without fee is hereby granted, provided that the above
10 1.1 xtraeme * copyright notice and this permission notice appear in all copies.
11 1.1 xtraeme *
12 1.1 xtraeme * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
13 1.1 xtraeme * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
14 1.1 xtraeme * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
15 1.1 xtraeme * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
16 1.1 xtraeme * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
17 1.1 xtraeme * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
18 1.1 xtraeme * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
19 1.1 xtraeme */
20 1.1 xtraeme
21 1.1 xtraeme /*
22 1.1 xtraeme * A driver for the ThinkPad Active Protection System based on notes from
23 1.1 xtraeme * http://www.almaden.ibm.com/cs/people/marksmith/tpaps.html
24 1.1 xtraeme */
25 1.1 xtraeme
26 1.1 xtraeme #include <sys/cdefs.h>
27 1.13 jmcneill __KERNEL_RCSID(0, "$NetBSD: aps.c,v 1.13 2011/01/18 16:45:11 jmcneill Exp $");
28 1.1 xtraeme
29 1.1 xtraeme #include <sys/param.h>
30 1.1 xtraeme #include <sys/systm.h>
31 1.1 xtraeme #include <sys/device.h>
32 1.1 xtraeme #include <sys/kernel.h>
33 1.1 xtraeme #include <sys/callout.h>
34 1.11 jmcneill #include <sys/module.h>
35 1.1 xtraeme
36 1.2 ad #include <sys/bus.h>
37 1.1 xtraeme
38 1.1 xtraeme #include <dev/sysmon/sysmonvar.h>
39 1.1 xtraeme
40 1.1 xtraeme #include <dev/isa/isareg.h>
41 1.1 xtraeme #include <dev/isa/isavar.h>
42 1.1 xtraeme
43 1.1 xtraeme #if defined(APSDEBUG)
44 1.1 xtraeme #define DPRINTF(x) do { printf x; } while (0)
45 1.1 xtraeme #else
46 1.1 xtraeme #define DPRINTF(x)
47 1.1 xtraeme #endif
48 1.1 xtraeme
49 1.1 xtraeme
50 1.11 jmcneill /*
51 1.11 jmcneill * EC interface on Thinkpad Laptops, from Linux HDAPS driver notes.
52 1.11 jmcneill * From Renesans H8S/2140B Group Hardware Manual
53 1.11 jmcneill * http://documentation.renesas.com/eng/products/mpumcu/rej09b0300_2140bhm.pdf
54 1.11 jmcneill *
55 1.11 jmcneill * EC uses LPC Channel 3 registers TWR0..15
56 1.11 jmcneill */
57 1.11 jmcneill
58 1.11 jmcneill /* STR3 status register */
59 1.11 jmcneill #define APS_STR3 0x04
60 1.11 jmcneill
61 1.11 jmcneill #define APS_STR3_IBF3B 0x80 /* Input buffer full (host->slave) */
62 1.11 jmcneill #define APS_STR3_OBF3B 0x40 /* Output buffer full (slave->host)*/
63 1.11 jmcneill #define APS_STR3_MWMF 0x20 /* Master write mode */
64 1.11 jmcneill #define APS_STR3_SWMF 0x10 /* Slave write mode */
65 1.11 jmcneill
66 1.11 jmcneill
67 1.11 jmcneill /* Base address of TWR registers */
68 1.11 jmcneill #define APS_TWR_BASE 0x10
69 1.11 jmcneill #define APS_TWR_RET 0x1f
70 1.11 jmcneill
71 1.11 jmcneill /* TWR registers */
72 1.11 jmcneill #define APS_CMD 0x00
73 1.11 jmcneill #define APS_ARG1 0x01
74 1.11 jmcneill #define APS_ARG2 0x02
75 1.11 jmcneill #define APS_ARG3 0x03
76 1.11 jmcneill #define APS_RET 0x0f
77 1.11 jmcneill
78 1.11 jmcneill /* Sensor values */
79 1.11 jmcneill #define APS_STATE 0x01
80 1.11 jmcneill #define APS_XACCEL 0x02
81 1.11 jmcneill #define APS_YACCEL 0x04
82 1.11 jmcneill #define APS_TEMP 0x06
83 1.11 jmcneill #define APS_XVAR 0x07
84 1.11 jmcneill #define APS_YVAR 0x09
85 1.11 jmcneill #define APS_TEMP2 0x0b
86 1.11 jmcneill #define APS_UNKNOWN 0x0c
87 1.11 jmcneill #define APS_INPUT 0x0d
88 1.11 jmcneill
89 1.11 jmcneill /* write masks for I/O, send command + 0-3 arguments*/
90 1.11 jmcneill #define APS_WRITE_0 0x0001
91 1.11 jmcneill #define APS_WRITE_1 0x0003
92 1.11 jmcneill #define APS_WRITE_2 0x0007
93 1.11 jmcneill #define APS_WRITE_3 0x000f
94 1.11 jmcneill
95 1.11 jmcneill /* read masks for I/O, read 0-3 values (skip command byte) */
96 1.11 jmcneill #define APS_READ_0 0x0000
97 1.11 jmcneill #define APS_READ_1 0x0002
98 1.11 jmcneill #define APS_READ_2 0x0006
99 1.11 jmcneill #define APS_READ_3 0x000e
100 1.1 xtraeme
101 1.11 jmcneill #define APS_READ_RET 0x8000
102 1.11 jmcneill #define APS_READ_ALL 0xffff
103 1.1 xtraeme
104 1.11 jmcneill /* Bit definitions for APS_INPUT value */
105 1.1 xtraeme #define APS_INPUT_KB (1 << 5)
106 1.1 xtraeme #define APS_INPUT_MS (1 << 6)
107 1.1 xtraeme #define APS_INPUT_LIDOPEN (1 << 7)
108 1.1 xtraeme
109 1.1 xtraeme #define APS_ADDR_SIZE 0x1f
110 1.1 xtraeme
111 1.1 xtraeme struct sensor_rec {
112 1.1 xtraeme uint8_t state;
113 1.1 xtraeme uint16_t x_accel;
114 1.1 xtraeme uint16_t y_accel;
115 1.1 xtraeme uint8_t temp1;
116 1.1 xtraeme uint16_t x_var;
117 1.1 xtraeme uint16_t y_var;
118 1.1 xtraeme uint8_t temp2;
119 1.1 xtraeme uint8_t unk;
120 1.1 xtraeme uint8_t input;
121 1.1 xtraeme };
122 1.1 xtraeme
123 1.1 xtraeme enum aps_sensors {
124 1.1 xtraeme APS_SENSOR_XACCEL = 0,
125 1.1 xtraeme APS_SENSOR_YACCEL,
126 1.1 xtraeme APS_SENSOR_XVAR,
127 1.1 xtraeme APS_SENSOR_YVAR,
128 1.1 xtraeme APS_SENSOR_TEMP1,
129 1.1 xtraeme APS_SENSOR_TEMP2,
130 1.1 xtraeme APS_SENSOR_KBACT,
131 1.1 xtraeme APS_SENSOR_MSACT,
132 1.1 xtraeme APS_SENSOR_LIDOPEN,
133 1.1 xtraeme APS_NUM_SENSORS
134 1.1 xtraeme };
135 1.1 xtraeme
136 1.1 xtraeme struct aps_softc {
137 1.1 xtraeme bus_space_tag_t sc_iot;
138 1.1 xtraeme bus_space_handle_t sc_ioh;
139 1.11 jmcneill bool sc_bus_space_valid;
140 1.1 xtraeme
141 1.3 xtraeme struct sysmon_envsys *sc_sme;
142 1.3 xtraeme envsys_data_t sc_sensor[APS_NUM_SENSORS];
143 1.1 xtraeme struct callout sc_callout;
144 1.1 xtraeme
145 1.1 xtraeme struct sensor_rec aps_data;
146 1.1 xtraeme };
147 1.1 xtraeme
148 1.8 xtraeme static int aps_match(device_t, cfdata_t, void *);
149 1.8 xtraeme static void aps_attach(device_t, device_t, void *);
150 1.8 xtraeme static int aps_detach(device_t, int);
151 1.1 xtraeme
152 1.1 xtraeme static int aps_init(struct aps_softc *);
153 1.11 jmcneill static int aps_read_data(struct aps_softc *);
154 1.11 jmcneill static void aps_refresh_sensor_data(struct aps_softc *);
155 1.1 xtraeme static void aps_refresh(void *);
156 1.11 jmcneill static int aps_do_io(bus_space_tag_t, bus_space_handle_t,
157 1.11 jmcneill unsigned char *, int, int);
158 1.10 dyoung static bool aps_suspend(device_t, const pmf_qual_t *);
159 1.10 dyoung static bool aps_resume(device_t, const pmf_qual_t *);
160 1.1 xtraeme
161 1.8 xtraeme CFATTACH_DECL_NEW(aps, sizeof(struct aps_softc),
162 1.1 xtraeme aps_match, aps_attach, aps_detach, NULL);
163 1.1 xtraeme
164 1.11 jmcneill /* properly communicate with the controller, writing a set of memory
165 1.11 jmcneill * locations and reading back another set */
166 1.11 jmcneill static int
167 1.11 jmcneill aps_do_io(bus_space_tag_t iot, bus_space_handle_t ioh,
168 1.11 jmcneill unsigned char *buf, int wmask, int rmask)
169 1.11 jmcneill {
170 1.11 jmcneill int bp, stat, n;
171 1.11 jmcneill
172 1.11 jmcneill DPRINTF(("aps_do_io: CMD: 0x%02x, wmask: 0x%04x, rmask: 0x%04x\n",
173 1.11 jmcneill buf[0], wmask, rmask));
174 1.11 jmcneill
175 1.11 jmcneill /* write init byte using arbitration */
176 1.11 jmcneill for (n = 0; n < 100; n++) {
177 1.11 jmcneill stat = bus_space_read_1(iot, ioh, APS_STR3);
178 1.11 jmcneill if (stat & (APS_STR3_OBF3B | APS_STR3_SWMF)) {
179 1.11 jmcneill bus_space_read_1(iot, ioh, APS_TWR_RET);
180 1.11 jmcneill continue;
181 1.11 jmcneill }
182 1.11 jmcneill bus_space_write_1(iot, ioh, APS_TWR_BASE, buf[0]);
183 1.11 jmcneill stat = bus_space_read_1(iot, ioh, APS_STR3);
184 1.11 jmcneill if (stat & (APS_STR3_MWMF))
185 1.11 jmcneill break;
186 1.11 jmcneill delay(1);
187 1.11 jmcneill }
188 1.11 jmcneill
189 1.11 jmcneill if (n == 100) {
190 1.11 jmcneill DPRINTF(("aps_do_io: Failed to get bus\n"));
191 1.11 jmcneill return 1;
192 1.11 jmcneill }
193 1.11 jmcneill
194 1.11 jmcneill /* write data bytes, init already sent */
195 1.11 jmcneill /* make sure last bye is always written as this will trigger slave */
196 1.11 jmcneill wmask |= APS_READ_RET;
197 1.11 jmcneill buf[APS_RET] = 0x01;
198 1.11 jmcneill
199 1.11 jmcneill for (n = 1, bp = 2; n < 16; bp <<= 1, n++) {
200 1.11 jmcneill if (wmask & bp) {
201 1.11 jmcneill bus_space_write_1(iot, ioh, APS_TWR_BASE + n, buf[n]);
202 1.11 jmcneill DPRINTF(("aps_do_io: write %2d 0x%02x\n", n, buf[n]));
203 1.11 jmcneill }
204 1.11 jmcneill }
205 1.11 jmcneill
206 1.11 jmcneill for (n = 0; n < 100; n++) {
207 1.11 jmcneill stat = bus_space_read_1(iot, ioh, APS_STR3);
208 1.11 jmcneill if (stat & (APS_STR3_OBF3B))
209 1.11 jmcneill break;
210 1.11 jmcneill delay(5 * 100);
211 1.11 jmcneill }
212 1.11 jmcneill
213 1.11 jmcneill if (n == 100) {
214 1.11 jmcneill DPRINTF(("aps_do_io: timeout waiting response\n"));
215 1.11 jmcneill return 1;
216 1.11 jmcneill }
217 1.11 jmcneill /* wait for data available */
218 1.11 jmcneill /* make sure to read the final byte to clear status */
219 1.11 jmcneill rmask |= APS_READ_RET;
220 1.11 jmcneill
221 1.11 jmcneill /* read cmd and data bytes */
222 1.11 jmcneill for (n = 0, bp = 1; n < 16; bp <<= 1, n++) {
223 1.11 jmcneill if (rmask & bp) {
224 1.11 jmcneill buf[n] = bus_space_read_1(iot, ioh, APS_TWR_BASE + n);
225 1.11 jmcneill DPRINTF(("aps_do_io: read %2d 0x%02x\n", n, buf[n]));
226 1.11 jmcneill }
227 1.11 jmcneill }
228 1.11 jmcneill
229 1.11 jmcneill return 0;
230 1.11 jmcneill }
231 1.11 jmcneill
232 1.8 xtraeme static int
233 1.8 xtraeme aps_match(device_t parent, cfdata_t match, void *aux)
234 1.1 xtraeme {
235 1.1 xtraeme struct isa_attach_args *ia = aux;
236 1.1 xtraeme bus_space_tag_t iot = ia->ia_iot;
237 1.1 xtraeme bus_space_handle_t ioh;
238 1.11 jmcneill unsigned char iobuf[16];
239 1.11 jmcneill int iobase;
240 1.1 xtraeme uint8_t cr;
241 1.1 xtraeme
242 1.1 xtraeme /* Must supply an address */
243 1.1 xtraeme if (ia->ia_nio < 1)
244 1.1 xtraeme return 0;
245 1.1 xtraeme
246 1.1 xtraeme if (ISA_DIRECT_CONFIG(ia))
247 1.1 xtraeme return 0;
248 1.1 xtraeme
249 1.1 xtraeme if (ia->ia_io[0].ir_addr == ISA_UNKNOWN_PORT)
250 1.1 xtraeme return 0;
251 1.1 xtraeme
252 1.1 xtraeme iobase = ia->ia_io[0].ir_addr;
253 1.1 xtraeme
254 1.1 xtraeme if (bus_space_map(iot, iobase, APS_ADDR_SIZE, 0, &ioh)) {
255 1.1 xtraeme aprint_error("aps: can't map i/o space\n");
256 1.1 xtraeme return 0;
257 1.1 xtraeme }
258 1.1 xtraeme
259 1.11 jmcneill
260 1.1 xtraeme /* See if this machine has APS */
261 1.1 xtraeme
262 1.11 jmcneill /* get APS mode */
263 1.11 jmcneill iobuf[APS_CMD] = 0x13;
264 1.11 jmcneill if (aps_do_io(iot, ioh, iobuf, APS_WRITE_0, APS_READ_1)) {
265 1.1 xtraeme bus_space_unmap(iot, ioh, APS_ADDR_SIZE);
266 1.1 xtraeme return 0;
267 1.1 xtraeme }
268 1.1 xtraeme
269 1.1 xtraeme /*
270 1.1 xtraeme * Observed values from Linux driver:
271 1.1 xtraeme * 0x01: T42
272 1.1 xtraeme * 0x02: chip already initialised
273 1.1 xtraeme * 0x03: T41
274 1.11 jmcneill * 0x05: T61
275 1.1 xtraeme */
276 1.11 jmcneill
277 1.11 jmcneill cr = iobuf[APS_ARG1];
278 1.11 jmcneill
279 1.1 xtraeme bus_space_unmap(iot, ioh, APS_ADDR_SIZE);
280 1.1 xtraeme DPRINTF(("aps: state register 0x%x\n", cr));
281 1.11 jmcneill
282 1.11 jmcneill if (iobuf[APS_RET] != 0 || cr < 1 || cr > 5) {
283 1.1 xtraeme DPRINTF(("aps0: unsupported state %d\n", cr));
284 1.1 xtraeme return 0;
285 1.1 xtraeme }
286 1.1 xtraeme
287 1.1 xtraeme ia->ia_nio = 1;
288 1.1 xtraeme ia->ia_io[0].ir_size = APS_ADDR_SIZE;
289 1.1 xtraeme ia->ia_niomem = 0;
290 1.1 xtraeme ia->ia_nirq = 0;
291 1.1 xtraeme ia->ia_ndrq = 0;
292 1.1 xtraeme
293 1.1 xtraeme return 1;
294 1.1 xtraeme }
295 1.1 xtraeme
296 1.8 xtraeme static void
297 1.8 xtraeme aps_attach(device_t parent, device_t self, void *aux)
298 1.1 xtraeme {
299 1.8 xtraeme struct aps_softc *sc = device_private(self);
300 1.1 xtraeme struct isa_attach_args *ia = aux;
301 1.1 xtraeme int iobase, i;
302 1.1 xtraeme
303 1.1 xtraeme sc->sc_iot = ia->ia_iot;
304 1.1 xtraeme iobase = ia->ia_io[0].ir_addr;
305 1.1 xtraeme
306 1.11 jmcneill callout_init(&sc->sc_callout, 0);
307 1.11 jmcneill callout_setfunc(&sc->sc_callout, aps_refresh, sc);
308 1.11 jmcneill
309 1.1 xtraeme if (bus_space_map(sc->sc_iot, iobase, APS_ADDR_SIZE, 0, &sc->sc_ioh)) {
310 1.1 xtraeme aprint_error(": can't map i/o space\n");
311 1.1 xtraeme return;
312 1.1 xtraeme }
313 1.11 jmcneill sc->sc_bus_space_valid = true;
314 1.1 xtraeme
315 1.1 xtraeme aprint_naive("\n");
316 1.12 jmcneill aprint_normal(": Thinkpad Active Protection System\n");
317 1.1 xtraeme
318 1.11 jmcneill if (aps_init(sc)) {
319 1.11 jmcneill aprint_error_dev(self, "failed to initialize\n");
320 1.8 xtraeme goto out;
321 1.1 xtraeme }
322 1.1 xtraeme
323 1.1 xtraeme /* Initialize sensors */
324 1.1 xtraeme #define INITDATA(idx, unit, string) \
325 1.6 xtraeme sc->sc_sensor[idx].units = unit; \
326 1.6 xtraeme strlcpy(sc->sc_sensor[idx].desc, string, \
327 1.6 xtraeme sizeof(sc->sc_sensor[idx].desc));
328 1.1 xtraeme
329 1.1 xtraeme INITDATA(APS_SENSOR_XACCEL, ENVSYS_INTEGER, "X_ACCEL");
330 1.1 xtraeme INITDATA(APS_SENSOR_YACCEL, ENVSYS_INTEGER, "Y_ACCEL");
331 1.1 xtraeme INITDATA(APS_SENSOR_TEMP1, ENVSYS_STEMP, "TEMP_1");
332 1.1 xtraeme INITDATA(APS_SENSOR_TEMP2, ENVSYS_STEMP, "TEMP_2");
333 1.1 xtraeme INITDATA(APS_SENSOR_XVAR, ENVSYS_INTEGER, "X_VAR");
334 1.1 xtraeme INITDATA(APS_SENSOR_YVAR, ENVSYS_INTEGER, "Y_VAR");
335 1.1 xtraeme INITDATA(APS_SENSOR_KBACT, ENVSYS_INDICATOR, "Keyboard Active");
336 1.1 xtraeme INITDATA(APS_SENSOR_MSACT, ENVSYS_INDICATOR, "Mouse Active");
337 1.1 xtraeme INITDATA(APS_SENSOR_LIDOPEN, ENVSYS_INDICATOR, "Lid Open");
338 1.1 xtraeme
339 1.3 xtraeme sc->sc_sme = sysmon_envsys_create();
340 1.3 xtraeme for (i = 0; i < APS_NUM_SENSORS; i++) {
341 1.4 kefren sc->sc_sensor[i].state = ENVSYS_SVALID;
342 1.3 xtraeme if (sysmon_envsys_sensor_attach(sc->sc_sme,
343 1.3 xtraeme &sc->sc_sensor[i])) {
344 1.3 xtraeme sysmon_envsys_destroy(sc->sc_sme);
345 1.8 xtraeme goto out;
346 1.3 xtraeme }
347 1.3 xtraeme }
348 1.1 xtraeme /*
349 1.1 xtraeme * Register with the sysmon_envsys(9) framework.
350 1.1 xtraeme */
351 1.8 xtraeme sc->sc_sme->sme_name = device_xname(self);
352 1.8 xtraeme sc->sc_sme->sme_flags = SME_DISABLE_REFRESH;
353 1.1 xtraeme
354 1.3 xtraeme if ((i = sysmon_envsys_register(sc->sc_sme))) {
355 1.8 xtraeme aprint_error_dev(self,
356 1.8 xtraeme "unable to register with sysmon (%d)\n", i);
357 1.3 xtraeme sysmon_envsys_destroy(sc->sc_sme);
358 1.8 xtraeme goto out;
359 1.1 xtraeme }
360 1.1 xtraeme
361 1.5 jmcneill if (!pmf_device_register(self, aps_suspend, aps_resume))
362 1.5 jmcneill aprint_error_dev(self, "couldn't establish power handler\n");
363 1.1 xtraeme
364 1.1 xtraeme /* Refresh sensor data every 0.5 seconds */
365 1.1 xtraeme callout_schedule(&sc->sc_callout, (hz) / 2);
366 1.1 xtraeme
367 1.8 xtraeme return;
368 1.8 xtraeme
369 1.8 xtraeme out:
370 1.8 xtraeme bus_space_unmap(sc->sc_iot, sc->sc_ioh, APS_ADDR_SIZE);
371 1.1 xtraeme }
372 1.1 xtraeme
373 1.1 xtraeme static int
374 1.1 xtraeme aps_init(struct aps_softc *sc)
375 1.1 xtraeme {
376 1.11 jmcneill unsigned char iobuf[16];
377 1.11 jmcneill
378 1.11 jmcneill /* command 0x17/0x81: check EC */
379 1.11 jmcneill iobuf[APS_CMD] = 0x17;
380 1.11 jmcneill iobuf[APS_ARG1] = 0x81;
381 1.11 jmcneill
382 1.11 jmcneill if (aps_do_io(sc->sc_iot, sc->sc_ioh, iobuf, APS_WRITE_1, APS_READ_3))
383 1.11 jmcneill return 1;
384 1.11 jmcneill if (iobuf[APS_RET] != 0 ||iobuf[APS_ARG3] != 0)
385 1.11 jmcneill return 1;
386 1.11 jmcneill
387 1.11 jmcneill /* Test values from the Linux driver */
388 1.11 jmcneill if ((iobuf[APS_ARG1] != 0 || iobuf[APS_ARG2] != 0x60) &&
389 1.11 jmcneill (iobuf[APS_ARG1] != 1 || iobuf[APS_ARG2] != 0))
390 1.11 jmcneill return 1;
391 1.11 jmcneill
392 1.11 jmcneill /* command 0x14: set power */
393 1.11 jmcneill iobuf[APS_CMD] = 0x14;
394 1.11 jmcneill iobuf[APS_ARG1] = 0x01;
395 1.11 jmcneill
396 1.11 jmcneill if (aps_do_io(sc->sc_iot, sc->sc_ioh, iobuf, APS_WRITE_1, APS_READ_0))
397 1.11 jmcneill return 1;
398 1.11 jmcneill
399 1.11 jmcneill if (iobuf[APS_RET] != 0)
400 1.11 jmcneill return 1;
401 1.11 jmcneill
402 1.11 jmcneill /* command 0x10: set config (sample rate and order) */
403 1.11 jmcneill iobuf[APS_CMD] = 0x10;
404 1.11 jmcneill iobuf[APS_ARG1] = 0xc8;
405 1.11 jmcneill iobuf[APS_ARG2] = 0x00;
406 1.11 jmcneill iobuf[APS_ARG3] = 0x02;
407 1.11 jmcneill
408 1.11 jmcneill if (aps_do_io(sc->sc_iot, sc->sc_ioh, iobuf, APS_WRITE_3, APS_READ_0))
409 1.11 jmcneill return 1;
410 1.11 jmcneill
411 1.11 jmcneill /* command 0x11: refresh data */
412 1.11 jmcneill iobuf[APS_CMD] = 0x11;
413 1.11 jmcneill if (aps_do_io(sc->sc_iot, sc->sc_ioh, iobuf, APS_WRITE_0, APS_READ_1))
414 1.11 jmcneill return 1;
415 1.11 jmcneill if (iobuf[APS_ARG1] != 0)
416 1.11 jmcneill return 1;
417 1.1 xtraeme
418 1.11 jmcneill return 0;
419 1.1 xtraeme }
420 1.1 xtraeme
421 1.1 xtraeme static int
422 1.8 xtraeme aps_detach(device_t self, int flags)
423 1.1 xtraeme {
424 1.1 xtraeme struct aps_softc *sc = device_private(self);
425 1.1 xtraeme
426 1.1 xtraeme callout_stop(&sc->sc_callout);
427 1.1 xtraeme callout_destroy(&sc->sc_callout);
428 1.11 jmcneill
429 1.11 jmcneill if (sc->sc_sme)
430 1.11 jmcneill sysmon_envsys_unregister(sc->sc_sme);
431 1.11 jmcneill if (sc->sc_bus_space_valid == true)
432 1.11 jmcneill bus_space_unmap(sc->sc_iot, sc->sc_ioh, APS_ADDR_SIZE);
433 1.1 xtraeme
434 1.1 xtraeme return 0;
435 1.1 xtraeme }
436 1.1 xtraeme
437 1.11 jmcneill static int
438 1.11 jmcneill aps_read_data(struct aps_softc *sc)
439 1.1 xtraeme {
440 1.11 jmcneill unsigned char iobuf[16];
441 1.11 jmcneill
442 1.11 jmcneill iobuf[APS_CMD] = 0x11;
443 1.11 jmcneill if (aps_do_io(sc->sc_iot, sc->sc_ioh, iobuf, APS_WRITE_0, APS_READ_ALL))
444 1.11 jmcneill return 1;
445 1.11 jmcneill
446 1.11 jmcneill sc->aps_data.state = iobuf[APS_STATE];
447 1.11 jmcneill sc->aps_data.x_accel = iobuf[APS_XACCEL] + 256 * iobuf[APS_XACCEL + 1];
448 1.11 jmcneill sc->aps_data.y_accel = iobuf[APS_YACCEL] + 256 * iobuf[APS_YACCEL + 1];
449 1.11 jmcneill sc->aps_data.temp1 = iobuf[APS_TEMP];
450 1.11 jmcneill sc->aps_data.x_var = iobuf[APS_XVAR] + 256 * iobuf[APS_XVAR + 1];
451 1.11 jmcneill sc->aps_data.y_var = iobuf[APS_YVAR] + 256 * iobuf[APS_YVAR + 1];
452 1.11 jmcneill sc->aps_data.temp2 = iobuf[APS_TEMP2];
453 1.11 jmcneill sc->aps_data.input = iobuf[APS_INPUT];
454 1.1 xtraeme
455 1.1 xtraeme return 0;
456 1.1 xtraeme }
457 1.1 xtraeme
458 1.1 xtraeme static void
459 1.1 xtraeme aps_refresh_sensor_data(struct aps_softc *sc)
460 1.1 xtraeme {
461 1.1 xtraeme int64_t temp;
462 1.1 xtraeme
463 1.13 jmcneill if (aps_read_data(sc)) {
464 1.13 jmcneill printf("aps0: read data failed\n");
465 1.1 xtraeme return;
466 1.13 jmcneill }
467 1.1 xtraeme
468 1.3 xtraeme sc->sc_sensor[APS_SENSOR_XACCEL].value_cur = sc->aps_data.x_accel;
469 1.3 xtraeme sc->sc_sensor[APS_SENSOR_YACCEL].value_cur = sc->aps_data.y_accel;
470 1.1 xtraeme
471 1.13 jmcneill if (sc->aps_data.temp1 == 0xff)
472 1.13 jmcneill sc->sc_sensor[APS_SENSOR_TEMP1].state = ENVSYS_SINVALID;
473 1.13 jmcneill else {
474 1.13 jmcneill /* convert to micro (mu) degrees */
475 1.13 jmcneill temp = sc->aps_data.temp1 * 1000000;
476 1.13 jmcneill /* convert to kelvin */
477 1.13 jmcneill temp += 273150000;
478 1.13 jmcneill sc->sc_sensor[APS_SENSOR_TEMP1].value_cur = temp;
479 1.13 jmcneill sc->sc_sensor[APS_SENSOR_TEMP1].state = ENVSYS_SVALID;
480 1.13 jmcneill }
481 1.13 jmcneill
482 1.13 jmcneill if (sc->aps_data.temp2 == 0xff)
483 1.13 jmcneill sc->sc_sensor[APS_SENSOR_TEMP2].state = ENVSYS_SINVALID;
484 1.13 jmcneill else {
485 1.13 jmcneill /* convert to micro (mu) degrees */
486 1.13 jmcneill temp = sc->aps_data.temp2 * 1000000;
487 1.13 jmcneill /* convert to kelvin */
488 1.13 jmcneill temp += 273150000;
489 1.13 jmcneill sc->sc_sensor[APS_SENSOR_TEMP2].value_cur = temp;
490 1.13 jmcneill sc->sc_sensor[APS_SENSOR_TEMP2].state = ENVSYS_SVALID;
491 1.13 jmcneill }
492 1.1 xtraeme
493 1.3 xtraeme sc->sc_sensor[APS_SENSOR_XVAR].value_cur = sc->aps_data.x_var;
494 1.3 xtraeme sc->sc_sensor[APS_SENSOR_YVAR].value_cur = sc->aps_data.y_var;
495 1.3 xtraeme sc->sc_sensor[APS_SENSOR_KBACT].value_cur =
496 1.1 xtraeme (sc->aps_data.input & APS_INPUT_KB) ? 1 : 0;
497 1.3 xtraeme sc->sc_sensor[APS_SENSOR_MSACT].value_cur =
498 1.1 xtraeme (sc->aps_data.input & APS_INPUT_MS) ? 1 : 0;
499 1.3 xtraeme sc->sc_sensor[APS_SENSOR_LIDOPEN].value_cur =
500 1.1 xtraeme (sc->aps_data.input & APS_INPUT_LIDOPEN) ? 1 : 0;
501 1.1 xtraeme }
502 1.1 xtraeme
503 1.1 xtraeme static void
504 1.1 xtraeme aps_refresh(void *arg)
505 1.1 xtraeme {
506 1.8 xtraeme struct aps_softc *sc = arg;
507 1.1 xtraeme
508 1.1 xtraeme aps_refresh_sensor_data(sc);
509 1.1 xtraeme callout_schedule(&sc->sc_callout, (hz) / 2);
510 1.1 xtraeme }
511 1.1 xtraeme
512 1.5 jmcneill static bool
513 1.10 dyoung aps_suspend(device_t dv, const pmf_qual_t *qual)
514 1.5 jmcneill {
515 1.5 jmcneill struct aps_softc *sc = device_private(dv);
516 1.5 jmcneill
517 1.5 jmcneill callout_stop(&sc->sc_callout);
518 1.5 jmcneill
519 1.5 jmcneill return true;
520 1.5 jmcneill }
521 1.5 jmcneill
522 1.5 jmcneill static bool
523 1.10 dyoung aps_resume(device_t dv, const pmf_qual_t *qual)
524 1.1 xtraeme {
525 1.5 jmcneill struct aps_softc *sc = device_private(dv);
526 1.11 jmcneill unsigned char iobuf[16];
527 1.5 jmcneill
528 1.5 jmcneill /*
529 1.5 jmcneill * Redo the init sequence on resume, because APS is
530 1.5 jmcneill * as forgetful as it is deaf.
531 1.5 jmcneill */
532 1.5 jmcneill
533 1.11 jmcneill /* get APS mode */
534 1.11 jmcneill iobuf[APS_CMD] = 0x13;
535 1.11 jmcneill if (aps_do_io(sc->sc_iot, sc->sc_ioh, iobuf, APS_WRITE_0, APS_READ_1)
536 1.11 jmcneill || aps_init(sc))
537 1.11 jmcneill aprint_error_dev(dv, "failed to wake up\n");
538 1.11 jmcneill else
539 1.5 jmcneill callout_schedule(&sc->sc_callout, (hz) / 2);
540 1.1 xtraeme
541 1.5 jmcneill return true;
542 1.1 xtraeme }
543 1.11 jmcneill
544 1.11 jmcneill MODULE(MODULE_CLASS_DRIVER, aps, NULL);
545 1.11 jmcneill
546 1.11 jmcneill #ifdef _MODULE
547 1.11 jmcneill #include "ioconf.c"
548 1.11 jmcneill #endif
549 1.11 jmcneill
550 1.11 jmcneill static int
551 1.11 jmcneill aps_modcmd(modcmd_t cmd, void *opaque)
552 1.11 jmcneill {
553 1.11 jmcneill switch (cmd) {
554 1.11 jmcneill case MODULE_CMD_INIT:
555 1.11 jmcneill #ifdef _MODULE
556 1.11 jmcneill return config_init_component(cfdriver_ioconf_aps,
557 1.11 jmcneill cfattach_ioconf_aps, cfdata_ioconf_aps);
558 1.11 jmcneill #else
559 1.11 jmcneill return 0;
560 1.11 jmcneill #endif
561 1.11 jmcneill case MODULE_CMD_FINI:
562 1.11 jmcneill #ifdef _MODULE
563 1.11 jmcneill return config_fini_component(cfdriver_ioconf_aps,
564 1.11 jmcneill cfattach_ioconf_aps, cfdata_ioconf_aps);
565 1.11 jmcneill #else
566 1.11 jmcneill return 0;
567 1.11 jmcneill #endif
568 1.11 jmcneill default:
569 1.11 jmcneill return ENOTTY;
570 1.11 jmcneill }
571 1.11 jmcneill }
572