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