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