smu.c revision 204270
1/*- 2 * Copyright (c) 2009 Nathan Whitehorn 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 WARRANTIES 16 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 17 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 18 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 19 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 20 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED 21 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 22 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24 * SUCH DAMAGE. 25 * 26 */ 27 28#include <sys/cdefs.h> 29__FBSDID("$FreeBSD: head/sys/powerpc/powermac/smu.c 204270 2010-02-24 01:27:36Z nwhitehorn $"); 30 31#include <sys/param.h> 32#include <sys/bus.h> 33#include <sys/systm.h> 34#include <sys/module.h> 35#include <sys/callout.h> 36#include <sys/conf.h> 37#include <sys/cpu.h> 38#include <sys/ctype.h> 39#include <sys/kernel.h> 40#include <sys/reboot.h> 41#include <sys/rman.h> 42#include <sys/sysctl.h> 43 44#include <machine/bus.h> 45#include <machine/md_var.h> 46 47#include <dev/led/led.h> 48#include <dev/ofw/openfirm.h> 49#include <dev/ofw/ofw_bus.h> 50#include <powerpc/powermac/macgpiovar.h> 51 52struct smu_cmd { 53 volatile uint8_t cmd; 54 uint8_t len; 55 uint8_t data[254]; 56}; 57 58struct smu_fan { 59 cell_t reg; 60 cell_t min_rpm; 61 cell_t max_rpm; 62 cell_t unmanaged_rpm; 63 char location[32]; 64 65 int old_style; 66 int setpoint; 67}; 68 69struct smu_sensor { 70 cell_t reg; 71 char location[32]; 72 enum { 73 SMU_CURRENT_SENSOR, 74 SMU_VOLTAGE_SENSOR, 75 SMU_POWER_SENSOR, 76 SMU_TEMP_SENSOR 77 } type; 78}; 79 80struct smu_softc { 81 device_t sc_dev; 82 struct mtx sc_mtx; 83 84 struct resource *sc_memr; 85 int sc_memrid; 86 87 bus_dma_tag_t sc_dmatag; 88 bus_space_tag_t sc_bt; 89 bus_space_handle_t sc_mailbox; 90 91 struct smu_cmd *sc_cmd; 92 bus_addr_t sc_cmd_phys; 93 bus_dmamap_t sc_cmd_dmamap; 94 95 struct smu_fan *sc_fans; 96 int sc_nfans; 97 struct smu_sensor *sc_sensors; 98 int sc_nsensors; 99 100 struct callout sc_fanmgt_callout; 101 time_t sc_lastuserchange; 102 103 /* Calibration data */ 104 uint16_t sc_cpu_diode_scale; 105 int16_t sc_cpu_diode_offset; 106 107 uint16_t sc_cpu_volt_scale; 108 int16_t sc_cpu_volt_offset; 109 uint16_t sc_cpu_curr_scale; 110 int16_t sc_cpu_curr_offset; 111 112 uint16_t sc_slots_pow_scale; 113 int16_t sc_slots_pow_offset; 114 115 /* Thermal management parameters */ 116 int sc_target_temp; /* Default 55 C */ 117 int sc_critical_temp; /* Default 90 C */ 118 119 struct cdev *sc_leddev; 120}; 121 122/* regular bus attachment functions */ 123 124static int smu_probe(device_t); 125static int smu_attach(device_t); 126 127/* cpufreq notification hooks */ 128 129static void smu_cpufreq_pre_change(device_t, const struct cf_level *level); 130static void smu_cpufreq_post_change(device_t, const struct cf_level *level); 131 132/* utility functions */ 133static int smu_run_cmd(device_t dev, struct smu_cmd *cmd); 134static int smu_get_datablock(device_t dev, int8_t id, uint8_t *buf, 135 size_t len); 136static void smu_attach_fans(device_t dev, phandle_t fanroot); 137static void smu_attach_sensors(device_t dev, phandle_t sensroot); 138static void smu_fanmgt_callout(void *xdev); 139static void smu_set_sleepled(void *xdev, int onoff); 140static int smu_server_mode(SYSCTL_HANDLER_ARGS); 141 142/* where to find the doorbell GPIO */ 143 144static device_t smu_doorbell = NULL; 145 146static device_method_t smu_methods[] = { 147 /* Device interface */ 148 DEVMETHOD(device_probe, smu_probe), 149 DEVMETHOD(device_attach, smu_attach), 150 { 0, 0 }, 151}; 152 153static driver_t smu_driver = { 154 "smu", 155 smu_methods, 156 sizeof(struct smu_softc) 157}; 158 159static devclass_t smu_devclass; 160 161DRIVER_MODULE(smu, nexus, smu_driver, smu_devclass, 0, 0); 162MALLOC_DEFINE(M_SMU, "smu", "SMU Sensor Information"); 163 164#define SMU_MAILBOX 0x8000860c 165#define SMU_FANMGT_INTERVAL 500 /* ms */ 166 167/* Command types */ 168#define SMU_ADC 0xd8 169#define SMU_FAN 0x4a 170#define SMU_I2C 0x9a 171#define SMU_I2C_SIMPLE 0x00 172#define SMU_I2C_NORMAL 0x01 173#define SMU_I2C_COMBINED 0x02 174#define SMU_MISC 0xee 175#define SMU_MISC_GET_DATA 0x02 176#define SMU_MISC_LED_CTRL 0x04 177#define SMU_POWER 0xaa 178#define SMU_POWER_EVENTS 0x8f 179#define SMU_PWR_GET_POWERUP 0x00 180#define SMU_PWR_SET_POWERUP 0x01 181#define SMU_PWR_CLR_POWERUP 0x02 182 183/* Power event types */ 184#define SMU_WAKEUP_KEYPRESS 0x01 185#define SMU_WAKEUP_AC_INSERT 0x02 186#define SMU_WAKEUP_AC_CHANGE 0x04 187#define SMU_WAKEUP_RING 0x10 188 189/* Data blocks */ 190#define SMU_CPUTEMP_CAL 0x18 191#define SMU_CPUVOLT_CAL 0x21 192#define SMU_SLOTPW_CAL 0x78 193 194/* Partitions */ 195#define SMU_PARTITION 0x3e 196#define SMU_PARTITION_LATEST 0x01 197#define SMU_PARTITION_BASE 0x02 198#define SMU_PARTITION_UPDATE 0x03 199 200static int 201smu_probe(device_t dev) 202{ 203 const char *name = ofw_bus_get_name(dev); 204 205 if (strcmp(name, "smu") != 0) 206 return (ENXIO); 207 208 device_set_desc(dev, "Apple System Management Unit"); 209 return (0); 210} 211 212static void 213smu_phys_callback(void *xsc, bus_dma_segment_t *segs, int nsegs, int error) 214{ 215 struct smu_softc *sc = xsc; 216 217 sc->sc_cmd_phys = segs[0].ds_addr; 218} 219 220static int 221smu_attach(device_t dev) 222{ 223 struct smu_softc *sc; 224 phandle_t node, child; 225 uint8_t data[12]; 226 227 sc = device_get_softc(dev); 228 229 mtx_init(&sc->sc_mtx, "smu", NULL, MTX_DEF); 230 231 /* 232 * Map the mailbox area. This should be determined from firmware, 233 * but I have not found a simple way to do that. 234 */ 235 bus_dma_tag_create(NULL, 16, 0, BUS_SPACE_MAXADDR_32BIT, 236 BUS_SPACE_MAXADDR, NULL, NULL, PAGE_SIZE, 1, PAGE_SIZE, 0, NULL, 237 NULL, &(sc->sc_dmatag)); 238 sc->sc_bt = &bs_le_tag; 239 bus_space_map(sc->sc_bt, SMU_MAILBOX, 4, 0, &sc->sc_mailbox); 240 241 /* 242 * Allocate the command buffer. This can be anywhere in the low 4 GB 243 * of memory. 244 */ 245 bus_dmamem_alloc(sc->sc_dmatag, (void **)&sc->sc_cmd, BUS_DMA_WAITOK | 246 BUS_DMA_ZERO, &sc->sc_cmd_dmamap); 247 bus_dmamap_load(sc->sc_dmatag, sc->sc_cmd_dmamap, 248 sc->sc_cmd, PAGE_SIZE, smu_phys_callback, sc, 0); 249 250 /* 251 * Set up handlers to change CPU voltage when CPU frequency is changed. 252 */ 253 EVENTHANDLER_REGISTER(cpufreq_pre_change, smu_cpufreq_pre_change, dev, 254 EVENTHANDLER_PRI_ANY); 255 EVENTHANDLER_REGISTER(cpufreq_post_change, smu_cpufreq_post_change, dev, 256 EVENTHANDLER_PRI_ANY); 257 258 /* 259 * Detect and attach child devices. 260 */ 261 node = ofw_bus_get_node(dev); 262 for (child = OF_child(node); child != 0; child = OF_peer(child)) { 263 char name[32]; 264 memset(name, 0, sizeof(name)); 265 OF_getprop(child, "name", name, sizeof(name)); 266 267 if (strncmp(name, "rpm-fans", 9) == 0 || 268 strncmp(name, "fans", 5) == 0) 269 smu_attach_fans(dev, child); 270 271 if (strncmp(name, "sensors", 8) == 0) 272 smu_attach_sensors(dev, child); 273 } 274 275 /* 276 * Collect calibration constants. 277 */ 278 smu_get_datablock(dev, SMU_CPUTEMP_CAL, data, sizeof(data)); 279 sc->sc_cpu_diode_scale = (data[4] << 8) + data[5]; 280 sc->sc_cpu_diode_offset = (data[6] << 8) + data[7]; 281 282 smu_get_datablock(dev, SMU_CPUVOLT_CAL, data, sizeof(data)); 283 sc->sc_cpu_volt_scale = (data[4] << 8) + data[5]; 284 sc->sc_cpu_volt_offset = (data[6] << 8) + data[7]; 285 sc->sc_cpu_curr_scale = (data[8] << 8) + data[9]; 286 sc->sc_cpu_curr_offset = (data[10] << 8) + data[11]; 287 288 smu_get_datablock(dev, SMU_SLOTPW_CAL, data, sizeof(data)); 289 sc->sc_slots_pow_scale = (data[4] << 8) + data[5]; 290 sc->sc_slots_pow_offset = (data[6] << 8) + data[7]; 291 292 /* 293 * Set up simple-minded thermal management. 294 */ 295 sc->sc_target_temp = 55; 296 sc->sc_critical_temp = 90; 297 298 SYSCTL_ADD_INT(device_get_sysctl_ctx(dev), 299 SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO, 300 "target_temp", CTLTYPE_INT | CTLFLAG_RW, &sc->sc_target_temp, 301 sizeof(int), "Target temperature (C)"); 302 SYSCTL_ADD_INT(device_get_sysctl_ctx(dev), 303 SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO, 304 "critical_temp", CTLTYPE_INT | CTLFLAG_RW, 305 &sc->sc_critical_temp, sizeof(int), "Critical temperature (C)"); 306 307 callout_init(&sc->sc_fanmgt_callout, 1); 308 smu_fanmgt_callout(dev); 309 310 /* 311 * Set up LED interface 312 */ 313 sc->sc_leddev = led_create(smu_set_sleepled, dev, "sleepled"); 314 315 /* 316 * Reset on power loss behavior 317 */ 318 319 SYSCTL_ADD_PROC(device_get_sysctl_ctx(dev), 320 SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO, 321 "server_mode", CTLTYPE_INT | CTLFLAG_RW, dev, 0, 322 smu_server_mode, "I", "Enable reboot after power failure"); 323 324 return (0); 325} 326 327static int 328smu_run_cmd(device_t dev, struct smu_cmd *cmd) 329{ 330 struct smu_softc *sc; 331 int doorbell_ack, result, oldpow; 332 333 sc = device_get_softc(dev); 334 335 mtx_lock(&sc->sc_mtx); 336 337 oldpow = powerpc_pow_enabled; 338 powerpc_pow_enabled = 0; 339 340 /* Copy the command to the mailbox */ 341 memcpy(sc->sc_cmd, cmd, sizeof(*cmd)); 342 bus_dmamap_sync(sc->sc_dmatag, sc->sc_cmd_dmamap, BUS_DMASYNC_PREWRITE); 343 bus_space_write_4(sc->sc_bt, sc->sc_mailbox, 0, sc->sc_cmd_phys); 344 345 /* Flush the cacheline it is in -- SMU bypasses the cache */ 346 __asm __volatile("sync; dcbf 0,%0; sync" :: "r"(sc->sc_cmd): "memory"); 347 348 /* Ring SMU doorbell */ 349 macgpio_write(smu_doorbell, GPIO_DDR_OUTPUT); 350 351 /* Wait for the doorbell GPIO to go high, signaling completion */ 352 do { 353 /* XXX: timeout */ 354 DELAY(50); 355 doorbell_ack = macgpio_read(smu_doorbell); 356 } while (doorbell_ack != (GPIO_DDR_OUTPUT | GPIO_LEVEL_RO | GPIO_DATA)); 357 358 /* Check result. First invalidate the cache again... */ 359 __asm __volatile("dcbf 0,%0; sync" :: "r"(sc->sc_cmd) : "memory"); 360 361 bus_dmamap_sync(sc->sc_dmatag, sc->sc_cmd_dmamap, BUS_DMASYNC_POSTREAD); 362 363 /* SMU acks the command by inverting the command bits */ 364 if (sc->sc_cmd->cmd == ((~cmd->cmd) & 0xff)) 365 result = 0; 366 else 367 result = EIO; 368 369 powerpc_pow_enabled = oldpow; 370 371 memcpy(cmd->data, sc->sc_cmd->data, sizeof(cmd->data)); 372 cmd->len = sc->sc_cmd->len; 373 374 mtx_unlock(&sc->sc_mtx); 375 376 return (result); 377} 378 379static int 380smu_get_datablock(device_t dev, int8_t id, uint8_t *buf, size_t len) 381{ 382 struct smu_cmd cmd; 383 uint8_t addr[4]; 384 385 cmd.cmd = SMU_PARTITION; 386 cmd.len = 2; 387 cmd.data[0] = SMU_PARTITION_LATEST; 388 cmd.data[1] = id; 389 390 smu_run_cmd(dev, &cmd); 391 392 addr[0] = addr[1] = 0; 393 addr[2] = cmd.data[0]; 394 addr[3] = cmd.data[1]; 395 396 cmd.cmd = SMU_MISC; 397 cmd.len = 7; 398 cmd.data[0] = SMU_MISC_GET_DATA; 399 cmd.data[1] = sizeof(addr); 400 memcpy(&cmd.data[2], addr, sizeof(addr)); 401 cmd.data[6] = len; 402 403 smu_run_cmd(dev, &cmd); 404 memcpy(buf, cmd.data, len); 405 return (0); 406} 407 408static void 409smu_slew_cpu_voltage(device_t dev, int to) 410{ 411 struct smu_cmd cmd; 412 413 cmd.cmd = SMU_POWER; 414 cmd.len = 8; 415 cmd.data[0] = 'V'; 416 cmd.data[1] = 'S'; 417 cmd.data[2] = 'L'; 418 cmd.data[3] = 'E'; 419 cmd.data[4] = 'W'; 420 cmd.data[5] = 0xff; 421 cmd.data[6] = 1; 422 cmd.data[7] = to; 423 424 smu_run_cmd(dev, &cmd); 425} 426 427static void 428smu_cpufreq_pre_change(device_t dev, const struct cf_level *level) 429{ 430 /* 431 * Make sure the CPU voltage is raised before we raise 432 * the clock. 433 */ 434 435 if (level->rel_set[0].freq == 10000 /* max */) 436 smu_slew_cpu_voltage(dev, 0); 437} 438 439static void 440smu_cpufreq_post_change(device_t dev, const struct cf_level *level) 441{ 442 /* We are safe to reduce CPU voltage after a downward transition */ 443 444 if (level->rel_set[0].freq < 10000 /* max */) 445 smu_slew_cpu_voltage(dev, 1); /* XXX: 1/4 voltage for 970MP? */ 446} 447 448/* Routines for probing the SMU doorbell GPIO */ 449static int doorbell_probe(device_t dev); 450static int doorbell_attach(device_t dev); 451 452static device_method_t doorbell_methods[] = { 453 /* Device interface */ 454 DEVMETHOD(device_probe, doorbell_probe), 455 DEVMETHOD(device_attach, doorbell_attach), 456 { 0, 0 }, 457}; 458 459static driver_t doorbell_driver = { 460 "smudoorbell", 461 doorbell_methods, 462 0 463}; 464 465static devclass_t doorbell_devclass; 466 467DRIVER_MODULE(smudoorbell, macgpio, doorbell_driver, doorbell_devclass, 0, 0); 468 469static int 470doorbell_probe(device_t dev) 471{ 472 const char *name = ofw_bus_get_name(dev); 473 474 if (strcmp(name, "smu-doorbell") != 0) 475 return (ENXIO); 476 477 device_set_desc(dev, "SMU Doorbell GPIO"); 478 device_quiet(dev); 479 return (0); 480} 481 482static int 483doorbell_attach(device_t dev) 484{ 485 smu_doorbell = dev; 486 return (0); 487} 488 489/* 490 * Sensor and fan management 491 */ 492 493static int 494smu_fan_set_rpm(device_t smu, struct smu_fan *fan, int rpm) 495{ 496 struct smu_cmd cmd; 497 int error; 498 499 cmd.cmd = SMU_FAN; 500 error = EIO; 501 502 /* Clamp to allowed range */ 503 rpm = max(fan->min_rpm, rpm); 504 rpm = min(fan->max_rpm, rpm); 505 506 /* 507 * Apple has two fan control mechanisms. We can't distinguish 508 * them except by seeing if the new one fails. If the new one 509 * fails, use the old one. 510 */ 511 512 if (!fan->old_style) { 513 cmd.len = 4; 514 cmd.data[0] = 0x30; 515 cmd.data[1] = fan->reg; 516 cmd.data[2] = (rpm >> 8) & 0xff; 517 cmd.data[3] = rpm & 0xff; 518 519 error = smu_run_cmd(smu, &cmd); 520 if (error) 521 fan->old_style = 1; 522 } 523 524 if (fan->old_style) { 525 cmd.len = 14; 526 cmd.data[0] = 0; 527 cmd.data[1] = 1 << fan->reg; 528 cmd.data[2 + 2*fan->reg] = (rpm >> 8) & 0xff; 529 cmd.data[3 + 2*fan->reg] = rpm & 0xff; 530 error = smu_run_cmd(smu, &cmd); 531 } 532 533 if (error == 0) 534 fan->setpoint = rpm; 535 536 return (error); 537} 538 539static int 540smu_fan_read_rpm(device_t smu, struct smu_fan *fan) 541{ 542 struct smu_cmd cmd; 543 544 cmd.cmd = SMU_FAN; 545 cmd.len = 1; 546 cmd.data[0] = 1; 547 548 smu_run_cmd(smu, &cmd); 549 550 return ((cmd.data[fan->reg*2+1] << 8) | cmd.data[fan->reg*2+2]); 551} 552 553static int 554smu_fanrpm_sysctl(SYSCTL_HANDLER_ARGS) 555{ 556 device_t smu; 557 struct smu_softc *sc; 558 struct smu_fan *fan; 559 int rpm, error; 560 561 smu = arg1; 562 sc = device_get_softc(smu); 563 fan = &sc->sc_fans[arg2]; 564 565 rpm = smu_fan_read_rpm(smu, fan); 566 error = sysctl_handle_int(oidp, &rpm, 0, req); 567 568 if (error || !req->newptr) 569 return (error); 570 571 sc->sc_lastuserchange = time_uptime; 572 573 return (smu_fan_set_rpm(smu, fan, rpm)); 574} 575 576static void 577smu_attach_fans(device_t dev, phandle_t fanroot) 578{ 579 struct smu_fan *fan; 580 struct smu_softc *sc; 581 struct sysctl_oid *oid, *fanroot_oid; 582 struct sysctl_ctx_list *ctx; 583 phandle_t child; 584 char type[32], sysctl_name[32]; 585 int i; 586 587 sc = device_get_softc(dev); 588 sc->sc_nfans = 0; 589 590 for (child = OF_child(fanroot); child != 0; child = OF_peer(child)) 591 sc->sc_nfans++; 592 593 if (sc->sc_nfans == 0) { 594 device_printf(dev, "WARNING: No fans detected!\n"); 595 return; 596 } 597 598 sc->sc_fans = malloc(sc->sc_nfans * sizeof(struct smu_fan), M_SMU, 599 M_WAITOK | M_ZERO); 600 601 fan = sc->sc_fans; 602 sc->sc_nfans = 0; 603 604 ctx = device_get_sysctl_ctx(dev); 605 fanroot_oid = SYSCTL_ADD_NODE(ctx, 606 SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO, "fans", 607 CTLFLAG_RD, 0, "SMU Fan Information"); 608 609 for (child = OF_child(fanroot); child != 0; child = OF_peer(child)) { 610 OF_getprop(child, "device_type", type, sizeof(type)); 611 if (strcmp(type, "fan-rpm-control") != 0) 612 continue; 613 614 fan->old_style = 0; 615 OF_getprop(child, "reg", &fan->reg, sizeof(cell_t)); 616 OF_getprop(child, "min-value", &fan->min_rpm, sizeof(cell_t)); 617 OF_getprop(child, "max-value", &fan->max_rpm, sizeof(cell_t)); 618 619 if (OF_getprop(child, "unmanaged-value", &fan->unmanaged_rpm, 620 sizeof(cell_t)) != sizeof(cell_t)) 621 fan->unmanaged_rpm = fan->max_rpm; 622 623 fan->setpoint = smu_fan_read_rpm(dev, fan); 624 625 OF_getprop(child, "location", fan->location, 626 sizeof(fan->location)); 627 628 /* Add sysctls */ 629 for (i = 0; i < strlen(fan->location); i++) { 630 sysctl_name[i] = tolower(fan->location[i]); 631 if (isspace(sysctl_name[i])) 632 sysctl_name[i] = '_'; 633 } 634 sysctl_name[i] = 0; 635 636 oid = SYSCTL_ADD_NODE(ctx, SYSCTL_CHILDREN(fanroot_oid), 637 OID_AUTO, sysctl_name, CTLFLAG_RD, 0, "Fan Information"); 638 SYSCTL_ADD_INT(ctx, SYSCTL_CHILDREN(oid), OID_AUTO, "minrpm", 639 CTLTYPE_INT | CTLFLAG_RD, &fan->min_rpm, sizeof(cell_t), 640 "Minimum allowed RPM"); 641 SYSCTL_ADD_INT(ctx, SYSCTL_CHILDREN(oid), OID_AUTO, "maxrpm", 642 CTLTYPE_INT | CTLFLAG_RD, &fan->max_rpm, sizeof(cell_t), 643 "Maximum allowed RPM"); 644 SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(oid), OID_AUTO, "rpm", 645 CTLTYPE_INT | CTLFLAG_RW, dev, sc->sc_nfans, 646 smu_fanrpm_sysctl, "I", "Fan RPM"); 647 648 fan++; 649 sc->sc_nfans++; 650 } 651} 652 653static int 654smu_sensor_read(device_t smu, struct smu_sensor *sens) 655{ 656 struct smu_cmd cmd; 657 struct smu_softc *sc; 658 int64_t value; 659 660 cmd.cmd = SMU_ADC; 661 cmd.len = 1; 662 cmd.data[0] = sens->reg; 663 664 smu_run_cmd(smu, &cmd); 665 666 sc = device_get_softc(smu); 667 value = (cmd.data[0] << 8) | cmd.data[1]; 668 669 switch (sens->type) { 670 case SMU_TEMP_SENSOR: 671 value *= sc->sc_cpu_diode_scale; 672 value >>= 3; 673 value += ((int64_t)sc->sc_cpu_diode_offset) << 9; 674 value <<= 1; 675 676 /* Convert from 16.16 fixed point degC into integer C. */ 677 value *= 15625; 678 value /= 1024; 679 value /= 1000000; 680 break; 681 case SMU_VOLTAGE_SENSOR: 682 value *= sc->sc_cpu_volt_scale; 683 value += sc->sc_cpu_volt_offset; 684 value <<= 4; 685 686 /* Convert from 16.16 fixed point V into mV. */ 687 value *= 15625; 688 value /= 1024; 689 value /= 1000; 690 break; 691 case SMU_CURRENT_SENSOR: 692 value *= sc->sc_cpu_curr_scale; 693 value += sc->sc_cpu_curr_offset; 694 value <<= 4; 695 696 /* Convert from 16.16 fixed point A into mA. */ 697 value *= 15625; 698 value /= 1024; 699 value /= 1000; 700 break; 701 case SMU_POWER_SENSOR: 702 value *= sc->sc_slots_pow_scale; 703 value += sc->sc_slots_pow_offset; 704 value <<= 4; 705 706 /* Convert from 16.16 fixed point W into mW. */ 707 value *= 15625; 708 value /= 1024; 709 value /= 1000; 710 break; 711 } 712 713 return (value); 714} 715 716static int 717smu_sensor_sysctl(SYSCTL_HANDLER_ARGS) 718{ 719 device_t smu; 720 struct smu_softc *sc; 721 struct smu_sensor *sens; 722 int value, error; 723 724 smu = arg1; 725 sc = device_get_softc(smu); 726 sens = &sc->sc_sensors[arg2]; 727 728 value = smu_sensor_read(smu, sens); 729 error = sysctl_handle_int(oidp, &value, 0, req); 730 731 return (error); 732} 733 734static void 735smu_attach_sensors(device_t dev, phandle_t sensroot) 736{ 737 struct smu_sensor *sens; 738 struct smu_softc *sc; 739 struct sysctl_oid *sensroot_oid; 740 struct sysctl_ctx_list *ctx; 741 phandle_t child; 742 char type[32]; 743 int i; 744 745 sc = device_get_softc(dev); 746 sc->sc_nsensors = 0; 747 748 for (child = OF_child(sensroot); child != 0; child = OF_peer(child)) 749 sc->sc_nsensors++; 750 751 if (sc->sc_nsensors == 0) { 752 device_printf(dev, "WARNING: No sensors detected!\n"); 753 return; 754 } 755 756 sc->sc_sensors = malloc(sc->sc_nsensors * sizeof(struct smu_sensor), 757 M_SMU, M_WAITOK | M_ZERO); 758 759 sens = sc->sc_sensors; 760 sc->sc_nsensors = 0; 761 762 ctx = device_get_sysctl_ctx(dev); 763 sensroot_oid = SYSCTL_ADD_NODE(ctx, 764 SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO, "sensors", 765 CTLFLAG_RD, 0, "SMU Sensor Information"); 766 767 for (child = OF_child(sensroot); child != 0; child = OF_peer(child)) { 768 char sysctl_name[40], sysctl_desc[40]; 769 const char *units; 770 771 OF_getprop(child, "device_type", type, sizeof(type)); 772 773 if (strcmp(type, "current-sensor") == 0) { 774 sens->type = SMU_CURRENT_SENSOR; 775 units = "mA"; 776 } else if (strcmp(type, "temp-sensor") == 0) { 777 sens->type = SMU_TEMP_SENSOR; 778 units = "C"; 779 } else if (strcmp(type, "voltage-sensor") == 0) { 780 sens->type = SMU_VOLTAGE_SENSOR; 781 units = "mV"; 782 } else if (strcmp(type, "power-sensor") == 0) { 783 sens->type = SMU_POWER_SENSOR; 784 units = "mW"; 785 } else { 786 continue; 787 } 788 789 OF_getprop(child, "reg", &sens->reg, sizeof(cell_t)); 790 OF_getprop(child, "location", sens->location, 791 sizeof(sens->location)); 792 793 for (i = 0; i < strlen(sens->location); i++) { 794 sysctl_name[i] = tolower(sens->location[i]); 795 if (isspace(sysctl_name[i])) 796 sysctl_name[i] = '_'; 797 } 798 sysctl_name[i] = 0; 799 800 sprintf(sysctl_desc,"%s (%s)", sens->location, units); 801 802 SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(sensroot_oid), OID_AUTO, 803 sysctl_name, CTLTYPE_INT | CTLFLAG_RD, dev, sc->sc_nsensors, 804 smu_sensor_sysctl, "I", sysctl_desc); 805 806 sens++; 807 sc->sc_nsensors++; 808 } 809} 810 811static int 812ms_to_ticks(int ms) 813{ 814 if (hz > 1000) 815 return ms*(hz/1000); 816 817 return ms/(1000/hz); 818} 819 820static void 821smu_fanmgt_callout(void *xdev) { 822 device_t smu = xdev; 823 struct smu_softc *sc; 824 int i, maxtemp, temp, factor; 825 826 sc = device_get_softc(smu); 827 828 if (time_uptime - sc->sc_lastuserchange < 3) { 829 /* 830 * If we have heard from a user process in the last 3 seconds, 831 * go away. 832 */ 833 834 callout_reset(&sc->sc_fanmgt_callout, 835 ms_to_ticks(SMU_FANMGT_INTERVAL), smu_fanmgt_callout, smu); 836 return; 837 } 838 839 maxtemp = 0; 840 for (i = 0; i < sc->sc_nsensors; i++) { 841 if (sc->sc_sensors[i].type != SMU_TEMP_SENSOR) 842 continue; 843 844 temp = smu_sensor_read(smu, &sc->sc_sensors[i]); 845 if (temp > maxtemp) 846 maxtemp = temp; 847 } 848 849 if (maxtemp < 10) { /* Bail if no good sensors */ 850 for (i = 0; i < sc->sc_nfans; i++) 851 smu_fan_set_rpm(smu, &sc->sc_fans[i], 852 sc->sc_fans[i].unmanaged_rpm); 853 return; 854 } 855 856 if (maxtemp > sc->sc_critical_temp) { 857 device_printf(smu, "WARNING: Current system temperature (%d C) " 858 "exceeds critical temperature (%d C)! Shutting down!\n", 859 maxtemp, sc->sc_critical_temp); 860 shutdown_nice(RB_POWEROFF); 861 } 862 863 if (maxtemp - sc->sc_target_temp > 20) 864 device_printf(smu, "WARNING: Current system temperature (%d C) " 865 "more than 20 degrees over target temperature (%d C)!\n", 866 maxtemp, sc->sc_target_temp); 867 868 if (maxtemp > sc->sc_target_temp) 869 factor = 110; 870 else if (sc->sc_target_temp - maxtemp > 4) 871 factor = 90; 872 else if (sc->sc_target_temp - maxtemp > 1) 873 factor = 95; 874 else 875 factor = 100; 876 877 for (i = 0; i < sc->sc_nfans; i++) 878 smu_fan_set_rpm(smu, &sc->sc_fans[i], 879 (sc->sc_fans[i].setpoint * factor) / 100); 880 881 callout_reset(&sc->sc_fanmgt_callout, 882 ms_to_ticks(SMU_FANMGT_INTERVAL), smu_fanmgt_callout, smu); 883} 884 885static void 886smu_set_sleepled(void *xdev, int onoff) 887{ 888 struct smu_cmd cmd; 889 device_t smu = xdev; 890 891 cmd.cmd = SMU_MISC; 892 cmd.len = 3; 893 cmd.data[0] = SMU_MISC_LED_CTRL; 894 cmd.data[1] = 0; 895 cmd.data[2] = onoff; 896 897 smu_run_cmd(smu, &cmd); 898} 899 900static int 901smu_server_mode(SYSCTL_HANDLER_ARGS) 902{ 903 struct smu_cmd cmd; 904 u_int server_mode; 905 device_t smu = arg1; 906 int error; 907 908 cmd.cmd = SMU_POWER_EVENTS; 909 cmd.len = 1; 910 cmd.data[0] = SMU_PWR_GET_POWERUP; 911 912 error = smu_run_cmd(smu, &cmd); 913 914 if (error) 915 return (error); 916 917 server_mode = (cmd.data[1] & SMU_WAKEUP_AC_INSERT) ? 1 : 0; 918 919 error = sysctl_handle_int(oidp, &server_mode, 0, req); 920 921 if (error || !req->newptr) 922 return (error); 923 924 if (server_mode == 1) 925 cmd.data[0] = SMU_PWR_SET_POWERUP; 926 else if (server_mode == 0) 927 cmd.data[0] = SMU_PWR_CLR_POWERUP; 928 else 929 return (EINVAL); 930 931 cmd.len = 3; 932 cmd.data[1] = 0; 933 cmd.data[2] = SMU_WAKEUP_AC_INSERT; 934 935 return (smu_run_cmd(smu, &cmd)); 936} 937 938