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