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