1 /*- 2 * Copyright (c) 2007 Rui Paulo <rpaulo@FreeBSD.org> 3 * All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions 7 * are met: 8 * 1. Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * 2. Redistributions in binary form must reproduce the above copyright 11 * notice, this list of conditions and the following disclaimer in the 12 * documentation and/or other materials provided with the distribution. 13 * 14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 15 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 16 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 17 * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, 18 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 19 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 20 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, 22 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN 23 * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 24 * POSSIBILITY OF SUCH DAMAGE. 25 * 26 */ 27 28 /* 29 * Driver for Apple's System Management Console (SMC). 30 * SMC can be found on the MacBook, MacBook Pro and Mac Mini. 31 * 32 * Inspired by the Linux applesmc driver. 33 */ 34 35 #include <sys/cdefs.h> 36 __FBSDID("$FreeBSD$"); 37 38 #include <sys/param.h> 39 #include <sys/bus.h> 40 #include <sys/conf.h> 41 #include <sys/kernel.h> 42 #include <sys/lock.h> 43 #include <sys/malloc.h> 44 #include <sys/module.h> 45 #include <sys/mutex.h> 46 #include <sys/sysctl.h> 47 #include <sys/systm.h> 48 #include <sys/taskqueue.h> 49 #include <isa/isavar.h> 50 #include <machine/bus.h> 51 #include <sys/rman.h> 52 #include <machine/resource.h> 53 54 #include <dev/asmc/asmcvar.h> 55 56 /* 57 * Device interface. 58 */ 59 static void asmc_identify(driver_t *driver, device_t parent); 60 static int asmc_probe(device_t dev); 61 static int asmc_attach(device_t dev); 62 static int asmc_detach(device_t dev); 63 64 /* 65 * SMC functions. 66 */ 67 static int asmc_init(device_t dev); 68 static int asmc_wait(device_t dev, uint8_t val); 69 static int asmc_key_write(device_t dev, const char *key, uint8_t *buf, 70 uint8_t len); 71 static int asmc_key_read(device_t dev, const char *key, uint8_t *buf, 72 uint8_t); 73 static int asmc_fan_count(device_t dev); 74 static int asmc_fan_getvalue(device_t dev, const char *key, int fan); 75 static int asmc_temp_getvalue(device_t dev, const char *key); 76 static int asmc_sms_read(device_t, const char *key, int16_t *val); 77 static void asmc_sms_calibrate(device_t dev); 78 static int asmc_sms_intrfast(void *arg); 79 #ifdef INTR_FILTER 80 static void asmc_sms_handler(void *arg); 81 #endif 82 static void asmc_sms_printintr(device_t dev, uint8_t); 83 static void asmc_sms_task(void *arg, int pending); 84 85 /* 86 * Model functions. 87 */ 88 static int asmc_mb_sysctl_fanspeed(SYSCTL_HANDLER_ARGS); 89 static int asmc_mb_sysctl_fansafespeed(SYSCTL_HANDLER_ARGS); 90 static int asmc_mb_sysctl_fanminspeed(SYSCTL_HANDLER_ARGS); 91 static int asmc_mb_sysctl_fanmaxspeed(SYSCTL_HANDLER_ARGS); 92 static int asmc_mb_sysctl_fantargetspeed(SYSCTL_HANDLER_ARGS); 93 static int asmc_temp_sysctl(SYSCTL_HANDLER_ARGS); 94 static int asmc_mb_sysctl_sms_x(SYSCTL_HANDLER_ARGS); 95 static int asmc_mb_sysctl_sms_y(SYSCTL_HANDLER_ARGS); 96 static int asmc_mb_sysctl_sms_z(SYSCTL_HANDLER_ARGS); 97 static int asmc_mbp_sysctl_light_left(SYSCTL_HANDLER_ARGS); 98 static int asmc_mbp_sysctl_light_right(SYSCTL_HANDLER_ARGS); 99 100 struct asmc_model { 101 const char *smc_model; /* smbios.system.product env var. */ 102 const char *smc_desc; /* driver description */ 103 104 /* Helper functions */ 105 int (*smc_sms_x)(SYSCTL_HANDLER_ARGS); 106 int (*smc_sms_y)(SYSCTL_HANDLER_ARGS); 107 int (*smc_sms_z)(SYSCTL_HANDLER_ARGS); 108 int (*smc_fan_speed)(SYSCTL_HANDLER_ARGS); 109 int (*smc_fan_safespeed)(SYSCTL_HANDLER_ARGS); 110 int (*smc_fan_minspeed)(SYSCTL_HANDLER_ARGS); 111 int (*smc_fan_maxspeed)(SYSCTL_HANDLER_ARGS); 112 int (*smc_fan_targetspeed)(SYSCTL_HANDLER_ARGS); 113 int (*smc_light_left)(SYSCTL_HANDLER_ARGS); 114 int (*smc_light_right)(SYSCTL_HANDLER_ARGS); 115 116 const char *smc_temps[8]; 117 const char *smc_tempnames[8]; 118 const char *smc_tempdescs[8]; 119 }; 120 121 static struct asmc_model *asmc_match(device_t dev); 122 123 #define ASMC_SMS_FUNCS asmc_mb_sysctl_sms_x, asmc_mb_sysctl_sms_y, \ 124 asmc_mb_sysctl_sms_z 125 126 #define ASMC_FAN_FUNCS asmc_mb_sysctl_fanspeed, asmc_mb_sysctl_fansafespeed, \ 127 asmc_mb_sysctl_fanminspeed, \ 128 asmc_mb_sysctl_fanmaxspeed, \ 129 asmc_mb_sysctl_fantargetspeed 130 #define ASMC_LIGHT_FUNCS asmc_mbp_sysctl_light_left, \ 131 asmc_mbp_sysctl_light_right 132 133 struct asmc_model asmc_models[] = { 134 { 135 "MacBook1,1", "Apple SMC MacBook Core Duo", 136 ASMC_SMS_FUNCS, ASMC_FAN_FUNCS, NULL, NULL, 137 ASMC_MB_TEMPS, ASMC_MB_TEMPNAMES, ASMC_MB_TEMPDESCS 138 }, 139 140 { 141 "MacBook2,1", "Apple SMC MacBook Core 2 Duo", 142 ASMC_SMS_FUNCS, ASMC_FAN_FUNCS, NULL, NULL, 143 ASMC_MB_TEMPS, ASMC_MB_TEMPNAMES, ASMC_MB_TEMPDESCS 144 }, 145 146 { 147 "MacBookPro1,1", "Apple SMC MacBook Pro Core Duo (15-inch)", 148 ASMC_SMS_FUNCS, ASMC_FAN_FUNCS, ASMC_LIGHT_FUNCS, 149 ASMC_MBP_TEMPS, ASMC_MBP_TEMPNAMES, ASMC_MBP_TEMPDESCS 150 }, 151 152 { 153 "MacBookPro1,2", "Apple SMC MacBook Pro Core Duo (17-inch)", 154 ASMC_SMS_FUNCS, ASMC_FAN_FUNCS, ASMC_LIGHT_FUNCS, 155 ASMC_MBP_TEMPS, ASMC_MBP_TEMPNAMES, ASMC_MBP_TEMPDESCS 156 }, 157 158 { 159 "MacBookPro2,1", "Apple SMC MacBook Pro Core 2 Duo (17-inch)", 160 ASMC_SMS_FUNCS, ASMC_FAN_FUNCS, ASMC_LIGHT_FUNCS, 161 ASMC_MBP_TEMPS, ASMC_MBP_TEMPNAMES, ASMC_MBP_TEMPDESCS 162 }, 163 164 { 165 "MacBookPro2,2", "Apple SMC MacBook Pro Core 2 Duo (15-inch)", 166 ASMC_SMS_FUNCS, ASMC_FAN_FUNCS, ASMC_LIGHT_FUNCS, 167 ASMC_MBP_TEMPS, ASMC_MBP_TEMPNAMES, ASMC_MBP_TEMPDESCS 168 }, 169 170 { 171 "MacBookPro3,1", "Apple SMC MacBook Pro Core 2 Duo (15-inch LED)", 172 ASMC_SMS_FUNCS, ASMC_FAN_FUNCS, ASMC_LIGHT_FUNCS, 173 ASMC_MBP_TEMPS, ASMC_MBP_TEMPNAMES, ASMC_MBP_TEMPDESCS 174 }, 175 176 { 177 "MacBookPro3,2", "Apple SMC MacBook Pro Core 2 Duo (17-inch HD)", 178 ASMC_SMS_FUNCS, ASMC_FAN_FUNCS, ASMC_LIGHT_FUNCS, 179 ASMC_MBP_TEMPS, ASMC_MBP_TEMPNAMES, ASMC_MBP_TEMPDESCS 180 }, 181 182 /* The Mac Mini has no SMS */ 183 { 184 "Macmini1,1", "Apple SMC Mac Mini", 185 NULL, NULL, NULL, 186 ASMC_FAN_FUNCS, 187 NULL, NULL, 188 ASMC_MM_TEMPS, ASMC_MM_TEMPNAMES, ASMC_MM_TEMPDESCS 189 }, 190 191 { NULL, NULL } 192 }; 193 194 #undef ASMC_SMS_FUNCS 195 #undef ASMC_FAN_FUNCS 196 #undef ASMC_LIGHT_FUNCS 197 198 /* 199 * Driver methods. 200 */ 201 static device_method_t asmc_methods[] = { 202 DEVMETHOD(device_identify, asmc_identify), 203 DEVMETHOD(device_probe, asmc_probe), 204 DEVMETHOD(device_attach, asmc_attach), 205 DEVMETHOD(device_detach, asmc_detach), 206 207 { 0, 0 } 208 }; 209 210 static driver_t asmc_driver = { 211 "asmc", 212 asmc_methods, 213 sizeof(struct asmc_softc) 214 }; 215 216 static devclass_t asmc_devclass; 217 218 DRIVER_MODULE(asmc, isa, asmc_driver, asmc_devclass, NULL, NULL); 219 220 static void 221 asmc_identify(driver_t *driver, device_t parent) 222 { 223 if (device_find_child(parent, "asmc", -1) == NULL && 224 asmc_match(parent)) 225 BUS_ADD_CHILD(parent, 0, "asmc", -1); 226 } 227 228 static struct asmc_model * 229 asmc_match(device_t dev) 230 { 231 int i; 232 char *model; 233 234 model = getenv("smbios.system.product"); 235 for (i = 0; asmc_models[i].smc_model; i++) { 236 if (!strncmp(model, asmc_models[i].smc_model, strlen(model))) { 237 freeenv(model); 238 return (&asmc_models[i]); 239 } 240 } 241 freeenv(model); 242 243 return (NULL); 244 } 245 246 static int 247 asmc_probe(device_t dev) 248 { 249 struct asmc_model *model; 250 251 if (resource_disabled("asmc", 0)) 252 return (ENXIO); 253 model = asmc_match(dev); 254 if (!model) 255 return (ENXIO); 256 if (isa_get_irq(dev) == -1) 257 bus_set_resource(dev, SYS_RES_IRQ, 0, ASMC_IRQ, 1); 258 device_set_desc(dev, model->smc_desc); 259 260 return (BUS_PROBE_DEFAULT); 261 } 262 263 static int 264 asmc_attach(device_t dev) 265 { 266 int i, j; 267 int ret; 268 char name[2]; 269 struct asmc_softc *sc = device_get_softc(dev); 270 struct sysctl_ctx_list *sysctlctx; 271 struct sysctl_oid *sysctlnode; 272 struct asmc_model *model; 273 274 sysctlctx = device_get_sysctl_ctx(dev); 275 sysctlnode = device_get_sysctl_tree(dev); 276 277 model = asmc_match(dev); 278 279 mtx_init(&sc->sc_mtx, "asmc", NULL, MTX_SPIN); 280 281 sc->sc_model = model; 282 asmc_init(dev); 283 284 /* 285 * dev.asmc.n.fan.* tree. 286 */ 287 sc->sc_fan_tree[0] = SYSCTL_ADD_NODE(sysctlctx, 288 SYSCTL_CHILDREN(sysctlnode), OID_AUTO, "fan", 289 CTLFLAG_RD, 0, "Fan Root Tree"); 290 291 for (i = 1; i <= sc->sc_nfan; i++) { 292 j = i - 1; 293 name[0] = '0' + j; 294 name[1] = 0; 295 sc->sc_fan_tree[i] = SYSCTL_ADD_NODE(sysctlctx, 296 SYSCTL_CHILDREN(sc->sc_fan_tree[0]), 297 OID_AUTO, name, CTLFLAG_RD, 0, 298 "Fan Subtree"); 299 300 SYSCTL_ADD_PROC(sysctlctx, 301 SYSCTL_CHILDREN(sc->sc_fan_tree[i]), 302 OID_AUTO, "speed", CTLTYPE_INT | CTLFLAG_RD, 303 dev, j, model->smc_fan_speed, "I", 304 "Fan speed in RPM"); 305 306 SYSCTL_ADD_PROC(sysctlctx, 307 SYSCTL_CHILDREN(sc->sc_fan_tree[i]), 308 OID_AUTO, "safespeed", 309 CTLTYPE_INT | CTLFLAG_RD, 310 dev, j, model->smc_fan_safespeed, "I", 311 "Fan safe speed in RPM"); 312 313 SYSCTL_ADD_PROC(sysctlctx, 314 SYSCTL_CHILDREN(sc->sc_fan_tree[i]), 315 OID_AUTO, "minspeed", 316 CTLTYPE_INT | CTLFLAG_RD, 317 dev, j, model->smc_fan_minspeed, "I", 318 "Fan minimum speed in RPM"); 319 320 SYSCTL_ADD_PROC(sysctlctx, 321 SYSCTL_CHILDREN(sc->sc_fan_tree[i]), 322 OID_AUTO, "maxspeed", 323 CTLTYPE_INT | CTLFLAG_RD, 324 dev, j, model->smc_fan_maxspeed, "I", 325 "Fan maximum speed in RPM"); 326 327 SYSCTL_ADD_PROC(sysctlctx, 328 SYSCTL_CHILDREN(sc->sc_fan_tree[i]), 329 OID_AUTO, "targetspeed", 330 CTLTYPE_INT | CTLFLAG_RD, 331 dev, j, model->smc_fan_targetspeed, "I", 332 "Fan target speed in RPM"); 333 } 334 335 /* 336 * dev.asmc.n.temp tree. 337 */ 338 sc->sc_temp_tree = SYSCTL_ADD_NODE(sysctlctx, 339 SYSCTL_CHILDREN(sysctlnode), OID_AUTO, "temp", 340 CTLFLAG_RD, 0, "Temperature sensors"); 341 342 for (i = 0; model->smc_temps[i]; i++) { 343 SYSCTL_ADD_PROC(sysctlctx, 344 SYSCTL_CHILDREN(sc->sc_temp_tree), 345 OID_AUTO, model->smc_tempnames[i], 346 CTLTYPE_INT | CTLFLAG_RD, 347 dev, i, asmc_temp_sysctl, "I", 348 model->smc_tempdescs[i]); 349 } 350 351 if (model->smc_sms_x == NULL) 352 goto nosms; 353 354 /* 355 * dev.asmc.n.sms tree. 356 */ 357 sc->sc_sms_tree = SYSCTL_ADD_NODE(sysctlctx, 358 SYSCTL_CHILDREN(sysctlnode), OID_AUTO, "sms", 359 CTLFLAG_RD, 0, "Sudden Motion Sensor"); 360 361 SYSCTL_ADD_PROC(sysctlctx, 362 SYSCTL_CHILDREN(sc->sc_sms_tree), 363 OID_AUTO, "x", CTLTYPE_INT | CTLFLAG_RD, 364 dev, 0, model->smc_sms_x, "I", 365 "Sudden Motion Sensor X value"); 366 367 SYSCTL_ADD_PROC(sysctlctx, 368 SYSCTL_CHILDREN(sc->sc_sms_tree), 369 OID_AUTO, "y", CTLTYPE_INT | CTLFLAG_RD, 370 dev, 0, model->smc_sms_y, "I", 371 "Sudden Motion Sensor Y value"); 372 373 SYSCTL_ADD_PROC(sysctlctx, 374 SYSCTL_CHILDREN(sc->sc_sms_tree), 375 OID_AUTO, "z", CTLTYPE_INT | CTLFLAG_RD, 376 dev, 0, model->smc_sms_z, "I", 377 "Sudden Motion Sensor Z value"); 378 379 /* 380 * dev.asmc.n.light 381 */ 382 if (model->smc_light_left) { 383 sc->sc_light_tree = SYSCTL_ADD_NODE(sysctlctx, 384 SYSCTL_CHILDREN(sysctlnode), OID_AUTO, "light", 385 CTLFLAG_RD, 0, "Keyboard backlight sensors"); 386 387 SYSCTL_ADD_PROC(sysctlctx, 388 SYSCTL_CHILDREN(sc->sc_light_tree), 389 OID_AUTO, "left", CTLTYPE_INT | CTLFLAG_RW, 390 dev, 0, model->smc_light_left, "I", 391 "Keyboard backlight left sensor"); 392 393 SYSCTL_ADD_PROC(sysctlctx, 394 SYSCTL_CHILDREN(sc->sc_light_tree), 395 OID_AUTO, "right", CTLTYPE_INT | CTLFLAG_RW, 396 dev, 0, model->smc_light_right, "I", 397 "Keyboard backlight right sensor"); 398 } 399 400 /* 401 * Need a taskqueue to send devctl_notify() events 402 * when the SMS interrupt us. 403 * 404 * PI_REALTIME is used due to the sensitivity of the 405 * interrupt. An interrupt from the SMS means that the 406 * disk heads should be turned off as quickly as possible. 407 * 408 * We only need to do this for the non INTR_FILTER case. 409 */ 410 sc->sc_sms_tq = NULL; 411 #ifndef INTR_FILTER 412 TASK_INIT(&sc->sc_sms_task, 0, asmc_sms_task, sc); 413 sc->sc_sms_tq = taskqueue_create_fast("asmc_taskq", M_WAITOK, 414 taskqueue_thread_enqueue, &sc->sc_sms_tq); 415 taskqueue_start_threads(&sc->sc_sms_tq, 1, PI_REALTIME, "%s sms taskq", 416 device_get_nameunit(dev)); 417 #endif 418 /* 419 * Allocate an IRQ for the SMS. 420 */ 421 sc->sc_rid = 0; 422 sc->sc_res = bus_alloc_resource(dev, SYS_RES_IRQ, &sc->sc_rid, 423 ASMC_IRQ, ASMC_IRQ, 1, RF_ACTIVE); 424 if (sc->sc_res == NULL) { 425 device_printf(dev, "unable to allocate IRQ resource\n"); 426 ret = ENXIO; 427 goto err2; 428 } 429 430 ret = bus_setup_intr(dev, sc->sc_res, 431 INTR_TYPE_MISC | INTR_MPSAFE, 432 #ifdef INTR_FILTER 433 asmc_sms_intrfast, asmc_sms_handler, 434 #else 435 asmc_sms_intrfast, NULL, 436 #endif 437 dev, &sc->sc_cookie); 438 439 if (ret) { 440 device_printf(dev, "unable to setup SMS IRQ\n"); 441 goto err1; 442 } 443 nosms: 444 return (0); 445 err1: 446 bus_release_resource(dev, SYS_RES_IRQ, sc->sc_rid, sc->sc_res); 447 err2: 448 mtx_destroy(&sc->sc_mtx); 449 if (sc->sc_sms_tq) 450 taskqueue_free(sc->sc_sms_tq); 451 452 return (ret); 453 } 454 455 static int 456 asmc_detach(device_t dev) 457 { 458 struct asmc_softc *sc = device_get_softc(dev); 459 460 if (sc->sc_sms_tq) { 461 taskqueue_drain(sc->sc_sms_tq, &sc->sc_sms_task); 462 taskqueue_free(sc->sc_sms_tq); 463 } 464 if (sc->sc_cookie) 465 bus_teardown_intr(dev, sc->sc_res, sc->sc_cookie); 466 if (sc->sc_res) 467 bus_release_resource(dev, SYS_RES_IRQ, sc->sc_rid, sc->sc_res); 468 mtx_destroy(&sc->sc_mtx); 469 470 return (0); 471 } 472 473 static int 474 asmc_init(device_t dev) 475 { 476 struct asmc_softc *sc = device_get_softc(dev); 477 int i, error = 1; 478 uint8_t buf[4]; 479 480 if (sc->sc_model->smc_sms_x == NULL) 481 goto nosms; 482 483 /* 484 * We are ready to recieve interrupts from the SMS. 485 */ 486 buf[0] = 0x01; 487 asmc_key_write(dev, ASMC_KEY_INTOK, buf, 1); 488 DELAY(50); 489 490 /* 491 * Initiate the polling intervals. 492 */ 493 buf[0] = 20; /* msecs */ 494 asmc_key_write(dev, ASMC_KEY_SMS_LOW_INT, buf, 1); 495 DELAY(200); 496 497 buf[0] = 20; /* msecs */ 498 asmc_key_write(dev, ASMC_KEY_SMS_HIGH_INT, buf, 1); 499 DELAY(200); 500 501 buf[0] = 0x00; 502 buf[1] = 0x60; 503 asmc_key_write(dev, ASMC_KEY_SMS_LOW, buf, 2); 504 DELAY(200); 505 506 buf[0] = 0x01; 507 buf[1] = 0xc0; 508 asmc_key_write(dev, ASMC_KEY_SMS_HIGH, buf, 2); 509 DELAY(200); 510 511 /* 512 * I'm not sure what this key does, but it seems to be 513 * required. 514 */ 515 buf[0] = 0x01; 516 asmc_key_write(dev, ASMC_KEY_SMS_FLAG, buf, 1); 517 DELAY(50); 518 519 /* 520 * Wait up to 5 seconds for SMS initialization. 521 */ 522 for (i = 0; i < 10000; i++) { 523 if (asmc_key_read(dev, ASMC_KEY_SMS, buf, 2) == 0 && 524 (buf[0] != 0x00 || buf[1] != 0x00)) { 525 error = 0; 526 goto nosms; 527 } 528 529 buf[0] = ASMC_SMS_INIT1; 530 buf[1] = ASMC_SMS_INIT2; 531 asmc_key_write(dev, ASMC_KEY_SMS, buf, 2); 532 DELAY(50); 533 } 534 535 asmc_sms_calibrate(dev); 536 nosms: 537 sc->sc_nfan = asmc_fan_count(dev); 538 if (sc->sc_nfan > ASMC_MAXFANS) { 539 device_printf(dev, "more than %d fans were detected. Please " 540 "report this.\n", ASMC_MAXFANS); 541 sc->sc_nfan = ASMC_MAXFANS; 542 } 543 544 if (bootverbose) { 545 /* 546 * XXX: The number of keys is a 32 bit buffer, but 547 * right now Apple only uses the last 8 bit. 548 */ 549 asmc_key_read(dev, ASMC_NKEYS, buf, 4); 550 device_printf(dev, "number of keys: %d\n", buf[3]); 551 } 552 553 return (error); 554 } 555 556 /* 557 * We need to make sure that the SMC acks the byte sent. 558 * Just wait up to 100 ms. 559 */ 560 static int 561 asmc_wait(device_t dev, uint8_t val) 562 { 563 u_int i; 564 565 val = val & ASMC_STATUS_MASK; 566 567 for (i = 0; i < 1000; i++) { 568 if ((inb(ASMC_CMDPORT) & ASMC_STATUS_MASK) == val) 569 return (0); 570 DELAY(10); 571 } 572 573 device_printf(dev, "%s failed: 0x%x, 0x%x\n", __func__, val, 574 inb(ASMC_CMDPORT)); 575 576 return (1); 577 } 578 579 static int 580 asmc_key_read(device_t dev, const char *key, uint8_t *buf, uint8_t len) 581 { 582 int i, error = 1; 583 struct asmc_softc *sc = device_get_softc(dev); 584 585 mtx_lock_spin(&sc->sc_mtx); 586 587 outb(ASMC_CMDPORT, ASMC_CMDREAD); 588 if (asmc_wait(dev, 0x0c)) 589 goto out; 590 591 for (i = 0; i < 4; i++) { 592 outb(ASMC_DATAPORT, key[i]); 593 if (asmc_wait(dev, 0x04)) 594 goto out; 595 } 596 597 outb(ASMC_DATAPORT, len); 598 599 for (i = 0; i < len; i++) { 600 if (asmc_wait(dev, 0x05)) 601 goto out; 602 buf[i] = inb(ASMC_DATAPORT); 603 } 604 605 error = 0; 606 out: 607 mtx_unlock_spin(&sc->sc_mtx); 608 609 return (error); 610 } 611 612 static int 613 asmc_key_write(device_t dev, const char *key, uint8_t *buf, uint8_t len) 614 { 615 int i, error = -1; 616 struct asmc_softc *sc = device_get_softc(dev); 617 618 mtx_lock_spin(&sc->sc_mtx); 619 620 outb(ASMC_CMDPORT, ASMC_CMDWRITE); 621 if (asmc_wait(dev, 0x0c)) 622 goto out; 623 624 for (i = 0; i < 4; i++) { 625 outb(ASMC_DATAPORT, key[i]); 626 if (asmc_wait(dev, 0x04)) 627 goto out; 628 } 629 630 outb(ASMC_DATAPORT, len); 631 632 for (i = 0; i < len; i++) { 633 if (asmc_wait(dev, 0x04)) 634 goto out; 635 outb(ASMC_DATAPORT, buf[i]); 636 } 637 638 error = 0; 639 out: 640 mtx_unlock_spin(&sc->sc_mtx); 641 642 return (error); 643 644 } 645 646 /* 647 * Fan control functions. 648 */ 649 static int 650 asmc_fan_count(device_t dev) 651 { 652 uint8_t buf[1]; 653 654 if (asmc_key_read(dev, ASMC_KEY_FANCOUNT, buf, 1) < 0) 655 return (-1); 656 657 return (buf[0]); 658 } 659 660 static int 661 asmc_fan_getvalue(device_t dev, const char *key, int fan) 662 { 663 int speed; 664 uint8_t buf[2]; 665 char fankey[5]; 666 667 snprintf(fankey, sizeof(fankey), key, fan); 668 if (asmc_key_read(dev, fankey, buf, 2) < 0) 669 return (-1); 670 speed = (buf[0] << 6) | (buf[1] >> 2); 671 672 return (speed); 673 } 674 675 static int 676 asmc_mb_sysctl_fanspeed(SYSCTL_HANDLER_ARGS) 677 { 678 device_t dev = (device_t) arg1; 679 int fan = arg2; 680 int error; 681 int32_t v; 682 683 v = asmc_fan_getvalue(dev, ASMC_KEY_FANSPEED, fan); 684 error = sysctl_handle_int(oidp, &v, 0, req); 685 686 return (error); 687 } 688 689 static int 690 asmc_mb_sysctl_fansafespeed(SYSCTL_HANDLER_ARGS) 691 { 692 device_t dev = (device_t) arg1; 693 int fan = arg2; 694 int error; 695 int32_t v; 696 697 v = asmc_fan_getvalue(dev, ASMC_KEY_FANSAFESPEED, fan); 698 error = sysctl_handle_int(oidp, &v, 0, req); 699 700 return (error); 701 } 702 703 704 static int 705 asmc_mb_sysctl_fanminspeed(SYSCTL_HANDLER_ARGS) 706 { 707 device_t dev = (device_t) arg1; 708 int fan = arg2; 709 int error; 710 int32_t v; 711 712 v = asmc_fan_getvalue(dev, ASMC_KEY_FANMINSPEED, fan); 713 error = sysctl_handle_int(oidp, &v, 0, req); 714 715 return (error); 716 } 717 718 static int 719 asmc_mb_sysctl_fanmaxspeed(SYSCTL_HANDLER_ARGS) 720 { 721 device_t dev = (device_t) arg1; 722 int fan = arg2; 723 int error; 724 int32_t v; 725 726 v = asmc_fan_getvalue(dev, ASMC_KEY_FANMAXSPEED, fan); 727 error = sysctl_handle_int(oidp, &v, 0, req); 728 729 return (error); 730 } 731 732 static int 733 asmc_mb_sysctl_fantargetspeed(SYSCTL_HANDLER_ARGS) 734 { 735 device_t dev = (device_t) arg1; 736 int fan = arg2; 737 int error; 738 int32_t v; 739 740 v = asmc_fan_getvalue(dev, ASMC_KEY_FANTARGETSPEED, fan); 741 error = sysctl_handle_int(oidp, &v, 0, req); 742 743 return (error); 744 } 745 746 /* 747 * Temperature functions. 748 */ 749 static int 750 asmc_temp_getvalue(device_t dev, const char *key) 751 { 752 uint8_t buf[2]; 753 754 /* 755 * Check for invalid temperatures. 756 */ 757 if (asmc_key_read(dev, key, buf, 2) < 0) 758 return (-1); 759 760 return (buf[0]); 761 } 762 763 static int 764 asmc_temp_sysctl(SYSCTL_HANDLER_ARGS) 765 { 766 device_t dev = (device_t) arg1; 767 struct asmc_softc *sc = device_get_softc(dev); 768 int error, val; 769 770 val = asmc_temp_getvalue(dev, sc->sc_model->smc_temps[arg2]); 771 error = sysctl_handle_int(oidp, &val, 0, req); 772 773 return (error); 774 } 775 776 /* 777 * Sudden Motion Sensor functions. 778 */ 779 static int 780 asmc_sms_read(device_t dev, const char *key, int16_t *val) 781 { 782 uint8_t buf[2]; 783 int error; 784 785 /* no need to do locking here as asmc_key_read() already does it */ 786 switch (key[3]) { 787 case 'X': 788 case 'Y': 789 case 'Z': 790 error = asmc_key_read(dev, key, buf, 2); 791 break; 792 default: 793 device_printf(dev, "%s called with invalid argument %s\n", 794 __func__, key); 795 error = 1; 796 goto out; 797 } 798 *val = ((int16_t)buf[0] << 8) | buf[1]; 799 out: 800 return (error); 801 } 802 803 static void 804 asmc_sms_calibrate(device_t dev) 805 { 806 struct asmc_softc *sc = device_get_softc(dev); 807 808 asmc_sms_read(dev, ASMC_KEY_SMS_X, &sc->sms_rest_x); 809 asmc_sms_read(dev, ASMC_KEY_SMS_Y, &sc->sms_rest_y); 810 asmc_sms_read(dev, ASMC_KEY_SMS_Z, &sc->sms_rest_z); 811 } 812 813 static int 814 asmc_sms_intrfast(void *arg) 815 { 816 uint8_t type; 817 device_t dev = (device_t) arg; 818 struct asmc_softc *sc = device_get_softc(dev); 819 820 mtx_lock_spin(&sc->sc_mtx); 821 type = inb(ASMC_INTPORT); 822 mtx_unlock_spin(&sc->sc_mtx); 823 824 sc->sc_sms_intrtype = type; 825 asmc_sms_printintr(dev, type); 826 827 #ifdef INTR_FILTER 828 return (FILTER_SCHEDULE_THREAD | FILTER_HANDLED); 829 #else 830 taskqueue_enqueue(sc->sc_sms_tq, &sc->sc_sms_task); 831 #endif 832 return (FILTER_HANDLED); 833 } 834 835 #ifdef INTR_FILTER 836 static void 837 asmc_sms_handler(void *arg) 838 { 839 struct asmc_softc *sc = device_get_softc(arg); 840 841 asmc_sms_task(sc, 0); 842 } 843 #endif 844 845 846 static void 847 asmc_sms_printintr(device_t dev, uint8_t type) 848 { 849 850 switch (type) { 851 case ASMC_SMS_INTFF: 852 device_printf(dev, "WARNING: possible free fall!\n"); 853 break; 854 case ASMC_SMS_INTHA: 855 device_printf(dev, "WARNING: high acceleration detected!\n"); 856 break; 857 case ASMC_SMS_INTSH: 858 device_printf(dev, "WARNING: possible shock!\n"); 859 break; 860 default: 861 device_printf(dev, "%s unknown interrupt\n", __func__); 862 } 863 } 864 865 static void 866 asmc_sms_task(void *arg, int pending) 867 { 868 struct asmc_softc *sc = (struct asmc_softc *)arg; 869 char notify[16]; 870 int type; 871 872 switch (sc->sc_sms_intrtype) { 873 case ASMC_SMS_INTFF: 874 type = 2; 875 break; 876 case ASMC_SMS_INTHA: 877 type = 1; 878 break; 879 case ASMC_SMS_INTSH: 880 type = 0; 881 break; 882 default: 883 type = 255; 884 } 885 886 snprintf(notify, sizeof(notify), " notify=0x%x", type); 887 devctl_notify("ISA", "asmc", "SMS", notify); 888 } 889 890 static int 891 asmc_mb_sysctl_sms_x(SYSCTL_HANDLER_ARGS) 892 { 893 device_t dev = (device_t) arg1; 894 int error; 895 int16_t val; 896 int32_t v; 897 898 asmc_sms_read(dev, ASMC_KEY_SMS_X, &val); 899 v = (int32_t) val; 900 error = sysctl_handle_int(oidp, &v, 0, req); 901 902 return (error); 903 } 904 905 static int 906 asmc_mb_sysctl_sms_y(SYSCTL_HANDLER_ARGS) 907 { 908 device_t dev = (device_t) arg1; 909 int error; 910 int16_t val; 911 int32_t v; 912 913 asmc_sms_read(dev, ASMC_KEY_SMS_Y, &val); 914 v = (int32_t) val; 915 error = sysctl_handle_int(oidp, &v, 0, req); 916 917 return (error); 918 } 919 920 static int 921 asmc_mb_sysctl_sms_z(SYSCTL_HANDLER_ARGS) 922 { 923 device_t dev = (device_t) arg1; 924 int error; 925 int16_t val; 926 int32_t v; 927 928 asmc_sms_read(dev, ASMC_KEY_SMS_Z, &val); 929 v = (int32_t) val; 930 error = sysctl_handle_int(oidp, &v, sizeof(v), req); 931 932 return (error); 933 } 934 935 static int 936 asmc_mbp_sysctl_light_left(SYSCTL_HANDLER_ARGS) 937 { 938 device_t dev = (device_t) arg1; 939 uint8_t buf[6]; 940 int error; 941 unsigned int level; 942 int32_t v; 943 944 asmc_key_read(dev, ASMC_KEY_LIGHTLEFT, buf, 6); 945 v = buf[2]; 946 error = sysctl_handle_int(oidp, &v, sizeof(v), req); 947 if (error == 0 && req->newptr != NULL) { 948 level = *(unsigned int *)req->newptr; 949 if (level > 255) 950 return (EINVAL); 951 buf[0] = level; 952 buf[1] = 0x00; 953 asmc_key_write(dev, ASMC_KEY_LIGHTVALUE, buf, 2); 954 } 955 956 return (error); 957 } 958 959 static int 960 asmc_mbp_sysctl_light_right(SYSCTL_HANDLER_ARGS) 961 { 962 device_t dev = (device_t) arg1; 963 uint8_t buf[6]; 964 int error; 965 unsigned int level; 966 int32_t v; 967 968 asmc_key_read(dev, ASMC_KEY_LIGHTRIGHT, buf, 6); 969 v = buf[2]; 970 error = sysctl_handle_int(oidp, &v, sizeof(v), req); 971 if (error == 0 && req->newptr != NULL) { 972 level = *(unsigned int *)req->newptr; 973 if (level > 255) 974 return (EINVAL); 975 buf[0] = level; 976 buf[1] = 0x00; 977 asmc_key_write(dev, ASMC_KEY_LIGHTVALUE, buf, 2); 978 } 979 980 return (error); 981 } 982