Annotation of sys/dev/isa/aps.c, Revision 1.1.1.1
1.1 nbrk 1: /* $OpenBSD: aps.c,v 1.15 2007/05/19 19:14:11 tedu Exp $ */
2: /*
3: * Copyright (c) 2005 Jonathan Gray <jsg@openbsd.org>
4: *
5: * Permission to use, copy, modify, and distribute this software for any
6: * purpose with or without fee is hereby granted, provided that the above
7: * copyright notice and this permission notice appear in all copies.
8: *
9: * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10: * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11: * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12: * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13: * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14: * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15: * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
16: */
17:
18: /*
19: * A driver for the ThinkPad Active Protection System based on notes from
20: * http://www.almaden.ibm.com/cs/people/marksmith/tpaps.html
21: */
22:
23: #include <sys/param.h>
24: #include <sys/systm.h>
25: #include <sys/device.h>
26: #include <sys/kernel.h>
27: #include <sys/sensors.h>
28: #include <sys/timeout.h>
29: #include <machine/bus.h>
30:
31: #include <dev/isa/isareg.h>
32: #include <dev/isa/isavar.h>
33:
34: #if defined(APSDEBUG)
35: #define DPRINTF(x) do { printf x; } while (0)
36: #else
37: #define DPRINTF(x)
38: #endif
39:
40: #define APS_ACCEL_STATE 0x04
41: #define APS_INIT 0x10
42: #define APS_STATE 0x11
43: #define APS_XACCEL 0x12
44: #define APS_YACCEL 0x14
45: #define APS_TEMP 0x16
46: #define APS_XVAR 0x17
47: #define APS_YVAR 0x19
48: #define APS_TEMP2 0x1b
49: #define APS_UNKNOWN 0x1c
50: #define APS_INPUT 0x1d
51: #define APS_CMD 0x1f
52:
53: #define APS_STATE_NEWDATA 0x50
54:
55: #define APS_CMD_START 0x01
56:
57: #define APS_INPUT_KB (1 << 5)
58: #define APS_INPUT_MS (1 << 6)
59: #define APS_INPUT_LIDOPEN (1 << 7)
60:
61: #define APS_ADDR_SIZE 0x1f
62:
63: struct sensor_rec {
64: u_int8_t state;
65: u_int16_t x_accel;
66: u_int16_t y_accel;
67: u_int8_t temp1;
68: u_int16_t x_var;
69: u_int16_t y_var;
70: u_int8_t temp2;
71: u_int8_t unk;
72: u_int8_t input;
73: };
74:
75: #define APS_NUM_SENSORS 9
76:
77: #define APS_SENSOR_XACCEL 0
78: #define APS_SENSOR_YACCEL 1
79: #define APS_SENSOR_XVAR 2
80: #define APS_SENSOR_YVAR 3
81: #define APS_SENSOR_TEMP1 4
82: #define APS_SENSOR_TEMP2 5
83: #define APS_SENSOR_KBACT 6
84: #define APS_SENSOR_MSACT 7
85: #define APS_SENSOR_LIDOPEN 8
86:
87: struct aps_softc {
88: struct device sc_dev;
89:
90: bus_space_tag_t aps_iot;
91: bus_space_handle_t aps_ioh;
92:
93: struct ksensor sensors[APS_NUM_SENSORS];
94: struct ksensordev sensordev;
95: void (*refresh_sensor_data)(struct aps_softc *);
96:
97: struct sensor_rec aps_data;
98: };
99:
100: int aps_match(struct device *, void *, void *);
101: void aps_attach(struct device *, struct device *, void *);
102:
103: int aps_init(bus_space_tag_t, bus_space_handle_t);
104: u_int8_t aps_mem_read_1(bus_space_tag_t, bus_space_handle_t, int, u_int8_t);
105: int aps_read_data(struct aps_softc *);
106: void aps_refresh_sensor_data(struct aps_softc *sc);
107: void aps_refresh(void *);
108: void aps_power(int, void *);
109:
110: struct cfattach aps_ca = {
111: sizeof(struct aps_softc),
112: aps_match,
113: aps_attach
114: };
115:
116: struct cfdriver aps_cd = {
117: NULL, "aps", DV_DULL
118: };
119:
120: struct timeout aps_timeout;
121:
122: int
123: aps_match(struct device *parent, void *match, void *aux)
124: {
125: bus_space_tag_t iot;
126: bus_space_handle_t ioh;
127: struct isa_attach_args *ia = aux;
128: int iobase, i;
129: u_int8_t cr;
130:
131: iot = ia->ia_iot;
132: iobase = ia->ipa_io[0].base;
133:
134: if (bus_space_map(iot, iobase, APS_ADDR_SIZE, 0, &ioh)) {
135: DPRINTF(("aps: can't map i/o space\n"));
136: return (0);
137: }
138:
139: /* See if this machine has APS */
140: bus_space_write_1(iot, ioh, APS_INIT, 0x13);
141: bus_space_write_1(iot, ioh, APS_CMD, 0x01);
142:
143: /* ask again as the X40 is slightly deaf in one ear */
144: bus_space_read_1(iot, ioh, APS_CMD);
145: bus_space_write_1(iot, ioh, APS_INIT, 0x13);
146: bus_space_write_1(iot, ioh, APS_CMD, 0x01);
147:
148: if (!aps_mem_read_1(iot, ioh, APS_CMD, 0x00)) {
149: bus_space_unmap(iot, ioh, APS_ADDR_SIZE);
150: return (0);
151: }
152:
153: /*
154: * Observed values from Linux driver:
155: * 0x01: T42
156: * 0x02: chip already initialised
157: * 0x03: T41
158: */
159: for (i = 0; i < 10; i++) {
160: cr = bus_space_read_1(iot, ioh, APS_STATE);
161: if (cr > 0 && cr < 6)
162: break;
163: delay(5 * 1000);
164: }
165:
166: bus_space_unmap(iot, ioh, APS_ADDR_SIZE);
167: DPRINTF(("aps: state register 0x%x\n", cr));
168: if (cr < 1 || cr > 5) {
169: DPRINTF(("aps0: unsupported state %d\n", cr));
170: return (0);
171: }
172:
173: ia->ipa_nio = 1;
174: ia->ipa_io[0].length = APS_ADDR_SIZE;
175: ia->ipa_nmem = 0;
176: ia->ipa_nirq = 0;
177: ia->ipa_ndrq = 0;
178:
179: return (1);
180: }
181:
182: void
183: aps_attach(struct device *parent, struct device *self, void *aux)
184: {
185: struct aps_softc *sc = (void *)self;
186: int iobase, i;
187: bus_space_tag_t iot;
188: bus_space_handle_t ioh;
189: struct isa_attach_args *ia = aux;
190:
191: iobase = ia->ipa_io[0].base;
192: iot = sc->aps_iot = ia->ia_iot;
193:
194: if (bus_space_map(iot, iobase, APS_ADDR_SIZE, 0, &sc->aps_ioh)) {
195: printf(": can't map i/o space\n");
196: return;
197: }
198:
199: ioh = sc->aps_ioh;
200:
201: printf("\n");
202:
203: if (!aps_init(iot, ioh))
204: goto out;
205:
206: sc->sensors[APS_SENSOR_XACCEL].type = SENSOR_INTEGER;
207: snprintf(sc->sensors[APS_SENSOR_XACCEL].desc,
208: sizeof(sc->sensors[APS_SENSOR_XACCEL].desc), "X_ACCEL");
209:
210: sc->sensors[APS_SENSOR_YACCEL].type = SENSOR_INTEGER;
211: snprintf(sc->sensors[APS_SENSOR_YACCEL].desc,
212: sizeof(sc->sensors[APS_SENSOR_YACCEL].desc), "Y_ACCEL");
213:
214: sc->sensors[APS_SENSOR_TEMP1].type = SENSOR_TEMP;
215: sc->sensors[APS_SENSOR_TEMP2].type = SENSOR_TEMP;
216:
217: sc->sensors[APS_SENSOR_XVAR].type = SENSOR_INTEGER;
218: snprintf(sc->sensors[APS_SENSOR_XVAR].desc,
219: sizeof(sc->sensors[APS_SENSOR_XVAR].desc), "X_VAR");
220:
221: sc->sensors[APS_SENSOR_YVAR].type = SENSOR_INTEGER;
222: snprintf(sc->sensors[APS_SENSOR_YVAR].desc,
223: sizeof(sc->sensors[APS_SENSOR_YVAR].desc), "Y_VAR");
224:
225: sc->sensors[APS_SENSOR_KBACT].type = SENSOR_INDICATOR;
226: snprintf(sc->sensors[APS_SENSOR_KBACT].desc,
227: sizeof(sc->sensors[APS_SENSOR_KBACT].desc), "Keyboard Active");
228:
229: sc->sensors[APS_SENSOR_MSACT].type = SENSOR_INDICATOR;
230: snprintf(sc->sensors[APS_SENSOR_MSACT].desc,
231: sizeof(sc->sensors[APS_SENSOR_MSACT].desc), "Mouse Active");
232:
233: sc->sensors[APS_SENSOR_LIDOPEN].type = SENSOR_INDICATOR;
234: snprintf(sc->sensors[APS_SENSOR_LIDOPEN].desc,
235: sizeof(sc->sensors[APS_SENSOR_LIDOPEN].desc), "Lid Open");
236:
237: /* stop hiding and report to the authorities */
238: strlcpy(sc->sensordev.xname, sc->sc_dev.dv_xname,
239: sizeof(sc->sensordev.xname));
240: for (i = 0; i < APS_NUM_SENSORS ; i++) {
241: sensor_attach(&sc->sensordev, &sc->sensors[i]);
242: }
243: sensordev_install(&sc->sensordev);
244:
245: powerhook_establish(aps_power, (void *)sc);
246:
247: /* Refresh sensor data every 0.5 seconds */
248: timeout_set(&aps_timeout, aps_refresh, sc);
249: timeout_add(&aps_timeout, (5 * hz) / 10);
250: return;
251: out:
252: printf("%s: failed to initialise\n", sc->sc_dev.dv_xname);
253: return;
254: }
255:
256: int
257: aps_init(bus_space_tag_t iot, bus_space_handle_t ioh)
258: {
259: bus_space_write_1(iot, ioh, APS_INIT, 0x17);
260: bus_space_write_1(iot, ioh, APS_STATE, 0x81);
261: bus_space_write_1(iot, ioh, APS_CMD, 0x01);
262: if (!aps_mem_read_1(iot, ioh, APS_CMD, 0x00))
263: return (0);
264: if (!aps_mem_read_1(iot, ioh, APS_STATE, 0x00))
265: return (0);
266: if (!aps_mem_read_1(iot, ioh, APS_XACCEL, 0x60))
267: return (0);
268: if (!aps_mem_read_1(iot, ioh, APS_XACCEL + 1, 0x00))
269: return (0);
270: bus_space_write_1(iot, ioh, APS_INIT, 0x14);
271: bus_space_write_1(iot, ioh, APS_STATE, 0x01);
272: bus_space_write_1(iot, ioh, APS_CMD, 0x01);
273: if (!aps_mem_read_1(iot, ioh, APS_CMD, 0x00))
274: return (0);
275: bus_space_write_1(iot, ioh, APS_INIT, 0x10);
276: bus_space_write_1(iot, ioh, APS_STATE, 0xc8);
277: bus_space_write_1(iot, ioh, APS_XACCEL, 0x00);
278: bus_space_write_1(iot, ioh, APS_XACCEL + 1, 0x02);
279: bus_space_write_1(iot, ioh, APS_CMD, 0x01);
280: if (!aps_mem_read_1(iot, ioh, APS_CMD, 0x00))
281: return (0);
282: /* refresh data */
283: bus_space_write_1(iot, ioh, APS_INIT, 0x11);
284: bus_space_write_1(iot, ioh, APS_CMD, 0x01);
285: if (!aps_mem_read_1(iot, ioh, APS_ACCEL_STATE, 0x50))
286: return (0);
287: if (!aps_mem_read_1(iot, ioh, APS_STATE, 0x00))
288: return (0);
289:
290: return (1);
291: }
292:
293: u_int8_t
294: aps_mem_read_1(bus_space_tag_t iot, bus_space_handle_t ioh, int reg,
295: u_int8_t val)
296: {
297: int i;
298: u_int8_t cr;
299: /* should take no longer than 50 microseconds */
300: for (i = 0; i < 10; i++) {
301: cr = bus_space_read_1(iot, ioh, reg);
302: if (cr == val)
303: return (1);
304: delay(5 * 1000);
305: }
306: DPRINTF(("aps: reg 0x%x not val 0x%x!\n", reg, val));
307: return (0);
308: }
309:
310: int
311: aps_read_data(struct aps_softc *sc)
312: {
313: bus_space_tag_t iot = sc->aps_iot;
314: bus_space_handle_t ioh = sc->aps_ioh;
315:
316: sc->aps_data.state = bus_space_read_1(iot, ioh, APS_STATE);
317: sc->aps_data.x_accel = bus_space_read_2(iot, ioh, APS_XACCEL);
318: sc->aps_data.y_accel = bus_space_read_2(iot, ioh, APS_YACCEL);
319: sc->aps_data.temp1 = bus_space_read_1(iot, ioh, APS_TEMP);
320: sc->aps_data.x_var = bus_space_read_2(iot, ioh, APS_XVAR);
321: sc->aps_data.y_var = bus_space_read_2(iot, ioh, APS_YVAR);
322: sc->aps_data.temp2 = bus_space_read_1(iot, ioh, APS_TEMP2);
323: sc->aps_data.input = bus_space_read_1(iot, ioh, APS_INPUT);
324:
325: return (1);
326: }
327:
328: void
329: aps_refresh_sensor_data(struct aps_softc *sc)
330: {
331: bus_space_tag_t iot = sc->aps_iot;
332: bus_space_handle_t ioh = sc->aps_ioh;
333: int64_t temp;
334: int i;
335:
336: /* ask for new data */
337: bus_space_write_1(iot, ioh, APS_INIT, 0x11);
338: bus_space_write_1(iot, ioh, APS_CMD, 0x01);
339: if (!aps_mem_read_1(iot, ioh, APS_ACCEL_STATE, 0x50))
340: return;
341: aps_read_data(sc);
342: bus_space_write_1(iot, ioh, APS_INIT, 0x11);
343: bus_space_write_1(iot, ioh, APS_CMD, 0x01);
344:
345: /* tell accelerometer we're done reading from it */
346: bus_space_read_1(iot, ioh, APS_CMD);
347: bus_space_read_1(iot, ioh, APS_ACCEL_STATE);
348:
349: for (i = 0; i < APS_NUM_SENSORS; i++) {
350: sc->sensors[i].flags &= ~SENSOR_FINVALID;
351: }
352:
353: sc->sensors[APS_SENSOR_XACCEL].value = sc->aps_data.x_accel;
354: sc->sensors[APS_SENSOR_YACCEL].value = sc->aps_data.y_accel;
355:
356: /* convert to micro (mu) degrees */
357: temp = sc->aps_data.temp1 * 1000000;
358: /* convert to kelvin */
359: temp += 273150000;
360: sc->sensors[APS_SENSOR_TEMP1].value = temp;
361:
362: /* convert to micro (mu) degrees */
363: temp = sc->aps_data.temp2 * 1000000;
364: /* convert to kelvin */
365: temp += 273150000;
366: sc->sensors[APS_SENSOR_TEMP2].value = temp;
367:
368: sc->sensors[APS_SENSOR_XVAR].value = sc->aps_data.x_var;
369: sc->sensors[APS_SENSOR_YVAR].value = sc->aps_data.y_var;
370: sc->sensors[APS_SENSOR_KBACT].value =
371: (sc->aps_data.input & APS_INPUT_KB) ? 1 : 0;
372: sc->sensors[APS_SENSOR_MSACT].value =
373: (sc->aps_data.input & APS_INPUT_MS) ? 1 : 0;
374: sc->sensors[APS_SENSOR_LIDOPEN].value =
375: (sc->aps_data.input & APS_INPUT_LIDOPEN) ? 1 : 0;
376: }
377:
378: void
379: aps_refresh(void *arg)
380: {
381: struct aps_softc *sc = (struct aps_softc *)arg;
382:
383: aps_refresh_sensor_data(sc);
384: timeout_add(&aps_timeout, (5 * hz) / 10);
385: }
386:
387: void
388: aps_power(int why, void *arg)
389: {
390: struct aps_softc *sc = (struct aps_softc *)arg;
391: bus_space_tag_t iot = sc->aps_iot;
392: bus_space_handle_t ioh = sc->aps_ioh;
393:
394: if (why != PWR_RESUME) {
395: if (timeout_pending(&aps_timeout))
396: timeout_del(&aps_timeout);
397: } else {
398: /*
399: * Redo the init sequence on resume, because APS is
400: * as forgetful as it is deaf.
401: */
402: bus_space_write_1(iot, ioh, APS_INIT, 0x13);
403: bus_space_write_1(iot, ioh, APS_CMD, 0x01);
404: bus_space_read_1(iot, ioh, APS_CMD);
405: bus_space_write_1(iot, ioh, APS_INIT, 0x13);
406: bus_space_write_1(iot, ioh, APS_CMD, 0x01);
407:
408: if (aps_mem_read_1(iot, ioh, APS_CMD, 0x00) &&
409: aps_init(iot, ioh))
410: timeout_add(&aps_timeout, (5 * hz) / 10);
411: else
412: printf("aps: failed to wake up\n");
413: }
414: }
415:
CVSweb