1 /* $NetBSD: aps.c,v 1.10 2010/02/24 22:37:58 dyoung Exp $ */ 2 /* $OpenBSD: aps.c,v 1.15 2007/05/19 19:14:11 tedu Exp $ */ 3 4 /* 5 * Copyright (c) 2005 Jonathan Gray <jsg@openbsd.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/cdefs.h> 26 __KERNEL_RCSID(0, "$NetBSD: aps.c,v 1.10 2010/02/24 22:37:58 dyoung Exp $"); 27 28 #include <sys/param.h> 29 #include <sys/systm.h> 30 #include <sys/device.h> 31 #include <sys/kernel.h> 32 #include <sys/callout.h> 33 34 #include <sys/bus.h> 35 36 #include <dev/sysmon/sysmonvar.h> 37 38 #include <dev/isa/isareg.h> 39 #include <dev/isa/isavar.h> 40 41 #if defined(APSDEBUG) 42 #define DPRINTF(x) do { printf x; } while (0) 43 #else 44 #define DPRINTF(x) 45 #endif 46 47 #define APS_ACCEL_STATE 0x04 48 #define APS_INIT 0x10 49 #define APS_STATE 0x11 50 #define APS_XACCEL 0x12 51 #define APS_YACCEL 0x14 52 #define APS_TEMP 0x16 53 #define APS_XVAR 0x17 54 #define APS_YVAR 0x19 55 #define APS_TEMP2 0x1b 56 #define APS_UNKNOWN 0x1c 57 #define APS_INPUT 0x1d 58 #define APS_CMD 0x1f 59 60 #define APS_STATE_NEWDATA 0x50 61 62 #define APS_CMD_START 0x01 63 64 #define APS_INPUT_KB (1 << 5) 65 #define APS_INPUT_MS (1 << 6) 66 #define APS_INPUT_LIDOPEN (1 << 7) 67 68 #define APS_ADDR_SIZE 0x1f 69 70 struct sensor_rec { 71 uint8_t state; 72 uint16_t x_accel; 73 uint16_t y_accel; 74 uint8_t temp1; 75 uint16_t x_var; 76 uint16_t y_var; 77 uint8_t temp2; 78 uint8_t unk; 79 uint8_t input; 80 }; 81 82 enum aps_sensors { 83 APS_SENSOR_XACCEL = 0, 84 APS_SENSOR_YACCEL, 85 APS_SENSOR_XVAR, 86 APS_SENSOR_YVAR, 87 APS_SENSOR_TEMP1, 88 APS_SENSOR_TEMP2, 89 APS_SENSOR_KBACT, 90 APS_SENSOR_MSACT, 91 APS_SENSOR_LIDOPEN, 92 APS_NUM_SENSORS 93 }; 94 95 struct aps_softc { 96 bus_space_tag_t sc_iot; 97 bus_space_handle_t sc_ioh; 98 99 struct sysmon_envsys *sc_sme; 100 envsys_data_t sc_sensor[APS_NUM_SENSORS]; 101 struct callout sc_callout; 102 103 struct sensor_rec aps_data; 104 }; 105 106 static int aps_match(device_t, cfdata_t, void *); 107 static void aps_attach(device_t, device_t, void *); 108 static int aps_detach(device_t, int); 109 110 static int aps_init(struct aps_softc *); 111 static uint8_t aps_mem_read_1(bus_space_tag_t, bus_space_handle_t, 112 int, uint8_t); 113 static void aps_refresh_sensor_data(struct aps_softc *sc); 114 static void aps_refresh(void *); 115 static bool aps_suspend(device_t, const pmf_qual_t *); 116 static bool aps_resume(device_t, const pmf_qual_t *); 117 118 CFATTACH_DECL_NEW(aps, sizeof(struct aps_softc), 119 aps_match, aps_attach, aps_detach, NULL); 120 121 static int 122 aps_match(device_t parent, cfdata_t match, void *aux) 123 { 124 struct isa_attach_args *ia = aux; 125 bus_space_tag_t iot = ia->ia_iot; 126 bus_space_handle_t ioh; 127 int iobase, i; 128 uint8_t cr; 129 130 /* Must supply an address */ 131 if (ia->ia_nio < 1) 132 return 0; 133 134 if (ISA_DIRECT_CONFIG(ia)) 135 return 0; 136 137 if (ia->ia_io[0].ir_addr == ISA_UNKNOWN_PORT) 138 return 0; 139 140 iobase = ia->ia_io[0].ir_addr; 141 142 if (bus_space_map(iot, iobase, APS_ADDR_SIZE, 0, &ioh)) { 143 aprint_error("aps: can't map i/o space\n"); 144 return 0; 145 } 146 147 /* See if this machine has APS */ 148 bus_space_write_1(iot, ioh, APS_INIT, 0x13); 149 bus_space_write_1(iot, ioh, APS_CMD, 0x01); 150 151 /* ask again as the X40 is slightly deaf in one ear */ 152 bus_space_read_1(iot, ioh, APS_CMD); 153 bus_space_write_1(iot, ioh, APS_INIT, 0x13); 154 bus_space_write_1(iot, ioh, APS_CMD, 0x01); 155 156 if (!aps_mem_read_1(iot, ioh, APS_CMD, 0x00)) { 157 bus_space_unmap(iot, ioh, APS_ADDR_SIZE); 158 return 0; 159 } 160 161 /* 162 * Observed values from Linux driver: 163 * 0x01: T42 164 * 0x02: chip already initialised 165 * 0x03: T41 166 */ 167 for (i = 0; i < 10; i++) { 168 cr = bus_space_read_1(iot, ioh, APS_STATE); 169 if (cr > 0 && cr < 6) 170 break; 171 delay(5 * 1000); 172 } 173 174 bus_space_unmap(iot, ioh, APS_ADDR_SIZE); 175 DPRINTF(("aps: state register 0x%x\n", cr)); 176 if (cr < 1 || cr > 5) { 177 DPRINTF(("aps0: unsupported state %d\n", cr)); 178 return 0; 179 } 180 181 ia->ia_nio = 1; 182 ia->ia_io[0].ir_size = APS_ADDR_SIZE; 183 ia->ia_niomem = 0; 184 ia->ia_nirq = 0; 185 ia->ia_ndrq = 0; 186 187 return 1; 188 } 189 190 static void 191 aps_attach(device_t parent, device_t self, void *aux) 192 { 193 struct aps_softc *sc = device_private(self); 194 struct isa_attach_args *ia = aux; 195 int iobase, i; 196 197 sc->sc_iot = ia->ia_iot; 198 iobase = ia->ia_io[0].ir_addr; 199 200 if (bus_space_map(sc->sc_iot, iobase, APS_ADDR_SIZE, 0, &sc->sc_ioh)) { 201 aprint_error(": can't map i/o space\n"); 202 return; 203 } 204 205 aprint_naive("\n"); 206 aprint_normal("\n"); 207 208 if (!aps_init(sc)) { 209 aprint_error_dev(self, "failed to initialise\n"); 210 goto out; 211 } 212 213 /* Initialize sensors */ 214 #define INITDATA(idx, unit, string) \ 215 sc->sc_sensor[idx].units = unit; \ 216 strlcpy(sc->sc_sensor[idx].desc, string, \ 217 sizeof(sc->sc_sensor[idx].desc)); 218 219 INITDATA(APS_SENSOR_XACCEL, ENVSYS_INTEGER, "X_ACCEL"); 220 INITDATA(APS_SENSOR_YACCEL, ENVSYS_INTEGER, "Y_ACCEL"); 221 INITDATA(APS_SENSOR_TEMP1, ENVSYS_STEMP, "TEMP_1"); 222 INITDATA(APS_SENSOR_TEMP2, ENVSYS_STEMP, "TEMP_2"); 223 INITDATA(APS_SENSOR_XVAR, ENVSYS_INTEGER, "X_VAR"); 224 INITDATA(APS_SENSOR_YVAR, ENVSYS_INTEGER, "Y_VAR"); 225 INITDATA(APS_SENSOR_KBACT, ENVSYS_INDICATOR, "Keyboard Active"); 226 INITDATA(APS_SENSOR_MSACT, ENVSYS_INDICATOR, "Mouse Active"); 227 INITDATA(APS_SENSOR_LIDOPEN, ENVSYS_INDICATOR, "Lid Open"); 228 229 sc->sc_sme = sysmon_envsys_create(); 230 for (i = 0; i < APS_NUM_SENSORS; i++) { 231 sc->sc_sensor[i].state = ENVSYS_SVALID; 232 if (sysmon_envsys_sensor_attach(sc->sc_sme, 233 &sc->sc_sensor[i])) { 234 sysmon_envsys_destroy(sc->sc_sme); 235 goto out; 236 } 237 } 238 /* 239 * Register with the sysmon_envsys(9) framework. 240 */ 241 sc->sc_sme->sme_name = device_xname(self); 242 sc->sc_sme->sme_flags = SME_DISABLE_REFRESH; 243 244 if ((i = sysmon_envsys_register(sc->sc_sme))) { 245 aprint_error_dev(self, 246 "unable to register with sysmon (%d)\n", i); 247 sysmon_envsys_destroy(sc->sc_sme); 248 goto out; 249 } 250 251 if (!pmf_device_register(self, aps_suspend, aps_resume)) 252 aprint_error_dev(self, "couldn't establish power handler\n"); 253 254 /* Refresh sensor data every 0.5 seconds */ 255 callout_init(&sc->sc_callout, 0); 256 callout_setfunc(&sc->sc_callout, aps_refresh, sc); 257 callout_schedule(&sc->sc_callout, (hz) / 2); 258 259 aprint_normal_dev(self, "Thinkpad Active Protection System\n"); 260 return; 261 262 out: 263 bus_space_unmap(sc->sc_iot, sc->sc_ioh, APS_ADDR_SIZE); 264 } 265 266 static int 267 aps_init(struct aps_softc *sc) 268 { 269 bus_space_write_1(sc->sc_iot, sc->sc_ioh, APS_INIT, 0x17); 270 bus_space_write_1(sc->sc_iot, sc->sc_ioh, APS_STATE, 0x81); 271 bus_space_write_1(sc->sc_iot, sc->sc_ioh, APS_CMD, 0x01); 272 if (!aps_mem_read_1(sc->sc_iot, sc->sc_ioh, APS_CMD, 0x00)) 273 return 0; 274 if (!aps_mem_read_1(sc->sc_iot, sc->sc_ioh, APS_STATE, 0x00)) 275 return 0; 276 if (!aps_mem_read_1(sc->sc_iot, sc->sc_ioh, APS_XACCEL, 0x60)) 277 return 0; 278 if (!aps_mem_read_1(sc->sc_iot, sc->sc_ioh, APS_XACCEL + 1, 0x00)) 279 return 0; 280 bus_space_write_1(sc->sc_iot, sc->sc_ioh, APS_INIT, 0x14); 281 bus_space_write_1(sc->sc_iot, sc->sc_ioh, APS_STATE, 0x01); 282 bus_space_write_1(sc->sc_iot, sc->sc_ioh, APS_CMD, 0x01); 283 if (!aps_mem_read_1(sc->sc_iot, sc->sc_ioh, APS_CMD, 0x00)) 284 return 0; 285 bus_space_write_1(sc->sc_iot, sc->sc_ioh, APS_INIT, 0x10); 286 bus_space_write_1(sc->sc_iot, sc->sc_ioh, APS_STATE, 0xc8); 287 bus_space_write_1(sc->sc_iot, sc->sc_ioh, APS_XACCEL, 0x00); 288 bus_space_write_1(sc->sc_iot, sc->sc_ioh, APS_XACCEL + 1, 0x02); 289 bus_space_write_1(sc->sc_iot, sc->sc_ioh, APS_CMD, 0x01); 290 if (!aps_mem_read_1(sc->sc_iot, sc->sc_ioh, APS_CMD, 0x00)) 291 return 0; 292 /* refresh data */ 293 bus_space_write_1(sc->sc_iot, sc->sc_ioh, APS_INIT, 0x11); 294 bus_space_write_1(sc->sc_iot, sc->sc_ioh, APS_CMD, 0x01); 295 if (!aps_mem_read_1(sc->sc_iot, sc->sc_ioh, APS_ACCEL_STATE, 0x50)) 296 return 0; 297 if (!aps_mem_read_1(sc->sc_iot, sc->sc_ioh, APS_STATE, 0x00)) 298 return 0; 299 300 return 1; 301 } 302 303 static int 304 aps_detach(device_t self, int flags) 305 { 306 struct aps_softc *sc = device_private(self); 307 308 callout_stop(&sc->sc_callout); 309 callout_destroy(&sc->sc_callout); 310 sysmon_envsys_unregister(sc->sc_sme); 311 bus_space_unmap(sc->sc_iot, sc->sc_ioh, APS_ADDR_SIZE); 312 313 return 0; 314 } 315 316 static uint8_t 317 aps_mem_read_1(bus_space_tag_t iot, bus_space_handle_t ioh, int reg, 318 uint8_t val) 319 { 320 int i; 321 uint8_t cr; 322 /* should take no longer than 50 microseconds */ 323 for (i = 0; i < 10; i++) { 324 cr = bus_space_read_1(iot, ioh, reg); 325 if (cr == val) 326 return 1; 327 delay(5 * 1000); 328 } 329 330 DPRINTF(("aps: reg 0x%x not val 0x%x!\n", reg, val)); 331 return 0; 332 } 333 334 static void 335 aps_refresh_sensor_data(struct aps_softc *sc) 336 { 337 int64_t temp; 338 339 /* ask for new data */ 340 bus_space_write_1(sc->sc_iot, sc->sc_ioh, APS_INIT, 0x11); 341 bus_space_write_1(sc->sc_iot, sc->sc_ioh, APS_CMD, 0x01); 342 if (!aps_mem_read_1(sc->sc_iot, sc->sc_ioh, APS_ACCEL_STATE, 0x50)) 343 return; 344 345 sc->aps_data.state = 346 bus_space_read_1(sc->sc_iot, sc->sc_ioh, APS_STATE); 347 sc->aps_data.x_accel = 348 bus_space_read_2(sc->sc_iot, sc->sc_ioh, APS_XACCEL); 349 sc->aps_data.y_accel = 350 bus_space_read_2(sc->sc_iot, sc->sc_ioh, APS_YACCEL); 351 sc->aps_data.temp1 = 352 bus_space_read_1(sc->sc_iot, sc->sc_ioh, APS_TEMP); 353 sc->aps_data.x_var = 354 bus_space_read_2(sc->sc_iot, sc->sc_ioh, APS_XVAR); 355 sc->aps_data.y_var = 356 bus_space_read_2(sc->sc_iot, sc->sc_ioh, APS_YVAR); 357 sc->aps_data.temp2 = 358 bus_space_read_1(sc->sc_iot, sc->sc_ioh, APS_TEMP2); 359 sc->aps_data.input = 360 bus_space_read_1(sc->sc_iot, sc->sc_ioh, APS_INPUT); 361 362 bus_space_write_1(sc->sc_iot, sc->sc_ioh, APS_INIT, 0x11); 363 bus_space_write_1(sc->sc_iot, sc->sc_ioh, APS_CMD, 0x01); 364 365 /* tell accelerometer we're done reading from it */ 366 bus_space_read_1(sc->sc_iot, sc->sc_ioh, APS_CMD); 367 bus_space_read_1(sc->sc_iot, sc->sc_ioh, APS_ACCEL_STATE); 368 369 sc->sc_sensor[APS_SENSOR_XACCEL].value_cur = sc->aps_data.x_accel; 370 sc->sc_sensor[APS_SENSOR_YACCEL].value_cur = sc->aps_data.y_accel; 371 372 /* convert to micro (mu) degrees */ 373 temp = sc->aps_data.temp1 * 1000000; 374 /* convert to kelvin */ 375 temp += 273150000; 376 sc->sc_sensor[APS_SENSOR_TEMP1].value_cur = temp; 377 378 /* convert to micro (mu) degrees */ 379 temp = sc->aps_data.temp2 * 1000000; 380 /* convert to kelvin */ 381 temp += 273150000; 382 sc->sc_sensor[APS_SENSOR_TEMP2].value_cur = temp; 383 384 sc->sc_sensor[APS_SENSOR_XVAR].value_cur = sc->aps_data.x_var; 385 sc->sc_sensor[APS_SENSOR_YVAR].value_cur = sc->aps_data.y_var; 386 sc->sc_sensor[APS_SENSOR_KBACT].value_cur = 387 (sc->aps_data.input & APS_INPUT_KB) ? 1 : 0; 388 sc->sc_sensor[APS_SENSOR_MSACT].value_cur = 389 (sc->aps_data.input & APS_INPUT_MS) ? 1 : 0; 390 sc->sc_sensor[APS_SENSOR_LIDOPEN].value_cur = 391 (sc->aps_data.input & APS_INPUT_LIDOPEN) ? 1 : 0; 392 } 393 394 static void 395 aps_refresh(void *arg) 396 { 397 struct aps_softc *sc = arg; 398 399 aps_refresh_sensor_data(sc); 400 callout_schedule(&sc->sc_callout, (hz) / 2); 401 } 402 403 static bool 404 aps_suspend(device_t dv, const pmf_qual_t *qual) 405 { 406 struct aps_softc *sc = device_private(dv); 407 408 callout_stop(&sc->sc_callout); 409 410 return true; 411 } 412 413 static bool 414 aps_resume(device_t dv, const pmf_qual_t *qual) 415 { 416 struct aps_softc *sc = device_private(dv); 417 418 /* 419 * Redo the init sequence on resume, because APS is 420 * as forgetful as it is deaf. 421 */ 422 bus_space_write_1(sc->sc_iot, sc->sc_ioh, APS_INIT, 0x13); 423 bus_space_write_1(sc->sc_iot, sc->sc_ioh, APS_CMD, 0x01); 424 bus_space_read_1(sc->sc_iot, sc->sc_ioh, APS_CMD); 425 bus_space_write_1(sc->sc_iot, sc->sc_ioh, APS_INIT, 0x13); 426 bus_space_write_1(sc->sc_iot, sc->sc_ioh, APS_CMD, 0x01); 427 428 if (aps_mem_read_1(sc->sc_iot, sc->sc_ioh, APS_CMD, 0x00) && 429 aps_init(sc)) 430 callout_schedule(&sc->sc_callout, (hz) / 2); 431 else 432 aprint_error_dev(dv, "failed to wake up\n"); 433 434 return true; 435 } 436