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