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