1 /* 2 * PMBus wrapper over SMBus 3 * 4 * Copyright 2021 Google LLC 5 * 6 * SPDX-License-Identifier: GPL-2.0-or-later 7 */ 8 9 #include "qemu/osdep.h" 10 #include <math.h> 11 #include "hw/i2c/pmbus_device.h" 12 #include "migration/vmstate.h" 13 #include "qemu/module.h" 14 #include "qemu/log.h" 15 16 uint16_t pmbus_data2direct_mode(PMBusCoefficients c, uint32_t value) 17 { 18 /* R is usually negative to fit large readings into 16 bits */ 19 uint16_t y = (c.m * value + c.b) * pow(10, c.R); 20 return y; 21 } 22 23 uint32_t pmbus_direct_mode2data(PMBusCoefficients c, uint16_t value) 24 { 25 /* X = (Y * 10^-R - b) / m */ 26 uint32_t x = (value / pow(10, c.R) - c.b) / c.m; 27 return x; 28 } 29 30 uint16_t pmbus_data2linear_mode(uint16_t value, int exp) 31 { 32 /* L = D * 2^(-e) */ 33 if (exp < 0) { 34 return value << (-exp); 35 } 36 return value >> exp; 37 } 38 39 uint16_t pmbus_linear_mode2data(uint16_t value, int exp) 40 { 41 /* D = L * 2^e */ 42 if (exp < 0) { 43 return value >> (-exp); 44 } 45 return value << exp; 46 } 47 48 void pmbus_send(PMBusDevice *pmdev, const uint8_t *data, uint16_t len) 49 { 50 if (pmdev->out_buf_len + len > SMBUS_DATA_MAX_LEN) { 51 qemu_log_mask(LOG_GUEST_ERROR, 52 "PMBus device tried to send too much data"); 53 len = 0; 54 } 55 56 for (int i = len - 1; i >= 0; i--) { 57 pmdev->out_buf[i + pmdev->out_buf_len] = data[len - i - 1]; 58 } 59 pmdev->out_buf_len += len; 60 } 61 62 /* Internal only, convert unsigned ints to the little endian bus */ 63 static void pmbus_send_uint(PMBusDevice *pmdev, uint64_t data, uint8_t size) 64 { 65 uint8_t bytes[8]; 66 g_assert(size <= 8); 67 68 for (int i = 0; i < size; i++) { 69 bytes[i] = data & 0xFF; 70 data = data >> 8; 71 } 72 pmbus_send(pmdev, bytes, size); 73 } 74 75 void pmbus_send8(PMBusDevice *pmdev, uint8_t data) 76 { 77 pmbus_send_uint(pmdev, data, 1); 78 } 79 80 void pmbus_send16(PMBusDevice *pmdev, uint16_t data) 81 { 82 pmbus_send_uint(pmdev, data, 2); 83 } 84 85 void pmbus_send32(PMBusDevice *pmdev, uint32_t data) 86 { 87 pmbus_send_uint(pmdev, data, 4); 88 } 89 90 void pmbus_send64(PMBusDevice *pmdev, uint64_t data) 91 { 92 pmbus_send_uint(pmdev, data, 8); 93 } 94 95 void pmbus_send_string(PMBusDevice *pmdev, const char *data) 96 { 97 if (!data) { 98 qemu_log_mask(LOG_GUEST_ERROR, 99 "%s: %s: uninitialised read from 0x%02x\n", 100 __func__, DEVICE(pmdev)->canonical_path, pmdev->code); 101 return; 102 } 103 104 size_t len = strlen(data); 105 g_assert(len > 0); 106 g_assert(len + pmdev->out_buf_len < SMBUS_DATA_MAX_LEN); 107 pmdev->out_buf[len + pmdev->out_buf_len] = len; 108 109 for (int i = len - 1; i >= 0; i--) { 110 pmdev->out_buf[i + pmdev->out_buf_len] = data[len - 1 - i]; 111 } 112 pmdev->out_buf_len += len + 1; 113 } 114 115 116 static uint64_t pmbus_receive_uint(PMBusDevice *pmdev) 117 { 118 uint64_t ret = 0; 119 120 /* Exclude command code from return value */ 121 pmdev->in_buf++; 122 pmdev->in_buf_len--; 123 124 for (int i = pmdev->in_buf_len - 1; i >= 0; i--) { 125 ret = ret << 8 | pmdev->in_buf[i]; 126 } 127 return ret; 128 } 129 130 uint8_t pmbus_receive8(PMBusDevice *pmdev) 131 { 132 if (pmdev->in_buf_len - 1 != 1) { 133 qemu_log_mask(LOG_GUEST_ERROR, 134 "%s: length mismatch. Expected 1 byte, got %d bytes\n", 135 __func__, pmdev->in_buf_len - 1); 136 } 137 return pmbus_receive_uint(pmdev); 138 } 139 140 uint16_t pmbus_receive16(PMBusDevice *pmdev) 141 { 142 if (pmdev->in_buf_len - 1 != 2) { 143 qemu_log_mask(LOG_GUEST_ERROR, 144 "%s: length mismatch. Expected 2 bytes, got %d bytes\n", 145 __func__, pmdev->in_buf_len - 1); 146 } 147 return pmbus_receive_uint(pmdev); 148 } 149 150 uint32_t pmbus_receive32(PMBusDevice *pmdev) 151 { 152 if (pmdev->in_buf_len - 1 != 4) { 153 qemu_log_mask(LOG_GUEST_ERROR, 154 "%s: length mismatch. Expected 4 bytes, got %d bytes\n", 155 __func__, pmdev->in_buf_len - 1); 156 } 157 return pmbus_receive_uint(pmdev); 158 } 159 160 uint64_t pmbus_receive64(PMBusDevice *pmdev) 161 { 162 if (pmdev->in_buf_len - 1 != 8) { 163 qemu_log_mask(LOG_GUEST_ERROR, 164 "%s: length mismatch. Expected 8 bytes, got %d bytes\n", 165 __func__, pmdev->in_buf_len - 1); 166 } 167 return pmbus_receive_uint(pmdev); 168 } 169 170 static uint8_t pmbus_out_buf_pop(PMBusDevice *pmdev) 171 { 172 if (pmdev->out_buf_len == 0) { 173 qemu_log_mask(LOG_GUEST_ERROR, 174 "%s: tried to read from empty buffer", 175 __func__); 176 return PMBUS_ERR_BYTE; 177 } 178 uint8_t data = pmdev->out_buf[pmdev->out_buf_len - 1]; 179 pmdev->out_buf_len--; 180 return data; 181 } 182 183 static void pmbus_quick_cmd(SMBusDevice *smd, uint8_t read) 184 { 185 PMBusDevice *pmdev = PMBUS_DEVICE(smd); 186 PMBusDeviceClass *pmdc = PMBUS_DEVICE_GET_CLASS(pmdev); 187 188 if (pmdc->quick_cmd) { 189 pmdc->quick_cmd(pmdev, read); 190 } 191 } 192 193 static void pmbus_pages_alloc(PMBusDevice *pmdev) 194 { 195 /* some PMBus devices don't use the PAGE command, so they get 1 page */ 196 PMBusDeviceClass *k = PMBUS_DEVICE_GET_CLASS(pmdev); 197 if (k->device_num_pages == 0) { 198 k->device_num_pages = 1; 199 } 200 pmdev->num_pages = k->device_num_pages; 201 pmdev->pages = g_new0(PMBusPage, k->device_num_pages); 202 } 203 204 void pmbus_check_limits(PMBusDevice *pmdev) 205 { 206 for (int i = 0; i < pmdev->num_pages; i++) { 207 if ((pmdev->pages[i].operation & PB_OP_ON) == 0) { 208 continue; /* don't check powered off devices */ 209 } 210 211 if (pmdev->pages[i].read_vout > pmdev->pages[i].vout_ov_fault_limit) { 212 pmdev->pages[i].status_word |= PB_STATUS_VOUT; 213 pmdev->pages[i].status_vout |= PB_STATUS_VOUT_OV_FAULT; 214 } 215 216 if (pmdev->pages[i].read_vout > pmdev->pages[i].vout_ov_warn_limit) { 217 pmdev->pages[i].status_word |= PB_STATUS_VOUT; 218 pmdev->pages[i].status_vout |= PB_STATUS_VOUT_OV_WARN; 219 } 220 221 if (pmdev->pages[i].read_vout < pmdev->pages[i].vout_uv_warn_limit) { 222 pmdev->pages[i].status_word |= PB_STATUS_VOUT; 223 pmdev->pages[i].status_vout |= PB_STATUS_VOUT_UV_WARN; 224 } 225 226 if (pmdev->pages[i].read_vout < pmdev->pages[i].vout_uv_fault_limit) { 227 pmdev->pages[i].status_word |= PB_STATUS_VOUT; 228 pmdev->pages[i].status_vout |= PB_STATUS_VOUT_UV_FAULT; 229 } 230 231 if (pmdev->pages[i].read_vin > pmdev->pages[i].vin_ov_warn_limit) { 232 pmdev->pages[i].status_word |= PB_STATUS_INPUT; 233 pmdev->pages[i].status_input |= PB_STATUS_INPUT_VIN_OV_WARN; 234 } 235 236 if (pmdev->pages[i].read_vin < pmdev->pages[i].vin_uv_warn_limit) { 237 pmdev->pages[i].status_word |= PB_STATUS_INPUT; 238 pmdev->pages[i].status_input |= PB_STATUS_INPUT_VIN_UV_WARN; 239 } 240 241 if (pmdev->pages[i].read_iout > pmdev->pages[i].iout_oc_warn_limit) { 242 pmdev->pages[i].status_word |= PB_STATUS_IOUT_POUT; 243 pmdev->pages[i].status_iout |= PB_STATUS_IOUT_OC_WARN; 244 } 245 246 if (pmdev->pages[i].read_iout > pmdev->pages[i].iout_oc_fault_limit) { 247 pmdev->pages[i].status_word |= PB_STATUS_IOUT_POUT; 248 pmdev->pages[i].status_iout |= PB_STATUS_IOUT_OC_FAULT; 249 } 250 251 if (pmdev->pages[i].read_pin > pmdev->pages[i].pin_op_warn_limit) { 252 pmdev->pages[i].status_word |= PB_STATUS_INPUT; 253 pmdev->pages[i].status_input |= PB_STATUS_INPUT_PIN_OP_WARN; 254 } 255 256 if (pmdev->pages[i].read_temperature_1 257 > pmdev->pages[i].ot_fault_limit) { 258 pmdev->pages[i].status_word |= PB_STATUS_TEMPERATURE; 259 pmdev->pages[i].status_temperature |= PB_STATUS_OT_FAULT; 260 } 261 262 if (pmdev->pages[i].read_temperature_1 263 > pmdev->pages[i].ot_warn_limit) { 264 pmdev->pages[i].status_word |= PB_STATUS_TEMPERATURE; 265 pmdev->pages[i].status_temperature |= PB_STATUS_OT_WARN; 266 } 267 } 268 } 269 270 void pmbus_idle(PMBusDevice *pmdev) 271 { 272 pmdev->code = PMBUS_IDLE_STATE; 273 } 274 275 /* assert the status_cml error upon receipt of malformed command */ 276 static void pmbus_cml_error(PMBusDevice *pmdev) 277 { 278 for (int i = 0; i < pmdev->num_pages; i++) { 279 pmdev->pages[i].status_word |= PMBUS_STATUS_CML; 280 pmdev->pages[i].status_cml |= PB_CML_FAULT_INVALID_CMD; 281 } 282 } 283 284 static uint8_t pmbus_receive_byte(SMBusDevice *smd) 285 { 286 PMBusDevice *pmdev = PMBUS_DEVICE(smd); 287 PMBusDeviceClass *pmdc = PMBUS_DEVICE_GET_CLASS(pmdev); 288 uint8_t ret = PMBUS_ERR_BYTE; 289 uint8_t index; 290 291 if (pmdev->out_buf_len != 0) { 292 ret = pmbus_out_buf_pop(pmdev); 293 return ret; 294 } 295 296 /* 297 * Reading from all pages will return the value from page 0, 298 * means that all subsequent commands are to be applied to all output. 299 */ 300 if (pmdev->page == PB_ALL_PAGES) { 301 index = 0; 302 } else if (pmdev->page > pmdev->num_pages - 1) { 303 qemu_log_mask(LOG_GUEST_ERROR, 304 "%s: page %d is out of range\n", 305 __func__, pmdev->page); 306 pmbus_cml_error(pmdev); 307 return PMBUS_ERR_BYTE; 308 } else { 309 index = pmdev->page; 310 } 311 312 switch (pmdev->code) { 313 case PMBUS_PAGE: 314 pmbus_send8(pmdev, pmdev->page); 315 break; 316 317 case PMBUS_OPERATION: /* R/W byte */ 318 pmbus_send8(pmdev, pmdev->pages[index].operation); 319 break; 320 321 case PMBUS_ON_OFF_CONFIG: /* R/W byte */ 322 pmbus_send8(pmdev, pmdev->pages[index].on_off_config); 323 break; 324 325 case PMBUS_PHASE: /* R/W byte */ 326 pmbus_send8(pmdev, pmdev->pages[index].phase); 327 break; 328 329 case PMBUS_WRITE_PROTECT: /* R/W byte */ 330 pmbus_send8(pmdev, pmdev->pages[index].write_protect); 331 break; 332 333 case PMBUS_CAPABILITY: 334 pmbus_send8(pmdev, pmdev->capability); 335 if (pmdev->capability & BIT(7)) { 336 qemu_log_mask(LOG_UNIMP, 337 "%s: PEC is enabled but not yet supported.\n", 338 __func__); 339 } 340 break; 341 342 case PMBUS_VOUT_MODE: /* R/W byte */ 343 if (pmdev->pages[index].page_flags & PB_HAS_VOUT_MODE) { 344 pmbus_send8(pmdev, pmdev->pages[index].vout_mode); 345 } else { 346 goto passthough; 347 } 348 break; 349 350 case PMBUS_VOUT_COMMAND: /* R/W word */ 351 if (pmdev->pages[index].page_flags & PB_HAS_VOUT) { 352 pmbus_send16(pmdev, pmdev->pages[index].vout_command); 353 } else { 354 goto passthough; 355 } 356 break; 357 358 case PMBUS_VOUT_TRIM: /* R/W word */ 359 if (pmdev->pages[index].page_flags & PB_HAS_VOUT) { 360 pmbus_send16(pmdev, pmdev->pages[index].vout_trim); 361 } else { 362 goto passthough; 363 } 364 break; 365 366 case PMBUS_VOUT_CAL_OFFSET: /* R/W word */ 367 if (pmdev->pages[index].page_flags & PB_HAS_VOUT) { 368 pmbus_send16(pmdev, pmdev->pages[index].vout_cal_offset); 369 } else { 370 goto passthough; 371 } 372 break; 373 374 case PMBUS_VOUT_MAX: /* R/W word */ 375 if (pmdev->pages[index].page_flags & PB_HAS_VOUT) { 376 pmbus_send16(pmdev, pmdev->pages[index].vout_max); 377 } else { 378 goto passthough; 379 } 380 break; 381 382 case PMBUS_VOUT_MARGIN_HIGH: /* R/W word */ 383 if (pmdev->pages[index].page_flags & PB_HAS_VOUT_MARGIN) { 384 pmbus_send16(pmdev, pmdev->pages[index].vout_margin_high); 385 } else { 386 goto passthough; 387 } 388 break; 389 390 case PMBUS_VOUT_MARGIN_LOW: /* R/W word */ 391 if (pmdev->pages[index].page_flags & PB_HAS_VOUT_MARGIN) { 392 pmbus_send16(pmdev, pmdev->pages[index].vout_margin_low); 393 } else { 394 goto passthough; 395 } 396 break; 397 398 case PMBUS_VOUT_TRANSITION_RATE: /* R/W word */ 399 if (pmdev->pages[index].page_flags & PB_HAS_VOUT) { 400 pmbus_send16(pmdev, pmdev->pages[index].vout_transition_rate); 401 } else { 402 goto passthough; 403 } 404 break; 405 406 case PMBUS_VOUT_DROOP: /* R/W word */ 407 if (pmdev->pages[index].page_flags & PB_HAS_VOUT) { 408 pmbus_send16(pmdev, pmdev->pages[index].vout_droop); 409 } else { 410 goto passthough; 411 } 412 break; 413 414 case PMBUS_VOUT_SCALE_LOOP: /* R/W word */ 415 if (pmdev->pages[index].page_flags & PB_HAS_VOUT) { 416 pmbus_send16(pmdev, pmdev->pages[index].vout_scale_loop); 417 } else { 418 goto passthough; 419 } 420 break; 421 422 case PMBUS_VOUT_SCALE_MONITOR: /* R/W word */ 423 if (pmdev->pages[index].page_flags & PB_HAS_VOUT) { 424 pmbus_send16(pmdev, pmdev->pages[index].vout_scale_monitor); 425 } else { 426 goto passthough; 427 } 428 break; 429 430 case PMBUS_VOUT_MIN: /* R/W word */ 431 if (pmdev->pages[index].page_flags & PB_HAS_VOUT_RATING) { 432 pmbus_send16(pmdev, pmdev->pages[index].vout_min); 433 } else { 434 goto passthough; 435 } 436 break; 437 438 /* TODO: implement coefficients support */ 439 440 case PMBUS_POUT_MAX: /* R/W word */ 441 if (pmdev->pages[index].page_flags & PB_HAS_POUT) { 442 pmbus_send16(pmdev, pmdev->pages[index].pout_max); 443 } else { 444 goto passthough; 445 } 446 break; 447 448 case PMBUS_VIN_ON: /* R/W word */ 449 if (pmdev->pages[index].page_flags & PB_HAS_VIN) { 450 pmbus_send16(pmdev, pmdev->pages[index].vin_on); 451 } else { 452 goto passthough; 453 } 454 break; 455 456 case PMBUS_VIN_OFF: /* R/W word */ 457 if (pmdev->pages[index].page_flags & PB_HAS_VIN) { 458 pmbus_send16(pmdev, pmdev->pages[index].vin_off); 459 } else { 460 goto passthough; 461 } 462 break; 463 464 case PMBUS_IOUT_CAL_GAIN: /* R/W word */ 465 if (pmdev->pages[index].page_flags & PB_HAS_IOUT_GAIN) { 466 pmbus_send16(pmdev, pmdev->pages[index].iout_cal_gain); 467 } else { 468 goto passthough; 469 } 470 break; 471 472 case PMBUS_VOUT_OV_FAULT_LIMIT: /* R/W word */ 473 if (pmdev->pages[index].page_flags & PB_HAS_VOUT) { 474 pmbus_send16(pmdev, pmdev->pages[index].vout_ov_fault_limit); 475 } else { 476 goto passthough; 477 } 478 break; 479 480 case PMBUS_VOUT_OV_FAULT_RESPONSE: /* R/W byte */ 481 if (pmdev->pages[index].page_flags & PB_HAS_VOUT) { 482 pmbus_send8(pmdev, pmdev->pages[index].vout_ov_fault_response); 483 } else { 484 goto passthough; 485 } 486 break; 487 488 case PMBUS_VOUT_OV_WARN_LIMIT: /* R/W word */ 489 if (pmdev->pages[index].page_flags & PB_HAS_VOUT) { 490 pmbus_send16(pmdev, pmdev->pages[index].vout_ov_warn_limit); 491 } else { 492 goto passthough; 493 } 494 break; 495 496 case PMBUS_VOUT_UV_WARN_LIMIT: /* R/W word */ 497 if (pmdev->pages[index].page_flags & PB_HAS_VOUT) { 498 pmbus_send16(pmdev, pmdev->pages[index].vout_uv_warn_limit); 499 } else { 500 goto passthough; 501 } 502 break; 503 504 case PMBUS_VOUT_UV_FAULT_LIMIT: /* R/W word */ 505 if (pmdev->pages[index].page_flags & PB_HAS_VOUT) { 506 pmbus_send16(pmdev, pmdev->pages[index].vout_uv_fault_limit); 507 } else { 508 goto passthough; 509 } 510 break; 511 512 case PMBUS_VOUT_UV_FAULT_RESPONSE: /* R/W byte */ 513 if (pmdev->pages[index].page_flags & PB_HAS_VOUT) { 514 pmbus_send8(pmdev, pmdev->pages[index].vout_uv_fault_response); 515 } else { 516 goto passthough; 517 } 518 break; 519 520 case PMBUS_IOUT_OC_FAULT_LIMIT: /* R/W word */ 521 if (pmdev->pages[index].page_flags & PB_HAS_IOUT) { 522 pmbus_send16(pmdev, pmdev->pages[index].iout_oc_fault_limit); 523 } else { 524 goto passthough; 525 } 526 break; 527 528 case PMBUS_IOUT_OC_FAULT_RESPONSE: /* R/W byte */ 529 if (pmdev->pages[index].page_flags & PB_HAS_IOUT) { 530 pmbus_send8(pmdev, pmdev->pages[index].iout_oc_fault_response); 531 } else { 532 goto passthough; 533 } 534 break; 535 536 case PMBUS_IOUT_OC_LV_FAULT_LIMIT: /* R/W word */ 537 if (pmdev->pages[index].page_flags & PB_HAS_IOUT) { 538 pmbus_send16(pmdev, pmdev->pages[index].iout_oc_lv_fault_limit); 539 } else { 540 goto passthough; 541 } 542 break; 543 544 case PMBUS_IOUT_OC_LV_FAULT_RESPONSE: /* R/W byte */ 545 if (pmdev->pages[index].page_flags & PB_HAS_IOUT) { 546 pmbus_send8(pmdev, pmdev->pages[index].iout_oc_lv_fault_response); 547 } else { 548 goto passthough; 549 } 550 break; 551 552 case PMBUS_IOUT_OC_WARN_LIMIT: /* R/W word */ 553 if (pmdev->pages[index].page_flags & PB_HAS_IOUT) { 554 pmbus_send16(pmdev, pmdev->pages[index].iout_oc_warn_limit); 555 } else { 556 goto passthough; 557 } 558 break; 559 560 case PMBUS_IOUT_UC_FAULT_LIMIT: /* R/W word */ 561 if (pmdev->pages[index].page_flags & PB_HAS_IOUT) { 562 pmbus_send16(pmdev, pmdev->pages[index].iout_uc_fault_limit); 563 } else { 564 goto passthough; 565 } 566 break; 567 568 case PMBUS_IOUT_UC_FAULT_RESPONSE: /* R/W byte */ 569 if (pmdev->pages[index].page_flags & PB_HAS_IOUT) { 570 pmbus_send8(pmdev, pmdev->pages[index].iout_uc_fault_response); 571 } else { 572 goto passthough; 573 } 574 break; 575 576 case PMBUS_OT_FAULT_LIMIT: /* R/W word */ 577 if (pmdev->pages[index].page_flags & PB_HAS_TEMPERATURE) { 578 pmbus_send16(pmdev, pmdev->pages[index].ot_fault_limit); 579 } else { 580 goto passthough; 581 } 582 break; 583 584 case PMBUS_OT_FAULT_RESPONSE: /* R/W byte */ 585 if (pmdev->pages[index].page_flags & PB_HAS_TEMPERATURE) { 586 pmbus_send8(pmdev, pmdev->pages[index].ot_fault_response); 587 } else { 588 goto passthough; 589 } 590 break; 591 592 case PMBUS_OT_WARN_LIMIT: /* R/W word */ 593 if (pmdev->pages[index].page_flags & PB_HAS_TEMPERATURE) { 594 pmbus_send16(pmdev, pmdev->pages[index].ot_warn_limit); 595 } else { 596 goto passthough; 597 } 598 break; 599 600 case PMBUS_UT_WARN_LIMIT: /* R/W word */ 601 if (pmdev->pages[index].page_flags & PB_HAS_TEMPERATURE) { 602 pmbus_send16(pmdev, pmdev->pages[index].ut_warn_limit); 603 } else { 604 goto passthough; 605 } 606 break; 607 608 case PMBUS_UT_FAULT_LIMIT: /* R/W word */ 609 if (pmdev->pages[index].page_flags & PB_HAS_TEMPERATURE) { 610 pmbus_send16(pmdev, pmdev->pages[index].ut_fault_limit); 611 } else { 612 goto passthough; 613 } 614 break; 615 616 case PMBUS_UT_FAULT_RESPONSE: /* R/W byte */ 617 if (pmdev->pages[index].page_flags & PB_HAS_TEMPERATURE) { 618 pmbus_send8(pmdev, pmdev->pages[index].ut_fault_response); 619 } else { 620 goto passthough; 621 } 622 break; 623 624 case PMBUS_VIN_OV_FAULT_LIMIT: /* R/W word */ 625 if (pmdev->pages[index].page_flags & PB_HAS_VIN) { 626 pmbus_send16(pmdev, pmdev->pages[index].vin_ov_fault_limit); 627 } else { 628 goto passthough; 629 } 630 break; 631 632 case PMBUS_VIN_OV_FAULT_RESPONSE: /* R/W byte */ 633 if (pmdev->pages[index].page_flags & PB_HAS_VIN) { 634 pmbus_send8(pmdev, pmdev->pages[index].vin_ov_fault_response); 635 } else { 636 goto passthough; 637 } 638 break; 639 640 case PMBUS_VIN_OV_WARN_LIMIT: /* R/W word */ 641 if (pmdev->pages[index].page_flags & PB_HAS_VIN) { 642 pmbus_send16(pmdev, pmdev->pages[index].vin_ov_warn_limit); 643 } else { 644 goto passthough; 645 } 646 break; 647 648 case PMBUS_VIN_UV_WARN_LIMIT: /* R/W word */ 649 if (pmdev->pages[index].page_flags & PB_HAS_VIN) { 650 pmbus_send16(pmdev, pmdev->pages[index].vin_uv_warn_limit); 651 } else { 652 goto passthough; 653 } 654 break; 655 656 case PMBUS_VIN_UV_FAULT_LIMIT: /* R/W word */ 657 if (pmdev->pages[index].page_flags & PB_HAS_VIN) { 658 pmbus_send16(pmdev, pmdev->pages[index].vin_uv_fault_limit); 659 } else { 660 goto passthough; 661 } 662 break; 663 664 case PMBUS_VIN_UV_FAULT_RESPONSE: /* R/W byte */ 665 if (pmdev->pages[index].page_flags & PB_HAS_VIN) { 666 pmbus_send8(pmdev, pmdev->pages[index].vin_uv_fault_response); 667 } else { 668 goto passthough; 669 } 670 break; 671 672 case PMBUS_IIN_OC_FAULT_LIMIT: /* R/W word */ 673 if (pmdev->pages[index].page_flags & PB_HAS_IIN) { 674 pmbus_send16(pmdev, pmdev->pages[index].iin_oc_fault_limit); 675 } else { 676 goto passthough; 677 } 678 break; 679 680 case PMBUS_IIN_OC_FAULT_RESPONSE: /* R/W byte */ 681 if (pmdev->pages[index].page_flags & PB_HAS_IIN) { 682 pmbus_send8(pmdev, pmdev->pages[index].iin_oc_fault_response); 683 } else { 684 goto passthough; 685 } 686 break; 687 688 case PMBUS_IIN_OC_WARN_LIMIT: /* R/W word */ 689 if (pmdev->pages[index].page_flags & PB_HAS_IIN) { 690 pmbus_send16(pmdev, pmdev->pages[index].iin_oc_warn_limit); 691 } else { 692 goto passthough; 693 } 694 break; 695 696 case PMBUS_POUT_OP_FAULT_LIMIT: /* R/W word */ 697 if (pmdev->pages[index].page_flags & PB_HAS_POUT) { 698 pmbus_send16(pmdev, pmdev->pages[index].pout_op_fault_limit); 699 } else { 700 goto passthough; 701 } 702 break; 703 704 case PMBUS_POUT_OP_FAULT_RESPONSE: /* R/W byte */ 705 if (pmdev->pages[index].page_flags & PB_HAS_POUT) { 706 pmbus_send8(pmdev, pmdev->pages[index].pout_op_fault_response); 707 } else { 708 goto passthough; 709 } 710 break; 711 712 case PMBUS_POUT_OP_WARN_LIMIT: /* R/W word */ 713 if (pmdev->pages[index].page_flags & PB_HAS_POUT) { 714 pmbus_send16(pmdev, pmdev->pages[index].pout_op_warn_limit); 715 } else { 716 goto passthough; 717 } 718 break; 719 720 case PMBUS_PIN_OP_WARN_LIMIT: /* R/W word */ 721 if (pmdev->pages[index].page_flags & PB_HAS_PIN) { 722 pmbus_send16(pmdev, pmdev->pages[index].pin_op_warn_limit); 723 } else { 724 goto passthough; 725 } 726 break; 727 728 case PMBUS_STATUS_BYTE: /* R/W byte */ 729 pmbus_send8(pmdev, pmdev->pages[index].status_word & 0xFF); 730 break; 731 732 case PMBUS_STATUS_WORD: /* R/W word */ 733 pmbus_send16(pmdev, pmdev->pages[index].status_word); 734 break; 735 736 case PMBUS_STATUS_VOUT: /* R/W byte */ 737 if (pmdev->pages[index].page_flags & PB_HAS_VOUT) { 738 pmbus_send8(pmdev, pmdev->pages[index].status_vout); 739 } else { 740 goto passthough; 741 } 742 break; 743 744 case PMBUS_STATUS_IOUT: /* R/W byte */ 745 if (pmdev->pages[index].page_flags & PB_HAS_IOUT) { 746 pmbus_send8(pmdev, pmdev->pages[index].status_iout); 747 } else { 748 goto passthough; 749 } 750 break; 751 752 case PMBUS_STATUS_INPUT: /* R/W byte */ 753 if (pmdev->pages[index].page_flags & PB_HAS_VIN || 754 pmdev->pages[index].page_flags & PB_HAS_IIN || 755 pmdev->pages[index].page_flags & PB_HAS_PIN) { 756 pmbus_send8(pmdev, pmdev->pages[index].status_input); 757 } else { 758 goto passthough; 759 } 760 break; 761 762 case PMBUS_STATUS_TEMPERATURE: /* R/W byte */ 763 if (pmdev->pages[index].page_flags & PB_HAS_TEMPERATURE) { 764 pmbus_send8(pmdev, pmdev->pages[index].status_temperature); 765 } else { 766 goto passthough; 767 } 768 break; 769 770 case PMBUS_STATUS_CML: /* R/W byte */ 771 pmbus_send8(pmdev, pmdev->pages[index].status_cml); 772 break; 773 774 case PMBUS_STATUS_OTHER: /* R/W byte */ 775 pmbus_send8(pmdev, pmdev->pages[index].status_other); 776 break; 777 778 case PMBUS_STATUS_MFR_SPECIFIC: /* R/W byte */ 779 pmbus_send8(pmdev, pmdev->pages[index].status_mfr_specific); 780 break; 781 782 case PMBUS_READ_EIN: /* Read-Only block 5 bytes */ 783 if (pmdev->pages[index].page_flags & PB_HAS_EIN) { 784 pmbus_send(pmdev, pmdev->pages[index].read_ein, 5); 785 } else { 786 goto passthough; 787 } 788 break; 789 790 case PMBUS_READ_EOUT: /* Read-Only block 5 bytes */ 791 if (pmdev->pages[index].page_flags & PB_HAS_EOUT) { 792 pmbus_send(pmdev, pmdev->pages[index].read_eout, 5); 793 } else { 794 goto passthough; 795 } 796 break; 797 798 case PMBUS_READ_VIN: /* Read-Only word */ 799 if (pmdev->pages[index].page_flags & PB_HAS_VIN) { 800 pmbus_send16(pmdev, pmdev->pages[index].read_vin); 801 } else { 802 goto passthough; 803 } 804 break; 805 806 case PMBUS_READ_IIN: /* Read-Only word */ 807 if (pmdev->pages[index].page_flags & PB_HAS_IIN) { 808 pmbus_send16(pmdev, pmdev->pages[index].read_iin); 809 } else { 810 goto passthough; 811 } 812 break; 813 814 case PMBUS_READ_VOUT: /* Read-Only word */ 815 if (pmdev->pages[index].page_flags & PB_HAS_VOUT) { 816 pmbus_send16(pmdev, pmdev->pages[index].read_vout); 817 } else { 818 goto passthough; 819 } 820 break; 821 822 case PMBUS_READ_IOUT: /* Read-Only word */ 823 if (pmdev->pages[index].page_flags & PB_HAS_IOUT) { 824 pmbus_send16(pmdev, pmdev->pages[index].read_iout); 825 } else { 826 goto passthough; 827 } 828 break; 829 830 case PMBUS_READ_TEMPERATURE_1: /* Read-Only word */ 831 if (pmdev->pages[index].page_flags & PB_HAS_TEMPERATURE) { 832 pmbus_send16(pmdev, pmdev->pages[index].read_temperature_1); 833 } else { 834 goto passthough; 835 } 836 break; 837 838 case PMBUS_READ_TEMPERATURE_2: /* Read-Only word */ 839 if (pmdev->pages[index].page_flags & PB_HAS_TEMP2) { 840 pmbus_send16(pmdev, pmdev->pages[index].read_temperature_2); 841 } else { 842 goto passthough; 843 } 844 break; 845 846 case PMBUS_READ_TEMPERATURE_3: /* Read-Only word */ 847 if (pmdev->pages[index].page_flags & PB_HAS_TEMP3) { 848 pmbus_send16(pmdev, pmdev->pages[index].read_temperature_3); 849 } else { 850 goto passthough; 851 } 852 break; 853 854 case PMBUS_READ_POUT: /* Read-Only word */ 855 if (pmdev->pages[index].page_flags & PB_HAS_POUT) { 856 pmbus_send16(pmdev, pmdev->pages[index].read_pout); 857 } else { 858 goto passthough; 859 } 860 break; 861 862 case PMBUS_READ_PIN: /* Read-Only word */ 863 if (pmdev->pages[index].page_flags & PB_HAS_PIN) { 864 pmbus_send16(pmdev, pmdev->pages[index].read_pin); 865 } else { 866 goto passthough; 867 } 868 break; 869 870 case PMBUS_REVISION: /* Read-Only byte */ 871 pmbus_send8(pmdev, pmdev->pages[index].revision); 872 break; 873 874 case PMBUS_MFR_ID: /* R/W block */ 875 if (pmdev->pages[index].page_flags & PB_HAS_MFR_INFO) { 876 pmbus_send_string(pmdev, pmdev->pages[index].mfr_id); 877 } else { 878 goto passthough; 879 } 880 break; 881 882 case PMBUS_MFR_MODEL: /* R/W block */ 883 if (pmdev->pages[index].page_flags & PB_HAS_MFR_INFO) { 884 pmbus_send_string(pmdev, pmdev->pages[index].mfr_model); 885 } else { 886 goto passthough; 887 } 888 break; 889 890 case PMBUS_MFR_REVISION: /* R/W block */ 891 if (pmdev->pages[index].page_flags & PB_HAS_MFR_INFO) { 892 pmbus_send_string(pmdev, pmdev->pages[index].mfr_revision); 893 } else { 894 goto passthough; 895 } 896 break; 897 898 case PMBUS_MFR_LOCATION: /* R/W block */ 899 if (pmdev->pages[index].page_flags & PB_HAS_MFR_INFO) { 900 pmbus_send_string(pmdev, pmdev->pages[index].mfr_location); 901 } else { 902 goto passthough; 903 } 904 break; 905 906 case PMBUS_MFR_VIN_MIN: /* Read-Only word */ 907 if (pmdev->pages[index].page_flags & PB_HAS_VIN_RATING) { 908 pmbus_send16(pmdev, pmdev->pages[index].mfr_vin_min); 909 } else { 910 goto passthough; 911 } 912 break; 913 914 case PMBUS_MFR_VIN_MAX: /* Read-Only word */ 915 if (pmdev->pages[index].page_flags & PB_HAS_VIN_RATING) { 916 pmbus_send16(pmdev, pmdev->pages[index].mfr_vin_max); 917 } else { 918 goto passthough; 919 } 920 break; 921 922 case PMBUS_MFR_IIN_MAX: /* Read-Only word */ 923 if (pmdev->pages[index].page_flags & PB_HAS_IIN_RATING) { 924 pmbus_send16(pmdev, pmdev->pages[index].mfr_iin_max); 925 } else { 926 goto passthough; 927 } 928 break; 929 930 case PMBUS_MFR_PIN_MAX: /* Read-Only word */ 931 if (pmdev->pages[index].page_flags & PB_HAS_PIN_RATING) { 932 pmbus_send16(pmdev, pmdev->pages[index].mfr_pin_max); 933 } else { 934 goto passthough; 935 } 936 break; 937 938 case PMBUS_MFR_VOUT_MIN: /* Read-Only word */ 939 if (pmdev->pages[index].page_flags & PB_HAS_VOUT_RATING) { 940 pmbus_send16(pmdev, pmdev->pages[index].mfr_vout_min); 941 } else { 942 goto passthough; 943 } 944 break; 945 946 case PMBUS_MFR_VOUT_MAX: /* Read-Only word */ 947 if (pmdev->pages[index].page_flags & PB_HAS_VOUT_RATING) { 948 pmbus_send16(pmdev, pmdev->pages[index].mfr_vout_max); 949 } else { 950 goto passthough; 951 } 952 break; 953 954 case PMBUS_MFR_IOUT_MAX: /* Read-Only word */ 955 if (pmdev->pages[index].page_flags & PB_HAS_IOUT_RATING) { 956 pmbus_send16(pmdev, pmdev->pages[index].mfr_iout_max); 957 } else { 958 goto passthough; 959 } 960 break; 961 962 case PMBUS_MFR_POUT_MAX: /* Read-Only word */ 963 if (pmdev->pages[index].page_flags & PB_HAS_POUT_RATING) { 964 pmbus_send16(pmdev, pmdev->pages[index].mfr_pout_max); 965 } else { 966 goto passthough; 967 } 968 break; 969 970 case PMBUS_MFR_MAX_TEMP_1: /* R/W word */ 971 if (pmdev->pages[index].page_flags & PB_HAS_TEMP_RATING) { 972 pmbus_send16(pmdev, pmdev->pages[index].mfr_max_temp_1); 973 } else { 974 goto passthough; 975 } 976 break; 977 978 case PMBUS_MFR_MAX_TEMP_2: /* R/W word */ 979 if (pmdev->pages[index].page_flags & PB_HAS_TEMP_RATING) { 980 pmbus_send16(pmdev, pmdev->pages[index].mfr_max_temp_2); 981 } else { 982 goto passthough; 983 } 984 break; 985 986 case PMBUS_MFR_MAX_TEMP_3: /* R/W word */ 987 if (pmdev->pages[index].page_flags & PB_HAS_TEMP_RATING) { 988 pmbus_send16(pmdev, pmdev->pages[index].mfr_max_temp_3); 989 } else { 990 goto passthough; 991 } 992 break; 993 994 case PMBUS_IDLE_STATE: 995 pmbus_send8(pmdev, PMBUS_ERR_BYTE); 996 break; 997 998 case PMBUS_CLEAR_FAULTS: /* Send Byte */ 999 case PMBUS_PAGE_PLUS_WRITE: /* Block Write-only */ 1000 case PMBUS_STORE_DEFAULT_ALL: /* Send Byte */ 1001 case PMBUS_RESTORE_DEFAULT_ALL: /* Send Byte */ 1002 case PMBUS_STORE_DEFAULT_CODE: /* Write-only Byte */ 1003 case PMBUS_RESTORE_DEFAULT_CODE: /* Write-only Byte */ 1004 case PMBUS_STORE_USER_ALL: /* Send Byte */ 1005 case PMBUS_RESTORE_USER_ALL: /* Send Byte */ 1006 case PMBUS_STORE_USER_CODE: /* Write-only Byte */ 1007 case PMBUS_RESTORE_USER_CODE: /* Write-only Byte */ 1008 case PMBUS_QUERY: /* Write-Only */ 1009 qemu_log_mask(LOG_GUEST_ERROR, 1010 "%s: reading from write only register 0x%02x\n", 1011 __func__, pmdev->code); 1012 break; 1013 1014 passthough: 1015 default: 1016 /* Pass through read request if not handled */ 1017 if (pmdc->receive_byte) { 1018 ret = pmdc->receive_byte(pmdev); 1019 } 1020 break; 1021 } 1022 1023 if (pmdev->out_buf_len != 0) { 1024 ret = pmbus_out_buf_pop(pmdev); 1025 return ret; 1026 } 1027 1028 return ret; 1029 } 1030 1031 /* 1032 * PMBus clear faults command applies to all status registers, existing faults 1033 * should separately get re-asserted. 1034 */ 1035 static void pmbus_clear_faults(PMBusDevice *pmdev) 1036 { 1037 for (uint8_t i = 0; i < pmdev->num_pages; i++) { 1038 pmdev->pages[i].status_word = 0; 1039 pmdev->pages[i].status_vout = 0; 1040 pmdev->pages[i].status_iout = 0; 1041 pmdev->pages[i].status_input = 0; 1042 pmdev->pages[i].status_temperature = 0; 1043 pmdev->pages[i].status_cml = 0; 1044 pmdev->pages[i].status_other = 0; 1045 pmdev->pages[i].status_mfr_specific = 0; 1046 pmdev->pages[i].status_fans_1_2 = 0; 1047 pmdev->pages[i].status_fans_3_4 = 0; 1048 } 1049 1050 } 1051 1052 /* 1053 * PMBus operation is used to turn On and Off PSUs 1054 * Therefore, default value for the Operation should be PB_OP_ON or 0x80 1055 */ 1056 static void pmbus_operation(PMBusDevice *pmdev) 1057 { 1058 uint8_t index = pmdev->page; 1059 if ((pmdev->pages[index].operation & PB_OP_ON) == 0) { 1060 pmdev->pages[index].read_vout = 0; 1061 pmdev->pages[index].read_iout = 0; 1062 pmdev->pages[index].read_pout = 0; 1063 return; 1064 } 1065 1066 if (pmdev->pages[index].operation & (PB_OP_ON | PB_OP_MARGIN_HIGH)) { 1067 pmdev->pages[index].read_vout = pmdev->pages[index].vout_margin_high; 1068 } 1069 1070 if (pmdev->pages[index].operation & (PB_OP_ON | PB_OP_MARGIN_LOW)) { 1071 pmdev->pages[index].read_vout = pmdev->pages[index].vout_margin_low; 1072 } 1073 pmbus_check_limits(pmdev); 1074 } 1075 1076 static int pmbus_write_data(SMBusDevice *smd, uint8_t *buf, uint8_t len) 1077 { 1078 PMBusDevice *pmdev = PMBUS_DEVICE(smd); 1079 PMBusDeviceClass *pmdc = PMBUS_DEVICE_GET_CLASS(pmdev); 1080 int ret = 0; 1081 uint8_t index; 1082 1083 if (len == 0) { 1084 qemu_log_mask(LOG_GUEST_ERROR, "%s: writing empty data\n", __func__); 1085 return PMBUS_ERR_BYTE; 1086 } 1087 1088 if (!pmdev->pages) { /* allocate memory for pages on first use */ 1089 pmbus_pages_alloc(pmdev); 1090 } 1091 1092 pmdev->in_buf_len = len; 1093 pmdev->in_buf = buf; 1094 1095 pmdev->code = buf[0]; /* PMBus command code */ 1096 if (len == 1) { /* Single length writes are command codes only */ 1097 return 0; 1098 } 1099 1100 if (pmdev->code == PMBUS_PAGE) { 1101 pmdev->page = pmbus_receive8(pmdev); 1102 return 0; 1103 } 1104 1105 /* loop through all the pages when 0xFF is received */ 1106 if (pmdev->page == PB_ALL_PAGES) { 1107 for (int i = 0; i < pmdev->num_pages; i++) { 1108 pmdev->page = i; 1109 pmbus_write_data(smd, buf, len); 1110 } 1111 pmdev->page = PB_ALL_PAGES; 1112 return 0; 1113 } 1114 1115 if (pmdev->page > pmdev->num_pages - 1) { 1116 qemu_log_mask(LOG_GUEST_ERROR, 1117 "%s: page %u is out of range\n", 1118 __func__, pmdev->page); 1119 pmdev->page = 0; /* undefined behaviour - reset to page 0 */ 1120 pmbus_cml_error(pmdev); 1121 return PMBUS_ERR_BYTE; 1122 } 1123 1124 index = pmdev->page; 1125 1126 switch (pmdev->code) { 1127 case PMBUS_OPERATION: /* R/W byte */ 1128 pmdev->pages[index].operation = pmbus_receive8(pmdev); 1129 pmbus_operation(pmdev); 1130 break; 1131 1132 case PMBUS_ON_OFF_CONFIG: /* R/W byte */ 1133 pmdev->pages[index].on_off_config = pmbus_receive8(pmdev); 1134 break; 1135 1136 case PMBUS_CLEAR_FAULTS: /* Send Byte */ 1137 pmbus_clear_faults(pmdev); 1138 break; 1139 1140 case PMBUS_PHASE: /* R/W byte */ 1141 pmdev->pages[index].phase = pmbus_receive8(pmdev); 1142 break; 1143 1144 case PMBUS_PAGE_PLUS_WRITE: /* Block Write-only */ 1145 case PMBUS_WRITE_PROTECT: /* R/W byte */ 1146 pmdev->pages[index].write_protect = pmbus_receive8(pmdev); 1147 break; 1148 1149 case PMBUS_VOUT_MODE: /* R/W byte */ 1150 if (pmdev->pages[index].page_flags & PB_HAS_VOUT_MODE) { 1151 pmdev->pages[index].vout_mode = pmbus_receive8(pmdev); 1152 } else { 1153 goto passthrough; 1154 } 1155 break; 1156 1157 case PMBUS_VOUT_COMMAND: /* R/W word */ 1158 if (pmdev->pages[index].page_flags & PB_HAS_VOUT) { 1159 pmdev->pages[index].vout_command = pmbus_receive16(pmdev); 1160 } else { 1161 goto passthrough; 1162 } 1163 break; 1164 1165 case PMBUS_VOUT_TRIM: /* R/W word */ 1166 if (pmdev->pages[index].page_flags & PB_HAS_VOUT) { 1167 pmdev->pages[index].vout_trim = pmbus_receive16(pmdev); 1168 } else { 1169 goto passthrough; 1170 } 1171 break; 1172 1173 case PMBUS_VOUT_CAL_OFFSET: /* R/W word */ 1174 if (pmdev->pages[index].page_flags & PB_HAS_VOUT) { 1175 pmdev->pages[index].vout_cal_offset = pmbus_receive16(pmdev); 1176 } else { 1177 goto passthrough; 1178 } 1179 break; 1180 1181 case PMBUS_VOUT_MAX: /* R/W word */ 1182 if (pmdev->pages[index].page_flags & PB_HAS_VOUT) { 1183 pmdev->pages[index].vout_max = pmbus_receive16(pmdev); 1184 } else { 1185 goto passthrough; 1186 } 1187 break; 1188 1189 case PMBUS_VOUT_MARGIN_HIGH: /* R/W word */ 1190 if (pmdev->pages[index].page_flags & PB_HAS_VOUT_MARGIN) { 1191 pmdev->pages[index].vout_margin_high = pmbus_receive16(pmdev); 1192 } else { 1193 goto passthrough; 1194 } 1195 break; 1196 1197 case PMBUS_VOUT_MARGIN_LOW: /* R/W word */ 1198 if (pmdev->pages[index].page_flags & PB_HAS_VOUT_MARGIN) { 1199 pmdev->pages[index].vout_margin_low = pmbus_receive16(pmdev); 1200 } else { 1201 goto passthrough; 1202 } 1203 break; 1204 1205 case PMBUS_VOUT_TRANSITION_RATE: /* R/W word */ 1206 if (pmdev->pages[index].page_flags & PB_HAS_VOUT) { 1207 pmdev->pages[index].vout_transition_rate = pmbus_receive16(pmdev); 1208 } else { 1209 goto passthrough; 1210 } 1211 break; 1212 1213 case PMBUS_VOUT_DROOP: /* R/W word */ 1214 if (pmdev->pages[index].page_flags & PB_HAS_VOUT) { 1215 pmdev->pages[index].vout_droop = pmbus_receive16(pmdev); 1216 } else { 1217 goto passthrough; 1218 } 1219 break; 1220 1221 case PMBUS_VOUT_SCALE_LOOP: /* R/W word */ 1222 if (pmdev->pages[index].page_flags & PB_HAS_VOUT) { 1223 pmdev->pages[index].vout_scale_loop = pmbus_receive16(pmdev); 1224 } else { 1225 goto passthrough; 1226 } 1227 break; 1228 1229 case PMBUS_VOUT_SCALE_MONITOR: /* R/W word */ 1230 if (pmdev->pages[index].page_flags & PB_HAS_VOUT) { 1231 pmdev->pages[index].vout_scale_monitor = pmbus_receive16(pmdev); 1232 } else { 1233 goto passthrough; 1234 } 1235 break; 1236 1237 case PMBUS_VOUT_MIN: /* R/W word */ 1238 if (pmdev->pages[index].page_flags & PB_HAS_VOUT_RATING) { 1239 pmdev->pages[index].vout_min = pmbus_receive16(pmdev); 1240 } else { 1241 goto passthrough; 1242 } 1243 break; 1244 1245 case PMBUS_POUT_MAX: /* R/W word */ 1246 if (pmdev->pages[index].page_flags & PB_HAS_VOUT) { 1247 pmdev->pages[index].pout_max = pmbus_receive16(pmdev); 1248 } else { 1249 goto passthrough; 1250 } 1251 break; 1252 1253 case PMBUS_VIN_ON: /* R/W word */ 1254 if (pmdev->pages[index].page_flags & PB_HAS_VIN) { 1255 pmdev->pages[index].vin_on = pmbus_receive16(pmdev); 1256 } else { 1257 goto passthrough; 1258 } 1259 break; 1260 1261 case PMBUS_VIN_OFF: /* R/W word */ 1262 if (pmdev->pages[index].page_flags & PB_HAS_VIN) { 1263 pmdev->pages[index].vin_off = pmbus_receive16(pmdev); 1264 } else { 1265 goto passthrough; 1266 } 1267 break; 1268 1269 case PMBUS_IOUT_CAL_GAIN: /* R/W word */ 1270 if (pmdev->pages[index].page_flags & PB_HAS_IOUT_GAIN) { 1271 pmdev->pages[index].iout_cal_gain = pmbus_receive16(pmdev); 1272 } else { 1273 goto passthrough; 1274 } 1275 break; 1276 1277 case PMBUS_VOUT_OV_FAULT_LIMIT: /* R/W word */ 1278 if (pmdev->pages[index].page_flags & PB_HAS_VOUT) { 1279 pmdev->pages[index].vout_ov_fault_limit = pmbus_receive16(pmdev); 1280 } else { 1281 goto passthrough; 1282 } 1283 break; 1284 1285 case PMBUS_VOUT_OV_FAULT_RESPONSE: /* R/W byte */ 1286 if (pmdev->pages[index].page_flags & PB_HAS_VOUT) { 1287 pmdev->pages[index].vout_ov_fault_response = pmbus_receive8(pmdev); 1288 } else { 1289 goto passthrough; 1290 } 1291 break; 1292 1293 case PMBUS_VOUT_OV_WARN_LIMIT: /* R/W word */ 1294 if (pmdev->pages[index].page_flags & PB_HAS_VOUT) { 1295 pmdev->pages[index].vout_ov_warn_limit = pmbus_receive16(pmdev); 1296 } else { 1297 goto passthrough; 1298 } 1299 break; 1300 1301 case PMBUS_VOUT_UV_WARN_LIMIT: /* R/W word */ 1302 if (pmdev->pages[index].page_flags & PB_HAS_VOUT) { 1303 pmdev->pages[index].vout_uv_warn_limit = pmbus_receive16(pmdev); 1304 } else { 1305 goto passthrough; 1306 } 1307 break; 1308 1309 case PMBUS_VOUT_UV_FAULT_LIMIT: /* R/W word */ 1310 if (pmdev->pages[index].page_flags & PB_HAS_VOUT) { 1311 pmdev->pages[index].vout_uv_fault_limit = pmbus_receive16(pmdev); 1312 } else { 1313 goto passthrough; 1314 } 1315 break; 1316 1317 case PMBUS_VOUT_UV_FAULT_RESPONSE: /* R/W byte */ 1318 if (pmdev->pages[index].page_flags & PB_HAS_VOUT) { 1319 pmdev->pages[index].vout_uv_fault_response = pmbus_receive8(pmdev); 1320 } else { 1321 goto passthrough; 1322 } 1323 break; 1324 1325 case PMBUS_IOUT_OC_FAULT_LIMIT: /* R/W word */ 1326 if (pmdev->pages[index].page_flags & PB_HAS_IOUT) { 1327 pmdev->pages[index].iout_oc_fault_limit = pmbus_receive16(pmdev); 1328 } else { 1329 goto passthrough; 1330 } 1331 break; 1332 1333 case PMBUS_IOUT_OC_FAULT_RESPONSE: /* R/W byte */ 1334 if (pmdev->pages[index].page_flags & PB_HAS_IOUT) { 1335 pmdev->pages[index].iout_oc_fault_response = pmbus_receive8(pmdev); 1336 } else { 1337 goto passthrough; 1338 } 1339 break; 1340 1341 case PMBUS_IOUT_OC_LV_FAULT_LIMIT: /* R/W word */ 1342 if (pmdev->pages[index].page_flags & PB_HAS_IOUT) { 1343 pmdev->pages[index].iout_oc_lv_fault_limit = pmbus_receive16(pmdev); 1344 } else { 1345 goto passthrough; 1346 } 1347 break; 1348 1349 case PMBUS_IOUT_OC_LV_FAULT_RESPONSE: /* R/W byte */ 1350 if (pmdev->pages[index].page_flags & PB_HAS_IOUT) { 1351 pmdev->pages[index].iout_oc_lv_fault_response 1352 = pmbus_receive8(pmdev); 1353 } else { 1354 goto passthrough; 1355 } 1356 break; 1357 1358 case PMBUS_IOUT_OC_WARN_LIMIT: /* R/W word */ 1359 if (pmdev->pages[index].page_flags & PB_HAS_IOUT) { 1360 pmdev->pages[index].iout_oc_warn_limit = pmbus_receive16(pmdev); 1361 } else { 1362 goto passthrough; 1363 } 1364 break; 1365 1366 case PMBUS_IOUT_UC_FAULT_LIMIT: /* R/W word */ 1367 if (pmdev->pages[index].page_flags & PB_HAS_IOUT) { 1368 pmdev->pages[index].iout_uc_fault_limit = pmbus_receive16(pmdev); 1369 } else { 1370 goto passthrough; 1371 } 1372 break; 1373 1374 case PMBUS_IOUT_UC_FAULT_RESPONSE: /* R/W byte */ 1375 if (pmdev->pages[index].page_flags & PB_HAS_IOUT) { 1376 pmdev->pages[index].iout_uc_fault_response = pmbus_receive8(pmdev); 1377 } else { 1378 goto passthrough; 1379 } 1380 break; 1381 1382 case PMBUS_OT_FAULT_LIMIT: /* R/W word */ 1383 if (pmdev->pages[index].page_flags & PB_HAS_TEMPERATURE) { 1384 pmdev->pages[index].ot_fault_limit = pmbus_receive16(pmdev); 1385 } else { 1386 goto passthrough; 1387 } 1388 break; 1389 1390 case PMBUS_OT_FAULT_RESPONSE: /* R/W byte */ 1391 if (pmdev->pages[index].page_flags & PB_HAS_TEMPERATURE) { 1392 pmdev->pages[index].ot_fault_response = pmbus_receive8(pmdev); 1393 } else { 1394 goto passthrough; 1395 } 1396 break; 1397 1398 case PMBUS_OT_WARN_LIMIT: /* R/W word */ 1399 if (pmdev->pages[index].page_flags & PB_HAS_TEMPERATURE) { 1400 pmdev->pages[index].ot_warn_limit = pmbus_receive16(pmdev); 1401 } else { 1402 goto passthrough; 1403 } 1404 break; 1405 1406 case PMBUS_UT_WARN_LIMIT: /* R/W word */ 1407 if (pmdev->pages[index].page_flags & PB_HAS_TEMPERATURE) { 1408 pmdev->pages[index].ut_warn_limit = pmbus_receive16(pmdev); 1409 } else { 1410 goto passthrough; 1411 } 1412 break; 1413 1414 case PMBUS_UT_FAULT_LIMIT: /* R/W word */ 1415 if (pmdev->pages[index].page_flags & PB_HAS_TEMPERATURE) { 1416 pmdev->pages[index].ut_fault_limit = pmbus_receive16(pmdev); 1417 } else { 1418 goto passthrough; 1419 } 1420 break; 1421 1422 case PMBUS_UT_FAULT_RESPONSE: /* R/W byte */ 1423 if (pmdev->pages[index].page_flags & PB_HAS_TEMPERATURE) { 1424 pmdev->pages[index].ut_fault_response = pmbus_receive8(pmdev); 1425 } else { 1426 goto passthrough; 1427 } 1428 break; 1429 1430 case PMBUS_VIN_OV_FAULT_LIMIT: /* R/W word */ 1431 if (pmdev->pages[index].page_flags & PB_HAS_VIN) { 1432 pmdev->pages[index].vin_ov_fault_limit = pmbus_receive16(pmdev); 1433 } else { 1434 goto passthrough; 1435 } 1436 break; 1437 1438 case PMBUS_VIN_OV_FAULT_RESPONSE: /* R/W byte */ 1439 if (pmdev->pages[index].page_flags & PB_HAS_VIN) { 1440 pmdev->pages[index].vin_ov_fault_response = pmbus_receive8(pmdev); 1441 } else { 1442 goto passthrough; 1443 } 1444 break; 1445 1446 case PMBUS_VIN_OV_WARN_LIMIT: /* R/W word */ 1447 if (pmdev->pages[index].page_flags & PB_HAS_VIN) { 1448 pmdev->pages[index].vin_ov_warn_limit = pmbus_receive16(pmdev); 1449 } else { 1450 goto passthrough; 1451 } 1452 break; 1453 1454 case PMBUS_VIN_UV_WARN_LIMIT: /* R/W word */ 1455 if (pmdev->pages[index].page_flags & PB_HAS_VIN) { 1456 pmdev->pages[index].vin_uv_warn_limit = pmbus_receive16(pmdev); 1457 } else { 1458 goto passthrough; 1459 } 1460 break; 1461 1462 case PMBUS_VIN_UV_FAULT_LIMIT: /* R/W word */ 1463 if (pmdev->pages[index].page_flags & PB_HAS_VIN) { 1464 pmdev->pages[index].vin_uv_fault_limit = pmbus_receive16(pmdev); 1465 } else { 1466 goto passthrough; 1467 } 1468 break; 1469 1470 case PMBUS_VIN_UV_FAULT_RESPONSE: /* R/W byte */ 1471 if (pmdev->pages[index].page_flags & PB_HAS_VIN) { 1472 pmdev->pages[index].vin_uv_fault_response = pmbus_receive8(pmdev); 1473 } else { 1474 goto passthrough; 1475 } 1476 break; 1477 1478 case PMBUS_IIN_OC_FAULT_LIMIT: /* R/W word */ 1479 if (pmdev->pages[index].page_flags & PB_HAS_IIN) { 1480 pmdev->pages[index].iin_oc_fault_limit = pmbus_receive16(pmdev); 1481 } else { 1482 goto passthrough; 1483 } 1484 break; 1485 1486 case PMBUS_IIN_OC_FAULT_RESPONSE: /* R/W byte */ 1487 if (pmdev->pages[index].page_flags & PB_HAS_IIN) { 1488 pmdev->pages[index].iin_oc_fault_response = pmbus_receive8(pmdev); 1489 } else { 1490 goto passthrough; 1491 } 1492 break; 1493 1494 case PMBUS_IIN_OC_WARN_LIMIT: /* R/W word */ 1495 if (pmdev->pages[index].page_flags & PB_HAS_IIN) { 1496 pmdev->pages[index].iin_oc_warn_limit = pmbus_receive16(pmdev); 1497 } else { 1498 goto passthrough; 1499 } 1500 break; 1501 1502 case PMBUS_POUT_OP_FAULT_LIMIT: /* R/W word */ 1503 if (pmdev->pages[index].page_flags & PB_HAS_VOUT) { 1504 pmdev->pages[index].pout_op_fault_limit = pmbus_receive16(pmdev); 1505 } else { 1506 goto passthrough; 1507 } 1508 break; 1509 1510 case PMBUS_POUT_OP_FAULT_RESPONSE: /* R/W byte */ 1511 if (pmdev->pages[index].page_flags & PB_HAS_VOUT) { 1512 pmdev->pages[index].pout_op_fault_response = pmbus_receive8(pmdev); 1513 } else { 1514 goto passthrough; 1515 } 1516 break; 1517 1518 case PMBUS_POUT_OP_WARN_LIMIT: /* R/W word */ 1519 if (pmdev->pages[index].page_flags & PB_HAS_VOUT) { 1520 pmdev->pages[index].pout_op_warn_limit = pmbus_receive16(pmdev); 1521 } else { 1522 goto passthrough; 1523 } 1524 break; 1525 1526 case PMBUS_PIN_OP_WARN_LIMIT: /* R/W word */ 1527 if (pmdev->pages[index].page_flags & PB_HAS_PIN) { 1528 pmdev->pages[index].pin_op_warn_limit = pmbus_receive16(pmdev); 1529 } else { 1530 goto passthrough; 1531 } 1532 break; 1533 1534 case PMBUS_STATUS_BYTE: /* R/W byte */ 1535 pmdev->pages[index].status_word = pmbus_receive8(pmdev); 1536 break; 1537 1538 case PMBUS_STATUS_WORD: /* R/W word */ 1539 pmdev->pages[index].status_word = pmbus_receive16(pmdev); 1540 break; 1541 1542 case PMBUS_STATUS_VOUT: /* R/W byte */ 1543 if (pmdev->pages[index].page_flags & PB_HAS_VOUT) { 1544 pmdev->pages[index].status_vout = pmbus_receive8(pmdev); 1545 } else { 1546 goto passthrough; 1547 } 1548 break; 1549 1550 case PMBUS_STATUS_IOUT: /* R/W byte */ 1551 if (pmdev->pages[index].page_flags & PB_HAS_IOUT) { 1552 pmdev->pages[index].status_iout = pmbus_receive8(pmdev); 1553 } else { 1554 goto passthrough; 1555 } 1556 break; 1557 1558 case PMBUS_STATUS_INPUT: /* R/W byte */ 1559 pmdev->pages[index].status_input = pmbus_receive8(pmdev); 1560 break; 1561 1562 case PMBUS_STATUS_TEMPERATURE: /* R/W byte */ 1563 if (pmdev->pages[index].page_flags & PB_HAS_TEMPERATURE) { 1564 pmdev->pages[index].status_temperature = pmbus_receive8(pmdev); 1565 } else { 1566 goto passthrough; 1567 } 1568 break; 1569 1570 case PMBUS_STATUS_CML: /* R/W byte */ 1571 pmdev->pages[index].status_cml = pmbus_receive8(pmdev); 1572 break; 1573 1574 case PMBUS_STATUS_OTHER: /* R/W byte */ 1575 pmdev->pages[index].status_other = pmbus_receive8(pmdev); 1576 break; 1577 1578 case PMBUS_STATUS_MFR_SPECIFIC: /* R/W byte */ 1579 pmdev->pages[index].status_mfr_specific = pmbus_receive8(pmdev); 1580 break; 1581 1582 case PMBUS_PAGE_PLUS_READ: /* Block Read-only */ 1583 case PMBUS_CAPABILITY: /* Read-Only byte */ 1584 case PMBUS_COEFFICIENTS: /* Read-only block 5 bytes */ 1585 case PMBUS_READ_EIN: /* Read-Only block 5 bytes */ 1586 case PMBUS_READ_EOUT: /* Read-Only block 5 bytes */ 1587 case PMBUS_READ_VIN: /* Read-Only word */ 1588 case PMBUS_READ_IIN: /* Read-Only word */ 1589 case PMBUS_READ_VCAP: /* Read-Only word */ 1590 case PMBUS_READ_VOUT: /* Read-Only word */ 1591 case PMBUS_READ_IOUT: /* Read-Only word */ 1592 case PMBUS_READ_TEMPERATURE_1: /* Read-Only word */ 1593 case PMBUS_READ_TEMPERATURE_2: /* Read-Only word */ 1594 case PMBUS_READ_TEMPERATURE_3: /* Read-Only word */ 1595 case PMBUS_READ_FAN_SPEED_1: /* Read-Only word */ 1596 case PMBUS_READ_FAN_SPEED_2: /* Read-Only word */ 1597 case PMBUS_READ_FAN_SPEED_3: /* Read-Only word */ 1598 case PMBUS_READ_FAN_SPEED_4: /* Read-Only word */ 1599 case PMBUS_READ_DUTY_CYCLE: /* Read-Only word */ 1600 case PMBUS_READ_FREQUENCY: /* Read-Only word */ 1601 case PMBUS_READ_POUT: /* Read-Only word */ 1602 case PMBUS_READ_PIN: /* Read-Only word */ 1603 case PMBUS_REVISION: /* Read-Only byte */ 1604 case PMBUS_APP_PROFILE_SUPPORT: /* Read-Only block-read */ 1605 case PMBUS_MFR_VIN_MIN: /* Read-Only word */ 1606 case PMBUS_MFR_VIN_MAX: /* Read-Only word */ 1607 case PMBUS_MFR_IIN_MAX: /* Read-Only word */ 1608 case PMBUS_MFR_PIN_MAX: /* Read-Only word */ 1609 case PMBUS_MFR_VOUT_MIN: /* Read-Only word */ 1610 case PMBUS_MFR_VOUT_MAX: /* Read-Only word */ 1611 case PMBUS_MFR_IOUT_MAX: /* Read-Only word */ 1612 case PMBUS_MFR_POUT_MAX: /* Read-Only word */ 1613 case PMBUS_MFR_TAMBIENT_MAX: /* Read-Only word */ 1614 case PMBUS_MFR_TAMBIENT_MIN: /* Read-Only word */ 1615 case PMBUS_MFR_EFFICIENCY_LL: /* Read-Only block 14 bytes */ 1616 case PMBUS_MFR_EFFICIENCY_HL: /* Read-Only block 14 bytes */ 1617 case PMBUS_MFR_PIN_ACCURACY: /* Read-Only byte */ 1618 case PMBUS_IC_DEVICE_ID: /* Read-Only block-read */ 1619 case PMBUS_IC_DEVICE_REV: /* Read-Only block-read */ 1620 qemu_log_mask(LOG_GUEST_ERROR, 1621 "%s: writing to read-only register 0x%02x\n", 1622 __func__, pmdev->code); 1623 break; 1624 1625 passthrough: 1626 /* Unimplimented registers get passed to the device */ 1627 default: 1628 if (pmdc->write_data) { 1629 ret = pmdc->write_data(pmdev, buf, len); 1630 } 1631 break; 1632 } 1633 pmbus_check_limits(pmdev); 1634 pmdev->in_buf_len = 0; 1635 return ret; 1636 } 1637 1638 int pmbus_page_config(PMBusDevice *pmdev, uint8_t index, uint64_t flags) 1639 { 1640 if (!pmdev->pages) { /* allocate memory for pages on first use */ 1641 pmbus_pages_alloc(pmdev); 1642 } 1643 1644 /* The 0xFF page is special for commands applying to all pages */ 1645 if (index == PB_ALL_PAGES) { 1646 for (int i = 0; i < pmdev->num_pages; i++) { 1647 pmdev->pages[i].page_flags = flags; 1648 } 1649 return 0; 1650 } 1651 1652 if (index > pmdev->num_pages - 1) { 1653 qemu_log_mask(LOG_GUEST_ERROR, 1654 "%s: index %u is out of range\n", 1655 __func__, index); 1656 return -1; 1657 } 1658 1659 pmdev->pages[index].page_flags = flags; 1660 1661 return 0; 1662 } 1663 1664 /* TODO: include pmbus page info in vmstate */ 1665 const VMStateDescription vmstate_pmbus_device = { 1666 .name = TYPE_PMBUS_DEVICE, 1667 .version_id = 0, 1668 .minimum_version_id = 0, 1669 .fields = (VMStateField[]) { 1670 VMSTATE_SMBUS_DEVICE(smb, PMBusDevice), 1671 VMSTATE_UINT8(num_pages, PMBusDevice), 1672 VMSTATE_UINT8(code, PMBusDevice), 1673 VMSTATE_UINT8(page, PMBusDevice), 1674 VMSTATE_UINT8(capability, PMBusDevice), 1675 VMSTATE_END_OF_LIST() 1676 } 1677 }; 1678 1679 static void pmbus_device_finalize(Object *obj) 1680 { 1681 PMBusDevice *pmdev = PMBUS_DEVICE(obj); 1682 g_free(pmdev->pages); 1683 } 1684 1685 static void pmbus_device_class_init(ObjectClass *klass, void *data) 1686 { 1687 SMBusDeviceClass *k = SMBUS_DEVICE_CLASS(klass); 1688 1689 k->quick_cmd = pmbus_quick_cmd; 1690 k->write_data = pmbus_write_data; 1691 k->receive_byte = pmbus_receive_byte; 1692 } 1693 1694 static const TypeInfo pmbus_device_type_info = { 1695 .name = TYPE_PMBUS_DEVICE, 1696 .parent = TYPE_SMBUS_DEVICE, 1697 .instance_size = sizeof(PMBusDevice), 1698 .instance_finalize = pmbus_device_finalize, 1699 .abstract = true, 1700 .class_size = sizeof(PMBusDeviceClass), 1701 .class_init = pmbus_device_class_init, 1702 }; 1703 1704 static void pmbus_device_register_types(void) 1705 { 1706 type_register_static(&pmbus_device_type_info); 1707 } 1708 1709 type_init(pmbus_device_register_types) 1710