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