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