smu.c revision 1.24
1/* $OpenBSD: smu.c,v 1.24 2014/03/29 18:09:29 guenther Exp $ */ 2 3/* 4 * Copyright (c) 2005 Mark Kettenis 5 * 6 * Permission to use, copy, modify, and distribute this software for any 7 * purpose with or without fee is hereby granted, provided that the above 8 * copyright notice and this permission notice appear in all copies. 9 * 10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 13 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 14 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 15 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 16 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17 */ 18 19#include <sys/param.h> 20#include <sys/systm.h> 21#include <sys/device.h> 22#include <sys/kernel.h> 23#include <sys/rwlock.h> 24#include <sys/proc.h> 25#include <sys/sensors.h> 26 27#include <machine/autoconf.h> 28#include <machine/cpu.h> 29 30#include <dev/clock_subr.h> 31#include <dev/i2c/i2cvar.h> 32#include <dev/ofw/openfirm.h> 33 34#include <macppc/dev/maci2cvar.h> 35#include <macppc/pci/macobio.h> 36 37int smu_match(struct device *, void *, void *); 38void smu_attach(struct device *, struct device *, void *); 39 40#define SMU_MAXFANS 3 41 42struct smu_fan { 43 u_int8_t reg; 44 u_int16_t min_rpm; 45 u_int16_t max_rpm; 46 u_int16_t unmanaged_rpm; 47 struct ksensor sensor; 48}; 49 50#define SMU_MAXSENSORS 4 51 52struct smu_sensor { 53 u_int8_t reg; 54 struct ksensor sensor; 55}; 56 57struct smu_softc { 58 struct device sc_dev; 59 60 /* SMU command buffer. */ 61 bus_dma_tag_t sc_dmat; 62 bus_dmamap_t sc_cmdmap; 63 bus_dma_segment_t sc_cmdseg[1]; 64 caddr_t sc_cmd; 65 struct rwlock sc_lock; 66 67 /* Doorbell and mailbox. */ 68 struct ppc_bus_space sc_mem_bus_space; 69 bus_space_tag_t sc_memt; 70 bus_space_handle_t sc_gpioh; 71 bus_space_handle_t sc_buffh; 72 73 struct smu_fan sc_fans[SMU_MAXFANS]; 74 int sc_num_fans; 75 76 struct smu_sensor sc_sensors[SMU_MAXSENSORS]; 77 int sc_num_sensors; 78 79 struct ksensordev sc_sensordev; 80 81 u_int16_t sc_cpu_diode_scale; 82 int16_t sc_cpu_diode_offset; 83 u_int16_t sc_cpu_volt_scale; 84 int16_t sc_cpu_volt_offset; 85 u_int16_t sc_cpu_curr_scale; 86 int16_t sc_cpu_curr_offset; 87 88 u_int16_t sc_slots_pow_scale; 89 int16_t sc_slots_pow_offset; 90 91 struct i2c_controller sc_i2c_tag; 92}; 93 94struct cfattach smu_ca = { 95 sizeof(struct smu_softc), smu_match, smu_attach 96}; 97 98struct cfdriver smu_cd = { 99 NULL, "smu", DV_DULL, 100}; 101 102/* SMU command */ 103struct smu_cmd { 104 u_int8_t cmd; 105 u_int8_t len; 106 u_int8_t data[254]; 107}; 108#define SMU_CMDSZ sizeof(struct smu_cmd) 109 110/* RTC */ 111#define SMU_RTC 0x8e 112#define SMU_RTC_SET_DATETIME 0x80 113#define SMU_RTC_GET_DATETIME 0x81 114 115/* ADC */ 116#define SMU_ADC 0xd8 117 118/* Fan control */ 119#define SMU_FAN 0x4a 120 121/* Data partitions */ 122#define SMU_PARTITION 0x3e 123#define SMU_PARTITION_LATEST 0x01 124#define SMU_PARTITION_BASE 0x02 125#define SMU_PARTITION_UPDATE 0x03 126 127/* I2C */ 128#define SMU_I2C 0x9a 129#define SMU_I2C_SIMPLE 0x00 130#define SMU_I2C_NORMAL 0x01 131#define SMU_I2C_COMBINED 0x02 132 133/* Power Management */ 134#define SMU_POWER 0xaa 135 136/* Miscellaneous */ 137#define SMU_MISC 0xee 138#define SMU_MISC_GET_DATA 0x02 139 140int smu_intr(void *); 141 142int smu_do_cmd(struct smu_softc *, int); 143int smu_time_read(time_t *); 144int smu_time_write(time_t); 145int smu_get_datablock(struct smu_softc *sc, u_int8_t, u_int8_t *, size_t); 146int smu_fan_set_rpm(struct smu_softc *, struct smu_fan *, u_int16_t); 147int smu_fan_refresh(struct smu_softc *, struct smu_fan *); 148int smu_sensor_refresh(struct smu_softc *, struct smu_sensor *); 149void smu_refresh_sensors(void *); 150 151int smu_i2c_acquire_bus(void *, int); 152void smu_i2c_release_bus(void *, int); 153int smu_i2c_exec(void *, i2c_op_t, i2c_addr_t, 154 const void *, size_t, void *buf, size_t, int); 155 156void smu_slew_voltage(u_int); 157 158int 159smu_match(struct device *parent, void *cf, void *aux) 160{ 161 struct confargs *ca = aux; 162 163 if (strcmp(ca->ca_name, "smu") == 0) 164 return (1); 165 return (0); 166} 167 168/* XXX */ 169extern struct powerpc_bus_dma_tag pci_bus_dma_tag; 170 171void 172smu_attach(struct device *parent, struct device *self, void *aux) 173{ 174 struct smu_softc *sc = (struct smu_softc *)self; 175 struct confargs *ca = aux; 176 struct i2cbus_attach_args iba; 177 struct smu_fan *fan; 178 struct smu_sensor *sensor; 179 int nseg, node; 180 char type[32], loc[32]; 181 u_int32_t reg, intr, gpio, val; 182 u_int8_t data[12]; 183 184 /* XXX */ 185 sc->sc_mem_bus_space.bus_base = 0x80000000; 186 sc->sc_mem_bus_space.bus_size = 0; 187 sc->sc_mem_bus_space.bus_io = 0; 188 sc->sc_memt = &sc->sc_mem_bus_space; 189 190 /* Map smu-doorbell gpio. */ 191 if (OF_getprop(ca->ca_node, "platform-doorbell-ack", 192 &node, sizeof node) <= 0 || 193 OF_getprop(node, "reg", ®, sizeof reg) <= 0 || 194 OF_getprop(node, "interrupts", &intr, sizeof intr) <= 0 || 195 OF_getprop(OF_parent(node), "reg", &gpio, sizeof gpio) <= 0) { 196 printf(": cannot find smu-doorbell gpio\n"); 197 return; 198 } 199 if (bus_space_map(sc->sc_memt, gpio + reg, 1, 0, &sc->sc_gpioh)) { 200 printf(": cannot map smu-doorbell gpio\n"); 201 return; 202 } 203 204 /* XXX Should get this from OF. */ 205 if (bus_space_map(sc->sc_memt, 0x860c, 4, 0, &sc->sc_buffh)) { 206 printf(": cannot map smu-doorbell buffer\n"); 207 return; 208 } 209 210 /* XXX */ 211 sc->sc_dmat = &pci_bus_dma_tag; 212 213 /* Allocate and map SMU command buffer. */ 214 if (bus_dmamem_alloc(sc->sc_dmat, SMU_CMDSZ, 0, 0, 215 sc->sc_cmdseg, 1, &nseg, BUS_DMA_NOWAIT)) { 216 printf(": cannot allocate cmd buffer\n"); 217 return; 218 } 219 if (bus_dmamem_map(sc->sc_dmat, sc->sc_cmdseg, nseg, 220 SMU_CMDSZ, &sc->sc_cmd, BUS_DMA_NOWAIT)) { 221 printf(": cannot map cmd buffer\n"); 222 bus_dmamem_free(sc->sc_dmat, sc->sc_cmdseg, 1); 223 return; 224 } 225 if (bus_dmamap_create(sc->sc_dmat, SMU_CMDSZ, 1, SMU_CMDSZ, 0, 226 BUS_DMA_NOWAIT | BUS_DMA_ALLOCNOW, &sc->sc_cmdmap)) { 227 printf(": cannot create cmd dmamap\n"); 228 bus_dmamem_unmap(sc->sc_dmat, sc->sc_cmd, SMU_CMDSZ); 229 bus_dmamem_free(sc->sc_dmat, sc->sc_cmdseg, 1); 230 return; 231 } 232 if (bus_dmamap_load(sc->sc_dmat, sc->sc_cmdmap, sc->sc_cmd, 233 SMU_CMDSZ, NULL, BUS_DMA_NOWAIT)) { 234 printf(": cannot load cmd dmamap\n"); 235 bus_dmamap_destroy(sc->sc_dmat, sc->sc_cmdmap); 236 bus_dmamem_unmap(sc->sc_dmat, sc->sc_cmd, SMU_CMDSZ); 237 bus_dmamem_free(sc->sc_dmat, sc->sc_cmdseg, nseg); 238 return; 239 } 240 241 rw_init(&sc->sc_lock, sc->sc_dev.dv_xname); 242 243 /* Establish smu-doorbell interrupt. */ 244 mac_intr_establish(parent, intr, IST_EDGE, IPL_BIO, 245 smu_intr, sc, sc->sc_dev.dv_xname); 246 247 /* Initialize global variables that control RTC functionality. */ 248 time_read = smu_time_read; 249 time_write = smu_time_write; 250 251 /* Fans */ 252 node = OF_getnodebyname(ca->ca_node, "rpm-fans"); 253 if (node == 0) 254 node = OF_getnodebyname(ca->ca_node, "fans"); 255 for (node = OF_child(node); node; node = OF_peer(node)) { 256 if (OF_getprop(node, "reg", ®, sizeof reg) <= 0 || 257 OF_getprop(node, "device_type", type, sizeof type) <= 0) 258 continue; 259 260 if (strcmp(type, "fan-rpm-control") != 0) { 261 printf(": unsupported fan type: %s\n", type); 262 return; 263 } 264 265 if (sc->sc_num_fans >= SMU_MAXFANS) { 266 printf(": too many fans\n"); 267 return; 268 } 269 270 fan = &sc->sc_fans[sc->sc_num_fans++]; 271 fan->sensor.type = SENSOR_FANRPM; 272 fan->sensor.flags = SENSOR_FINVALID; 273 fan->reg = reg; 274 275 if (OF_getprop(node, "min-value", &val, sizeof val) <= 0) 276 val = 0; 277 fan->min_rpm = val; 278 if (OF_getprop(node, "max-value", &val, sizeof val) <= 0) 279 val = 0xffff; 280 fan->max_rpm = val; 281 if (OF_getprop(node, "unmanage-value", &val, sizeof val) <= 0) 282 val = fan->max_rpm; 283 fan->unmanaged_rpm = val; 284 285 if (OF_getprop(node, "location", loc, sizeof loc) <= 0) 286 strlcpy(loc, "Unknown", sizeof loc); 287 strlcpy(fan->sensor.desc, loc, sizeof sensor->sensor.desc); 288 289 /* Start running fans at their "unmanaged" speed. */ 290 smu_fan_set_rpm(sc, fan, fan->unmanaged_rpm); 291 292 sensor_attach(&sc->sc_sensordev, &fan->sensor); 293 } 294 295 /* 296 * Bail out if we didn't find any fans. If we don't set the 297 * fans to a safe speed, but tickle the SMU periodically by 298 * reading sensors, the fans will never spin up and the 299 * machine might overheat. 300 */ 301 if (sc->sc_num_fans == 0) { 302 printf(": no fans\n"); 303 return; 304 } 305 306 /* Sensors */ 307 node = OF_getnodebyname(ca->ca_node, "sensors"); 308 for (node = OF_child(node); node; node = OF_peer(node)) { 309 if (OF_getprop(node, "reg", &val, sizeof val) <= 0 || 310 OF_getprop(node, "device_type", type, sizeof type) <= 0) 311 continue; 312 313 if (sc->sc_num_sensors >= SMU_MAXSENSORS) { 314 printf(": too many sensors\n"); 315 return; 316 } 317 318 sensor = &sc->sc_sensors[sc->sc_num_sensors++]; 319 sensor->sensor.flags = SENSOR_FINVALID; 320 sensor->reg = val; 321 322 if (strcmp(type, "current-sensor") == 0) { 323 sensor->sensor.type = SENSOR_AMPS; 324 } else if (strcmp(type, "temp-sensor") == 0) { 325 sensor->sensor.type = SENSOR_TEMP; 326 } else if (strcmp(type, "voltage-sensor") == 0) { 327 sensor->sensor.type = SENSOR_VOLTS_DC; 328 } else if (strcmp(type, "power-sensor") == 0) { 329 sensor->sensor.type = SENSOR_WATTS; 330 } else { 331 sensor->sensor.type = SENSOR_INTEGER; 332 } 333 334 if (OF_getprop(node, "location", loc, sizeof loc) <= 0) 335 strlcpy(loc, "Unknown", sizeof loc); 336 strlcpy(sensor->sensor.desc, loc, sizeof sensor->sensor.desc); 337 338 sensor_attach(&sc->sc_sensordev, &sensor->sensor); 339 } 340 341 /* Register sensor device with sysctl */ 342 strlcpy(sc->sc_sensordev.xname, sc->sc_dev.dv_xname, 343 sizeof(sc->sc_sensordev.xname)); 344 sensordev_install(&sc->sc_sensordev); 345 346 /* CPU temperature diode calibration */ 347 smu_get_datablock(sc, 0x18, data, sizeof data); 348 sc->sc_cpu_diode_scale = (data[4] << 8) + data[5]; 349 sc->sc_cpu_diode_offset = (data[6] << 8) + data[7]; 350 351 /* CPU power (voltage and current) calibration */ 352 smu_get_datablock(sc, 0x21, data, sizeof data); 353 sc->sc_cpu_volt_scale = (data[4] << 8) + data[5]; 354 sc->sc_cpu_volt_offset = (data[6] << 8) + data[7]; 355 sc->sc_cpu_curr_scale = (data[8] << 8) + data[9]; 356 sc->sc_cpu_curr_offset = (data[10] << 8) + data[11]; 357 358 /* Slots power calibration */ 359 smu_get_datablock(sc, 0x78, data, sizeof data); 360 sc->sc_slots_pow_scale = (data[4] << 8) + data[5]; 361 sc->sc_slots_pow_offset = (data[6] << 8) + data[7]; 362 363 sensor_task_register(sc, smu_refresh_sensors, 5); 364 printf("\n"); 365 366 ppc64_slew_voltage = smu_slew_voltage; 367 368 sc->sc_i2c_tag.ic_cookie = sc; 369 sc->sc_i2c_tag.ic_acquire_bus = smu_i2c_acquire_bus; 370 sc->sc_i2c_tag.ic_release_bus = smu_i2c_release_bus; 371 sc->sc_i2c_tag.ic_exec = smu_i2c_exec; 372 373 /* 374 * Early versions of the SMU have the i2c bus node directly 375 * below the "smu" node, while later models have an 376 * intermediate "smu-i2c-control" node. 377 */ 378 node = OF_getnodebyname(ca->ca_node, "smu-i2c-control"); 379 if (node) 380 node = OF_child(node); 381 else 382 node = OF_getnodebyname(ca->ca_node, "i2c"); 383 384 bzero(&iba, sizeof iba); 385 iba.iba_name = "iic"; 386 iba.iba_tag = &sc->sc_i2c_tag; 387 iba.iba_bus_scan = maciic_scan; 388 iba.iba_bus_scan_arg = &node; 389 config_found(&sc->sc_dev, &iba, NULL); 390} 391 392int 393smu_intr(void *arg) 394{ 395 wakeup(arg); 396 return 1; 397} 398 399int 400smu_do_cmd(struct smu_softc *sc, int timo) 401{ 402 struct smu_cmd *cmd = (struct smu_cmd *)sc->sc_cmd; 403 u_int8_t gpio, ack = ~cmd->cmd; 404 int error; 405 406 /* Write to mailbox. */ 407 bus_space_write_4(sc->sc_memt, sc->sc_buffh, 0, 408 sc->sc_cmdmap->dm_segs->ds_addr); 409 410 /* Flush to RAM. */ 411 asm volatile ("dcbst 0,%0; sync" :: "r"(sc->sc_cmd): "memory"); 412 413 /* Ring doorbell. */ 414 bus_space_write_1(sc->sc_memt, sc->sc_gpioh, 0, GPIO_DDR_OUTPUT); 415 416 do { 417 error = tsleep(sc, PWAIT, "smu", (timo * hz) / 1000); 418 if (error) 419 return (error); 420 gpio = bus_space_read_1(sc->sc_memt, sc->sc_gpioh, 0); 421 } while (!(gpio & (GPIO_DATA))); 422 423 /* CPU might have brought back the cache line. */ 424 asm volatile ("dcbf 0,%0; sync" :: "r"(sc->sc_cmd) : "memory"); 425 426 if (cmd->cmd != ack) 427 return (EIO); 428 return (0); 429} 430 431int 432smu_time_read(time_t *secs) 433{ 434 struct smu_softc *sc = smu_cd.cd_devs[0]; 435 struct smu_cmd *cmd = (struct smu_cmd *)sc->sc_cmd; 436 struct clock_ymdhms dt; 437 int error; 438 439 rw_enter_write(&sc->sc_lock); 440 441 cmd->cmd = SMU_RTC; 442 cmd->len = 1; 443 cmd->data[0] = SMU_RTC_GET_DATETIME; 444 error = smu_do_cmd(sc, 800); 445 if (error) { 446 rw_exit_write(&sc->sc_lock); 447 448 *secs = 0; 449 return (error); 450 } 451 452 dt.dt_year = 2000 + FROMBCD(cmd->data[6]); 453 dt.dt_mon = FROMBCD(cmd->data[5]); 454 dt.dt_day = FROMBCD(cmd->data[4]); 455 dt.dt_hour = FROMBCD(cmd->data[2]); 456 dt.dt_min = FROMBCD(cmd->data[1]); 457 dt.dt_sec = FROMBCD(cmd->data[0]); 458 459 rw_exit_write(&sc->sc_lock); 460 461 *secs = clock_ymdhms_to_secs(&dt); 462 return (0); 463} 464 465int 466smu_time_write(time_t secs) 467{ 468 struct smu_softc *sc = smu_cd.cd_devs[0]; 469 struct smu_cmd *cmd = (struct smu_cmd *)sc->sc_cmd; 470 struct clock_ymdhms dt; 471 int error; 472 473 clock_secs_to_ymdhms(secs, &dt); 474 475 rw_enter_write(&sc->sc_lock); 476 477 cmd->cmd = SMU_RTC; 478 cmd->len = 8; 479 cmd->data[0] = SMU_RTC_SET_DATETIME; 480 cmd->data[1] = TOBCD(dt.dt_sec); 481 cmd->data[2] = TOBCD(dt.dt_min); 482 cmd->data[3] = TOBCD(dt.dt_hour); 483 cmd->data[4] = TOBCD(dt.dt_wday); 484 cmd->data[5] = TOBCD(dt.dt_day); 485 cmd->data[6] = TOBCD(dt.dt_mon); 486 cmd->data[7] = TOBCD(dt.dt_year - 2000); 487 error = smu_do_cmd(sc, 800); 488 489 rw_exit_write(&sc->sc_lock); 490 491 return (error); 492} 493 494 495int 496smu_get_datablock(struct smu_softc *sc, u_int8_t id, u_int8_t *buf, size_t len) 497{ 498 struct smu_cmd *cmd = (struct smu_cmd *)sc->sc_cmd; 499 u_int8_t addr[4]; 500 int error; 501 502 cmd->cmd = SMU_PARTITION; 503 cmd->len = 2; 504 cmd->data[0] = SMU_PARTITION_LATEST; 505 cmd->data[1] = id; 506 error = smu_do_cmd(sc, 800); 507 if (error) 508 return (error); 509 510 addr[0] = 0x00; 511 addr[1] = 0x00; 512 addr[2] = cmd->data[0]; 513 addr[3] = cmd->data[1]; 514 515 cmd->cmd = SMU_MISC; 516 cmd->len = 7; 517 cmd->data[0] = SMU_MISC_GET_DATA; 518 cmd->data[1] = sizeof(u_int32_t); 519 cmd->data[2] = addr[0]; 520 cmd->data[3] = addr[1]; 521 cmd->data[4] = addr[2]; 522 cmd->data[5] = addr[3]; 523 cmd->data[6] = len; 524 error = smu_do_cmd(sc, 800); 525 if (error) 526 return (error); 527 528 memcpy(buf, cmd->data, len); 529 return (0); 530} 531 532int 533smu_fan_set_rpm(struct smu_softc *sc, struct smu_fan *fan, u_int16_t rpm) 534{ 535 struct smu_cmd *cmd = (struct smu_cmd *)sc->sc_cmd; 536 537 /* 538 * On the PowerMac8,2 this command expects the requested fan 539 * speed at a different location in the command block than on 540 * the PowerMac8,1. We simply store the value at both 541 * locations. 542 */ 543 cmd->cmd = SMU_FAN; 544 cmd->len = 14; 545 cmd->data[0] = 0x00; /* fan-rpm-control */ 546 cmd->data[1] = 0x01 << fan->reg; 547 cmd->data[2] = cmd->data[2 + fan->reg * 2] = (rpm >> 8) & 0xff; 548 cmd->data[3] = cmd->data[3 + fan->reg * 2] = (rpm & 0xff); 549 return smu_do_cmd(sc, 800); 550} 551 552int 553smu_fan_refresh(struct smu_softc *sc, struct smu_fan *fan) 554{ 555 struct smu_cmd *cmd = (struct smu_cmd *)sc->sc_cmd; 556 int error; 557 558 cmd->cmd = SMU_FAN; 559 cmd->len = 2; 560 cmd->data[0] = 0x01; /* fan-rpm-control */ 561 cmd->data[1] = 0x01 << fan->reg; 562 error = smu_do_cmd(sc, 800); 563 if (error) { 564 fan->sensor.flags = SENSOR_FINVALID; 565 return (error); 566 } 567 fan->sensor.value = (cmd->data[1] << 8) + cmd->data[2]; 568 fan->sensor.flags = 0; 569 return (0); 570} 571 572int 573smu_sensor_refresh(struct smu_softc *sc, struct smu_sensor *sensor) 574{ 575 struct smu_cmd *cmd = (struct smu_cmd *)sc->sc_cmd; 576 int64_t value; 577 int error; 578 579 cmd->cmd = SMU_ADC; 580 cmd->len = 1; 581 cmd->data[0] = sensor->reg; 582 error = smu_do_cmd(sc, 800); 583 if (error) { 584 sensor->sensor.flags = SENSOR_FINVALID; 585 return (error); 586 } 587 value = (cmd->data[0] << 8) + cmd->data[1]; 588 switch (sensor->sensor.type) { 589 case SENSOR_TEMP: 590 value *= sc->sc_cpu_diode_scale; 591 value >>= 3; 592 value += ((int64_t)sc->sc_cpu_diode_offset) << 9; 593 value <<= 1; 594 595 /* Convert from 16.16 fixed point degC into muK. */ 596 value *= 15625; 597 value /= 1024; 598 value += 273150000; 599 break; 600 601 case SENSOR_VOLTS_DC: 602 value *= sc->sc_cpu_volt_scale; 603 value += sc->sc_cpu_volt_offset; 604 value <<= 4; 605 606 /* Convert from 16.16 fixed point V into muV. */ 607 value *= 15625; 608 value /= 1024; 609 break; 610 611 case SENSOR_AMPS: 612 value *= sc->sc_cpu_curr_scale; 613 value += sc->sc_cpu_curr_offset; 614 value <<= 4; 615 616 /* Convert from 16.16 fixed point A into muA. */ 617 value *= 15625; 618 value /= 1024; 619 break; 620 621 case SENSOR_WATTS: 622 value *= sc->sc_slots_pow_scale; 623 value += sc->sc_slots_pow_offset; 624 value <<= 4; 625 626 /* Convert from 16.16 fixed point W into muW. */ 627 value *= 15625; 628 value /= 1024; 629 break; 630 631 default: 632 break; 633 } 634 sensor->sensor.value = value; 635 sensor->sensor.flags = 0; 636 return (0); 637} 638 639void 640smu_refresh_sensors(void *arg) 641{ 642 struct smu_softc *sc = arg; 643 int i; 644 645 rw_enter_write(&sc->sc_lock); 646 for (i = 0; i < sc->sc_num_sensors; i++) 647 smu_sensor_refresh(sc, &sc->sc_sensors[i]); 648 for (i = 0; i < sc->sc_num_fans; i++) 649 smu_fan_refresh(sc, &sc->sc_fans[i]); 650 rw_exit_write(&sc->sc_lock); 651} 652 653int 654smu_i2c_acquire_bus(void *cookie, int flags) 655{ 656 struct smu_softc *sc = cookie; 657 658 if (flags & I2C_F_POLL) 659 return (0); 660 661 return (rw_enter(&sc->sc_lock, RW_WRITE)); 662} 663 664void 665smu_i2c_release_bus(void *cookie, int flags) 666{ 667 struct smu_softc *sc = cookie; 668 669 if (flags & I2C_F_POLL) 670 return; 671 672 rw_exit(&sc->sc_lock); 673} 674 675int 676smu_i2c_exec(void *cookie, i2c_op_t op, i2c_addr_t addr, 677 const void *cmdbuf, size_t cmdlen, void *buf, size_t len, int flags) 678{ 679 struct smu_softc *sc = cookie; 680 struct smu_cmd *cmd = (struct smu_cmd *)sc->sc_cmd; 681 u_int8_t smu_op = SMU_I2C_NORMAL; 682 int error, retries = 10; 683 684 if (!I2C_OP_STOP_P(op) || cmdlen > 3 || len > 5) 685 return (EINVAL); 686 687 if(cmdlen == 0) 688 smu_op = SMU_I2C_SIMPLE; 689 else if (I2C_OP_READ_P(op)) 690 smu_op = SMU_I2C_COMBINED; 691 692 cmd->cmd = SMU_I2C; 693 cmd->len = 9 + len; 694 cmd->data[0] = 0xb; 695 cmd->data[1] = smu_op; 696 cmd->data[2] = addr << 1; 697 cmd->data[3] = cmdlen; 698 memcpy (&cmd->data[4], cmdbuf, cmdlen); 699 cmd->data[7] = addr << 1 | I2C_OP_READ_P(op); 700 cmd->data[8] = len; 701 memcpy(&cmd->data[9], buf, len); 702 703 error = smu_do_cmd(sc, 250); 704 if (error) 705 return error; 706 707 while (retries--) { 708 cmd->cmd = SMU_I2C; 709 cmd->len = 1; 710 cmd->data[0] = 0; 711 memset(&cmd->data[1], 0xff, len); 712 713 error = smu_do_cmd(sc, 250); 714 if (error) 715 return error; 716 717 if ((cmd->data[0] & 0x80) == 0) 718 break; 719 if (cmd->data[0] == 0xfd) 720 break; 721 722 DELAY(15 * 1000); 723 } 724 725 if (cmd->data[0] & 0x80) 726 return (EIO); 727 728 if (I2C_OP_READ_P(op)) 729 memcpy(buf, &cmd->data[1], len); 730 return (0); 731} 732 733void 734smu_slew_voltage(u_int freq_scale) 735{ 736 struct smu_softc *sc = smu_cd.cd_devs[0]; 737 struct smu_cmd *cmd = (struct smu_cmd *)sc->sc_cmd; 738 739 cmd->cmd = SMU_POWER; 740 cmd->len = 8; 741 memcpy(cmd->data, "VSLEW", 5); 742 cmd->data[5] = 0xff; 743 cmd->data[6] = 1; 744 cmd->data[7] = freq_scale; 745 746 smu_do_cmd(sc, 250); 747} 748