1 /* $OpenBSD: aps.c,v 1.19 2009/05/24 16:40:18 jsg Exp $ */ 2 /* 3 * Copyright (c) 2005 Jonathan Gray <jsg@openbsd.org> 4 * Copyright (c) 2008 Can Erkin Acar <canacar@openbsd.org> 5 * Copyright (c) 2010 Constantine A. Murenin <cnst++@dragonflybsd.org> 6 * 7 * Permission to use, copy, modify, and distribute this software for any 8 * purpose with or without fee is hereby granted, provided that the above 9 * copyright notice and this permission notice appear in all copies. 10 * 11 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 12 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 13 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 14 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 15 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 16 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 17 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 18 */ 19 20 /* 21 * A driver for the ThinkPad Active Protection System based on notes from 22 * http://www.almaden.ibm.com/cs/people/marksmith/tpaps.html 23 */ 24 25 #include <sys/param.h> 26 #include <sys/systm.h> 27 #include <sys/bus.h> 28 #include <sys/kernel.h> 29 #include <sys/module.h> 30 #include <sys/rman.h> 31 #include <sys/sensors.h> 32 33 #include <bus/isa/isavar.h> 34 35 #if defined(APSDEBUG) 36 #define DPRINTF(x) do { kprintf 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_BASE 0x1600 102 #define APS_ADDR_SIZE 0x1f 103 104 struct aps_sensor_rec { 105 u_int8_t state; 106 u_int16_t x_accel; 107 u_int16_t y_accel; 108 u_int8_t temp1; 109 u_int16_t x_var; 110 u_int16_t y_var; 111 u_int8_t temp2; 112 u_int8_t unk; 113 u_int8_t input; 114 }; 115 116 #define APS_NUM_SENSORS 9 117 118 #define APS_SENSOR_XACCEL 0 119 #define APS_SENSOR_YACCEL 1 120 #define APS_SENSOR_XVAR 2 121 #define APS_SENSOR_YVAR 3 122 #define APS_SENSOR_TEMP1 4 123 #define APS_SENSOR_TEMP2 5 124 #define APS_SENSOR_KBACT 6 125 #define APS_SENSOR_MSACT 7 126 #define APS_SENSOR_LIDOPEN 8 127 128 struct aps_softc { 129 device_t sc_dev; 130 131 struct resource *sc_iores; 132 int sc_iorid; 133 134 struct ksensor sensors[APS_NUM_SENSORS]; 135 struct ksensordev sensordev; 136 137 struct aps_sensor_rec aps_data; 138 }; 139 140 static void aps_identify(driver_t *, device_t); 141 static int aps_probe(device_t); 142 static int aps_attach(device_t); 143 static int aps_detach(device_t); 144 145 static int aps_resume(device_t); 146 static int aps_suspend(device_t); 147 148 static int aps_init(struct resource *); 149 static int aps_read_data(struct aps_softc *); 150 static void aps_refresh_sensor_data(struct aps_softc *); 151 static void aps_refresh(void *); 152 static int aps_do_io(struct resource *, unsigned char *, int, int); 153 154 static device_method_t aps_methods[] = { 155 DEVMETHOD(device_identify, aps_identify), 156 DEVMETHOD(device_probe, aps_probe), 157 DEVMETHOD(device_attach, aps_attach), 158 DEVMETHOD(device_detach, aps_detach), 159 160 DEVMETHOD(device_resume, aps_resume), 161 DEVMETHOD(device_suspend, aps_suspend), 162 { NULL, NULL } 163 }; 164 165 static driver_t aps_driver = { 166 "aps", 167 aps_methods, 168 sizeof(struct aps_softc) 169 }; 170 171 static devclass_t aps_devclass; 172 173 DRIVER_MODULE(aps, isa, aps_driver, aps_devclass, NULL, NULL); 174 175 176 177 /* properly communicate with the controller, writing a set of memory 178 * locations and reading back another set */ 179 static int 180 aps_do_io(struct resource *iores, unsigned char *buf, int wmask, int rmask) 181 { 182 bus_space_tag_t iot = rman_get_bustag(iores); 183 bus_space_handle_t ioh = rman_get_bushandle(iores); 184 int bp, stat, n; 185 186 DPRINTF(("aps_do_io: CMD: 0x%02x, wmask: 0x%04x, rmask: 0x%04x\n", 187 buf[0], wmask, rmask)); 188 189 /* write init byte using arbitration */ 190 for (n = 0; n < 100; n++) { 191 stat = bus_space_read_1(iot, ioh, APS_STR3); 192 if (stat & (APS_STR3_OBF3B | APS_STR3_SWMF)) { 193 bus_space_read_1(iot, ioh, APS_TWR_RET); 194 continue; 195 } 196 bus_space_write_1(iot, ioh, APS_TWR_BASE, buf[0]); 197 stat = bus_space_read_1(iot, ioh, APS_STR3); 198 if (stat & (APS_STR3_MWMF)) 199 break; 200 DRIVERSLEEP(1); 201 } 202 203 if (n == 100) { 204 DPRINTF(("aps_do_io: Failed to get bus\n")); 205 return (1); 206 } 207 208 /* write data bytes, init already sent */ 209 /* make sure last bye is always written as this will trigger slave */ 210 wmask |= APS_READ_RET; 211 buf[APS_RET] = 0x01; 212 213 for (n = 1, bp = 2; n < 16; bp <<= 1, n++) { 214 if (wmask & bp) { 215 bus_space_write_1(iot, ioh, APS_TWR_BASE + n, buf[n]); 216 DPRINTF(("aps_do_io: write %2d 0x%02x\n", n, buf[n])); 217 } 218 } 219 220 for (n = 0; n < 100; n++) { 221 stat = bus_space_read_1(iot, ioh, APS_STR3); 222 if (stat & (APS_STR3_OBF3B)) 223 break; 224 DRIVERSLEEP(500); 225 } 226 227 if (n == 100) { 228 DPRINTF(("aps_do_io: timeout waiting response\n")); 229 return (1); 230 } 231 /* wait for data available */ 232 /* make sure to read the final byte to clear status */ 233 rmask |= APS_READ_RET; 234 235 /* read cmd and data bytes */ 236 for (n = 0, bp = 1; n < 16; bp <<= 1, n++) { 237 if (rmask & bp) { 238 buf[n] = bus_space_read_1(iot, ioh, APS_TWR_BASE + n); 239 DPRINTF(("aps_do_io: read %2d 0x%02x\n", n, buf[n])); 240 } 241 } 242 243 return (0); 244 } 245 246 /* for hints, see /sys/bus/isa/isahint.c */ 247 248 static void 249 aps_identify(driver_t *driver, device_t parent) 250 { 251 device_t child; 252 253 child = device_find_child(parent, driver->name, -1); 254 if (child != NULL) { 255 if (isa_get_portsize(child) == 0) { 256 // aps(4) must have been compiled into the kernel 257 if (bootverbose) 258 kprintf("%s: will specify the port\n", 259 __func__); 260 } else if (isa_get_port(child) != APS_ADDR_BASE) 261 kprintf("%s: will overwrite specified port\n", 262 __func__); 263 else { 264 if (isa_get_portsize(child) == APS_ADDR_SIZE) { 265 // aps.ko must have been reloaded 266 kprintf("%s: already have been invoked\n", 267 __func__); 268 return; 269 } else 270 kprintf("%s: will amend the portsize\n", 271 __func__); 272 } 273 } else { 274 // first invocation of `kldload aps.ko` 275 kprintf("%s: creating a new %s\n", 276 __func__, driver->name); 277 child = BUS_ADD_CHILD(parent, parent, ISA_ORDER_PNP, 278 driver->name, -1); 279 if (child == NULL) { 280 kprintf("%s: cannot add child\n", __func__); 281 return; 282 } 283 } 284 if (bus_set_resource(child, SYS_RES_IOPORT, 0, 285 APS_ADDR_BASE, APS_ADDR_SIZE, -1)) 286 kprintf("%s: cannot set resource\n", __func__); 287 } 288 289 static int 290 aps_probe(device_t dev) 291 { 292 struct resource *iores; 293 int iorid = 0; 294 u_int8_t cr; 295 unsigned char iobuf[16]; 296 297 #if defined(APSDEBUG) || defined(KLD_MODULE) 298 device_printf(dev, "%s: port 0x%x\n", __func__, isa_get_port(dev)); 299 #endif 300 301 if (device_get_unit(dev) != 0) 302 return ENXIO; 303 304 iores = bus_alloc_resource_any(dev, SYS_RES_IOPORT, &iorid, RF_ACTIVE); 305 if (iores == NULL) { 306 DPRINTF(("aps: can't map i/o space\n")); 307 return ENXIO; 308 } 309 310 311 /* See if this machine has APS */ 312 313 /* get APS mode */ 314 iobuf[APS_CMD] = 0x13; 315 if (aps_do_io(iores, iobuf, APS_WRITE_0, APS_READ_1)) { 316 bus_release_resource(dev, SYS_RES_IOPORT, iorid, iores); 317 return ENXIO; 318 } 319 320 /* 321 * Observed values from Linux driver: 322 * 0x01: T42 323 * 0x02: chip already initialised 324 * 0x03: T41 325 * 0x05: T61 326 */ 327 328 cr = iobuf[APS_ARG1]; 329 330 bus_release_resource(dev, SYS_RES_IOPORT, iorid, iores); 331 DPRINTF(("aps: state register 0x%x\n", cr)); 332 333 if (iobuf[APS_RET] != 0 || cr < 1 || cr > 5) { 334 DPRINTF(("aps: unsupported state %d\n", cr)); 335 return ENXIO; 336 } 337 device_set_desc(dev, "ThinkPad Active Protection System"); 338 return 0; 339 } 340 341 static int 342 aps_attach(device_t dev) 343 { 344 struct aps_softc *sc = device_get_softc(dev); 345 346 sc->sc_dev = dev; 347 sc->sc_iores = bus_alloc_resource_any(dev, SYS_RES_IOPORT, 348 &sc->sc_iorid, RF_ACTIVE); 349 if (sc->sc_iores == NULL) { 350 device_printf(dev, "can't map i/o space\n"); 351 return ENXIO; 352 } 353 354 if (aps_init(sc->sc_iores)) { 355 device_printf(dev, "failed to initialise\n"); 356 bus_release_resource(dev, SYS_RES_IOPORT, sc->sc_iorid, sc->sc_iores); 357 return ENXIO; 358 } 359 360 sc->sensors[APS_SENSOR_XACCEL].type = SENSOR_INTEGER; 361 ksnprintf(sc->sensors[APS_SENSOR_XACCEL].desc, 362 sizeof(sc->sensors[APS_SENSOR_XACCEL].desc), "X_ACCEL"); 363 364 sc->sensors[APS_SENSOR_YACCEL].type = SENSOR_INTEGER; 365 ksnprintf(sc->sensors[APS_SENSOR_YACCEL].desc, 366 sizeof(sc->sensors[APS_SENSOR_YACCEL].desc), "Y_ACCEL"); 367 368 sc->sensors[APS_SENSOR_TEMP1].type = SENSOR_TEMP; 369 sc->sensors[APS_SENSOR_TEMP2].type = SENSOR_TEMP; 370 371 sc->sensors[APS_SENSOR_XVAR].type = SENSOR_INTEGER; 372 ksnprintf(sc->sensors[APS_SENSOR_XVAR].desc, 373 sizeof(sc->sensors[APS_SENSOR_XVAR].desc), "X_VAR"); 374 375 sc->sensors[APS_SENSOR_YVAR].type = SENSOR_INTEGER; 376 ksnprintf(sc->sensors[APS_SENSOR_YVAR].desc, 377 sizeof(sc->sensors[APS_SENSOR_YVAR].desc), "Y_VAR"); 378 379 sc->sensors[APS_SENSOR_KBACT].type = SENSOR_INDICATOR; 380 ksnprintf(sc->sensors[APS_SENSOR_KBACT].desc, 381 sizeof(sc->sensors[APS_SENSOR_KBACT].desc), "Keyboard Active"); 382 383 sc->sensors[APS_SENSOR_MSACT].type = SENSOR_INDICATOR; 384 ksnprintf(sc->sensors[APS_SENSOR_MSACT].desc, 385 sizeof(sc->sensors[APS_SENSOR_MSACT].desc), "Mouse Active"); 386 387 sc->sensors[APS_SENSOR_LIDOPEN].type = SENSOR_INDICATOR; 388 ksnprintf(sc->sensors[APS_SENSOR_LIDOPEN].desc, 389 sizeof(sc->sensors[APS_SENSOR_LIDOPEN].desc), "Lid Open"); 390 391 /* stop hiding and report to the authorities */ 392 strlcpy(sc->sensordev.xname, device_get_nameunit(dev), 393 sizeof(sc->sensordev.xname)); 394 for (int i = 0; i < APS_NUM_SENSORS ; i++) 395 sensor_attach(&sc->sensordev, &sc->sensors[i]); 396 397 /* Refresh sensor data every 1 second */ 398 /* XXX: a more frequent refresh might be appropriate */ 399 sensor_task_register(sc, aps_refresh, 1); 400 401 sensordev_install(&sc->sensordev); 402 return 0; 403 } 404 405 static int 406 aps_detach(device_t dev) 407 { 408 struct aps_softc *sc = device_get_softc(dev); 409 410 sensordev_deinstall(&sc->sensordev); 411 sensor_task_unregister(sc); 412 return bus_release_resource(dev, SYS_RES_IOPORT, 413 sc->sc_iorid, sc->sc_iores); 414 } 415 416 static int 417 aps_init(struct resource *iores) 418 { 419 unsigned char iobuf[16]; 420 421 /* command 0x17/0x81: check EC */ 422 iobuf[APS_CMD] = 0x17; 423 iobuf[APS_ARG1] = 0x81; 424 425 if (aps_do_io(iores, iobuf, APS_WRITE_1, APS_READ_3)) 426 return (1); 427 428 if (iobuf[APS_RET] != 0 ||iobuf[APS_ARG3] != 0) 429 return (1); 430 431 /* Test values from the Linux driver */ 432 if ((iobuf[APS_ARG1] != 0 || iobuf[APS_ARG2] != 0x60) && 433 (iobuf[APS_ARG1] != 1 || iobuf[APS_ARG2] != 0)) 434 return (1); 435 436 /* command 0x14: set power */ 437 iobuf[APS_CMD] = 0x14; 438 iobuf[APS_ARG1] = 0x01; 439 440 if (aps_do_io(iores, iobuf, APS_WRITE_1, APS_READ_0)) 441 return (1); 442 443 if (iobuf[APS_RET] != 0) 444 return (1); 445 446 /* command 0x10: set config (sample rate and order) */ 447 iobuf[APS_CMD] = 0x10; 448 iobuf[APS_ARG1] = 0xc8; 449 iobuf[APS_ARG2] = 0x00; 450 iobuf[APS_ARG3] = 0x02; 451 452 if (aps_do_io(iores, iobuf, APS_WRITE_3, APS_READ_0)) 453 return (1); 454 455 if (iobuf[APS_RET] != 0) 456 return (1); 457 458 /* command 0x11: refresh data */ 459 iobuf[APS_CMD] = 0x11; 460 if (aps_do_io(iores, iobuf, APS_WRITE_0, APS_READ_1)) 461 return (1); 462 if (iobuf[APS_ARG1] != 0) 463 return (1); 464 465 return (0); 466 } 467 468 static int 469 aps_read_data(struct aps_softc *sc) 470 { 471 unsigned char iobuf[16]; 472 473 /* command 0x11: refresh data */ 474 iobuf[APS_CMD] = 0x11; 475 if (aps_do_io(sc->sc_iores, iobuf, APS_WRITE_0, APS_READ_ALL)) 476 return (1); 477 478 sc->aps_data.state = iobuf[APS_STATE]; 479 sc->aps_data.x_accel = iobuf[APS_XACCEL] + 256 * iobuf[APS_XACCEL + 1]; 480 sc->aps_data.y_accel = iobuf[APS_YACCEL] + 256 * iobuf[APS_YACCEL + 1]; 481 sc->aps_data.temp1 = iobuf[APS_TEMP]; 482 sc->aps_data.x_var = iobuf[APS_XVAR] + 256 * iobuf[APS_XVAR + 1]; 483 sc->aps_data.y_var = iobuf[APS_YVAR] + 256 * iobuf[APS_YVAR + 1]; 484 sc->aps_data.temp2 = iobuf[APS_TEMP2]; 485 sc->aps_data.input = iobuf[APS_INPUT]; 486 487 return (0); 488 } 489 490 static void 491 aps_refresh_sensor_data(struct aps_softc *sc) 492 { 493 int64_t temp; 494 495 if (aps_read_data(sc)) { 496 for (int i = 0; i < APS_NUM_SENSORS; i++) 497 sc->sensors[i].flags |= SENSOR_FINVALID; 498 return; 499 } 500 501 sc->sensors[APS_SENSOR_XACCEL].value = sc->aps_data.x_accel; 502 sc->sensors[APS_SENSOR_YACCEL].value = sc->aps_data.y_accel; 503 504 /* convert to micro (mu) degrees */ 505 temp = sc->aps_data.temp1 * 1000000; 506 /* convert to kelvin */ 507 temp += 273150000; 508 sc->sensors[APS_SENSOR_TEMP1].value = temp; 509 510 /* convert to micro (mu) degrees */ 511 temp = sc->aps_data.temp2 * 1000000; 512 /* convert to kelvin */ 513 temp += 273150000; 514 sc->sensors[APS_SENSOR_TEMP2].value = temp; 515 516 sc->sensors[APS_SENSOR_XVAR].value = sc->aps_data.x_var; 517 sc->sensors[APS_SENSOR_YVAR].value = sc->aps_data.y_var; 518 sc->sensors[APS_SENSOR_KBACT].value = 519 (sc->aps_data.input & APS_INPUT_KB) ? 1 : 0; 520 sc->sensors[APS_SENSOR_MSACT].value = 521 (sc->aps_data.input & APS_INPUT_MS) ? 1 : 0; 522 sc->sensors[APS_SENSOR_LIDOPEN].value = 523 (sc->aps_data.input & APS_INPUT_LIDOPEN) ? 1 : 0; 524 525 for (int i = 0; i < APS_NUM_SENSORS; i++) 526 sc->sensors[i].flags &= ~SENSOR_FINVALID; 527 } 528 529 static void 530 aps_refresh(void *arg) 531 { 532 struct aps_softc *sc = (struct aps_softc *)arg; 533 534 aps_refresh_sensor_data(sc); 535 } 536 537 static int 538 aps_resume(device_t dev) 539 { 540 struct aps_softc *sc = device_get_softc(dev); 541 unsigned char iobuf[16]; 542 543 /* 544 * Redo the init sequence on resume, because APS is 545 * as forgetful as it is deaf. 546 */ 547 548 /* get APS mode */ 549 iobuf[APS_CMD] = 0x13; 550 if (aps_do_io(sc->sc_iores, iobuf, APS_WRITE_0, APS_READ_1) 551 || aps_init(sc->sc_iores)) { 552 device_printf(sc->sc_dev, "failed to wake up\n"); 553 return EIO; 554 } 555 556 sensor_task_register(sc, aps_refresh, 1); 557 return 0; 558 } 559 560 static int 561 aps_suspend(device_t dev) 562 { 563 struct aps_softc *sc = device_get_softc(dev); 564 565 for (int i = 0; i < APS_NUM_SENSORS; i++) 566 sc->sensors[i].flags |= SENSOR_FINVALID; 567 sensor_task_unregister(sc); 568 return 0; 569 } 570