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