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