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/sensors.h> 29 30 #include <sys/rman.h> 31 32 #include <bus/isa/isavar.h> 33 34 #if defined(APSDEBUG) 35 #define DPRINTF(x) do { kprintf x; } while (0) 36 #else 37 #define DPRINTF(x) 38 #endif 39 40 41 /* 42 * EC interface on Thinkpad Laptops, from Linux HDAPS driver notes. 43 * From Renesans H8S/2140B Group Hardware Manual 44 * http://documentation.renesas.com/eng/products/mpumcu/rej09b0300_2140bhm.pdf 45 * 46 * EC uses LPC Channel 3 registers TWR0..15 47 */ 48 49 /* STR3 status register */ 50 #define APS_STR3 0x04 51 52 #define APS_STR3_IBF3B 0x80 /* Input buffer full (host->slave) */ 53 #define APS_STR3_OBF3B 0x40 /* Output buffer full (slave->host)*/ 54 #define APS_STR3_MWMF 0x20 /* Master write mode */ 55 #define APS_STR3_SWMF 0x10 /* Slave write mode */ 56 57 58 /* Base address of TWR registers */ 59 #define APS_TWR_BASE 0x10 60 #define APS_TWR_RET 0x1f 61 62 /* TWR registers */ 63 #define APS_CMD 0x00 64 #define APS_ARG1 0x01 65 #define APS_ARG2 0x02 66 #define APS_ARG3 0x03 67 #define APS_RET 0x0f 68 69 /* Sensor values */ 70 #define APS_STATE 0x01 71 #define APS_XACCEL 0x02 72 #define APS_YACCEL 0x04 73 #define APS_TEMP 0x06 74 #define APS_XVAR 0x07 75 #define APS_YVAR 0x09 76 #define APS_TEMP2 0x0b 77 #define APS_UNKNOWN 0x0c 78 #define APS_INPUT 0x0d 79 80 /* write masks for I/O, send command + 0-3 arguments*/ 81 #define APS_WRITE_0 0x0001 82 #define APS_WRITE_1 0x0003 83 #define APS_WRITE_2 0x0007 84 #define APS_WRITE_3 0x000f 85 86 /* read masks for I/O, read 0-3 values (skip command byte) */ 87 #define APS_READ_0 0x0000 88 #define APS_READ_1 0x0002 89 #define APS_READ_2 0x0006 90 #define APS_READ_3 0x000e 91 92 #define APS_READ_RET 0x8000 93 #define APS_READ_ALL 0xffff 94 95 /* Bit definitions for APS_INPUT value */ 96 #define APS_INPUT_KB (1 << 5) 97 #define APS_INPUT_MS (1 << 6) 98 #define APS_INPUT_LIDOPEN (1 << 7) 99 100 #define APS_ADDR_BASE 0x1600 101 #define APS_ADDR_SIZE 0x1f 102 103 struct aps_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 struct resource *sc_iores; 131 int sc_iorid; 132 133 struct ksensor sensors[APS_NUM_SENSORS]; 134 struct ksensordev sensordev; 135 136 struct aps_sensor_rec aps_data; 137 }; 138 139 static int aps_probe(struct device *); 140 static int aps_attach(struct device *); 141 static int aps_detach(struct device *); 142 143 static int aps_resume(struct device *); 144 static int aps_suspend(struct device *); 145 146 static int aps_init(struct resource *); 147 static int aps_read_data(struct aps_softc *); 148 static void aps_refresh_sensor_data(struct aps_softc *); 149 static void aps_refresh(void *); 150 static int aps_do_io(struct resource *, unsigned char *, int, int); 151 152 static device_method_t aps_methods[] = { 153 DEVMETHOD(device_probe, aps_probe), 154 DEVMETHOD(device_attach, aps_attach), 155 DEVMETHOD(device_detach, aps_detach), 156 157 DEVMETHOD(device_resume, aps_resume), 158 DEVMETHOD(device_suspend, aps_suspend), 159 { NULL, NULL } 160 }; 161 162 static driver_t aps_driver = { 163 "aps", 164 aps_methods, 165 sizeof(struct aps_softc) 166 }; 167 168 static devclass_t aps_devclass; 169 170 DRIVER_MODULE(aps, isa, aps_driver, aps_devclass, NULL, NULL); 171 172 173 174 /* properly communicate with the controller, writing a set of memory 175 * locations and reading back another set */ 176 static int 177 aps_do_io(struct resource *iores, unsigned char *buf, int wmask, int rmask) 178 { 179 bus_space_tag_t iot = rman_get_bustag(iores); 180 bus_space_handle_t ioh = rman_get_bushandle(iores); 181 int bp, stat, n; 182 183 DPRINTF(("aps_do_io: CMD: 0x%02x, wmask: 0x%04x, rmask: 0x%04x\n", 184 buf[0], wmask, rmask)); 185 186 /* write init byte using arbitration */ 187 for (n = 0; n < 100; n++) { 188 stat = bus_space_read_1(iot, ioh, APS_STR3); 189 if (stat & (APS_STR3_OBF3B | APS_STR3_SWMF)) { 190 bus_space_read_1(iot, ioh, APS_TWR_RET); 191 continue; 192 } 193 bus_space_write_1(iot, ioh, APS_TWR_BASE, buf[0]); 194 stat = bus_space_read_1(iot, ioh, APS_STR3); 195 if (stat & (APS_STR3_MWMF)) 196 break; 197 /* XXX: OpenBSD has an intended delay of 1 us */ 198 tsleep(iores, 0, __func__, hz / 100); 199 } 200 201 if (n == 100) { 202 DPRINTF(("aps_do_io: Failed to get bus\n")); 203 return (1); 204 } 205 206 /* write data bytes, init already sent */ 207 /* make sure last bye is always written as this will trigger slave */ 208 wmask |= APS_READ_RET; 209 buf[APS_RET] = 0x01; 210 211 for (n = 1, bp = 2; n < 16; bp <<= 1, n++) { 212 if (wmask & bp) { 213 bus_space_write_1(iot, ioh, APS_TWR_BASE + n, buf[n]); 214 DPRINTF(("aps_do_io: write %2d 0x%02x\n", n, buf[n])); 215 } 216 } 217 218 for (n = 0; n < 100; n++) { 219 stat = bus_space_read_1(iot, ioh, APS_STR3); 220 if (stat & (APS_STR3_OBF3B)) 221 break; 222 /* XXX: OpenBSD has an intended delay of 500 us */ 223 tsleep(iores, 0, __func__, hz / 100); 224 } 225 226 if (n == 100) { 227 DPRINTF(("aps_do_io: timeout waiting response\n")); 228 return (1); 229 } 230 /* wait for data available */ 231 /* make sure to read the final byte to clear status */ 232 rmask |= APS_READ_RET; 233 234 /* read cmd and data bytes */ 235 for (n = 0, bp = 1; n < 16; bp <<= 1, n++) { 236 if (rmask & bp) { 237 buf[n] = bus_space_read_1(iot, ioh, APS_TWR_BASE + n); 238 DPRINTF(("aps_do_io: read %2d 0x%02x\n", n, buf[n])); 239 } 240 } 241 242 return (0); 243 } 244 245 static int 246 aps_probe(struct device *dev) 247 { 248 struct resource *iores; 249 int iorid = 0; 250 u_int8_t cr; 251 unsigned char iobuf[16]; 252 253 #if defined(APSDEBUG) || defined(KLD_MODULE) 254 device_printf(dev, "%s: 0x%x\n", __func__, isa_get_port(dev)); 255 #endif 256 257 if (device_get_unit(dev) != 0) 258 return ENXIO; 259 260 #ifdef KLD_MODULE /* XXX: isa modules need more work */ 261 iores = bus_alloc_resource(dev, SYS_RES_IOPORT, &iorid, 262 APS_ADDR_BASE, APS_ADDR_BASE + APS_ADDR_SIZE - 1, APS_ADDR_SIZE, 263 RF_ACTIVE); 264 #else 265 iores = bus_alloc_resource(dev, SYS_RES_IOPORT, &iorid, 266 0ul, ~0ul, APS_ADDR_SIZE, RF_ACTIVE); 267 #endif 268 if (iores == NULL) { 269 DPRINTF(("aps: can't map i/o space\n")); 270 return ENXIO; 271 } 272 273 274 /* See if this machine has APS */ 275 276 /* get APS mode */ 277 iobuf[APS_CMD] = 0x13; 278 if (aps_do_io(iores, iobuf, APS_WRITE_0, APS_READ_1)) { 279 bus_release_resource(dev, SYS_RES_IOPORT, iorid, iores); 280 return ENXIO; 281 } 282 283 /* 284 * Observed values from Linux driver: 285 * 0x01: T42 286 * 0x02: chip already initialised 287 * 0x03: T41 288 * 0x05: T61 289 */ 290 291 cr = iobuf[APS_ARG1]; 292 293 bus_release_resource(dev, SYS_RES_IOPORT, iorid, iores); 294 DPRINTF(("aps: state register 0x%x\n", cr)); 295 296 if (iobuf[APS_RET] != 0 || cr < 1 || cr > 5) { 297 DPRINTF(("aps: unsupported state %d\n", cr)); 298 return ENXIO; 299 } 300 device_set_desc(dev, "ThinkPad Active Protection System"); 301 return 0; 302 } 303 304 static int 305 aps_attach(struct device *dev) 306 { 307 struct aps_softc *sc; 308 309 sc = device_get_softc(dev); 310 sc->sc_dev = dev; 311 312 #ifdef KLD_MODULE /* XXX: isa modules need more work */ 313 device_printf(dev, "%s: 0x%x\n", __func__, isa_get_port(dev)); 314 sc->sc_iores = bus_alloc_resource(dev, SYS_RES_IOPORT, &sc->sc_iorid, 315 APS_ADDR_BASE, APS_ADDR_BASE + APS_ADDR_SIZE - 1, APS_ADDR_SIZE, 316 RF_ACTIVE); 317 #else 318 sc->sc_iores = bus_alloc_resource(dev, SYS_RES_IOPORT, &sc->sc_iorid, 319 0ul, ~0ul, APS_ADDR_SIZE, RF_ACTIVE); 320 #endif 321 if (sc->sc_iores == NULL) { 322 device_printf(dev, "can't map i/o space\n"); 323 return ENXIO; 324 } 325 326 if (aps_init(sc->sc_iores)) { 327 device_printf(dev, "failed to initialise\n"); 328 bus_release_resource(dev, SYS_RES_IOPORT, sc->sc_iorid, sc->sc_iores); 329 return ENXIO; 330 } 331 332 sc->sensors[APS_SENSOR_XACCEL].type = SENSOR_INTEGER; 333 ksnprintf(sc->sensors[APS_SENSOR_XACCEL].desc, 334 sizeof(sc->sensors[APS_SENSOR_XACCEL].desc), "X_ACCEL"); 335 336 sc->sensors[APS_SENSOR_YACCEL].type = SENSOR_INTEGER; 337 ksnprintf(sc->sensors[APS_SENSOR_YACCEL].desc, 338 sizeof(sc->sensors[APS_SENSOR_YACCEL].desc), "Y_ACCEL"); 339 340 sc->sensors[APS_SENSOR_TEMP1].type = SENSOR_TEMP; 341 sc->sensors[APS_SENSOR_TEMP2].type = SENSOR_TEMP; 342 343 sc->sensors[APS_SENSOR_XVAR].type = SENSOR_INTEGER; 344 ksnprintf(sc->sensors[APS_SENSOR_XVAR].desc, 345 sizeof(sc->sensors[APS_SENSOR_XVAR].desc), "X_VAR"); 346 347 sc->sensors[APS_SENSOR_YVAR].type = SENSOR_INTEGER; 348 ksnprintf(sc->sensors[APS_SENSOR_YVAR].desc, 349 sizeof(sc->sensors[APS_SENSOR_YVAR].desc), "Y_VAR"); 350 351 sc->sensors[APS_SENSOR_KBACT].type = SENSOR_INDICATOR; 352 ksnprintf(sc->sensors[APS_SENSOR_KBACT].desc, 353 sizeof(sc->sensors[APS_SENSOR_KBACT].desc), "Keyboard Active"); 354 355 sc->sensors[APS_SENSOR_MSACT].type = SENSOR_INDICATOR; 356 ksnprintf(sc->sensors[APS_SENSOR_MSACT].desc, 357 sizeof(sc->sensors[APS_SENSOR_MSACT].desc), "Mouse Active"); 358 359 sc->sensors[APS_SENSOR_LIDOPEN].type = SENSOR_INDICATOR; 360 ksnprintf(sc->sensors[APS_SENSOR_LIDOPEN].desc, 361 sizeof(sc->sensors[APS_SENSOR_LIDOPEN].desc), "Lid Open"); 362 363 /* stop hiding and report to the authorities */ 364 strlcpy(sc->sensordev.xname, device_get_nameunit(dev), 365 sizeof(sc->sensordev.xname)); 366 for (int i = 0; i < APS_NUM_SENSORS ; i++) 367 sensor_attach(&sc->sensordev, &sc->sensors[i]); 368 369 /* Refresh sensor data every 1 second */ 370 /* XXX: a more frequent refresh might be appropriate */ 371 if (sensor_task_register(sc, aps_refresh, 1)) { 372 device_printf(dev, "unable to register update task\n"); 373 bus_release_resource(dev, SYS_RES_IOPORT, sc->sc_iorid, sc->sc_iores); 374 return ENXIO; 375 } 376 377 sensordev_install(&sc->sensordev); 378 return 0; 379 } 380 381 static int 382 aps_detach(struct device *dev) 383 { 384 struct aps_softc *sc = device_get_softc(dev); 385 386 sensordev_deinstall(&sc->sensordev); 387 sensor_task_unregister(sc); 388 return bus_release_resource(dev, SYS_RES_IOPORT, 389 sc->sc_iorid, sc->sc_iores); 390 } 391 392 static int 393 aps_init(struct resource *iores) 394 { 395 unsigned char iobuf[16]; 396 397 /* command 0x17/0x81: check EC */ 398 iobuf[APS_CMD] = 0x17; 399 iobuf[APS_ARG1] = 0x81; 400 401 if (aps_do_io(iores, iobuf, APS_WRITE_1, APS_READ_3)) 402 return (1); 403 404 if (iobuf[APS_RET] != 0 ||iobuf[APS_ARG3] != 0) 405 return (1); 406 407 /* Test values from the Linux driver */ 408 if ((iobuf[APS_ARG1] != 0 || iobuf[APS_ARG2] != 0x60) && 409 (iobuf[APS_ARG1] != 1 || iobuf[APS_ARG2] != 0)) 410 return (1); 411 412 /* command 0x14: set power */ 413 iobuf[APS_CMD] = 0x14; 414 iobuf[APS_ARG1] = 0x01; 415 416 if (aps_do_io(iores, iobuf, APS_WRITE_1, APS_READ_0)) 417 return (1); 418 419 if (iobuf[APS_RET] != 0) 420 return (1); 421 422 /* command 0x10: set config (sample rate and order) */ 423 iobuf[APS_CMD] = 0x10; 424 iobuf[APS_ARG1] = 0xc8; 425 iobuf[APS_ARG2] = 0x00; 426 iobuf[APS_ARG3] = 0x02; 427 428 if (aps_do_io(iores, iobuf, APS_WRITE_3, APS_READ_0)) 429 return (1); 430 431 if (iobuf[APS_RET] != 0) 432 return (1); 433 434 /* command 0x11: refresh data */ 435 iobuf[APS_CMD] = 0x11; 436 if (aps_do_io(iores, iobuf, APS_WRITE_0, APS_READ_1)) 437 return (1); 438 if (iobuf[APS_ARG1] != 0) 439 return (1); 440 441 return (0); 442 } 443 444 static int 445 aps_read_data(struct aps_softc *sc) 446 { 447 unsigned char iobuf[16]; 448 449 /* command 0x11: refresh data */ 450 iobuf[APS_CMD] = 0x11; 451 if (aps_do_io(sc->sc_iores, iobuf, APS_WRITE_0, APS_READ_ALL)) 452 return (1); 453 454 sc->aps_data.state = iobuf[APS_STATE]; 455 sc->aps_data.x_accel = iobuf[APS_XACCEL] + 256 * iobuf[APS_XACCEL + 1]; 456 sc->aps_data.y_accel = iobuf[APS_YACCEL] + 256 * iobuf[APS_YACCEL + 1]; 457 sc->aps_data.temp1 = iobuf[APS_TEMP]; 458 sc->aps_data.x_var = iobuf[APS_XVAR] + 256 * iobuf[APS_XVAR + 1]; 459 sc->aps_data.y_var = iobuf[APS_YVAR] + 256 * iobuf[APS_YVAR + 1]; 460 sc->aps_data.temp2 = iobuf[APS_TEMP2]; 461 sc->aps_data.input = iobuf[APS_INPUT]; 462 463 return (0); 464 } 465 466 static void 467 aps_refresh_sensor_data(struct aps_softc *sc) 468 { 469 int64_t temp; 470 471 if (aps_read_data(sc)) { 472 for (int i = 0; i < APS_NUM_SENSORS; i++) 473 sc->sensors[i].flags |= SENSOR_FINVALID; 474 return; 475 } 476 477 sc->sensors[APS_SENSOR_XACCEL].value = sc->aps_data.x_accel; 478 sc->sensors[APS_SENSOR_YACCEL].value = sc->aps_data.y_accel; 479 480 /* convert to micro (mu) degrees */ 481 temp = sc->aps_data.temp1 * 1000000; 482 /* convert to kelvin */ 483 temp += 273150000; 484 sc->sensors[APS_SENSOR_TEMP1].value = temp; 485 486 /* convert to micro (mu) degrees */ 487 temp = sc->aps_data.temp2 * 1000000; 488 /* convert to kelvin */ 489 temp += 273150000; 490 sc->sensors[APS_SENSOR_TEMP2].value = temp; 491 492 sc->sensors[APS_SENSOR_XVAR].value = sc->aps_data.x_var; 493 sc->sensors[APS_SENSOR_YVAR].value = sc->aps_data.y_var; 494 sc->sensors[APS_SENSOR_KBACT].value = 495 (sc->aps_data.input & APS_INPUT_KB) ? 1 : 0; 496 sc->sensors[APS_SENSOR_MSACT].value = 497 (sc->aps_data.input & APS_INPUT_MS) ? 1 : 0; 498 sc->sensors[APS_SENSOR_LIDOPEN].value = 499 (sc->aps_data.input & APS_INPUT_LIDOPEN) ? 1 : 0; 500 501 for (int i = 0; i < APS_NUM_SENSORS; i++) 502 sc->sensors[i].flags &= ~SENSOR_FINVALID; 503 } 504 505 static void 506 aps_refresh(void *arg) 507 { 508 struct aps_softc *sc = (struct aps_softc *)arg; 509 510 aps_refresh_sensor_data(sc); 511 } 512 513 static int 514 aps_resume(struct device *dev) 515 { 516 struct aps_softc *sc = device_get_softc(dev); 517 unsigned char iobuf[16]; 518 519 /* 520 * Redo the init sequence on resume, because APS is 521 * as forgetful as it is deaf. 522 */ 523 524 /* get APS mode */ 525 iobuf[APS_CMD] = 0x13; 526 if (aps_do_io(sc->sc_iores, iobuf, APS_WRITE_0, APS_READ_1) 527 || aps_init(sc->sc_iores)) { 528 device_printf(sc->sc_dev, "failed to wake up\n"); 529 return EIO; 530 } 531 532 sensor_task_register(sc, aps_refresh, 1); 533 return 0; 534 } 535 536 static int 537 aps_suspend(struct device *dev) 538 { 539 struct aps_softc *sc = device_get_softc(dev); 540 541 for (int i = 0; i < APS_NUM_SENSORS; i++) 542 sc->sensors[i].flags |= SENSOR_FINVALID; 543 sensor_task_unregister(sc); 544 return 0; 545 } 546