1 /* $OpenBSD: aps.c,v 1.24 2011/04/04 10:17:13 deraadt 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 #include <sys/event.h> 32 33 #include <dev/isa/isareg.h> 34 #include <dev/isa/isavar.h> 35 36 #ifdef __i386__ 37 #include "apm.h" 38 #include <machine/acpiapm.h> 39 #include <machine/biosvar.h> 40 #include <machine/apmvar.h> 41 #endif 42 43 #if defined(APSDEBUG) 44 #define DPRINTF(x) do { printf x; } while (0) 45 #else 46 #define DPRINTF(x) 47 #endif 48 49 50 /* 51 * EC interface on Thinkpad Laptops, from Linux HDAPS driver notes. 52 * From Renesans H8S/2140B Group Hardware Manual 53 * http://documentation.renesas.com/eng/products/mpumcu/rej09b0300_2140bhm.pdf 54 * 55 * EC uses LPC Channel 3 registers TWR0..15 56 */ 57 58 /* STR3 status register */ 59 #define APS_STR3 0x04 60 61 #define APS_STR3_IBF3B 0x80 /* Input buffer full (host->slave) */ 62 #define APS_STR3_OBF3B 0x40 /* Output buffer full (slave->host)*/ 63 #define APS_STR3_MWMF 0x20 /* Master write mode */ 64 #define APS_STR3_SWMF 0x10 /* Slave write mode */ 65 66 67 /* Base address of TWR registers */ 68 #define APS_TWR_BASE 0x10 69 #define APS_TWR_RET 0x1f 70 71 /* TWR registers */ 72 #define APS_CMD 0x00 73 #define APS_ARG1 0x01 74 #define APS_ARG2 0x02 75 #define APS_ARG3 0x03 76 #define APS_RET 0x0f 77 78 /* Sensor values */ 79 #define APS_STATE 0x01 80 #define APS_XACCEL 0x02 81 #define APS_YACCEL 0x04 82 #define APS_TEMP 0x06 83 #define APS_XVAR 0x07 84 #define APS_YVAR 0x09 85 #define APS_TEMP2 0x0b 86 #define APS_UNKNOWN 0x0c 87 #define APS_INPUT 0x0d 88 89 /* write masks for I/O, send command + 0-3 arguments*/ 90 #define APS_WRITE_0 0x0001 91 #define APS_WRITE_1 0x0003 92 #define APS_WRITE_2 0x0007 93 #define APS_WRITE_3 0x000f 94 95 /* read masks for I/O, read 0-3 values (skip command byte) */ 96 #define APS_READ_0 0x0000 97 #define APS_READ_1 0x0002 98 #define APS_READ_2 0x0006 99 #define APS_READ_3 0x000e 100 101 #define APS_READ_RET 0x8000 102 #define APS_READ_ALL 0xffff 103 104 /* Bit definitions for APS_INPUT value */ 105 #define APS_INPUT_KB (1 << 5) 106 #define APS_INPUT_MS (1 << 6) 107 #define APS_INPUT_LIDOPEN (1 << 7) 108 109 #define APS_ADDR_SIZE 0x1f 110 111 struct sensor_rec { 112 u_int8_t state; 113 u_int16_t x_accel; 114 u_int16_t y_accel; 115 u_int8_t temp1; 116 u_int16_t x_var; 117 u_int16_t y_var; 118 u_int8_t temp2; 119 u_int8_t unk; 120 u_int8_t input; 121 }; 122 123 #define APS_NUM_SENSORS 9 124 125 #define APS_SENSOR_XACCEL 0 126 #define APS_SENSOR_YACCEL 1 127 #define APS_SENSOR_XVAR 2 128 #define APS_SENSOR_YVAR 3 129 #define APS_SENSOR_TEMP1 4 130 #define APS_SENSOR_TEMP2 5 131 #define APS_SENSOR_KBACT 6 132 #define APS_SENSOR_MSACT 7 133 #define APS_SENSOR_LIDOPEN 8 134 135 struct aps_softc { 136 struct device sc_dev; 137 138 bus_space_tag_t aps_iot; 139 bus_space_handle_t aps_ioh; 140 141 struct ksensor sensors[APS_NUM_SENSORS]; 142 struct ksensordev sensordev; 143 void (*refresh_sensor_data)(struct aps_softc *); 144 145 struct sensor_rec aps_data; 146 }; 147 148 int aps_match(struct device *, void *, void *); 149 void aps_attach(struct device *, struct device *, void *); 150 int aps_activate(struct device *, int); 151 152 int aps_init(bus_space_tag_t, bus_space_handle_t); 153 int aps_read_data(struct aps_softc *); 154 void aps_refresh_sensor_data(struct aps_softc *); 155 void aps_refresh(void *); 156 int aps_do_io(bus_space_tag_t, bus_space_handle_t, 157 unsigned char *, int, int); 158 159 struct cfattach aps_ca = { 160 sizeof(struct aps_softc), 161 aps_match, aps_attach, NULL, aps_activate 162 }; 163 164 struct cfdriver aps_cd = { 165 NULL, "aps", DV_DULL 166 }; 167 168 struct timeout aps_timeout; 169 170 171 172 /* properly communicate with the controller, writing a set of memory 173 * locations and reading back another set */ 174 int 175 aps_do_io(bus_space_tag_t iot, bus_space_handle_t ioh, 176 unsigned char *buf, int wmask, int rmask) 177 { 178 int bp, stat, n; 179 180 DPRINTF(("aps_do_io: CMD: 0x%02x, wmask: 0x%04x, rmask: 0x%04x\n", 181 buf[0], wmask, rmask)); 182 183 /* write init byte using arbitration */ 184 for (n = 0; n < 100; n++) { 185 stat = bus_space_read_1(iot, ioh, APS_STR3); 186 if (stat & (APS_STR3_OBF3B | APS_STR3_SWMF)) { 187 bus_space_read_1(iot, ioh, APS_TWR_RET); 188 continue; 189 } 190 bus_space_write_1(iot, ioh, APS_TWR_BASE, buf[0]); 191 stat = bus_space_read_1(iot, ioh, APS_STR3); 192 if (stat & (APS_STR3_MWMF)) 193 break; 194 delay(1); 195 } 196 197 if (n == 100) { 198 DPRINTF(("aps_do_io: Failed to get bus\n")); 199 return (1); 200 } 201 202 /* write data bytes, init already sent */ 203 /* make sure last bye is always written as this will trigger slave */ 204 wmask |= APS_READ_RET; 205 buf[APS_RET] = 0x01; 206 207 for (n = 1, bp = 2; n < 16; bp <<= 1, n++) { 208 if (wmask & bp) { 209 bus_space_write_1(iot, ioh, APS_TWR_BASE + n, buf[n]); 210 DPRINTF(("aps_do_io: write %2d 0x%02x\n", n, buf[n])); 211 } 212 } 213 214 for (n = 0; n < 100; n++) { 215 stat = bus_space_read_1(iot, ioh, APS_STR3); 216 if (stat & (APS_STR3_OBF3B)) 217 break; 218 delay(5 * 100); 219 } 220 221 if (n == 100) { 222 DPRINTF(("aps_do_io: timeout waiting response\n")); 223 return (1); 224 } 225 /* wait for data available */ 226 /* make sure to read the final byte to clear status */ 227 rmask |= APS_READ_RET; 228 229 /* read cmd and data bytes */ 230 for (n = 0, bp = 1; n < 16; bp <<= 1, n++) { 231 if (rmask & bp) { 232 buf[n] = bus_space_read_1(iot, ioh, APS_TWR_BASE + n); 233 DPRINTF(("aps_do_io: read %2d 0x%02x\n", n, buf[n])); 234 } 235 } 236 237 return (0); 238 } 239 240 int 241 aps_match(struct device *parent, void *match, void *aux) 242 { 243 struct isa_attach_args *ia = aux; 244 bus_space_tag_t iot = ia->ia_iot; 245 bus_space_handle_t ioh; 246 int iobase = ia->ipa_io[0].base; 247 u_int8_t cr; 248 unsigned char iobuf[16]; 249 250 if (bus_space_map(iot, iobase, APS_ADDR_SIZE, 0, &ioh)) { 251 DPRINTF(("aps: can't map i/o space\n")); 252 return (0); 253 } 254 /* get APS mode */ 255 iobuf[APS_CMD] = 0x13; 256 if (aps_do_io(iot, ioh, iobuf, APS_WRITE_0, APS_READ_1)) { 257 bus_space_unmap(iot, ioh, APS_ADDR_SIZE); 258 return (0); 259 } 260 261 /* 262 * Observed values from Linux driver: 263 * 0x01: T42 264 * 0x02: chip already initialised 265 * 0x03: T41 266 * 0x05: T61 267 */ 268 269 cr = iobuf[APS_ARG1]; 270 DPRINTF(("aps: state register 0x%x\n", cr)); 271 272 if (aps_init(iot, ioh)) { 273 bus_space_unmap(iot, ioh, APS_ADDR_SIZE); 274 return (0); 275 } 276 277 bus_space_unmap(iot, ioh, APS_ADDR_SIZE); 278 279 if (iobuf[APS_RET] != 0 || cr < 1 || cr > 5) { 280 DPRINTF(("aps0: unsupported state %d\n", cr)); 281 return (0); 282 } 283 284 ia->ipa_nio = 1; 285 ia->ipa_io[0].length = APS_ADDR_SIZE; 286 ia->ipa_nmem = 0; 287 ia->ipa_nirq = 0; 288 ia->ipa_ndrq = 0; 289 return (1); 290 } 291 292 void 293 aps_attach(struct device *parent, struct device *self, void *aux) 294 { 295 struct aps_softc *sc = (void *)self; 296 int iobase, i; 297 bus_space_tag_t iot; 298 bus_space_handle_t ioh; 299 struct isa_attach_args *ia = aux; 300 301 iobase = ia->ipa_io[0].base; 302 iot = sc->aps_iot = ia->ia_iot; 303 304 if (bus_space_map(iot, iobase, APS_ADDR_SIZE, 0, &sc->aps_ioh)) { 305 printf(": can't map i/o space\n"); 306 return; 307 } 308 309 ioh = sc->aps_ioh; 310 311 printf("\n"); 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 /* Refresh sensor data every 0.5 seconds */ 353 timeout_set(&aps_timeout, aps_refresh, sc); 354 timeout_add_msec(&aps_timeout, 500); 355 } 356 357 int 358 aps_init(bus_space_tag_t iot, bus_space_handle_t ioh) 359 { 360 unsigned char iobuf[16]; 361 362 363 /* command 0x17/0x81: check EC */ 364 iobuf[APS_CMD] = 0x17; 365 iobuf[APS_ARG1] = 0x81; 366 367 if (aps_do_io(iot, ioh, iobuf, APS_WRITE_1, APS_READ_3)) 368 return (1); 369 370 if (iobuf[APS_RET] != 0 ||iobuf[APS_ARG3] != 0) 371 return (2); 372 373 /* Test values from the Linux driver */ 374 if ((iobuf[APS_ARG1] != 0 || iobuf[APS_ARG2] != 0x60) && 375 (iobuf[APS_ARG1] != 1 || iobuf[APS_ARG2] != 0)) 376 return (3); 377 378 /* command 0x14: set power */ 379 iobuf[APS_CMD] = 0x14; 380 iobuf[APS_ARG1] = 0x01; 381 382 if (aps_do_io(iot, ioh, iobuf, APS_WRITE_1, APS_READ_0)) 383 return (4); 384 385 if (iobuf[APS_RET] != 0) 386 return (5); 387 388 /* command 0x10: set config (sample rate and order) */ 389 iobuf[APS_CMD] = 0x10; 390 iobuf[APS_ARG1] = 0xc8; 391 iobuf[APS_ARG2] = 0x00; 392 iobuf[APS_ARG3] = 0x02; 393 394 if (aps_do_io(iot, ioh, iobuf, APS_WRITE_3, APS_READ_0)) 395 return (6); 396 397 if (iobuf[APS_RET] != 0) 398 return (7); 399 400 /* command 0x11: refresh data */ 401 iobuf[APS_CMD] = 0x11; 402 if (aps_do_io(iot, ioh, iobuf, APS_WRITE_0, APS_READ_1)) 403 return (8); 404 405 return (0); 406 } 407 408 int 409 aps_read_data(struct aps_softc *sc) 410 { 411 bus_space_tag_t iot = sc->aps_iot; 412 bus_space_handle_t ioh = sc->aps_ioh; 413 unsigned char iobuf[16]; 414 415 /* command 0x11: refresh data */ 416 iobuf[APS_CMD] = 0x11; 417 if (aps_do_io(iot, ioh, iobuf, APS_WRITE_0, APS_READ_ALL)) 418 return (1); 419 420 sc->aps_data.state = iobuf[APS_STATE]; 421 sc->aps_data.x_accel = iobuf[APS_XACCEL] + 256 * iobuf[APS_XACCEL + 1]; 422 sc->aps_data.y_accel = iobuf[APS_YACCEL] + 256 * iobuf[APS_YACCEL + 1]; 423 sc->aps_data.temp1 = iobuf[APS_TEMP]; 424 sc->aps_data.x_var = iobuf[APS_XVAR] + 256 * iobuf[APS_XVAR + 1]; 425 sc->aps_data.y_var = iobuf[APS_YVAR] + 256 * iobuf[APS_YVAR + 1]; 426 sc->aps_data.temp2 = iobuf[APS_TEMP2]; 427 sc->aps_data.input = iobuf[APS_INPUT]; 428 429 return (0); 430 } 431 432 void 433 aps_refresh_sensor_data(struct aps_softc *sc) 434 { 435 int64_t temp; 436 int i; 437 #if NAPM > 0 438 extern int lid_suspend; 439 extern int apm_lidclose; 440 #endif 441 442 if (aps_read_data(sc)) 443 return; 444 445 for (i = 0; i < APS_NUM_SENSORS; i++) { 446 sc->sensors[i].flags &= ~SENSOR_FINVALID; 447 } 448 449 sc->sensors[APS_SENSOR_XACCEL].value = sc->aps_data.x_accel; 450 sc->sensors[APS_SENSOR_YACCEL].value = sc->aps_data.y_accel; 451 452 /* convert to micro (mu) degrees */ 453 temp = sc->aps_data.temp1 * 1000000; 454 /* convert to kelvin */ 455 temp += 273150000; 456 sc->sensors[APS_SENSOR_TEMP1].value = temp; 457 458 /* convert to micro (mu) degrees */ 459 temp = sc->aps_data.temp2 * 1000000; 460 /* convert to kelvin */ 461 temp += 273150000; 462 sc->sensors[APS_SENSOR_TEMP2].value = temp; 463 464 sc->sensors[APS_SENSOR_XVAR].value = sc->aps_data.x_var; 465 sc->sensors[APS_SENSOR_YVAR].value = sc->aps_data.y_var; 466 sc->sensors[APS_SENSOR_KBACT].value = 467 (sc->aps_data.input & APS_INPUT_KB) ? 1 : 0; 468 sc->sensors[APS_SENSOR_MSACT].value = 469 (sc->aps_data.input & APS_INPUT_MS) ? 1 : 0; 470 #if NAPM > 0 471 if (lid_suspend && 472 (sc->sensors[APS_SENSOR_LIDOPEN].value == 1) && 473 (sc->aps_data.input & APS_INPUT_LIDOPEN) == 0) 474 /* Inform APM that the lid has closed */ 475 apm_lidclose = 1; 476 #endif 477 sc->sensors[APS_SENSOR_LIDOPEN].value = 478 (sc->aps_data.input & APS_INPUT_LIDOPEN) ? 1 : 0; 479 } 480 481 void 482 aps_refresh(void *arg) 483 { 484 struct aps_softc *sc = (struct aps_softc *)arg; 485 486 aps_refresh_sensor_data(sc); 487 timeout_add_msec(&aps_timeout, 500); 488 } 489 490 int 491 aps_activate(struct device *self, int act) 492 { 493 struct aps_softc *sc = (struct aps_softc *)self; 494 bus_space_tag_t iot = sc->aps_iot; 495 bus_space_handle_t ioh = sc->aps_ioh; 496 unsigned char iobuf[16]; 497 498 /* check if we bombed during attach */ 499 if (!timeout_initialized(&aps_timeout)) 500 return (0); 501 502 switch (act) { 503 case DVACT_SUSPEND: 504 timeout_del(&aps_timeout); 505 break; 506 case DVACT_RESUME: 507 /* 508 * Redo the init sequence on resume, because APS is 509 * as forgetful as it is deaf. 510 */ 511 512 /* get APS mode */ 513 iobuf[APS_CMD] = 0x13; 514 aps_do_io(iot, ioh, iobuf, APS_WRITE_0, APS_READ_1); 515 516 aps_init(iot, ioh); 517 timeout_add_msec(&aps_timeout, 500); 518 break; 519 } 520 return (0); 521 } 522