xref: /openbsd/sys/dev/isa/aps.c (revision 33972537)
1*33972537Sjsg /*	$OpenBSD: aps.c,v 1.2 2005/08/05 07:29:02 jsg Exp $	*/
24569cca6Sjsg /*
34569cca6Sjsg  * Copyright (c) 2005 Jonathan Gray <jsg@openbsd.org>
44569cca6Sjsg  *
54569cca6Sjsg  * Permission to use, copy, modify, and distribute this software for any
64569cca6Sjsg  * purpose with or without fee is hereby granted, provided that the above
74569cca6Sjsg  * copyright notice and this permission notice appear in all copies.
84569cca6Sjsg  *
94569cca6Sjsg  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
104569cca6Sjsg  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
114569cca6Sjsg  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
124569cca6Sjsg  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
134569cca6Sjsg  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
144569cca6Sjsg  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
154569cca6Sjsg  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
164569cca6Sjsg  */
174569cca6Sjsg 
184569cca6Sjsg /*
194569cca6Sjsg  * A driver for the ThinkPad Active Protection System based on notes from
204569cca6Sjsg  * http://www.almaden.ibm.com/cs/people/marksmith/aps.html
214569cca6Sjsg  */
224569cca6Sjsg 
234569cca6Sjsg #include <sys/param.h>
244569cca6Sjsg #include <sys/systm.h>
254569cca6Sjsg #include <sys/device.h>
264569cca6Sjsg #include <sys/kernel.h>
274569cca6Sjsg #include <sys/sensors.h>
284569cca6Sjsg #include <sys/timeout.h>
294569cca6Sjsg #include <machine/bus.h>
304569cca6Sjsg 
314569cca6Sjsg #include <dev/isa/isareg.h>
324569cca6Sjsg #include <dev/isa/isavar.h>
334569cca6Sjsg 
344569cca6Sjsg #include <dev/isa/apsreg.h>
354569cca6Sjsg #include <dev/isa/apsvar.h>
364569cca6Sjsg 
374569cca6Sjsg #if defined(APSDEBUG)
384569cca6Sjsg #define DPRINTF(x)		do { printf x; } while (0)
394569cca6Sjsg #else
404569cca6Sjsg #define DPRINTF(x)
414569cca6Sjsg #endif
424569cca6Sjsg 
434569cca6Sjsg int aps_match(struct device *, void *, void *);
444569cca6Sjsg void aps_attach(struct device *, struct device *, void *);
454569cca6Sjsg 
464569cca6Sjsg u_int8_t aps_mem_read_1(bus_space_tag_t, bus_space_handle_t, int, u_int8_t);
474569cca6Sjsg int aps_read_data(struct aps_softc *);
484569cca6Sjsg void aps_refresh_sensor_data(struct aps_softc *sc);
494569cca6Sjsg void aps_refresh(void *);
504569cca6Sjsg 
514569cca6Sjsg struct cfattach aps_ca = {
524569cca6Sjsg 	sizeof(struct aps_softc),
534569cca6Sjsg 	aps_match,
544569cca6Sjsg 	aps_attach
554569cca6Sjsg };
564569cca6Sjsg 
574569cca6Sjsg struct cfdriver aps_cd = {
584569cca6Sjsg 	NULL, "aps", DV_DULL
594569cca6Sjsg };
604569cca6Sjsg 
614569cca6Sjsg struct timeout aps_timeout;
624569cca6Sjsg 
634569cca6Sjsg int
644569cca6Sjsg aps_match(struct device *parent, void *match, void *aux)
654569cca6Sjsg {
664569cca6Sjsg 	bus_space_tag_t iot;
674569cca6Sjsg 	bus_space_handle_t ioh;
684569cca6Sjsg 	struct isa_attach_args *ia = aux;
694569cca6Sjsg 	int iobase, i;
704569cca6Sjsg 	u_int8_t cr;
714569cca6Sjsg 
724569cca6Sjsg 	iot = ia->ia_iot;
734569cca6Sjsg 	iobase = ia->ipa_io[0].base;
744569cca6Sjsg 
754569cca6Sjsg 	if (bus_space_map(iot, iobase, APS_ADDR_SIZE, 0, &ioh)) {
764569cca6Sjsg 		DPRINTF(("aps: can't map i/o space\n"));
774569cca6Sjsg 		return (0);
784569cca6Sjsg 	}
794569cca6Sjsg 
804569cca6Sjsg 	/* See if this machine has APS */
814569cca6Sjsg 	bus_space_write_1(iot, ioh, APS_INIT, 0x13);
824569cca6Sjsg 	bus_space_write_1(iot, ioh, APS_CMD, 0x01);
83*33972537Sjsg 
84*33972537Sjsg 	/* ask again as the X40 is slightly deaf in one ear */
85*33972537Sjsg 	bus_space_write_1(iot, ioh, APS_INIT, 0x13);
86*33972537Sjsg 	bus_space_write_1(iot, ioh, APS_CMD, 0x01);
87*33972537Sjsg 
884569cca6Sjsg 	if (!aps_mem_read_1(iot, ioh, APS_CMD, 0x00)) {
894569cca6Sjsg 		bus_space_unmap(iot, ioh, APS_ADDR_SIZE);
904569cca6Sjsg 		return (0);
914569cca6Sjsg 	}
924569cca6Sjsg 
934569cca6Sjsg 	/*
944569cca6Sjsg 	 * Observed values from Linux driver:
954569cca6Sjsg 	 * 0x01: T42
964569cca6Sjsg 	 * 0x02: chip already initialised
974569cca6Sjsg 	 * 0x03: T41
984569cca6Sjsg 	 */
994569cca6Sjsg 	for (i = 0; i < 10; i++) {
1004569cca6Sjsg 		cr = bus_space_read_1(iot, ioh, APS_STATE);
1014569cca6Sjsg 		if (cr > 0 && cr < 4)
1024569cca6Sjsg 			break;
1034569cca6Sjsg 		delay(5 * 1000);
1044569cca6Sjsg 	}
1054569cca6Sjsg 
1064569cca6Sjsg 	bus_space_unmap(iot, ioh, APS_ADDR_SIZE);
1074569cca6Sjsg 	DPRINTF(("aps: state register 0x%x\n", cr));
1084569cca6Sjsg 	if (cr < 1 || cr > 3) {
1094569cca6Sjsg 		DPRINTF(("aps0: unsupported state %d\n", cr));
1104569cca6Sjsg 		return (0);
1114569cca6Sjsg 	}
1124569cca6Sjsg 
1134569cca6Sjsg 	ia->ipa_nio = 1;
1144569cca6Sjsg 	ia->ipa_io[0].length = APS_ADDR_SIZE;
1154569cca6Sjsg 	ia->ipa_nmem = 0;
1164569cca6Sjsg 	ia->ipa_nirq = 0;
1174569cca6Sjsg 	ia->ipa_ndrq = 0;
1184569cca6Sjsg 
1194569cca6Sjsg 	return (1);
1204569cca6Sjsg }
1214569cca6Sjsg 
1224569cca6Sjsg void
1234569cca6Sjsg aps_attach(struct device *parent, struct device *self, void *aux)
1244569cca6Sjsg {
1254569cca6Sjsg 	struct aps_softc *sc = (void *)self;
1264569cca6Sjsg 	int iobase, i;
1274569cca6Sjsg 	bus_space_tag_t iot;
1284569cca6Sjsg 	bus_space_handle_t ioh;
1294569cca6Sjsg 	struct isa_attach_args *ia = aux;
1304569cca6Sjsg 
1314569cca6Sjsg 	iobase = ia->ipa_io[0].base;
1324569cca6Sjsg 	iot = sc->aps_iot = ia->ia_iot;
1334569cca6Sjsg 
1344569cca6Sjsg 	if (bus_space_map(iot, iobase, APS_ADDR_SIZE, 0, &sc->aps_ioh)) {
1354569cca6Sjsg 		printf(": can't map i/o space\n");
1364569cca6Sjsg 		return;
1374569cca6Sjsg 	}
1384569cca6Sjsg 
1394569cca6Sjsg 	ioh = sc->aps_ioh;
1404569cca6Sjsg 
1414569cca6Sjsg 	printf("\n");
1424569cca6Sjsg 
1434569cca6Sjsg 	bus_space_write_1(iot, ioh, APS_INIT, 0x17);
1444569cca6Sjsg 	bus_space_write_1(iot, ioh, APS_STATE, 0x81);
1454569cca6Sjsg 	bus_space_write_1(iot, ioh, APS_CMD, 0x01);
1464569cca6Sjsg 	if (!aps_mem_read_1(iot, ioh, APS_CMD, 0x00))
1474569cca6Sjsg 		goto out;
1484569cca6Sjsg 	if (!aps_mem_read_1(iot, ioh, APS_STATE, 0x00))
1494569cca6Sjsg 		goto out;
1504569cca6Sjsg 	if (!aps_mem_read_1(iot, ioh, APS_XACCEL, 0x60))
1514569cca6Sjsg 		goto out;
1524569cca6Sjsg 	if (!aps_mem_read_1(iot, ioh, APS_XACCEL + 1, 0x00))
1534569cca6Sjsg 		goto out;
1544569cca6Sjsg 	bus_space_write_1(iot, ioh, APS_INIT, 0x14);
1554569cca6Sjsg 	bus_space_write_1(iot, ioh, APS_STATE, 0x01);
1564569cca6Sjsg 	bus_space_write_1(iot, ioh, APS_CMD, 0x01);
1574569cca6Sjsg 	if (!aps_mem_read_1(iot, ioh, APS_CMD, 0x00))
1584569cca6Sjsg 		goto out;
1594569cca6Sjsg 	bus_space_write_1(iot, ioh, APS_INIT, 0x10);
1604569cca6Sjsg 	bus_space_write_1(iot, ioh, APS_STATE, 0xc8);
1614569cca6Sjsg 	bus_space_write_1(iot, ioh, APS_XACCEL, 0x00);
1624569cca6Sjsg 	bus_space_write_1(iot, ioh, APS_XACCEL + 1, 0x02);
1634569cca6Sjsg 	bus_space_write_1(iot, ioh, APS_CMD, 0x01);
1644569cca6Sjsg 	if (!aps_mem_read_1(iot, ioh, APS_CMD, 0x00))
1654569cca6Sjsg 		goto out;
1664569cca6Sjsg 	/* refresh data */
1674569cca6Sjsg 	bus_space_write_1(iot, ioh, APS_INIT, 0x11);
1684569cca6Sjsg 	bus_space_write_1(iot, ioh, APS_CMD, 0x01);
1694569cca6Sjsg 	if (!aps_mem_read_1(iot, ioh, APS_ACCEL_STATE, 0x50))
1704569cca6Sjsg 		goto out;
1714569cca6Sjsg 	if (!aps_mem_read_1(iot, ioh, APS_STATE, 0x00))
1724569cca6Sjsg 		goto out;
1734569cca6Sjsg 
1744569cca6Sjsg 	sc->numsensors = APS_NUM_SENSORS;
1754569cca6Sjsg 
1764569cca6Sjsg 	sc->sensors[APS_SENSOR_XACCEL].type = SENSOR_INTEGER;
1774569cca6Sjsg 	snprintf(sc->sensors[APS_SENSOR_XACCEL].desc,
1784569cca6Sjsg 	    sizeof(sc->sensors[APS_SENSOR_XACCEL].desc), "X_ACCEL");
1794569cca6Sjsg 	sc->sensors[APS_SENSOR_XACCEL].rfact = 1;
1804569cca6Sjsg 
1814569cca6Sjsg 	sc->sensors[APS_SENSOR_YACCEL].type = SENSOR_INTEGER;
1824569cca6Sjsg 	snprintf(sc->sensors[APS_SENSOR_YACCEL].desc,
1834569cca6Sjsg 	    sizeof(sc->sensors[APS_SENSOR_YACCEL].desc), "Y_ACCEL");
1844569cca6Sjsg 	sc->sensors[APS_SENSOR_YACCEL].rfact = 1;
1854569cca6Sjsg 
1864569cca6Sjsg 	sc->sensors[APS_SENSOR_TEMP1].type = SENSOR_TEMP;
1874569cca6Sjsg 	snprintf(sc->sensors[APS_SENSOR_TEMP1].desc,
1884569cca6Sjsg 	    sizeof(sc->sensors[APS_SENSOR_TEMP1].desc), "Temp1");
1894569cca6Sjsg 
1904569cca6Sjsg 	sc->sensors[APS_SENSOR_TEMP2].type = SENSOR_TEMP;
1914569cca6Sjsg 	snprintf(sc->sensors[APS_SENSOR_TEMP2].desc,
1924569cca6Sjsg 	    sizeof(sc->sensors[APS_SENSOR_TEMP2].desc), "Temp2");
1934569cca6Sjsg 
1944569cca6Sjsg 	sc->sensors[APS_SENSOR_XVAR].type = SENSOR_INTEGER;
1954569cca6Sjsg 	snprintf(sc->sensors[APS_SENSOR_XVAR].desc,
1964569cca6Sjsg 	    sizeof(sc->sensors[APS_SENSOR_XVAR].desc), "X_VAR");
1974569cca6Sjsg 	sc->sensors[APS_SENSOR_XVAR].rfact = 1;
1984569cca6Sjsg 
1994569cca6Sjsg 	sc->sensors[APS_SENSOR_YVAR].type = SENSOR_INTEGER;
2004569cca6Sjsg 	snprintf(sc->sensors[APS_SENSOR_YVAR].desc,
2014569cca6Sjsg 	    sizeof(sc->sensors[APS_SENSOR_YVAR].desc), "Y_VAR");
2024569cca6Sjsg 	sc->sensors[APS_SENSOR_YVAR].rfact = 1;
2034569cca6Sjsg 
2044569cca6Sjsg 	sc->sensors[APS_SENSOR_UNK].type = SENSOR_INTEGER;
2054569cca6Sjsg 	snprintf(sc->sensors[APS_SENSOR_UNK].desc,
2064569cca6Sjsg 	    sizeof(sc->sensors[APS_SENSOR_UNK].desc), "unknown");
2074569cca6Sjsg 	sc->sensors[APS_SENSOR_YVAR].rfact = 1;
2084569cca6Sjsg 
2094569cca6Sjsg 	sc->sensors[APS_SENSOR_KBACT].type = SENSOR_INTEGER;
2104569cca6Sjsg 	snprintf(sc->sensors[APS_SENSOR_KBACT].desc,
2114569cca6Sjsg 	    sizeof(sc->sensors[APS_SENSOR_KBACT].desc), "KBD_ACT");
2124569cca6Sjsg 	sc->sensors[APS_SENSOR_KBACT].rfact = 1;
2134569cca6Sjsg 
2144569cca6Sjsg 	sc->sensors[APS_SENSOR_MSACT].type = SENSOR_INTEGER;
2154569cca6Sjsg 	snprintf(sc->sensors[APS_SENSOR_MSACT].desc,
2164569cca6Sjsg 	    sizeof(sc->sensors[APS_SENSOR_MSACT].desc), "MS_ACT");
2174569cca6Sjsg 	sc->sensors[APS_SENSOR_MSACT].rfact = 1;
2184569cca6Sjsg 
2194569cca6Sjsg 	sc->sensors[APS_SENSOR_LIDOPEN].type = SENSOR_INTEGER;
2204569cca6Sjsg 	snprintf(sc->sensors[APS_SENSOR_LIDOPEN].desc,
2214569cca6Sjsg 	    sizeof(sc->sensors[APS_SENSOR_LIDOPEN].desc), "LID_OPEN");
2224569cca6Sjsg 	sc->sensors[APS_SENSOR_LIDOPEN].rfact = 1;
2234569cca6Sjsg 
2244569cca6Sjsg 	/* stop hiding and report to the authorities */
2254569cca6Sjsg 	for (i = 0; i < sc->numsensors; i++) {
2264569cca6Sjsg 		strlcpy(sc->sensors[i].device, sc->sc_dev.dv_xname,
2274569cca6Sjsg 		    sizeof(sc->sensors[i].device));
2284569cca6Sjsg 		SENSOR_ADD(&sc->sensors[i]);
2294569cca6Sjsg 	}
2304569cca6Sjsg 
2314569cca6Sjsg 	/* Refresh sensor data every 0.5 seconds */
2324569cca6Sjsg 	timeout_set(&aps_timeout, aps_refresh, sc);
2334569cca6Sjsg 	timeout_add(&aps_timeout, (5 * hz) / 10);
2344569cca6Sjsg 	return;
2354569cca6Sjsg out:
2364569cca6Sjsg 	printf("%s: failed to initialise\n", sc->sc_dev.dv_xname);
2374569cca6Sjsg 	return;
2384569cca6Sjsg }
2394569cca6Sjsg 
2404569cca6Sjsg u_int8_t
2414569cca6Sjsg aps_mem_read_1(bus_space_tag_t iot, bus_space_handle_t ioh, int reg,
2424569cca6Sjsg     u_int8_t val)
2434569cca6Sjsg {
2444569cca6Sjsg 	int i;
2454569cca6Sjsg 	u_int8_t cr;
2464569cca6Sjsg 	/* should take no longer than 50 microseconds */
2474569cca6Sjsg 	for (i = 0; i < 10; i++) {
2484569cca6Sjsg 		cr = bus_space_read_1(iot, ioh, reg);
2494569cca6Sjsg 		if (cr == val)
2504569cca6Sjsg 			return (1);
2514569cca6Sjsg 		delay(5 * 1000);
2524569cca6Sjsg 	}
2534569cca6Sjsg 	DPRINTF(("aps: reg 0x%x not val 0x%x!\n", reg, val));
2544569cca6Sjsg 	return (0);
2554569cca6Sjsg }
2564569cca6Sjsg 
2574569cca6Sjsg int
2584569cca6Sjsg aps_read_data(struct aps_softc *sc)
2594569cca6Sjsg {
2604569cca6Sjsg 	bus_space_tag_t iot = sc->aps_iot;
2614569cca6Sjsg 	bus_space_handle_t ioh = sc->aps_ioh;
2624569cca6Sjsg 
2634569cca6Sjsg 	sc->aps_data.state = bus_space_read_1(iot, ioh, APS_STATE);
2644569cca6Sjsg 	sc->aps_data.x_accel = bus_space_read_2(iot, ioh, APS_XACCEL);
2654569cca6Sjsg 	sc->aps_data.y_accel = bus_space_read_2(iot, ioh, APS_YACCEL);
2664569cca6Sjsg 	sc->aps_data.temp1 = bus_space_read_1(iot, ioh, APS_TEMP);
2674569cca6Sjsg 	sc->aps_data.x_var = bus_space_read_2(iot, ioh, APS_XVAR);
2684569cca6Sjsg 	sc->aps_data.y_var = bus_space_read_2(iot, ioh, APS_YVAR);
2694569cca6Sjsg 	sc->aps_data.temp2 = bus_space_read_1(iot, ioh, APS_TEMP2);
2704569cca6Sjsg 	sc->aps_data.unk = bus_space_read_1(iot, ioh, APS_UNKNOWN);
2714569cca6Sjsg 	sc->aps_data.input = bus_space_read_1(iot, ioh, APS_INPUT);
2724569cca6Sjsg 
2734569cca6Sjsg 	return (1);
2744569cca6Sjsg }
2754569cca6Sjsg 
2764569cca6Sjsg void
2774569cca6Sjsg aps_refresh_sensor_data(struct aps_softc *sc)
2784569cca6Sjsg {
2794569cca6Sjsg 	bus_space_tag_t iot = sc->aps_iot;
2804569cca6Sjsg 	bus_space_handle_t ioh = sc->aps_ioh;
2814569cca6Sjsg 	int64_t temp;
2824569cca6Sjsg 	int i;
2834569cca6Sjsg 
2844569cca6Sjsg 	/* ask for new data */
2854569cca6Sjsg 	bus_space_write_1(iot, ioh, APS_INIT, 0x11);
2864569cca6Sjsg 	bus_space_write_1(iot, ioh, APS_CMD, 0x01);
2874569cca6Sjsg 	if (!aps_mem_read_1(iot, ioh, APS_ACCEL_STATE, 0x50))
2884569cca6Sjsg 		return;
2894569cca6Sjsg 	aps_read_data(sc);
2904569cca6Sjsg 	bus_space_write_1(iot, ioh, APS_INIT, 0x11);
2914569cca6Sjsg 	bus_space_write_1(iot, ioh, APS_CMD, 0x01);
2924569cca6Sjsg 
2934569cca6Sjsg 	/* tell accelerometer we're done reading from it */
2944569cca6Sjsg 	bus_space_read_1(iot, ioh, APS_CMD);
2954569cca6Sjsg 	bus_space_read_1(iot, ioh, APS_ACCEL_STATE);
2964569cca6Sjsg 
2974569cca6Sjsg 	for (i = 0; i < APS_NUM_SENSORS; i++) {
2984569cca6Sjsg 		sc->sensors[i].flags &= ~SENSOR_FINVALID;
2994569cca6Sjsg 	}
3004569cca6Sjsg 
3014569cca6Sjsg 	sc->sensors[APS_SENSOR_XACCEL].value = sc->aps_data.x_accel;
3024569cca6Sjsg 	sc->sensors[APS_SENSOR_YACCEL].value = sc->aps_data.y_accel;
3034569cca6Sjsg 
3044569cca6Sjsg 	/* convert to micro (mu) degrees */
3054569cca6Sjsg 	temp = sc->aps_data.temp1 * 1000000;
3064569cca6Sjsg 	/* convert to kelvin */
3074569cca6Sjsg 	temp += 273150000;
3084569cca6Sjsg 	sc->sensors[APS_SENSOR_TEMP1].value = temp;
3094569cca6Sjsg 
3104569cca6Sjsg 	/* convert to micro (mu) degrees */
3114569cca6Sjsg 	temp = sc->aps_data.temp2 * 1000000;
3124569cca6Sjsg 	/* convert to kelvin */
3134569cca6Sjsg 	temp += 273150000;
3144569cca6Sjsg 	sc->sensors[APS_SENSOR_TEMP2].value = temp;
3154569cca6Sjsg 
3164569cca6Sjsg 	sc->sensors[APS_SENSOR_XVAR].value = sc->aps_data.x_var;
3174569cca6Sjsg 	sc->sensors[APS_SENSOR_YVAR].value = sc->aps_data.y_var;
3184569cca6Sjsg 	sc->sensors[APS_SENSOR_UNK].value = sc->aps_data.unk;
3194569cca6Sjsg 	sc->sensors[APS_SENSOR_KBACT].value =
3204569cca6Sjsg 	    (sc->aps_data.input &  APS_INPUT_KB) ? 1 : 0;
3214569cca6Sjsg 	sc->sensors[APS_SENSOR_MSACT].value =
3224569cca6Sjsg 	    (sc->aps_data.input & APS_INPUT_MS) ? 1 : 0;
3234569cca6Sjsg 	sc->sensors[APS_SENSOR_LIDOPEN].value =
3244569cca6Sjsg 	    (sc->aps_data.input & APS_INPUT_LIDOPEN) ? 1 : 0;
3254569cca6Sjsg }
3264569cca6Sjsg 
3274569cca6Sjsg void
3284569cca6Sjsg aps_refresh(void *arg)
3294569cca6Sjsg {
3304569cca6Sjsg 	struct aps_softc *sc = (struct aps_softc *)arg;
3314569cca6Sjsg 
3324569cca6Sjsg 	aps_refresh_sensor_data(sc);
3334569cca6Sjsg 	timeout_add(&aps_timeout, (5 * hz) / 10);
3344569cca6Sjsg }
335