xref: /netbsd-src/sys/dev/isa/aps.c (revision b1c86f5f087524e68db12794ee9c3e3da1ab17a0)
1 /*	$NetBSD: aps.c,v 1.10 2010/02/24 22:37:58 dyoung 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@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.10 2010/02/24 22:37:58 dyoung 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 	bus_space_tag_t sc_iot;
97 	bus_space_handle_t sc_ioh;
98 
99 	struct sysmon_envsys *sc_sme;
100 	envsys_data_t sc_sensor[APS_NUM_SENSORS];
101 	struct callout sc_callout;
102 
103 	struct sensor_rec aps_data;
104 };
105 
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);
109 
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, const pmf_qual_t *);
116 static bool 	aps_resume(device_t, const pmf_qual_t *);
117 
118 CFATTACH_DECL_NEW(aps, sizeof(struct aps_softc),
119 	      aps_match, aps_attach, aps_detach, NULL);
120 
121 static int
122 aps_match(device_t parent, cfdata_t match, void *aux)
123 {
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;
129 
130 	/* Must supply an address */
131 	if (ia->ia_nio < 1)
132 		return 0;
133 
134 	if (ISA_DIRECT_CONFIG(ia))
135 		return 0;
136 
137 	if (ia->ia_io[0].ir_addr == ISA_UNKNOWN_PORT)
138 		return 0;
139 
140 	iobase = ia->ia_io[0].ir_addr;
141 
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;
145 	}
146 
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);
150 
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);
155 
156 	if (!aps_mem_read_1(iot, ioh, APS_CMD, 0x00)) {
157 		bus_space_unmap(iot, ioh, APS_ADDR_SIZE);
158 		return 0;
159 	}
160 
161 	/*
162 	 * Observed values from Linux driver:
163 	 * 0x01: T42
164 	 * 0x02: chip already initialised
165 	 * 0x03: T41
166 	 */
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);
172 	}
173 
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;
179 	}
180 
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;
186 
187 	return 1;
188 }
189 
190 static void
191 aps_attach(device_t parent, device_t self, void *aux)
192 {
193 	struct aps_softc *sc = device_private(self);
194 	struct isa_attach_args *ia = aux;
195 	int iobase, i;
196 
197 	sc->sc_iot = ia->ia_iot;
198 	iobase = ia->ia_io[0].ir_addr;
199 
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;
203 	}
204 
205 	aprint_naive("\n");
206 	aprint_normal("\n");
207 
208 	if (!aps_init(sc)) {
209 		aprint_error_dev(self, "failed to initialise\n");
210 		goto out;
211 	}
212 
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));
218 
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");
228 
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;
236 		}
237 	}
238         /*
239          * Register with the sysmon_envsys(9) framework.
240          */
241 	sc->sc_sme->sme_name = device_xname(self);
242 	sc->sc_sme->sme_flags = SME_DISABLE_REFRESH;
243 
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;
249 	}
250 
251 	if (!pmf_device_register(self, aps_suspend, aps_resume))
252 		aprint_error_dev(self, "couldn't establish power handler\n");
253 
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);
258 
259         aprint_normal_dev(self, "Thinkpad Active Protection System\n");
260 	return;
261 
262 out:
263 	bus_space_unmap(sc->sc_iot, sc->sc_ioh, APS_ADDR_SIZE);
264 }
265 
266 static int
267 aps_init(struct aps_softc *sc)
268 {
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;
299 
300 	return 1;
301 }
302 
303 static int
304 aps_detach(device_t self, int flags)
305 {
306 	struct aps_softc *sc = device_private(self);
307 
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);
312 
313 	return 0;
314 }
315 
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)
319 {
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);
328 	}
329 
330 	DPRINTF(("aps: reg 0x%x not val 0x%x!\n", reg, val));
331 	return 0;
332 }
333 
334 static void
335 aps_refresh_sensor_data(struct aps_softc *sc)
336 {
337 	int64_t temp;
338 
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;
344 
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);
361 
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);
364 
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);
368 
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;
371 
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;
377 
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;
383 
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;
392 }
393 
394 static void
395 aps_refresh(void *arg)
396 {
397 	struct aps_softc *sc = arg;
398 
399 	aps_refresh_sensor_data(sc);
400 	callout_schedule(&sc->sc_callout, (hz) / 2);
401 }
402 
403 static bool
404 aps_suspend(device_t dv, const pmf_qual_t *qual)
405 {
406 	struct aps_softc *sc = device_private(dv);
407 
408 	callout_stop(&sc->sc_callout);
409 
410 	return true;
411 }
412 
413 static bool
414 aps_resume(device_t dv, const pmf_qual_t *qual)
415 {
416 	struct aps_softc *sc = device_private(dv);
417 
418 	/*
419 	 * Redo the init sequence on resume, because APS is
420 	 * as forgetful as it is deaf.
421 	 */
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);
427 
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");
433 
434 	return true;
435 }
436