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