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