1 /* $NetBSD: aps.c,v 1.5 2007/12/09 20:28:01 jmcneill 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.5 2007/12/09 20:28:01 jmcneill 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 struct device sc_dev; 97 98 bus_space_tag_t sc_iot; 99 bus_space_handle_t sc_ioh; 100 101 struct sysmon_envsys *sc_sme; 102 envsys_data_t sc_sensor[APS_NUM_SENSORS]; 103 struct callout sc_callout; 104 105 struct sensor_rec aps_data; 106 }; 107 108 static int aps_match(struct device *, struct cfdata *, void *); 109 static void aps_attach(struct device *, struct device *, void *); 110 static int aps_detach(struct device *, int); 111 112 static int aps_init(struct aps_softc *); 113 static uint8_t aps_mem_read_1(bus_space_tag_t, bus_space_handle_t, 114 int, uint8_t); 115 static void aps_refresh_sensor_data(struct aps_softc *sc); 116 static void aps_refresh(void *); 117 static bool aps_suspend(device_t); 118 static bool aps_resume(device_t); 119 120 CFATTACH_DECL(aps, sizeof(struct aps_softc), 121 aps_match, aps_attach, aps_detach, NULL); 122 123 int 124 aps_match(struct device *parent, struct cfdata *match, void *aux) 125 { 126 struct isa_attach_args *ia = aux; 127 bus_space_tag_t iot = ia->ia_iot; 128 bus_space_handle_t ioh; 129 int iobase, i; 130 uint8_t cr; 131 132 /* Must supply an address */ 133 if (ia->ia_nio < 1) 134 return 0; 135 136 if (ISA_DIRECT_CONFIG(ia)) 137 return 0; 138 139 if (ia->ia_io[0].ir_addr == ISA_UNKNOWN_PORT) 140 return 0; 141 142 iobase = ia->ia_io[0].ir_addr; 143 144 if (bus_space_map(iot, iobase, APS_ADDR_SIZE, 0, &ioh)) { 145 aprint_error("aps: can't map i/o space\n"); 146 return 0; 147 } 148 149 /* See if this machine has APS */ 150 bus_space_write_1(iot, ioh, APS_INIT, 0x13); 151 bus_space_write_1(iot, ioh, APS_CMD, 0x01); 152 153 /* ask again as the X40 is slightly deaf in one ear */ 154 bus_space_read_1(iot, ioh, APS_CMD); 155 bus_space_write_1(iot, ioh, APS_INIT, 0x13); 156 bus_space_write_1(iot, ioh, APS_CMD, 0x01); 157 158 if (!aps_mem_read_1(iot, ioh, APS_CMD, 0x00)) { 159 bus_space_unmap(iot, ioh, APS_ADDR_SIZE); 160 return 0; 161 } 162 163 /* 164 * Observed values from Linux driver: 165 * 0x01: T42 166 * 0x02: chip already initialised 167 * 0x03: T41 168 */ 169 for (i = 0; i < 10; i++) { 170 cr = bus_space_read_1(iot, ioh, APS_STATE); 171 if (cr > 0 && cr < 6) 172 break; 173 delay(5 * 1000); 174 } 175 176 bus_space_unmap(iot, ioh, APS_ADDR_SIZE); 177 DPRINTF(("aps: state register 0x%x\n", cr)); 178 if (cr < 1 || cr > 5) { 179 DPRINTF(("aps0: unsupported state %d\n", cr)); 180 return 0; 181 } 182 183 ia->ia_nio = 1; 184 ia->ia_io[0].ir_size = APS_ADDR_SIZE; 185 ia->ia_niomem = 0; 186 ia->ia_nirq = 0; 187 ia->ia_ndrq = 0; 188 189 return 1; 190 } 191 192 void 193 aps_attach(struct device *parent, struct device *self, void *aux) 194 { 195 struct aps_softc *sc = (void *)self; 196 struct isa_attach_args *ia = aux; 197 int iobase, i; 198 199 sc->sc_iot = ia->ia_iot; 200 iobase = ia->ia_io[0].ir_addr; 201 202 if (bus_space_map(sc->sc_iot, iobase, APS_ADDR_SIZE, 0, &sc->sc_ioh)) { 203 aprint_error(": can't map i/o space\n"); 204 return; 205 } 206 207 aprint_naive("\n"); 208 aprint_normal("\n"); 209 210 if (!aps_init(sc)) { 211 aprint_error("%s: failed to initialise\n", 212 device_xname(&sc->sc_dev)); 213 return; 214 } 215 216 /* Initialize sensors */ 217 #define INITDATA(idx, unit, string) \ 218 sc->sc_sensor[idx].units = unit; \ 219 snprintf(sc->sc_sensor[idx].desc, sizeof(sc->sc_sensor[idx].desc), \ 220 "%s %s", sc->sc_dev.dv_xname, string); 221 222 INITDATA(APS_SENSOR_XACCEL, ENVSYS_INTEGER, "X_ACCEL"); 223 INITDATA(APS_SENSOR_YACCEL, ENVSYS_INTEGER, "Y_ACCEL"); 224 INITDATA(APS_SENSOR_TEMP1, ENVSYS_STEMP, "TEMP_1"); 225 INITDATA(APS_SENSOR_TEMP2, ENVSYS_STEMP, "TEMP_2"); 226 INITDATA(APS_SENSOR_XVAR, ENVSYS_INTEGER, "X_VAR"); 227 INITDATA(APS_SENSOR_YVAR, ENVSYS_INTEGER, "Y_VAR"); 228 INITDATA(APS_SENSOR_KBACT, ENVSYS_INDICATOR, "Keyboard Active"); 229 INITDATA(APS_SENSOR_MSACT, ENVSYS_INDICATOR, "Mouse Active"); 230 INITDATA(APS_SENSOR_LIDOPEN, ENVSYS_INDICATOR, "Lid Open"); 231 232 sc->sc_sme = sysmon_envsys_create(); 233 for (i = 0; i < APS_NUM_SENSORS; i++) { 234 sc->sc_sensor[i].state = ENVSYS_SVALID; 235 if (sysmon_envsys_sensor_attach(sc->sc_sme, 236 &sc->sc_sensor[i])) { 237 sysmon_envsys_destroy(sc->sc_sme); 238 return; 239 } 240 } 241 /* 242 * Register with the sysmon_envsys(9) framework. 243 */ 244 sc->sc_sme->sme_name = sc->sc_dev.dv_xname; 245 sc->sc_sme->sme_flags |= SME_DISABLE_REFRESH; 246 247 if ((i = sysmon_envsys_register(sc->sc_sme))) { 248 aprint_error("%s: unable to register with sysmon (%d)\n", 249 device_xname(&sc->sc_dev), i); 250 sysmon_envsys_destroy(sc->sc_sme); 251 return; 252 } 253 254 if (!pmf_device_register(self, aps_suspend, aps_resume)) 255 aprint_error_dev(self, "couldn't establish power handler\n"); 256 257 /* Refresh sensor data every 0.5 seconds */ 258 callout_init(&sc->sc_callout, 0); 259 callout_setfunc(&sc->sc_callout, aps_refresh, sc); 260 callout_schedule(&sc->sc_callout, (hz) / 2); 261 262 aprint_normal("%s: Thinkpad Active Protection System\n", 263 device_xname(&sc->sc_dev)); 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(struct device *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 = (struct aps_softc *)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) 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) 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