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