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