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