cachepc-qemu

Fork of AMDESE/qemu with changes for cachepc side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-qemu
Log | Files | Refs | Submodules | LICENSE | sfeed.txt

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)