smu.c revision 1.14
1/* $OpenBSD: smu.c,v 1.14 2007/03/01 20:54:33 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/lock.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 sensor sensor; 47}; 48 49#define SMU_MAXSENSORS 3 50 51struct smu_sensor { 52 u_int8_t reg; 53 struct sensor 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 lock 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 sensordev 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 90struct cfattach smu_ca = { 91 sizeof(struct smu_softc), smu_match, smu_attach 92}; 93 94struct cfdriver smu_cd = { 95 NULL, "smu", DV_DULL, 96}; 97 98/* SMU command */ 99struct 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 136int smu_intr(void *); 137 138int smu_do_cmd(struct smu_softc *, int); 139int smu_time_read(time_t *); 140int smu_time_write(time_t); 141int smu_get_datablock(struct smu_softc *sc, u_int8_t, u_int8_t *, size_t); 142int smu_fan_set_rpm(struct smu_softc *, struct smu_fan *, u_int16_t); 143int smu_fan_refresh(struct smu_softc *, struct smu_fan *); 144int smu_sensor_refresh(struct smu_softc *, struct smu_sensor *); 145void smu_refresh_sensors(void *); 146 147int smu_i2c_acquire_bus(void *, int); 148void smu_i2c_release_bus(void *, int); 149int smu_i2c_exec(void *, i2c_op_t, i2c_addr_t, 150 const void *, size_t, void *buf, size_t, int); 151 152void 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 162int 163smu_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 */ 173extern struct powerpc_bus_dma_tag pci_bus_dma_tag; 174 175void 176smu_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 lockinit(&sc->sc_lock, PZERO, sc->sc_dev.dv_xname, 0, 0); 246 247 /* Establish smu-doorbell interrupt. */ 248 mac_intr_establish(parent, intr, IST_EDGE, IPL_BIO, 249 smu_intr, sc, "smu"); 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, "fans"); 257 for (node = OF_child(node); node; node = OF_peer(node)) { 258 if (OF_getprop(node, "reg", ®, sizeof reg) <= 0 || 259 OF_getprop(node, "device_type", type, sizeof type) <= 0) 260 continue; 261 262 if (strcmp(type, "fan-rpm-control") != 0) { 263 printf(": unsupported fan type: %s\n", type); 264 return; 265 } 266 267 if (sc->sc_num_fans >= SMU_MAXFANS) { 268 printf(": too many fans\n"); 269 return; 270 } 271 272 fan = &sc->sc_fans[sc->sc_num_fans++]; 273 fan->sensor.type = SENSOR_FANRPM; 274 fan->sensor.flags = SENSOR_FINVALID; 275 fan->reg = reg; 276 277 if (OF_getprop(node, "min-value", &val, sizeof val) <= 0) 278 val = 0; 279 fan->min_rpm = val; 280 if (OF_getprop(node, "max-value", &val, sizeof val) <= 0) 281 val = 0xffff; 282 fan->max_rpm = val; 283 if (OF_getprop(node, "unmanage-value", &val, sizeof val) <= 0) 284 val = fan->max_rpm; 285 fan->unmanaged_rpm = val; 286 287 if (OF_getprop(node, "location", loc, sizeof loc) <= 0) 288 strlcpy(loc, "Unknown", sizeof loc); 289 strlcpy(fan->sensor.desc, loc, sizeof sensor->sensor.desc); 290 291 /* Start running fans at their "unmanaged" speed. */ 292 smu_fan_set_rpm(sc, fan, fan->unmanaged_rpm); 293 294 sensor_attach(&sc->sc_sensordev, &fan->sensor); 295 } 296 297 /* 298 * Bail out if we didn't find any fans. If we don't set the 299 * fans to a safe speed, but tickle the SMU periodically by 300 * reading sensors, the fans will never spin up and the 301 * machine might overheat. 302 */ 303 if (sc->sc_num_fans == 0) { 304 printf(": no fans\n"); 305 return; 306 } 307 308 /* Sensors */ 309 node = OF_getnodebyname(ca->ca_node, "sensors"); 310 for (node = OF_child(node); node; node = OF_peer(node)) { 311 if (OF_getprop(node, "reg", &val, sizeof val) <= 0 || 312 OF_getprop(node, "device_type", type, sizeof type) <= 0) 313 continue; 314 315 sensor = &sc->sc_sensors[sc->sc_num_sensors++]; 316 sensor->sensor.flags = SENSOR_FINVALID; 317 sensor->reg = val; 318 319 if (strcmp(type, "current-sensor") == 0) { 320 sensor->sensor.type = SENSOR_AMPS; 321 } else if (strcmp(type, "temp-sensor") == 0) { 322 sensor->sensor.type = SENSOR_TEMP; 323 } else if (strcmp(type, "voltage-sensor") == 0) { 324 sensor->sensor.type = SENSOR_VOLTS_DC; 325 } else { 326 sensor->sensor.type = SENSOR_INTEGER; 327 } 328 329 if (OF_getprop(node, "location", loc, sizeof loc) <= 0) 330 strlcpy(loc, "Unknown", sizeof loc); 331 strlcpy(sensor->sensor.desc, loc, sizeof sensor->sensor.desc); 332 333 sensor_attach(&sc->sc_sensordev, &sensor->sensor); 334 } 335 336 /* Register sensor device with sysctl */ 337 strlcpy(sc->sc_sensordev.xname, sc->sc_dev.dv_xname, 338 sizeof(sc->sc_sensordev.xname)); 339 sensordev_install(&sc->sc_sensordev); 340 341 /* CPU temperature diode calibration */ 342 smu_get_datablock(sc, 0x18, data, sizeof data); 343 sc->sc_cpu_diode_scale = (data[4] << 8) + data[5]; 344 sc->sc_cpu_diode_offset = (data[6] << 8) + data[7]; 345 346 /* CPU power (voltage and current) calibration */ 347 smu_get_datablock(sc, 0x21, data, sizeof data); 348 sc->sc_cpu_volt_scale = (data[4] << 8) + data[5]; 349 sc->sc_cpu_volt_offset = (data[6] << 8) + data[7]; 350 sc->sc_cpu_curr_scale = (data[8] << 8) + data[9]; 351 sc->sc_cpu_curr_offset = (data[10] << 8) + data[11]; 352 353 sensor_task_register(sc, smu_refresh_sensors, 5); 354 printf("\n"); 355 356 ppc64_slew_voltage = smu_slew_voltage; 357 358 sc->sc_i2c_tag.ic_cookie = sc; 359 sc->sc_i2c_tag.ic_acquire_bus = smu_i2c_acquire_bus; 360 sc->sc_i2c_tag.ic_release_bus = smu_i2c_release_bus; 361 sc->sc_i2c_tag.ic_exec = smu_i2c_exec; 362 363 node = OF_getnodebyname(ca->ca_node, "smu-i2c-control"); 364 node = OF_child(node); 365 366 bzero(&iba, sizeof iba); 367 iba.iba_name = "iic"; 368 iba.iba_tag = &sc->sc_i2c_tag; 369 iba.iba_bus_scan = maciic_scan; 370 iba.iba_bus_scan_arg = &node; 371 config_found(&sc->sc_dev, &iba, NULL); 372} 373 374int 375smu_intr(void *arg) 376{ 377 wakeup(arg); 378 return 1; 379} 380 381int 382smu_do_cmd(struct smu_softc *sc, int timo) 383{ 384 struct smu_cmd *cmd = (struct smu_cmd *)sc->sc_cmd; 385 u_int8_t gpio, ack = ~cmd->cmd; 386 int error; 387 388 /* Write to mailbox. */ 389 bus_space_write_4(sc->sc_memt, sc->sc_buffh, 0, 390 sc->sc_cmdmap->dm_segs->ds_addr); 391 392 /* Flush to RAM. */ 393 asm __volatile__ ("dcbst 0,%0; sync" :: "r"(sc->sc_cmd): "memory"); 394 395 /* Ring doorbell. */ 396 bus_space_write_1(sc->sc_memt, sc->sc_gpioh, 0, GPIO_DDR_OUTPUT); 397 398 do { 399 error = tsleep(sc, PWAIT, "smu", (timo * hz) / 1000); 400 if (error) 401 return (error); 402 gpio = bus_space_read_1(sc->sc_memt, sc->sc_gpioh, 0); 403 } while (!(gpio & (GPIO_DATA))); 404 405 /* CPU might have brought back the cache line. */ 406 asm __volatile__ ("dcbf 0,%0; sync" :: "r"(sc->sc_cmd) : "memory"); 407 408 if (cmd->cmd != ack) 409 return (EIO); 410 return (0); 411} 412 413int 414smu_time_read(time_t *secs) 415{ 416 struct smu_softc *sc = smu_cd.cd_devs[0]; 417 struct smu_cmd *cmd = (struct smu_cmd *)sc->sc_cmd; 418 struct clock_ymdhms dt; 419 int error; 420 421 lockmgr(&sc->sc_lock, LK_EXCLUSIVE, NULL); 422 423 cmd->cmd = SMU_RTC; 424 cmd->len = 1; 425 cmd->data[0] = SMU_RTC_GET_DATETIME; 426 error = smu_do_cmd(sc, 800); 427 if (error) { 428 lockmgr(&sc->sc_lock, LK_RELEASE, NULL); 429 430 *secs = 0; 431 return (error); 432 } 433 434 dt.dt_year = 2000 + FROMBCD(cmd->data[6]); 435 dt.dt_mon = FROMBCD(cmd->data[5]); 436 dt.dt_day = FROMBCD(cmd->data[4]); 437 dt.dt_hour = FROMBCD(cmd->data[2]); 438 dt.dt_min = FROMBCD(cmd->data[1]); 439 dt.dt_sec = FROMBCD(cmd->data[0]); 440 441 lockmgr(&sc->sc_lock, LK_RELEASE, NULL); 442 443 *secs = clock_ymdhms_to_secs(&dt); 444 return (0); 445} 446 447int 448smu_time_write(time_t secs) 449{ 450 struct smu_softc *sc = smu_cd.cd_devs[0]; 451 struct smu_cmd *cmd = (struct smu_cmd *)sc->sc_cmd; 452 struct clock_ymdhms dt; 453 int error; 454 455 clock_secs_to_ymdhms(secs, &dt); 456 457 lockmgr(&sc->sc_lock, LK_EXCLUSIVE, NULL); 458 459 cmd->cmd = SMU_RTC; 460 cmd->len = 8; 461 cmd->data[0] = SMU_RTC_SET_DATETIME; 462 cmd->data[1] = TOBCD(dt.dt_sec); 463 cmd->data[2] = TOBCD(dt.dt_min); 464 cmd->data[3] = TOBCD(dt.dt_hour); 465 cmd->data[4] = TOBCD(dt.dt_wday); 466 cmd->data[5] = TOBCD(dt.dt_day); 467 cmd->data[6] = TOBCD(dt.dt_mon); 468 cmd->data[7] = TOBCD(dt.dt_year - 2000); 469 error = smu_do_cmd(sc, 800); 470 471 lockmgr(&sc->sc_lock, LK_RELEASE, NULL); 472 473 return (error); 474} 475 476 477int 478smu_get_datablock(struct smu_softc *sc, u_int8_t id, u_int8_t *buf, size_t len) 479{ 480 struct smu_cmd *cmd = (struct smu_cmd *)sc->sc_cmd; 481 u_int8_t addr[4]; 482 int error; 483 484 cmd->cmd = SMU_PARTITION; 485 cmd->len = 2; 486 cmd->data[0] = SMU_PARTITION_LATEST; 487 cmd->data[1] = id; 488 error = smu_do_cmd(sc, 800); 489 if (error) 490 return (error); 491 492 addr[0] = 0x00; 493 addr[1] = 0x00; 494 addr[2] = cmd->data[0]; 495 addr[3] = cmd->data[1]; 496 497 cmd->cmd = SMU_MISC; 498 cmd->len = 7; 499 cmd->data[0] = SMU_MISC_GET_DATA; 500 cmd->data[1] = sizeof(u_int32_t); 501 cmd->data[2] = addr[0]; 502 cmd->data[3] = addr[1]; 503 cmd->data[4] = addr[2]; 504 cmd->data[5] = addr[3]; 505 cmd->data[6] = len; 506 error = smu_do_cmd(sc, 800); 507 if (error) 508 return (error); 509 510 memcpy(buf, cmd->data, len); 511 return (0); 512} 513 514int 515smu_fan_set_rpm(struct smu_softc *sc, struct smu_fan *fan, u_int16_t rpm) 516{ 517 struct smu_cmd *cmd = (struct smu_cmd *)sc->sc_cmd; 518 519 cmd->cmd = SMU_FAN; 520 cmd->len = 4; 521 cmd->data[0] = 0x00; /* fan-rpm-control */ 522 cmd->data[1] = 0x01 << fan->reg; 523 cmd->data[2] = (rpm >> 8) & 0xff; 524 cmd->data[3] = (rpm & 0xff); 525 return smu_do_cmd(sc, 800); 526} 527 528int 529smu_fan_refresh(struct smu_softc *sc, struct smu_fan *fan) 530{ 531 struct smu_cmd *cmd = (struct smu_cmd *)sc->sc_cmd; 532 int error; 533 534 cmd->cmd = SMU_FAN; 535 cmd->len = 2; 536 cmd->data[0] = 0x01; /* fan-rpm-control */ 537 cmd->data[1] = 0x01 << fan->reg; 538 error = smu_do_cmd(sc, 800); 539 if (error) { 540 fan->sensor.flags = SENSOR_FINVALID; 541 return (error); 542 } 543 fan->sensor.value = (cmd->data[1] << 8) + cmd->data[2]; 544 fan->sensor.flags = 0; 545 return (0); 546} 547 548int 549smu_sensor_refresh(struct smu_softc *sc, struct smu_sensor *sensor) 550{ 551 struct smu_cmd *cmd = (struct smu_cmd *)sc->sc_cmd; 552 int64_t value; 553 int error; 554 555 cmd->cmd = SMU_ADC; 556 cmd->len = 1; 557 cmd->data[0] = sensor->reg; 558 error = smu_do_cmd(sc, 800); 559 if (error) { 560 sensor->sensor.flags = SENSOR_FINVALID; 561 return (error); 562 } 563 value = (cmd->data[0] << 8) + cmd->data[1]; 564 switch (sensor->sensor.type) { 565 case SENSOR_TEMP: 566 value *= sc->sc_cpu_diode_scale; 567 value >>= 3; 568 value += ((int64_t)sc->sc_cpu_diode_offset) << 9; 569 value <<= 1; 570 571 /* Convert from 16.16 fixed point degC into muK. */ 572 value *= 15625; 573 value /= 1024; 574 value += 273150000; 575 break; 576 577 case SENSOR_VOLTS_DC: 578 value *= sc->sc_cpu_volt_scale; 579 value += sc->sc_cpu_volt_offset; 580 value <<= 4; 581 582 /* Convert from 16.16 fixed point V into muV. */ 583 value *= 15625; 584 value /= 1024; 585 break; 586 587 case SENSOR_AMPS: 588 value *= sc->sc_cpu_curr_scale; 589 value += sc->sc_cpu_curr_offset; 590 value <<= 4; 591 592 /* Convert from 16.16 fixed point A into muA. */ 593 value *= 15625; 594 value /= 1024; 595 break; 596 597 default: 598 break; 599 } 600 sensor->sensor.value = value; 601 sensor->sensor.flags = 0; 602 return (0); 603} 604 605void 606smu_refresh_sensors(void *arg) 607{ 608 struct smu_softc *sc = arg; 609 int i; 610 611 lockmgr(&sc->sc_lock, LK_EXCLUSIVE, NULL); 612 for (i = 0; i < sc->sc_num_sensors; i++) 613 smu_sensor_refresh(sc, &sc->sc_sensors[i]); 614 for (i = 0; i < sc->sc_num_fans; i++) 615 smu_fan_refresh(sc, &sc->sc_fans[i]); 616 lockmgr(&sc->sc_lock, LK_RELEASE, NULL); 617} 618 619int 620smu_i2c_acquire_bus(void *cookie, int flags) 621{ 622 struct smu_softc *sc = cookie; 623 624 if (flags & I2C_F_POLL) 625 return (0); 626 627 return (lockmgr(&sc->sc_lock, LK_EXCLUSIVE, NULL)); 628} 629 630void 631smu_i2c_release_bus(void *cookie, int flags) 632{ 633 struct smu_softc *sc = cookie; 634 635 if (flags & I2C_F_POLL) 636 return; 637 638 lockmgr(&sc->sc_lock, LK_RELEASE, NULL); 639} 640 641int 642smu_i2c_exec(void *cookie, i2c_op_t op, i2c_addr_t addr, 643 const void *cmdbuf, size_t cmdlen, void *buf, size_t len, int flags) 644{ 645 struct smu_softc *sc = cookie; 646 struct smu_cmd *cmd = (struct smu_cmd *)sc->sc_cmd; 647 u_int8_t smu_op = SMU_I2C_NORMAL; 648 int error, retries = 10; 649 650 if (!I2C_OP_STOP_P(op) || cmdlen > 3 || len > 5) 651 return (EINVAL); 652 653 if(cmdlen == 0) 654 smu_op = SMU_I2C_SIMPLE; 655 else if (I2C_OP_READ_P(op)) 656 smu_op = SMU_I2C_COMBINED; 657 658 cmd->cmd = SMU_I2C; 659 cmd->len = 9 + len; 660 cmd->data[0] = 0xb; 661 cmd->data[1] = smu_op; 662 cmd->data[2] = addr << 1; 663 cmd->data[3] = cmdlen; 664 memcpy (&cmd->data[4], cmdbuf, cmdlen); 665 cmd->data[7] = addr << 1 | I2C_OP_READ_P(op); 666 cmd->data[8] = len; 667 memcpy(&cmd->data[9], buf, len); 668 669 error = smu_do_cmd(sc, 250); 670 if (error) 671 return error; 672 673 while (retries--) { 674 cmd->cmd = SMU_I2C; 675 cmd->len = 1; 676 cmd->data[0] = 0; 677 memset(&cmd->data[1], 0xff, len); 678 679 error = smu_do_cmd(sc, 250); 680 if (error) 681 return error; 682 683 if ((cmd->data[0] & 0x80) == 0) 684 break; 685 if (cmd->data[0] == 0xfd) 686 break; 687 688 DELAY(15 * 1000); 689 } 690 691 if (cmd->data[0] & 0x80) 692 return (EIO); 693 694 if (I2C_OP_READ_P(op)) 695 memcpy(buf, &cmd->data[1], len); 696 return (0); 697} 698 699void 700smu_slew_voltage(u_int freq_scale) 701{ 702 struct smu_softc *sc = smu_cd.cd_devs[0]; 703 struct smu_cmd *cmd = (struct smu_cmd *)sc->sc_cmd; 704 705 cmd->cmd = SMU_POWER; 706 cmd->len = 8; 707 memcpy(cmd->data, "VSLEW", 5); 708 cmd->data[5] = 0xff; 709 cmd->data[6] = 1; 710 cmd->data[7] = freq_scale; 711 712 smu_do_cmd(sc, 250); 713} 714