1 /* $OpenBSD: aps.c,v 1.18 2009/04/21 19:36:17 mk Exp $ */ 2 /* 3 * Copyright (c) 2005 Jonathan Gray <jsg@openbsd.org> 4 * Copyright (c) 2008 Can Erkin Acar <canacar@openbsd.org> 5 * 6 * Permission to use, copy, modify, and distribute this software for any 7 * purpose with or without fee is hereby granted, provided that the above 8 * copyright notice and this permission notice appear in all copies. 9 * 10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 13 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 14 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 15 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 16 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17 */ 18 19 /* 20 * A driver for the ThinkPad Active Protection System based on notes from 21 * http://www.almaden.ibm.com/cs/people/marksmith/tpaps.html 22 */ 23 24 #include <sys/param.h> 25 #include <sys/systm.h> 26 #include <sys/device.h> 27 #include <sys/kernel.h> 28 #include <sys/sensors.h> 29 #include <sys/timeout.h> 30 #include <machine/bus.h> 31 32 #include <dev/isa/isareg.h> 33 #include <dev/isa/isavar.h> 34 35 #if defined(APSDEBUG) 36 #define DPRINTF(x) do { printf x; } while (0) 37 #else 38 #define DPRINTF(x) 39 #endif 40 41 42 /* 43 * EC interface on Thinkpad Laptops, from Linux HDAPS driver notes. 44 * From Renesans H8S/2140B Group Hardware Manual 45 * http://documentation.renesas.com/eng/products/mpumcu/rej09b0300_2140bhm.pdf 46 * 47 * EC uses LPC Channel 3 registers TWR0..15 48 */ 49 50 /* STR3 status register */ 51 #define APS_STR3 0x04 52 53 #define APS_STR3_IBF3B 0x80 /* Input buffer full (host->slave) */ 54 #define APS_STR3_OBF3B 0x40 /* Output buffer full (slave->host)*/ 55 #define APS_STR3_MWMF 0x20 /* Master write mode */ 56 #define APS_STR3_SWMF 0x10 /* Slave write mode */ 57 58 59 /* Base address of TWR registers */ 60 #define APS_TWR_BASE 0x10 61 #define APS_TWR_RET 0x1f 62 63 /* TWR registers */ 64 #define APS_CMD 0x00 65 #define APS_ARG1 0x01 66 #define APS_ARG2 0x02 67 #define APS_ARG3 0x03 68 #define APS_RET 0x0f 69 70 /* Sensor values */ 71 #define APS_STATE 0x01 72 #define APS_XACCEL 0x02 73 #define APS_YACCEL 0x04 74 #define APS_TEMP 0x06 75 #define APS_XVAR 0x07 76 #define APS_YVAR 0x09 77 #define APS_TEMP2 0x0b 78 #define APS_UNKNOWN 0x0c 79 #define APS_INPUT 0x0d 80 81 /* write masks for I/O, send command + 0-3 arguments*/ 82 #define APS_WRITE_0 0x0001 83 #define APS_WRITE_1 0x0003 84 #define APS_WRITE_2 0x0007 85 #define APS_WRITE_3 0x000f 86 87 /* read masks for I/O, read 0-3 values (skip command byte) */ 88 #define APS_READ_0 0x0000 89 #define APS_READ_1 0x0002 90 #define APS_READ_2 0x0006 91 #define APS_READ_3 0x000e 92 93 #define APS_READ_RET 0x8000 94 #define APS_READ_ALL 0xffff 95 96 /* Bit definitions for APS_INPUT value */ 97 #define APS_INPUT_KB (1 << 5) 98 #define APS_INPUT_MS (1 << 6) 99 #define APS_INPUT_LIDOPEN (1 << 7) 100 101 #define APS_ADDR_SIZE 0x1f 102 103 struct sensor_rec { 104 u_int8_t state; 105 u_int16_t x_accel; 106 u_int16_t y_accel; 107 u_int8_t temp1; 108 u_int16_t x_var; 109 u_int16_t y_var; 110 u_int8_t temp2; 111 u_int8_t unk; 112 u_int8_t input; 113 }; 114 115 #define APS_NUM_SENSORS 9 116 117 #define APS_SENSOR_XACCEL 0 118 #define APS_SENSOR_YACCEL 1 119 #define APS_SENSOR_XVAR 2 120 #define APS_SENSOR_YVAR 3 121 #define APS_SENSOR_TEMP1 4 122 #define APS_SENSOR_TEMP2 5 123 #define APS_SENSOR_KBACT 6 124 #define APS_SENSOR_MSACT 7 125 #define APS_SENSOR_LIDOPEN 8 126 127 struct aps_softc { 128 struct device sc_dev; 129 130 bus_space_tag_t aps_iot; 131 bus_space_handle_t aps_ioh; 132 133 struct ksensor sensors[APS_NUM_SENSORS]; 134 struct ksensordev sensordev; 135 void (*refresh_sensor_data)(struct aps_softc *); 136 137 struct sensor_rec aps_data; 138 }; 139 140 int aps_match(struct device *, void *, void *); 141 void aps_attach(struct device *, struct device *, void *); 142 143 int aps_init(bus_space_tag_t, bus_space_handle_t); 144 int aps_read_data(struct aps_softc *); 145 void aps_refresh_sensor_data(struct aps_softc *); 146 void aps_refresh(void *); 147 void aps_power(int, void *); 148 int aps_do_io(bus_space_tag_t, bus_space_handle_t, 149 unsigned char *, int, int); 150 151 struct cfattach aps_ca = { 152 sizeof(struct aps_softc), 153 aps_match, 154 aps_attach 155 }; 156 157 struct cfdriver aps_cd = { 158 NULL, "aps", DV_DULL 159 }; 160 161 struct timeout aps_timeout; 162 163 164 165 /* properly communicate with the controller, writing a set of memory 166 * locations and reading back another set */ 167 int 168 aps_do_io(bus_space_tag_t iot, bus_space_handle_t ioh, 169 unsigned char *buf, int wmask, int rmask) 170 { 171 int bp, stat, n; 172 173 DPRINTF(("aps_do_io: CMD: 0x%02x, wmask: 0x%04x, rmask: 0x%04x\n", 174 buf[0], wmask, rmask)); 175 176 /* write init byte using arbitration */ 177 for (n = 0; n < 100; n++) { 178 stat = bus_space_read_1(iot, ioh, APS_STR3); 179 if (stat & (APS_STR3_OBF3B | APS_STR3_SWMF)) { 180 bus_space_read_1(iot, ioh, APS_TWR_RET); 181 continue; 182 } 183 bus_space_write_1(iot, ioh, APS_TWR_BASE, buf[0]); 184 stat = bus_space_read_1(iot, ioh, APS_STR3); 185 if (stat & (APS_STR3_MWMF)) 186 break; 187 delay(1); 188 } 189 190 if (n == 100) { 191 DPRINTF(("aps_do_io: Failed to get bus\n")); 192 return (1); 193 } 194 195 /* write data bytes, init already sent */ 196 /* make sure last bye is always written as this will trigger slave */ 197 wmask |= APS_READ_RET; 198 buf[APS_RET] = 0x01; 199 200 for (n = 1, bp = 2; n < 16; bp <<= 1, n++) { 201 if (wmask & bp) { 202 bus_space_write_1(iot, ioh, APS_TWR_BASE + n, buf[n]); 203 DPRINTF(("aps_do_io: write %2d 0x%02x\n", n, buf[n])); 204 } 205 } 206 207 for (n = 0; n < 100; n++) { 208 stat = bus_space_read_1(iot, ioh, APS_STR3); 209 if (stat & (APS_STR3_OBF3B)) 210 break; 211 delay(5 * 100); 212 } 213 214 if (n == 100) { 215 DPRINTF(("aps_do_io: timeout waiting response\n")); 216 return (1); 217 } 218 /* wait for data available */ 219 /* make sure to read the final byte to clear status */ 220 rmask |= APS_READ_RET; 221 222 /* read cmd and data bytes */ 223 for (n = 0, bp = 1; n < 16; bp <<= 1, n++) { 224 if (rmask & bp) { 225 buf[n] = bus_space_read_1(iot, ioh, APS_TWR_BASE + n); 226 DPRINTF(("aps_do_io: read %2d 0x%02x\n", n, buf[n])); 227 } 228 } 229 230 return (0); 231 } 232 233 int 234 aps_match(struct device *parent, void *match, void *aux) 235 { 236 bus_space_tag_t iot; 237 bus_space_handle_t ioh; 238 struct isa_attach_args *ia = aux; 239 int iobase; 240 u_int8_t cr; 241 242 char iobuf[16]; 243 244 iot = ia->ia_iot; 245 iobase = ia->ipa_io[0].base; 246 247 if (bus_space_map(iot, iobase, APS_ADDR_SIZE, 0, &ioh)) { 248 DPRINTF(("aps: can't map i/o space\n")); 249 return (0); 250 } 251 252 253 /* See if this machine has APS */ 254 255 /* get APS mode */ 256 iobuf[APS_CMD] = 0x13; 257 if (aps_do_io(iot, ioh, iobuf, APS_WRITE_0, APS_READ_1)) { 258 bus_space_unmap(iot, ioh, APS_ADDR_SIZE); 259 return (0); 260 } 261 262 /* 263 * Observed values from Linux driver: 264 * 0x01: T42 265 * 0x02: chip already initialised 266 * 0x03: T41 267 * 0x05: T61 268 */ 269 270 cr = iobuf[APS_ARG1]; 271 272 bus_space_unmap(iot, ioh, APS_ADDR_SIZE); 273 DPRINTF(("aps: state register 0x%x\n", cr)); 274 275 if (iobuf[APS_RET] != 0 || cr < 1 || cr > 5) { 276 DPRINTF(("aps0: unsupported state %d\n", cr)); 277 return (0); 278 } 279 280 ia->ipa_nio = 1; 281 ia->ipa_io[0].length = APS_ADDR_SIZE; 282 ia->ipa_nmem = 0; 283 ia->ipa_nirq = 0; 284 ia->ipa_ndrq = 0; 285 286 return (1); 287 } 288 289 void 290 aps_attach(struct device *parent, struct device *self, void *aux) 291 { 292 struct aps_softc *sc = (void *)self; 293 int iobase, i; 294 bus_space_tag_t iot; 295 bus_space_handle_t ioh; 296 struct isa_attach_args *ia = aux; 297 298 iobase = ia->ipa_io[0].base; 299 iot = sc->aps_iot = ia->ia_iot; 300 301 if (bus_space_map(iot, iobase, APS_ADDR_SIZE, 0, &sc->aps_ioh)) { 302 printf(": can't map i/o space\n"); 303 return; 304 } 305 306 ioh = sc->aps_ioh; 307 308 printf("\n"); 309 310 if (aps_init(iot, ioh)) 311 goto out; 312 313 sc->sensors[APS_SENSOR_XACCEL].type = SENSOR_INTEGER; 314 snprintf(sc->sensors[APS_SENSOR_XACCEL].desc, 315 sizeof(sc->sensors[APS_SENSOR_XACCEL].desc), "X_ACCEL"); 316 317 sc->sensors[APS_SENSOR_YACCEL].type = SENSOR_INTEGER; 318 snprintf(sc->sensors[APS_SENSOR_YACCEL].desc, 319 sizeof(sc->sensors[APS_SENSOR_YACCEL].desc), "Y_ACCEL"); 320 321 sc->sensors[APS_SENSOR_TEMP1].type = SENSOR_TEMP; 322 sc->sensors[APS_SENSOR_TEMP2].type = SENSOR_TEMP; 323 324 sc->sensors[APS_SENSOR_XVAR].type = SENSOR_INTEGER; 325 snprintf(sc->sensors[APS_SENSOR_XVAR].desc, 326 sizeof(sc->sensors[APS_SENSOR_XVAR].desc), "X_VAR"); 327 328 sc->sensors[APS_SENSOR_YVAR].type = SENSOR_INTEGER; 329 snprintf(sc->sensors[APS_SENSOR_YVAR].desc, 330 sizeof(sc->sensors[APS_SENSOR_YVAR].desc), "Y_VAR"); 331 332 sc->sensors[APS_SENSOR_KBACT].type = SENSOR_INDICATOR; 333 snprintf(sc->sensors[APS_SENSOR_KBACT].desc, 334 sizeof(sc->sensors[APS_SENSOR_KBACT].desc), "Keyboard Active"); 335 336 sc->sensors[APS_SENSOR_MSACT].type = SENSOR_INDICATOR; 337 snprintf(sc->sensors[APS_SENSOR_MSACT].desc, 338 sizeof(sc->sensors[APS_SENSOR_MSACT].desc), "Mouse Active"); 339 340 sc->sensors[APS_SENSOR_LIDOPEN].type = SENSOR_INDICATOR; 341 snprintf(sc->sensors[APS_SENSOR_LIDOPEN].desc, 342 sizeof(sc->sensors[APS_SENSOR_LIDOPEN].desc), "Lid Open"); 343 344 /* stop hiding and report to the authorities */ 345 strlcpy(sc->sensordev.xname, sc->sc_dev.dv_xname, 346 sizeof(sc->sensordev.xname)); 347 for (i = 0; i < APS_NUM_SENSORS ; i++) { 348 sensor_attach(&sc->sensordev, &sc->sensors[i]); 349 } 350 sensordev_install(&sc->sensordev); 351 352 powerhook_establish(aps_power, (void *)sc); 353 354 /* Refresh sensor data every 0.5 seconds */ 355 timeout_set(&aps_timeout, aps_refresh, sc); 356 timeout_add_msec(&aps_timeout, 500); 357 return; 358 out: 359 printf("%s: failed to initialize\n", sc->sc_dev.dv_xname); 360 return; 361 } 362 363 int 364 aps_init(bus_space_tag_t iot, bus_space_handle_t ioh) 365 { 366 unsigned char iobuf[16]; 367 368 369 /* command 0x17/0x81: check EC */ 370 iobuf[APS_CMD] = 0x17; 371 iobuf[APS_ARG1] = 0x81; 372 373 if (aps_do_io(iot, ioh, iobuf, APS_WRITE_1, APS_READ_3)) 374 return (1); 375 376 if (iobuf[APS_RET] != 0 ||iobuf[APS_ARG3] != 0) 377 return (1); 378 379 /* Test values from the Linux driver */ 380 if ((iobuf[APS_ARG1] != 0 || iobuf[APS_ARG2] != 0x60) && 381 (iobuf[APS_ARG1] != 1 || iobuf[APS_ARG2] != 0)) 382 return (1); 383 384 /* command 0x14: set power */ 385 iobuf[APS_CMD] = 0x14; 386 iobuf[APS_ARG1] = 0x01; 387 388 if (aps_do_io(iot, ioh, iobuf, APS_WRITE_1, APS_READ_0)) 389 return (1); 390 391 if (iobuf[APS_RET] != 0) 392 return (1); 393 394 /* command 0x10: set config (sample rate and order) */ 395 iobuf[APS_CMD] = 0x10; 396 iobuf[APS_ARG1] = 0xc8; 397 iobuf[APS_ARG2] = 0x00; 398 iobuf[APS_ARG3] = 0x02; 399 400 if (aps_do_io(iot, ioh, iobuf, APS_WRITE_3, APS_READ_0)) 401 return (1); 402 403 if (iobuf[APS_RET] != 0) 404 return (1); 405 406 /* command 0x11: refresh data */ 407 iobuf[APS_CMD] = 0x11; 408 if (aps_do_io(iot, ioh, iobuf, APS_WRITE_0, APS_READ_1)) 409 return (1); 410 if (iobuf[APS_ARG1] != 0) 411 return (1); 412 413 return (0); 414 } 415 416 int 417 aps_read_data(struct aps_softc *sc) 418 { 419 bus_space_tag_t iot = sc->aps_iot; 420 bus_space_handle_t ioh = sc->aps_ioh; 421 unsigned char iobuf[16]; 422 423 /* command 0x11: refresh data */ 424 iobuf[APS_CMD] = 0x11; 425 if (aps_do_io(iot, ioh, iobuf, APS_WRITE_0, APS_READ_ALL)) 426 return (1); 427 428 sc->aps_data.state = iobuf[APS_STATE]; 429 sc->aps_data.x_accel = iobuf[APS_XACCEL] + 256 * iobuf[APS_XACCEL + 1]; 430 sc->aps_data.y_accel = iobuf[APS_YACCEL] + 256 * iobuf[APS_YACCEL + 1]; 431 sc->aps_data.temp1 = iobuf[APS_TEMP]; 432 sc->aps_data.x_var = iobuf[APS_XVAR] + 256 * iobuf[APS_XVAR + 1]; 433 sc->aps_data.y_var = iobuf[APS_YVAR] + 256 * iobuf[APS_YVAR + 1]; 434 sc->aps_data.temp2 = iobuf[APS_TEMP2]; 435 sc->aps_data.input = iobuf[APS_INPUT]; 436 437 return (0); 438 } 439 440 void 441 aps_refresh_sensor_data(struct aps_softc *sc) 442 { 443 int64_t temp; 444 int i; 445 446 if (aps_read_data(sc)) 447 return; 448 449 for (i = 0; i < APS_NUM_SENSORS; i++) { 450 sc->sensors[i].flags &= ~SENSOR_FINVALID; 451 } 452 453 sc->sensors[APS_SENSOR_XACCEL].value = sc->aps_data.x_accel; 454 sc->sensors[APS_SENSOR_YACCEL].value = sc->aps_data.y_accel; 455 456 /* convert to micro (mu) degrees */ 457 temp = sc->aps_data.temp1 * 1000000; 458 /* convert to kelvin */ 459 temp += 273150000; 460 sc->sensors[APS_SENSOR_TEMP1].value = temp; 461 462 /* convert to micro (mu) degrees */ 463 temp = sc->aps_data.temp2 * 1000000; 464 /* convert to kelvin */ 465 temp += 273150000; 466 sc->sensors[APS_SENSOR_TEMP2].value = temp; 467 468 sc->sensors[APS_SENSOR_XVAR].value = sc->aps_data.x_var; 469 sc->sensors[APS_SENSOR_YVAR].value = sc->aps_data.y_var; 470 sc->sensors[APS_SENSOR_KBACT].value = 471 (sc->aps_data.input & APS_INPUT_KB) ? 1 : 0; 472 sc->sensors[APS_SENSOR_MSACT].value = 473 (sc->aps_data.input & APS_INPUT_MS) ? 1 : 0; 474 sc->sensors[APS_SENSOR_LIDOPEN].value = 475 (sc->aps_data.input & APS_INPUT_LIDOPEN) ? 1 : 0; 476 } 477 478 void 479 aps_refresh(void *arg) 480 { 481 struct aps_softc *sc = (struct aps_softc *)arg; 482 483 aps_refresh_sensor_data(sc); 484 timeout_add_msec(&aps_timeout, 500); 485 } 486 487 void 488 aps_power(int why, void *arg) 489 { 490 struct aps_softc *sc = (struct aps_softc *)arg; 491 bus_space_tag_t iot = sc->aps_iot; 492 bus_space_handle_t ioh = sc->aps_ioh; 493 unsigned char iobuf[16]; 494 495 if (why != PWR_RESUME) { 496 timeout_del(&aps_timeout); 497 return; 498 } 499 /* 500 * Redo the init sequence on resume, because APS is 501 * as forgetful as it is deaf. 502 */ 503 504 /* get APS mode */ 505 iobuf[APS_CMD] = 0x13; 506 if (aps_do_io(iot, ioh, iobuf, APS_WRITE_0, APS_READ_1) 507 || aps_init(iot, ioh)) 508 printf("aps: failed to wake up\n"); 509 else 510 timeout_add_msec(&aps_timeout, 500); 511 } 512