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