No empty .Rs/.Re
[netbsd-mini2440.git] / sys / dev / isa / aps.c
blob74aeaad02d3bd93794597ad18c27dff79b834a30
1 /* $NetBSD: aps.c,v 1.8 2008/04/04 09:41:40 xtraeme Exp $ */
2 /* $OpenBSD: aps.c,v 1.15 2007/05/19 19:14:11 tedu Exp $ */
4 /*
5 * Copyright (c) 2005 Jonathan Gray <jsg@openbsd.org>
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.
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.
21 * A driver for the ThinkPad Active Protection System based on notes from
22 * http://www.almaden.ibm.com/cs/people/marksmith/tpaps.html
25 #include <sys/cdefs.h>
26 __KERNEL_RCSID(0, "$NetBSD: aps.c,v 1.8 2008/04/04 09:41:40 xtraeme Exp $");
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>
34 #include <sys/bus.h>
36 #include <dev/sysmon/sysmonvar.h>
38 #include <dev/isa/isareg.h>
39 #include <dev/isa/isavar.h>
41 #if defined(APSDEBUG)
42 #define DPRINTF(x) do { printf x; } while (0)
43 #else
44 #define DPRINTF(x)
45 #endif
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
60 #define APS_STATE_NEWDATA 0x50
62 #define APS_CMD_START 0x01
64 #define APS_INPUT_KB (1 << 5)
65 #define APS_INPUT_MS (1 << 6)
66 #define APS_INPUT_LIDOPEN (1 << 7)
68 #define APS_ADDR_SIZE 0x1f
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;
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
95 struct aps_softc {
96 bus_space_tag_t sc_iot;
97 bus_space_handle_t sc_ioh;
99 struct sysmon_envsys *sc_sme;
100 envsys_data_t sc_sensor[APS_NUM_SENSORS];
101 struct callout sc_callout;
103 struct sensor_rec aps_data;
106 static int aps_match(device_t, cfdata_t, void *);
107 static void aps_attach(device_t, device_t, void *);
108 static int aps_detach(device_t, int);
110 static int aps_init(struct aps_softc *);
111 static uint8_t aps_mem_read_1(bus_space_tag_t, bus_space_handle_t,
112 int, uint8_t);
113 static void aps_refresh_sensor_data(struct aps_softc *sc);
114 static void aps_refresh(void *);
115 static bool aps_suspend(device_t, pmf_qual_t);
116 static bool aps_resume(device_t, pmf_qual_t);
118 CFATTACH_DECL_NEW(aps, sizeof(struct aps_softc),
119 aps_match, aps_attach, aps_detach, NULL);
121 static int
122 aps_match(device_t parent, cfdata_t match, void *aux)
124 struct isa_attach_args *ia = aux;
125 bus_space_tag_t iot = ia->ia_iot;
126 bus_space_handle_t ioh;
127 int iobase, i;
128 uint8_t cr;
130 /* Must supply an address */
131 if (ia->ia_nio < 1)
132 return 0;
134 if (ISA_DIRECT_CONFIG(ia))
135 return 0;
137 if (ia->ia_io[0].ir_addr == ISA_UNKNOWN_PORT)
138 return 0;
140 iobase = ia->ia_io[0].ir_addr;
142 if (bus_space_map(iot, iobase, APS_ADDR_SIZE, 0, &ioh)) {
143 aprint_error("aps: can't map i/o space\n");
144 return 0;
147 /* See if this machine has APS */
148 bus_space_write_1(iot, ioh, APS_INIT, 0x13);
149 bus_space_write_1(iot, ioh, APS_CMD, 0x01);
151 /* ask again as the X40 is slightly deaf in one ear */
152 bus_space_read_1(iot, ioh, APS_CMD);
153 bus_space_write_1(iot, ioh, APS_INIT, 0x13);
154 bus_space_write_1(iot, ioh, APS_CMD, 0x01);
156 if (!aps_mem_read_1(iot, ioh, APS_CMD, 0x00)) {
157 bus_space_unmap(iot, ioh, APS_ADDR_SIZE);
158 return 0;
162 * Observed values from Linux driver:
163 * 0x01: T42
164 * 0x02: chip already initialised
165 * 0x03: T41
167 for (i = 0; i < 10; i++) {
168 cr = bus_space_read_1(iot, ioh, APS_STATE);
169 if (cr > 0 && cr < 6)
170 break;
171 delay(5 * 1000);
174 bus_space_unmap(iot, ioh, APS_ADDR_SIZE);
175 DPRINTF(("aps: state register 0x%x\n", cr));
176 if (cr < 1 || cr > 5) {
177 DPRINTF(("aps0: unsupported state %d\n", cr));
178 return 0;
181 ia->ia_nio = 1;
182 ia->ia_io[0].ir_size = APS_ADDR_SIZE;
183 ia->ia_niomem = 0;
184 ia->ia_nirq = 0;
185 ia->ia_ndrq = 0;
187 return 1;
190 static void
191 aps_attach(device_t parent, device_t self, void *aux)
193 struct aps_softc *sc = device_private(self);
194 struct isa_attach_args *ia = aux;
195 int iobase, i;
197 sc->sc_iot = ia->ia_iot;
198 iobase = ia->ia_io[0].ir_addr;
200 if (bus_space_map(sc->sc_iot, iobase, APS_ADDR_SIZE, 0, &sc->sc_ioh)) {
201 aprint_error(": can't map i/o space\n");
202 return;
205 aprint_naive("\n");
206 aprint_normal("\n");
208 if (!aps_init(sc)) {
209 aprint_error_dev(self, "failed to initialise\n");
210 goto out;
213 /* Initialize sensors */
214 #define INITDATA(idx, unit, string) \
215 sc->sc_sensor[idx].units = unit; \
216 strlcpy(sc->sc_sensor[idx].desc, string, \
217 sizeof(sc->sc_sensor[idx].desc));
219 INITDATA(APS_SENSOR_XACCEL, ENVSYS_INTEGER, "X_ACCEL");
220 INITDATA(APS_SENSOR_YACCEL, ENVSYS_INTEGER, "Y_ACCEL");
221 INITDATA(APS_SENSOR_TEMP1, ENVSYS_STEMP, "TEMP_1");
222 INITDATA(APS_SENSOR_TEMP2, ENVSYS_STEMP, "TEMP_2");
223 INITDATA(APS_SENSOR_XVAR, ENVSYS_INTEGER, "X_VAR");
224 INITDATA(APS_SENSOR_YVAR, ENVSYS_INTEGER, "Y_VAR");
225 INITDATA(APS_SENSOR_KBACT, ENVSYS_INDICATOR, "Keyboard Active");
226 INITDATA(APS_SENSOR_MSACT, ENVSYS_INDICATOR, "Mouse Active");
227 INITDATA(APS_SENSOR_LIDOPEN, ENVSYS_INDICATOR, "Lid Open");
229 sc->sc_sme = sysmon_envsys_create();
230 for (i = 0; i < APS_NUM_SENSORS; i++) {
231 sc->sc_sensor[i].state = ENVSYS_SVALID;
232 if (sysmon_envsys_sensor_attach(sc->sc_sme,
233 &sc->sc_sensor[i])) {
234 sysmon_envsys_destroy(sc->sc_sme);
235 goto out;
239 * Register with the sysmon_envsys(9) framework.
241 sc->sc_sme->sme_name = device_xname(self);
242 sc->sc_sme->sme_flags = SME_DISABLE_REFRESH;
244 if ((i = sysmon_envsys_register(sc->sc_sme))) {
245 aprint_error_dev(self,
246 "unable to register with sysmon (%d)\n", i);
247 sysmon_envsys_destroy(sc->sc_sme);
248 goto out;
251 if (!pmf_device_register(self, aps_suspend, aps_resume))
252 aprint_error_dev(self, "couldn't establish power handler\n");
254 /* Refresh sensor data every 0.5 seconds */
255 callout_init(&sc->sc_callout, 0);
256 callout_setfunc(&sc->sc_callout, aps_refresh, sc);
257 callout_schedule(&sc->sc_callout, (hz) / 2);
259 aprint_normal_dev(self, "Thinkpad Active Protection System\n");
260 return;
262 out:
263 bus_space_unmap(sc->sc_iot, sc->sc_ioh, APS_ADDR_SIZE);
266 static int
267 aps_init(struct aps_softc *sc)
269 bus_space_write_1(sc->sc_iot, sc->sc_ioh, APS_INIT, 0x17);
270 bus_space_write_1(sc->sc_iot, sc->sc_ioh, APS_STATE, 0x81);
271 bus_space_write_1(sc->sc_iot, sc->sc_ioh, APS_CMD, 0x01);
272 if (!aps_mem_read_1(sc->sc_iot, sc->sc_ioh, APS_CMD, 0x00))
273 return 0;
274 if (!aps_mem_read_1(sc->sc_iot, sc->sc_ioh, APS_STATE, 0x00))
275 return 0;
276 if (!aps_mem_read_1(sc->sc_iot, sc->sc_ioh, APS_XACCEL, 0x60))
277 return 0;
278 if (!aps_mem_read_1(sc->sc_iot, sc->sc_ioh, APS_XACCEL + 1, 0x00))
279 return 0;
280 bus_space_write_1(sc->sc_iot, sc->sc_ioh, APS_INIT, 0x14);
281 bus_space_write_1(sc->sc_iot, sc->sc_ioh, APS_STATE, 0x01);
282 bus_space_write_1(sc->sc_iot, sc->sc_ioh, APS_CMD, 0x01);
283 if (!aps_mem_read_1(sc->sc_iot, sc->sc_ioh, APS_CMD, 0x00))
284 return 0;
285 bus_space_write_1(sc->sc_iot, sc->sc_ioh, APS_INIT, 0x10);
286 bus_space_write_1(sc->sc_iot, sc->sc_ioh, APS_STATE, 0xc8);
287 bus_space_write_1(sc->sc_iot, sc->sc_ioh, APS_XACCEL, 0x00);
288 bus_space_write_1(sc->sc_iot, sc->sc_ioh, APS_XACCEL + 1, 0x02);
289 bus_space_write_1(sc->sc_iot, sc->sc_ioh, APS_CMD, 0x01);
290 if (!aps_mem_read_1(sc->sc_iot, sc->sc_ioh, APS_CMD, 0x00))
291 return 0;
292 /* refresh data */
293 bus_space_write_1(sc->sc_iot, sc->sc_ioh, APS_INIT, 0x11);
294 bus_space_write_1(sc->sc_iot, sc->sc_ioh, APS_CMD, 0x01);
295 if (!aps_mem_read_1(sc->sc_iot, sc->sc_ioh, APS_ACCEL_STATE, 0x50))
296 return 0;
297 if (!aps_mem_read_1(sc->sc_iot, sc->sc_ioh, APS_STATE, 0x00))
298 return 0;
300 return 1;
303 static int
304 aps_detach(device_t self, int flags)
306 struct aps_softc *sc = device_private(self);
308 callout_stop(&sc->sc_callout);
309 callout_destroy(&sc->sc_callout);
310 sysmon_envsys_unregister(sc->sc_sme);
311 bus_space_unmap(sc->sc_iot, sc->sc_ioh, APS_ADDR_SIZE);
313 return 0;
316 static uint8_t
317 aps_mem_read_1(bus_space_tag_t iot, bus_space_handle_t ioh, int reg,
318 uint8_t val)
320 int i;
321 uint8_t cr;
322 /* should take no longer than 50 microseconds */
323 for (i = 0; i < 10; i++) {
324 cr = bus_space_read_1(iot, ioh, reg);
325 if (cr == val)
326 return 1;
327 delay(5 * 1000);
330 DPRINTF(("aps: reg 0x%x not val 0x%x!\n", reg, val));
331 return 0;
334 static void
335 aps_refresh_sensor_data(struct aps_softc *sc)
337 int64_t temp;
339 /* ask for new data */
340 bus_space_write_1(sc->sc_iot, sc->sc_ioh, APS_INIT, 0x11);
341 bus_space_write_1(sc->sc_iot, sc->sc_ioh, APS_CMD, 0x01);
342 if (!aps_mem_read_1(sc->sc_iot, sc->sc_ioh, APS_ACCEL_STATE, 0x50))
343 return;
345 sc->aps_data.state =
346 bus_space_read_1(sc->sc_iot, sc->sc_ioh, APS_STATE);
347 sc->aps_data.x_accel =
348 bus_space_read_2(sc->sc_iot, sc->sc_ioh, APS_XACCEL);
349 sc->aps_data.y_accel =
350 bus_space_read_2(sc->sc_iot, sc->sc_ioh, APS_YACCEL);
351 sc->aps_data.temp1 =
352 bus_space_read_1(sc->sc_iot, sc->sc_ioh, APS_TEMP);
353 sc->aps_data.x_var =
354 bus_space_read_2(sc->sc_iot, sc->sc_ioh, APS_XVAR);
355 sc->aps_data.y_var =
356 bus_space_read_2(sc->sc_iot, sc->sc_ioh, APS_YVAR);
357 sc->aps_data.temp2 =
358 bus_space_read_1(sc->sc_iot, sc->sc_ioh, APS_TEMP2);
359 sc->aps_data.input =
360 bus_space_read_1(sc->sc_iot, sc->sc_ioh, APS_INPUT);
362 bus_space_write_1(sc->sc_iot, sc->sc_ioh, APS_INIT, 0x11);
363 bus_space_write_1(sc->sc_iot, sc->sc_ioh, APS_CMD, 0x01);
365 /* tell accelerometer we're done reading from it */
366 bus_space_read_1(sc->sc_iot, sc->sc_ioh, APS_CMD);
367 bus_space_read_1(sc->sc_iot, sc->sc_ioh, APS_ACCEL_STATE);
369 sc->sc_sensor[APS_SENSOR_XACCEL].value_cur = sc->aps_data.x_accel;
370 sc->sc_sensor[APS_SENSOR_YACCEL].value_cur = sc->aps_data.y_accel;
372 /* convert to micro (mu) degrees */
373 temp = sc->aps_data.temp1 * 1000000;
374 /* convert to kelvin */
375 temp += 273150000;
376 sc->sc_sensor[APS_SENSOR_TEMP1].value_cur = temp;
378 /* convert to micro (mu) degrees */
379 temp = sc->aps_data.temp2 * 1000000;
380 /* convert to kelvin */
381 temp += 273150000;
382 sc->sc_sensor[APS_SENSOR_TEMP2].value_cur = temp;
384 sc->sc_sensor[APS_SENSOR_XVAR].value_cur = sc->aps_data.x_var;
385 sc->sc_sensor[APS_SENSOR_YVAR].value_cur = sc->aps_data.y_var;
386 sc->sc_sensor[APS_SENSOR_KBACT].value_cur =
387 (sc->aps_data.input & APS_INPUT_KB) ? 1 : 0;
388 sc->sc_sensor[APS_SENSOR_MSACT].value_cur =
389 (sc->aps_data.input & APS_INPUT_MS) ? 1 : 0;
390 sc->sc_sensor[APS_SENSOR_LIDOPEN].value_cur =
391 (sc->aps_data.input & APS_INPUT_LIDOPEN) ? 1 : 0;
394 static void
395 aps_refresh(void *arg)
397 struct aps_softc *sc = arg;
399 aps_refresh_sensor_data(sc);
400 callout_schedule(&sc->sc_callout, (hz) / 2);
403 static bool
404 aps_suspend(device_t dv, pmf_qual_t qual)
406 struct aps_softc *sc = device_private(dv);
408 callout_stop(&sc->sc_callout);
410 return true;
413 static bool
414 aps_resume(device_t dv, pmf_qual_t qual)
416 struct aps_softc *sc = device_private(dv);
419 * Redo the init sequence on resume, because APS is
420 * as forgetful as it is deaf.
422 bus_space_write_1(sc->sc_iot, sc->sc_ioh, APS_INIT, 0x13);
423 bus_space_write_1(sc->sc_iot, sc->sc_ioh, APS_CMD, 0x01);
424 bus_space_read_1(sc->sc_iot, sc->sc_ioh, APS_CMD);
425 bus_space_write_1(sc->sc_iot, sc->sc_ioh, APS_INIT, 0x13);
426 bus_space_write_1(sc->sc_iot, sc->sc_ioh, APS_CMD, 0x01);
428 if (aps_mem_read_1(sc->sc_iot, sc->sc_ioh, APS_CMD, 0x00) &&
429 aps_init(sc))
430 callout_schedule(&sc->sc_callout, (hz) / 2);
431 else
432 aprint_error_dev(dv, "failed to wake up\n");
434 return true;