qemu/hw/i2c/pmbus_device.c
<<
>>
Prefs
   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)
1613