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