13746d5c1STitus Rwantare /* 23746d5c1STitus Rwantare * PMBus wrapper over SMBus 33746d5c1STitus Rwantare * 43746d5c1STitus Rwantare * Copyright 2021 Google LLC 53746d5c1STitus Rwantare * 63746d5c1STitus Rwantare * SPDX-License-Identifier: GPL-2.0-or-later 73746d5c1STitus Rwantare */ 83746d5c1STitus Rwantare 93746d5c1STitus Rwantare #include "qemu/osdep.h" 103746d5c1STitus Rwantare #include <math.h> 113746d5c1STitus Rwantare #include <string.h> 123746d5c1STitus Rwantare #include "hw/i2c/pmbus_device.h" 133746d5c1STitus Rwantare #include "migration/vmstate.h" 143746d5c1STitus Rwantare #include "qemu/module.h" 153746d5c1STitus Rwantare #include "qemu/log.h" 163746d5c1STitus Rwantare 173746d5c1STitus Rwantare uint16_t pmbus_data2direct_mode(PMBusCoefficients c, uint32_t value) 183746d5c1STitus Rwantare { 193746d5c1STitus Rwantare /* R is usually negative to fit large readings into 16 bits */ 203746d5c1STitus Rwantare uint16_t y = (c.m * value + c.b) * pow(10, c.R); 213746d5c1STitus Rwantare return y; 223746d5c1STitus Rwantare } 233746d5c1STitus Rwantare 243746d5c1STitus Rwantare uint32_t pmbus_direct_mode2data(PMBusCoefficients c, uint16_t value) 253746d5c1STitus Rwantare { 263746d5c1STitus Rwantare /* X = (Y * 10^-R - b) / m */ 273746d5c1STitus Rwantare uint32_t x = (value / pow(10, c.R) - c.b) / c.m; 283746d5c1STitus Rwantare return x; 293746d5c1STitus Rwantare } 303746d5c1STitus Rwantare 31*648a4882SShengtan Mao uint16_t pmbus_data2linear_mode(uint16_t value, int exp) 32*648a4882SShengtan Mao { 33*648a4882SShengtan Mao /* L = D * 2^(-e) */ 34*648a4882SShengtan Mao if (exp < 0) { 35*648a4882SShengtan Mao return value << (-exp); 36*648a4882SShengtan Mao } 37*648a4882SShengtan Mao return value >> exp; 38*648a4882SShengtan Mao } 39*648a4882SShengtan Mao 40*648a4882SShengtan Mao uint16_t pmbus_linear_mode2data(uint16_t value, int exp) 41*648a4882SShengtan Mao { 42*648a4882SShengtan Mao /* D = L * 2^e */ 43*648a4882SShengtan Mao if (exp < 0) { 44*648a4882SShengtan Mao return value >> (-exp); 45*648a4882SShengtan Mao } 46*648a4882SShengtan Mao return value << exp; 47*648a4882SShengtan Mao } 48*648a4882SShengtan Mao 493746d5c1STitus Rwantare void pmbus_send(PMBusDevice *pmdev, const uint8_t *data, uint16_t len) 503746d5c1STitus Rwantare { 513746d5c1STitus Rwantare if (pmdev->out_buf_len + len > SMBUS_DATA_MAX_LEN) { 523746d5c1STitus Rwantare qemu_log_mask(LOG_GUEST_ERROR, 533746d5c1STitus Rwantare "PMBus device tried to send too much data"); 543746d5c1STitus Rwantare len = 0; 553746d5c1STitus Rwantare } 563746d5c1STitus Rwantare 573746d5c1STitus Rwantare for (int i = len - 1; i >= 0; i--) { 583746d5c1STitus Rwantare pmdev->out_buf[i + pmdev->out_buf_len] = data[len - i - 1]; 593746d5c1STitus Rwantare } 603746d5c1STitus Rwantare pmdev->out_buf_len += len; 613746d5c1STitus Rwantare } 623746d5c1STitus Rwantare 633746d5c1STitus Rwantare /* Internal only, convert unsigned ints to the little endian bus */ 643746d5c1STitus Rwantare static void pmbus_send_uint(PMBusDevice *pmdev, uint64_t data, uint8_t size) 653746d5c1STitus Rwantare { 663746d5c1STitus Rwantare uint8_t bytes[8]; 673746d5c1STitus Rwantare g_assert(size <= 8); 683746d5c1STitus Rwantare 693746d5c1STitus Rwantare for (int i = 0; i < size; i++) { 703746d5c1STitus Rwantare bytes[i] = data & 0xFF; 713746d5c1STitus Rwantare data = data >> 8; 723746d5c1STitus Rwantare } 733746d5c1STitus Rwantare pmbus_send(pmdev, bytes, size); 743746d5c1STitus Rwantare } 753746d5c1STitus Rwantare 763746d5c1STitus Rwantare void pmbus_send8(PMBusDevice *pmdev, uint8_t data) 773746d5c1STitus Rwantare { 783746d5c1STitus Rwantare pmbus_send_uint(pmdev, data, 1); 793746d5c1STitus Rwantare } 803746d5c1STitus Rwantare 813746d5c1STitus Rwantare void pmbus_send16(PMBusDevice *pmdev, uint16_t data) 823746d5c1STitus Rwantare { 833746d5c1STitus Rwantare pmbus_send_uint(pmdev, data, 2); 843746d5c1STitus Rwantare } 853746d5c1STitus Rwantare 863746d5c1STitus Rwantare void pmbus_send32(PMBusDevice *pmdev, uint32_t data) 873746d5c1STitus Rwantare { 883746d5c1STitus Rwantare pmbus_send_uint(pmdev, data, 4); 893746d5c1STitus Rwantare } 903746d5c1STitus Rwantare 913746d5c1STitus Rwantare void pmbus_send64(PMBusDevice *pmdev, uint64_t data) 923746d5c1STitus Rwantare { 933746d5c1STitus Rwantare pmbus_send_uint(pmdev, data, 8); 943746d5c1STitus Rwantare } 953746d5c1STitus Rwantare 963746d5c1STitus Rwantare void pmbus_send_string(PMBusDevice *pmdev, const char *data) 973746d5c1STitus Rwantare { 983746d5c1STitus Rwantare size_t len = strlen(data); 993746d5c1STitus Rwantare g_assert(len > 0); 1003746d5c1STitus Rwantare g_assert(len + pmdev->out_buf_len < SMBUS_DATA_MAX_LEN); 1013746d5c1STitus Rwantare pmdev->out_buf[len + pmdev->out_buf_len] = len; 1023746d5c1STitus Rwantare 1033746d5c1STitus Rwantare for (int i = len - 1; i >= 0; i--) { 1043746d5c1STitus Rwantare pmdev->out_buf[i + pmdev->out_buf_len] = data[len - 1 - i]; 1053746d5c1STitus Rwantare } 1063746d5c1STitus Rwantare pmdev->out_buf_len += len + 1; 1073746d5c1STitus Rwantare } 1083746d5c1STitus Rwantare 1093746d5c1STitus Rwantare 11078fdfc59STitus Rwantare static uint64_t pmbus_receive_uint(PMBusDevice *pmdev) 1113746d5c1STitus Rwantare { 1123746d5c1STitus Rwantare uint64_t ret = 0; 1133746d5c1STitus Rwantare 1143746d5c1STitus Rwantare /* Exclude command code from return value */ 11578fdfc59STitus Rwantare pmdev->in_buf++; 11678fdfc59STitus Rwantare pmdev->in_buf_len--; 1173746d5c1STitus Rwantare 11878fdfc59STitus Rwantare for (int i = pmdev->in_buf_len - 1; i >= 0; i--) { 11978fdfc59STitus Rwantare ret = ret << 8 | pmdev->in_buf[i]; 1203746d5c1STitus Rwantare } 1213746d5c1STitus Rwantare return ret; 1223746d5c1STitus Rwantare } 1233746d5c1STitus Rwantare 1243746d5c1STitus Rwantare uint8_t pmbus_receive8(PMBusDevice *pmdev) 1253746d5c1STitus Rwantare { 1263746d5c1STitus Rwantare if (pmdev->in_buf_len - 1 != 1) { 1273746d5c1STitus Rwantare qemu_log_mask(LOG_GUEST_ERROR, 1283746d5c1STitus Rwantare "%s: length mismatch. Expected 1 byte, got %d bytes\n", 1293746d5c1STitus Rwantare __func__, pmdev->in_buf_len - 1); 1303746d5c1STitus Rwantare } 13178fdfc59STitus Rwantare return pmbus_receive_uint(pmdev); 1323746d5c1STitus Rwantare } 1333746d5c1STitus Rwantare 1343746d5c1STitus Rwantare uint16_t pmbus_receive16(PMBusDevice *pmdev) 1353746d5c1STitus Rwantare { 1363746d5c1STitus Rwantare if (pmdev->in_buf_len - 1 != 2) { 1373746d5c1STitus Rwantare qemu_log_mask(LOG_GUEST_ERROR, 1383746d5c1STitus Rwantare "%s: length mismatch. Expected 2 bytes, got %d bytes\n", 1393746d5c1STitus Rwantare __func__, pmdev->in_buf_len - 1); 1403746d5c1STitus Rwantare } 14178fdfc59STitus Rwantare return pmbus_receive_uint(pmdev); 1423746d5c1STitus Rwantare } 1433746d5c1STitus Rwantare 1443746d5c1STitus Rwantare uint32_t pmbus_receive32(PMBusDevice *pmdev) 1453746d5c1STitus Rwantare { 1463746d5c1STitus Rwantare if (pmdev->in_buf_len - 1 != 4) { 1473746d5c1STitus Rwantare qemu_log_mask(LOG_GUEST_ERROR, 1483746d5c1STitus Rwantare "%s: length mismatch. Expected 4 bytes, got %d bytes\n", 1493746d5c1STitus Rwantare __func__, pmdev->in_buf_len - 1); 1503746d5c1STitus Rwantare } 15178fdfc59STitus Rwantare return pmbus_receive_uint(pmdev); 1523746d5c1STitus Rwantare } 1533746d5c1STitus Rwantare 1543746d5c1STitus Rwantare uint64_t pmbus_receive64(PMBusDevice *pmdev) 1553746d5c1STitus Rwantare { 1563746d5c1STitus Rwantare if (pmdev->in_buf_len - 1 != 8) { 1573746d5c1STitus Rwantare qemu_log_mask(LOG_GUEST_ERROR, 1583746d5c1STitus Rwantare "%s: length mismatch. Expected 8 bytes, got %d bytes\n", 1593746d5c1STitus Rwantare __func__, pmdev->in_buf_len - 1); 1603746d5c1STitus Rwantare } 16178fdfc59STitus Rwantare return pmbus_receive_uint(pmdev); 1623746d5c1STitus Rwantare } 1633746d5c1STitus Rwantare 1643746d5c1STitus Rwantare static uint8_t pmbus_out_buf_pop(PMBusDevice *pmdev) 1653746d5c1STitus Rwantare { 1663746d5c1STitus Rwantare if (pmdev->out_buf_len == 0) { 1673746d5c1STitus Rwantare qemu_log_mask(LOG_GUEST_ERROR, 1683746d5c1STitus Rwantare "%s: tried to read from empty buffer", 1693746d5c1STitus Rwantare __func__); 17038870253STitus Rwantare return PMBUS_ERR_BYTE; 1713746d5c1STitus Rwantare } 1723746d5c1STitus Rwantare uint8_t data = pmdev->out_buf[pmdev->out_buf_len - 1]; 1733746d5c1STitus Rwantare pmdev->out_buf_len--; 1743746d5c1STitus Rwantare return data; 1753746d5c1STitus Rwantare } 1763746d5c1STitus Rwantare 1773746d5c1STitus Rwantare static void pmbus_quick_cmd(SMBusDevice *smd, uint8_t read) 1783746d5c1STitus Rwantare { 1793746d5c1STitus Rwantare PMBusDevice *pmdev = PMBUS_DEVICE(smd); 1803746d5c1STitus Rwantare PMBusDeviceClass *pmdc = PMBUS_DEVICE_GET_CLASS(pmdev); 1813746d5c1STitus Rwantare 1823746d5c1STitus Rwantare if (pmdc->quick_cmd) { 1833746d5c1STitus Rwantare pmdc->quick_cmd(pmdev, read); 1843746d5c1STitus Rwantare } 1853746d5c1STitus Rwantare } 1863746d5c1STitus Rwantare 1873746d5c1STitus Rwantare static void pmbus_pages_alloc(PMBusDevice *pmdev) 1883746d5c1STitus Rwantare { 1893746d5c1STitus Rwantare /* some PMBus devices don't use the PAGE command, so they get 1 page */ 1903746d5c1STitus Rwantare PMBusDeviceClass *k = PMBUS_DEVICE_GET_CLASS(pmdev); 1913746d5c1STitus Rwantare if (k->device_num_pages == 0) { 1923746d5c1STitus Rwantare k->device_num_pages = 1; 1933746d5c1STitus Rwantare } 1943746d5c1STitus Rwantare pmdev->num_pages = k->device_num_pages; 1953746d5c1STitus Rwantare pmdev->pages = g_new0(PMBusPage, k->device_num_pages); 1963746d5c1STitus Rwantare } 1973746d5c1STitus Rwantare 1983746d5c1STitus Rwantare void pmbus_check_limits(PMBusDevice *pmdev) 1993746d5c1STitus Rwantare { 2003746d5c1STitus Rwantare for (int i = 0; i < pmdev->num_pages; i++) { 2013746d5c1STitus Rwantare if ((pmdev->pages[i].operation & PB_OP_ON) == 0) { 2023746d5c1STitus Rwantare continue; /* don't check powered off devices */ 2033746d5c1STitus Rwantare } 2043746d5c1STitus Rwantare 2053746d5c1STitus Rwantare if (pmdev->pages[i].read_vout > pmdev->pages[i].vout_ov_fault_limit) { 2063746d5c1STitus Rwantare pmdev->pages[i].status_word |= PB_STATUS_VOUT; 2073746d5c1STitus Rwantare pmdev->pages[i].status_vout |= PB_STATUS_VOUT_OV_FAULT; 2083746d5c1STitus Rwantare } 2093746d5c1STitus Rwantare 2103746d5c1STitus Rwantare if (pmdev->pages[i].read_vout > pmdev->pages[i].vout_ov_warn_limit) { 2113746d5c1STitus Rwantare pmdev->pages[i].status_word |= PB_STATUS_VOUT; 2123746d5c1STitus Rwantare pmdev->pages[i].status_vout |= PB_STATUS_VOUT_OV_WARN; 2133746d5c1STitus Rwantare } 2143746d5c1STitus Rwantare 2153746d5c1STitus Rwantare if (pmdev->pages[i].read_vout < pmdev->pages[i].vout_uv_warn_limit) { 2163746d5c1STitus Rwantare pmdev->pages[i].status_word |= PB_STATUS_VOUT; 2173746d5c1STitus Rwantare pmdev->pages[i].status_vout |= PB_STATUS_VOUT_UV_WARN; 2183746d5c1STitus Rwantare } 2193746d5c1STitus Rwantare 2203746d5c1STitus Rwantare if (pmdev->pages[i].read_vout < pmdev->pages[i].vout_uv_fault_limit) { 2213746d5c1STitus Rwantare pmdev->pages[i].status_word |= PB_STATUS_VOUT; 2223746d5c1STitus Rwantare pmdev->pages[i].status_vout |= PB_STATUS_VOUT_UV_FAULT; 2233746d5c1STitus Rwantare } 2243746d5c1STitus Rwantare 2253746d5c1STitus Rwantare if (pmdev->pages[i].read_vin > pmdev->pages[i].vin_ov_warn_limit) { 2263746d5c1STitus Rwantare pmdev->pages[i].status_word |= PB_STATUS_INPUT; 2273746d5c1STitus Rwantare pmdev->pages[i].status_input |= PB_STATUS_INPUT_VIN_OV_WARN; 2283746d5c1STitus Rwantare } 2293746d5c1STitus Rwantare 2303746d5c1STitus Rwantare if (pmdev->pages[i].read_vin < pmdev->pages[i].vin_uv_warn_limit) { 2313746d5c1STitus Rwantare pmdev->pages[i].status_word |= PB_STATUS_INPUT; 2323746d5c1STitus Rwantare pmdev->pages[i].status_input |= PB_STATUS_INPUT_VIN_UV_WARN; 2333746d5c1STitus Rwantare } 2343746d5c1STitus Rwantare 2353746d5c1STitus Rwantare if (pmdev->pages[i].read_iout > pmdev->pages[i].iout_oc_warn_limit) { 2363746d5c1STitus Rwantare pmdev->pages[i].status_word |= PB_STATUS_IOUT_POUT; 2373746d5c1STitus Rwantare pmdev->pages[i].status_iout |= PB_STATUS_IOUT_OC_WARN; 2383746d5c1STitus Rwantare } 2393746d5c1STitus Rwantare 2403746d5c1STitus Rwantare if (pmdev->pages[i].read_iout > pmdev->pages[i].iout_oc_fault_limit) { 2413746d5c1STitus Rwantare pmdev->pages[i].status_word |= PB_STATUS_IOUT_POUT; 2423746d5c1STitus Rwantare pmdev->pages[i].status_iout |= PB_STATUS_IOUT_OC_FAULT; 2433746d5c1STitus Rwantare } 2443746d5c1STitus Rwantare 2453746d5c1STitus Rwantare if (pmdev->pages[i].read_pin > pmdev->pages[i].pin_op_warn_limit) { 2463746d5c1STitus Rwantare pmdev->pages[i].status_word |= PB_STATUS_INPUT; 2473746d5c1STitus Rwantare pmdev->pages[i].status_input |= PB_STATUS_INPUT_PIN_OP_WARN; 2483746d5c1STitus Rwantare } 2493746d5c1STitus Rwantare 2503746d5c1STitus Rwantare if (pmdev->pages[i].read_temperature_1 2513746d5c1STitus Rwantare > pmdev->pages[i].ot_fault_limit) { 2523746d5c1STitus Rwantare pmdev->pages[i].status_word |= PB_STATUS_TEMPERATURE; 2533746d5c1STitus Rwantare pmdev->pages[i].status_temperature |= PB_STATUS_OT_FAULT; 2543746d5c1STitus Rwantare } 2553746d5c1STitus Rwantare 2563746d5c1STitus Rwantare if (pmdev->pages[i].read_temperature_1 2573746d5c1STitus Rwantare > pmdev->pages[i].ot_warn_limit) { 2583746d5c1STitus Rwantare pmdev->pages[i].status_word |= PB_STATUS_TEMPERATURE; 2593746d5c1STitus Rwantare pmdev->pages[i].status_temperature |= PB_STATUS_OT_WARN; 2603746d5c1STitus Rwantare } 2613746d5c1STitus Rwantare } 2623746d5c1STitus Rwantare } 2633746d5c1STitus Rwantare 26438870253STitus Rwantare /* assert the status_cml error upon receipt of malformed command */ 26538870253STitus Rwantare static void pmbus_cml_error(PMBusDevice *pmdev) 26638870253STitus Rwantare { 26738870253STitus Rwantare for (int i = 0; i < pmdev->num_pages; i++) { 26838870253STitus Rwantare pmdev->pages[i].status_word |= PMBUS_STATUS_CML; 26938870253STitus Rwantare pmdev->pages[i].status_cml |= PB_CML_FAULT_INVALID_CMD; 27038870253STitus Rwantare } 27138870253STitus Rwantare } 27238870253STitus Rwantare 2733746d5c1STitus Rwantare static uint8_t pmbus_receive_byte(SMBusDevice *smd) 2743746d5c1STitus Rwantare { 2753746d5c1STitus Rwantare PMBusDevice *pmdev = PMBUS_DEVICE(smd); 2763746d5c1STitus Rwantare PMBusDeviceClass *pmdc = PMBUS_DEVICE_GET_CLASS(pmdev); 27738870253STitus Rwantare uint8_t ret = PMBUS_ERR_BYTE; 27838870253STitus Rwantare uint8_t index; 2793746d5c1STitus Rwantare 2803746d5c1STitus Rwantare if (pmdev->out_buf_len != 0) { 2813746d5c1STitus Rwantare ret = pmbus_out_buf_pop(pmdev); 2823746d5c1STitus Rwantare return ret; 2833746d5c1STitus Rwantare } 2843746d5c1STitus Rwantare 28538870253STitus Rwantare /* 28638870253STitus Rwantare * Reading from all pages will return the value from page 0, 28738870253STitus Rwantare * this is unspecified behaviour in general. 28838870253STitus Rwantare */ 28938870253STitus Rwantare if (pmdev->page == PB_ALL_PAGES) { 29038870253STitus Rwantare index = 0; 29138870253STitus Rwantare qemu_log_mask(LOG_GUEST_ERROR, 29238870253STitus Rwantare "%s: tried to read from all pages\n", 29338870253STitus Rwantare __func__); 29438870253STitus Rwantare pmbus_cml_error(pmdev); 29538870253STitus Rwantare } else if (pmdev->page > pmdev->num_pages - 1) { 29638870253STitus Rwantare qemu_log_mask(LOG_GUEST_ERROR, 29738870253STitus Rwantare "%s: page %d is out of range\n", 29838870253STitus Rwantare __func__, pmdev->page); 29938870253STitus Rwantare pmbus_cml_error(pmdev); 30038870253STitus Rwantare return PMBUS_ERR_BYTE; 30138870253STitus Rwantare } else { 30238870253STitus Rwantare index = pmdev->page; 30338870253STitus Rwantare } 30438870253STitus Rwantare 3053746d5c1STitus Rwantare switch (pmdev->code) { 3063746d5c1STitus Rwantare case PMBUS_PAGE: 3073746d5c1STitus Rwantare pmbus_send8(pmdev, pmdev->page); 3083746d5c1STitus Rwantare break; 3093746d5c1STitus Rwantare 3103746d5c1STitus Rwantare case PMBUS_OPERATION: /* R/W byte */ 3113746d5c1STitus Rwantare pmbus_send8(pmdev, pmdev->pages[index].operation); 3123746d5c1STitus Rwantare break; 3133746d5c1STitus Rwantare 3143746d5c1STitus Rwantare case PMBUS_ON_OFF_CONFIG: /* R/W byte */ 3153746d5c1STitus Rwantare pmbus_send8(pmdev, pmdev->pages[index].on_off_config); 3163746d5c1STitus Rwantare break; 3173746d5c1STitus Rwantare 3183746d5c1STitus Rwantare case PMBUS_PHASE: /* R/W byte */ 3193746d5c1STitus Rwantare pmbus_send8(pmdev, pmdev->pages[index].phase); 3203746d5c1STitus Rwantare break; 3213746d5c1STitus Rwantare 3223746d5c1STitus Rwantare case PMBUS_WRITE_PROTECT: /* R/W byte */ 3233746d5c1STitus Rwantare pmbus_send8(pmdev, pmdev->pages[index].write_protect); 3243746d5c1STitus Rwantare break; 3253746d5c1STitus Rwantare 3263746d5c1STitus Rwantare case PMBUS_CAPABILITY: 3273746d5c1STitus Rwantare pmbus_send8(pmdev, pmdev->capability); 3282192aaaeSTitus Rwantare if (pmdev->capability & BIT(7)) { 3292192aaaeSTitus Rwantare qemu_log_mask(LOG_UNIMP, 3302192aaaeSTitus Rwantare "%s: PEC is enabled but not yet supported.\n", 3312192aaaeSTitus Rwantare __func__); 3322192aaaeSTitus Rwantare } 3333746d5c1STitus Rwantare break; 3343746d5c1STitus Rwantare 3353746d5c1STitus Rwantare case PMBUS_VOUT_MODE: /* R/W byte */ 3363746d5c1STitus Rwantare if (pmdev->pages[index].page_flags & PB_HAS_VOUT_MODE) { 3373746d5c1STitus Rwantare pmbus_send8(pmdev, pmdev->pages[index].vout_mode); 3383746d5c1STitus Rwantare } else { 3393746d5c1STitus Rwantare goto passthough; 3403746d5c1STitus Rwantare } 3413746d5c1STitus Rwantare break; 3423746d5c1STitus Rwantare 3433746d5c1STitus Rwantare case PMBUS_VOUT_COMMAND: /* R/W word */ 3443746d5c1STitus Rwantare if (pmdev->pages[index].page_flags & PB_HAS_VOUT) { 3453746d5c1STitus Rwantare pmbus_send16(pmdev, pmdev->pages[index].vout_command); 3463746d5c1STitus Rwantare } else { 3473746d5c1STitus Rwantare goto passthough; 3483746d5c1STitus Rwantare } 3493746d5c1STitus Rwantare break; 3503746d5c1STitus Rwantare 3513746d5c1STitus Rwantare case PMBUS_VOUT_TRIM: /* R/W word */ 3523746d5c1STitus Rwantare if (pmdev->pages[index].page_flags & PB_HAS_VOUT) { 3533746d5c1STitus Rwantare pmbus_send16(pmdev, pmdev->pages[index].vout_trim); 3543746d5c1STitus Rwantare } else { 3553746d5c1STitus Rwantare goto passthough; 3563746d5c1STitus Rwantare } 3573746d5c1STitus Rwantare break; 3583746d5c1STitus Rwantare 3593746d5c1STitus Rwantare case PMBUS_VOUT_CAL_OFFSET: /* R/W word */ 3603746d5c1STitus Rwantare if (pmdev->pages[index].page_flags & PB_HAS_VOUT) { 3613746d5c1STitus Rwantare pmbus_send16(pmdev, pmdev->pages[index].vout_cal_offset); 3623746d5c1STitus Rwantare } else { 3633746d5c1STitus Rwantare goto passthough; 3643746d5c1STitus Rwantare } 3653746d5c1STitus Rwantare break; 3663746d5c1STitus Rwantare 3673746d5c1STitus Rwantare case PMBUS_VOUT_MAX: /* R/W word */ 3683746d5c1STitus Rwantare if (pmdev->pages[index].page_flags & PB_HAS_VOUT) { 3693746d5c1STitus Rwantare pmbus_send16(pmdev, pmdev->pages[index].vout_max); 3703746d5c1STitus Rwantare } else { 3713746d5c1STitus Rwantare goto passthough; 3723746d5c1STitus Rwantare } 3733746d5c1STitus Rwantare break; 3743746d5c1STitus Rwantare 3753746d5c1STitus Rwantare case PMBUS_VOUT_MARGIN_HIGH: /* R/W word */ 3763746d5c1STitus Rwantare if (pmdev->pages[index].page_flags & PB_HAS_VOUT_MARGIN) { 3773746d5c1STitus Rwantare pmbus_send16(pmdev, pmdev->pages[index].vout_margin_high); 3783746d5c1STitus Rwantare } else { 3793746d5c1STitus Rwantare goto passthough; 3803746d5c1STitus Rwantare } 3813746d5c1STitus Rwantare break; 3823746d5c1STitus Rwantare 3833746d5c1STitus Rwantare case PMBUS_VOUT_MARGIN_LOW: /* R/W word */ 3843746d5c1STitus Rwantare if (pmdev->pages[index].page_flags & PB_HAS_VOUT_MARGIN) { 3853746d5c1STitus Rwantare pmbus_send16(pmdev, pmdev->pages[index].vout_margin_low); 3863746d5c1STitus Rwantare } else { 3873746d5c1STitus Rwantare goto passthough; 3883746d5c1STitus Rwantare } 3893746d5c1STitus Rwantare break; 3903746d5c1STitus Rwantare 3913746d5c1STitus Rwantare case PMBUS_VOUT_TRANSITION_RATE: /* R/W word */ 3923746d5c1STitus Rwantare if (pmdev->pages[index].page_flags & PB_HAS_VOUT) { 3933746d5c1STitus Rwantare pmbus_send16(pmdev, pmdev->pages[index].vout_transition_rate); 3943746d5c1STitus Rwantare } else { 3953746d5c1STitus Rwantare goto passthough; 3963746d5c1STitus Rwantare } 3973746d5c1STitus Rwantare break; 3983746d5c1STitus Rwantare 3993746d5c1STitus Rwantare case PMBUS_VOUT_DROOP: /* R/W word */ 4003746d5c1STitus Rwantare if (pmdev->pages[index].page_flags & PB_HAS_VOUT) { 4013746d5c1STitus Rwantare pmbus_send16(pmdev, pmdev->pages[index].vout_droop); 4023746d5c1STitus Rwantare } else { 4033746d5c1STitus Rwantare goto passthough; 4043746d5c1STitus Rwantare } 4053746d5c1STitus Rwantare break; 4063746d5c1STitus Rwantare 4073746d5c1STitus Rwantare case PMBUS_VOUT_SCALE_LOOP: /* R/W word */ 4083746d5c1STitus Rwantare if (pmdev->pages[index].page_flags & PB_HAS_VOUT) { 4093746d5c1STitus Rwantare pmbus_send16(pmdev, pmdev->pages[index].vout_scale_loop); 4103746d5c1STitus Rwantare } else { 4113746d5c1STitus Rwantare goto passthough; 4123746d5c1STitus Rwantare } 4133746d5c1STitus Rwantare break; 4143746d5c1STitus Rwantare 4153746d5c1STitus Rwantare case PMBUS_VOUT_SCALE_MONITOR: /* R/W word */ 4163746d5c1STitus Rwantare if (pmdev->pages[index].page_flags & PB_HAS_VOUT) { 4173746d5c1STitus Rwantare pmbus_send16(pmdev, pmdev->pages[index].vout_scale_monitor); 4183746d5c1STitus Rwantare } else { 4193746d5c1STitus Rwantare goto passthough; 4203746d5c1STitus Rwantare } 4213746d5c1STitus Rwantare break; 4223746d5c1STitus Rwantare 42332480293STitus Rwantare case PMBUS_VOUT_MIN: /* R/W word */ 42432480293STitus Rwantare if (pmdev->pages[index].page_flags & PB_HAS_VOUT_RATING) { 42532480293STitus Rwantare pmbus_send16(pmdev, pmdev->pages[index].vout_min); 42632480293STitus Rwantare } else { 42732480293STitus Rwantare goto passthough; 42832480293STitus Rwantare } 42932480293STitus Rwantare break; 43032480293STitus Rwantare 4313746d5c1STitus Rwantare /* TODO: implement coefficients support */ 4323746d5c1STitus Rwantare 4333746d5c1STitus Rwantare case PMBUS_POUT_MAX: /* R/W word */ 4343746d5c1STitus Rwantare if (pmdev->pages[index].page_flags & PB_HAS_POUT) { 4353746d5c1STitus Rwantare pmbus_send16(pmdev, pmdev->pages[index].pout_max); 4363746d5c1STitus Rwantare } else { 4373746d5c1STitus Rwantare goto passthough; 4383746d5c1STitus Rwantare } 4393746d5c1STitus Rwantare break; 4403746d5c1STitus Rwantare 4413746d5c1STitus Rwantare case PMBUS_VIN_ON: /* R/W word */ 4423746d5c1STitus Rwantare if (pmdev->pages[index].page_flags & PB_HAS_VIN) { 4433746d5c1STitus Rwantare pmbus_send16(pmdev, pmdev->pages[index].vin_on); 4443746d5c1STitus Rwantare } else { 4453746d5c1STitus Rwantare goto passthough; 4463746d5c1STitus Rwantare } 4473746d5c1STitus Rwantare break; 4483746d5c1STitus Rwantare 4493746d5c1STitus Rwantare case PMBUS_VIN_OFF: /* R/W word */ 4503746d5c1STitus Rwantare if (pmdev->pages[index].page_flags & PB_HAS_VIN) { 4513746d5c1STitus Rwantare pmbus_send16(pmdev, pmdev->pages[index].vin_off); 4523746d5c1STitus Rwantare } else { 4533746d5c1STitus Rwantare goto passthough; 4543746d5c1STitus Rwantare } 4553746d5c1STitus Rwantare break; 4563746d5c1STitus Rwantare 4573746d5c1STitus Rwantare case PMBUS_IOUT_CAL_GAIN: /* R/W word */ 4583746d5c1STitus Rwantare if (pmdev->pages[index].page_flags & PB_HAS_IOUT_GAIN) { 4593746d5c1STitus Rwantare pmbus_send16(pmdev, pmdev->pages[index].iout_cal_gain); 4603746d5c1STitus Rwantare } else { 4613746d5c1STitus Rwantare goto passthough; 4623746d5c1STitus Rwantare } 4633746d5c1STitus Rwantare break; 4643746d5c1STitus Rwantare 4653746d5c1STitus Rwantare case PMBUS_VOUT_OV_FAULT_LIMIT: /* R/W word */ 4663746d5c1STitus Rwantare if (pmdev->pages[index].page_flags & PB_HAS_VOUT) { 4673746d5c1STitus Rwantare pmbus_send16(pmdev, pmdev->pages[index].vout_ov_fault_limit); 4683746d5c1STitus Rwantare } else { 4693746d5c1STitus Rwantare goto passthough; 4703746d5c1STitus Rwantare } 4713746d5c1STitus Rwantare break; 4723746d5c1STitus Rwantare 4733746d5c1STitus Rwantare case PMBUS_VOUT_OV_FAULT_RESPONSE: /* R/W byte */ 4743746d5c1STitus Rwantare if (pmdev->pages[index].page_flags & PB_HAS_VOUT) { 4753746d5c1STitus Rwantare pmbus_send8(pmdev, pmdev->pages[index].vout_ov_fault_response); 4763746d5c1STitus Rwantare } else { 4773746d5c1STitus Rwantare goto passthough; 4783746d5c1STitus Rwantare } 4793746d5c1STitus Rwantare break; 4803746d5c1STitus Rwantare 4813746d5c1STitus Rwantare case PMBUS_VOUT_OV_WARN_LIMIT: /* R/W word */ 4823746d5c1STitus Rwantare if (pmdev->pages[index].page_flags & PB_HAS_VOUT) { 4833746d5c1STitus Rwantare pmbus_send16(pmdev, pmdev->pages[index].vout_ov_warn_limit); 4843746d5c1STitus Rwantare } else { 4853746d5c1STitus Rwantare goto passthough; 4863746d5c1STitus Rwantare } 4873746d5c1STitus Rwantare break; 4883746d5c1STitus Rwantare 4893746d5c1STitus Rwantare case PMBUS_VOUT_UV_WARN_LIMIT: /* R/W word */ 4903746d5c1STitus Rwantare if (pmdev->pages[index].page_flags & PB_HAS_VOUT) { 4913746d5c1STitus Rwantare pmbus_send16(pmdev, pmdev->pages[index].vout_uv_warn_limit); 4923746d5c1STitus Rwantare } else { 4933746d5c1STitus Rwantare goto passthough; 4943746d5c1STitus Rwantare } 4953746d5c1STitus Rwantare break; 4963746d5c1STitus Rwantare 4973746d5c1STitus Rwantare case PMBUS_VOUT_UV_FAULT_LIMIT: /* R/W word */ 4983746d5c1STitus Rwantare if (pmdev->pages[index].page_flags & PB_HAS_VOUT) { 4993746d5c1STitus Rwantare pmbus_send16(pmdev, pmdev->pages[index].vout_uv_fault_limit); 5003746d5c1STitus Rwantare } else { 5013746d5c1STitus Rwantare goto passthough; 5023746d5c1STitus Rwantare } 5033746d5c1STitus Rwantare break; 5043746d5c1STitus Rwantare 5053746d5c1STitus Rwantare case PMBUS_VOUT_UV_FAULT_RESPONSE: /* R/W byte */ 5063746d5c1STitus Rwantare if (pmdev->pages[index].page_flags & PB_HAS_VOUT) { 5073746d5c1STitus Rwantare pmbus_send8(pmdev, pmdev->pages[index].vout_uv_fault_response); 5083746d5c1STitus Rwantare } else { 5093746d5c1STitus Rwantare goto passthough; 5103746d5c1STitus Rwantare } 5113746d5c1STitus Rwantare break; 5123746d5c1STitus Rwantare 5133746d5c1STitus Rwantare case PMBUS_IOUT_OC_FAULT_LIMIT: /* R/W word */ 5143746d5c1STitus Rwantare if (pmdev->pages[index].page_flags & PB_HAS_IOUT) { 5153746d5c1STitus Rwantare pmbus_send16(pmdev, pmdev->pages[index].iout_oc_fault_limit); 5163746d5c1STitus Rwantare } else { 5173746d5c1STitus Rwantare goto passthough; 5183746d5c1STitus Rwantare } 5193746d5c1STitus Rwantare break; 5203746d5c1STitus Rwantare 5213746d5c1STitus Rwantare case PMBUS_IOUT_OC_FAULT_RESPONSE: /* R/W byte */ 5223746d5c1STitus Rwantare if (pmdev->pages[index].page_flags & PB_HAS_IOUT) { 5233746d5c1STitus Rwantare pmbus_send8(pmdev, pmdev->pages[index].iout_oc_fault_response); 5243746d5c1STitus Rwantare } else { 5253746d5c1STitus Rwantare goto passthough; 5263746d5c1STitus Rwantare } 5273746d5c1STitus Rwantare break; 5283746d5c1STitus Rwantare 5293746d5c1STitus Rwantare case PMBUS_IOUT_OC_LV_FAULT_LIMIT: /* R/W word */ 5303746d5c1STitus Rwantare if (pmdev->pages[index].page_flags & PB_HAS_IOUT) { 5313746d5c1STitus Rwantare pmbus_send16(pmdev, pmdev->pages[index].iout_oc_lv_fault_limit); 5323746d5c1STitus Rwantare } else { 5333746d5c1STitus Rwantare goto passthough; 5343746d5c1STitus Rwantare } 5353746d5c1STitus Rwantare break; 5363746d5c1STitus Rwantare 5373746d5c1STitus Rwantare case PMBUS_IOUT_OC_LV_FAULT_RESPONSE: /* R/W byte */ 5383746d5c1STitus Rwantare if (pmdev->pages[index].page_flags & PB_HAS_IOUT) { 5393746d5c1STitus Rwantare pmbus_send8(pmdev, pmdev->pages[index].iout_oc_lv_fault_response); 5403746d5c1STitus Rwantare } else { 5413746d5c1STitus Rwantare goto passthough; 5423746d5c1STitus Rwantare } 5433746d5c1STitus Rwantare break; 5443746d5c1STitus Rwantare 5453746d5c1STitus Rwantare case PMBUS_IOUT_OC_WARN_LIMIT: /* R/W word */ 5463746d5c1STitus Rwantare if (pmdev->pages[index].page_flags & PB_HAS_IOUT) { 5473746d5c1STitus Rwantare pmbus_send16(pmdev, pmdev->pages[index].iout_oc_warn_limit); 5483746d5c1STitus Rwantare } else { 5493746d5c1STitus Rwantare goto passthough; 5503746d5c1STitus Rwantare } 5513746d5c1STitus Rwantare break; 5523746d5c1STitus Rwantare 5533746d5c1STitus Rwantare case PMBUS_IOUT_UC_FAULT_LIMIT: /* R/W word */ 5543746d5c1STitus Rwantare if (pmdev->pages[index].page_flags & PB_HAS_IOUT) { 5553746d5c1STitus Rwantare pmbus_send16(pmdev, pmdev->pages[index].iout_uc_fault_limit); 5563746d5c1STitus Rwantare } else { 5573746d5c1STitus Rwantare goto passthough; 5583746d5c1STitus Rwantare } 5593746d5c1STitus Rwantare break; 5603746d5c1STitus Rwantare 5613746d5c1STitus Rwantare case PMBUS_IOUT_UC_FAULT_RESPONSE: /* R/W byte */ 5623746d5c1STitus Rwantare if (pmdev->pages[index].page_flags & PB_HAS_IOUT) { 5633746d5c1STitus Rwantare pmbus_send8(pmdev, pmdev->pages[index].iout_uc_fault_response); 5643746d5c1STitus Rwantare } else { 5653746d5c1STitus Rwantare goto passthough; 5663746d5c1STitus Rwantare } 5673746d5c1STitus Rwantare break; 5683746d5c1STitus Rwantare 5693746d5c1STitus Rwantare case PMBUS_OT_FAULT_LIMIT: /* R/W word */ 5703746d5c1STitus Rwantare if (pmdev->pages[index].page_flags & PB_HAS_TEMPERATURE) { 5713746d5c1STitus Rwantare pmbus_send16(pmdev, pmdev->pages[index].ot_fault_limit); 5723746d5c1STitus Rwantare } else { 5733746d5c1STitus Rwantare goto passthough; 5743746d5c1STitus Rwantare } 5753746d5c1STitus Rwantare break; 5763746d5c1STitus Rwantare 5773746d5c1STitus Rwantare case PMBUS_OT_FAULT_RESPONSE: /* R/W byte */ 5783746d5c1STitus Rwantare if (pmdev->pages[index].page_flags & PB_HAS_TEMPERATURE) { 5793746d5c1STitus Rwantare pmbus_send8(pmdev, pmdev->pages[index].ot_fault_response); 5803746d5c1STitus Rwantare } else { 5813746d5c1STitus Rwantare goto passthough; 5823746d5c1STitus Rwantare } 5833746d5c1STitus Rwantare break; 5843746d5c1STitus Rwantare 5853746d5c1STitus Rwantare case PMBUS_OT_WARN_LIMIT: /* R/W word */ 5863746d5c1STitus Rwantare if (pmdev->pages[index].page_flags & PB_HAS_TEMPERATURE) { 5873746d5c1STitus Rwantare pmbus_send16(pmdev, pmdev->pages[index].ot_warn_limit); 5883746d5c1STitus Rwantare } else { 5893746d5c1STitus Rwantare goto passthough; 5903746d5c1STitus Rwantare } 5913746d5c1STitus Rwantare break; 5923746d5c1STitus Rwantare 5933746d5c1STitus Rwantare case PMBUS_UT_WARN_LIMIT: /* R/W word */ 5943746d5c1STitus Rwantare if (pmdev->pages[index].page_flags & PB_HAS_TEMPERATURE) { 5953746d5c1STitus Rwantare pmbus_send16(pmdev, pmdev->pages[index].ut_warn_limit); 5963746d5c1STitus Rwantare } else { 5973746d5c1STitus Rwantare goto passthough; 5983746d5c1STitus Rwantare } 5993746d5c1STitus Rwantare break; 6003746d5c1STitus Rwantare 6013746d5c1STitus Rwantare case PMBUS_UT_FAULT_LIMIT: /* R/W word */ 6023746d5c1STitus Rwantare if (pmdev->pages[index].page_flags & PB_HAS_TEMPERATURE) { 6033746d5c1STitus Rwantare pmbus_send16(pmdev, pmdev->pages[index].ut_fault_limit); 6043746d5c1STitus Rwantare } else { 6053746d5c1STitus Rwantare goto passthough; 6063746d5c1STitus Rwantare } 6073746d5c1STitus Rwantare break; 6083746d5c1STitus Rwantare 6093746d5c1STitus Rwantare case PMBUS_UT_FAULT_RESPONSE: /* R/W byte */ 6103746d5c1STitus Rwantare if (pmdev->pages[index].page_flags & PB_HAS_TEMPERATURE) { 6113746d5c1STitus Rwantare pmbus_send8(pmdev, pmdev->pages[index].ut_fault_response); 6123746d5c1STitus Rwantare } else { 6133746d5c1STitus Rwantare goto passthough; 6143746d5c1STitus Rwantare } 6153746d5c1STitus Rwantare break; 6163746d5c1STitus Rwantare 6173746d5c1STitus Rwantare case PMBUS_VIN_OV_FAULT_LIMIT: /* R/W word */ 6183746d5c1STitus Rwantare if (pmdev->pages[index].page_flags & PB_HAS_VIN) { 6193746d5c1STitus Rwantare pmbus_send16(pmdev, pmdev->pages[index].vin_ov_fault_limit); 6203746d5c1STitus Rwantare } else { 6213746d5c1STitus Rwantare goto passthough; 6223746d5c1STitus Rwantare } 6233746d5c1STitus Rwantare break; 6243746d5c1STitus Rwantare 6253746d5c1STitus Rwantare case PMBUS_VIN_OV_FAULT_RESPONSE: /* R/W byte */ 6263746d5c1STitus Rwantare if (pmdev->pages[index].page_flags & PB_HAS_VIN) { 6273746d5c1STitus Rwantare pmbus_send8(pmdev, pmdev->pages[index].vin_ov_fault_response); 6283746d5c1STitus Rwantare } else { 6293746d5c1STitus Rwantare goto passthough; 6303746d5c1STitus Rwantare } 6313746d5c1STitus Rwantare break; 6323746d5c1STitus Rwantare 6333746d5c1STitus Rwantare case PMBUS_VIN_OV_WARN_LIMIT: /* R/W word */ 6343746d5c1STitus Rwantare if (pmdev->pages[index].page_flags & PB_HAS_VIN) { 6353746d5c1STitus Rwantare pmbus_send16(pmdev, pmdev->pages[index].vin_ov_warn_limit); 6363746d5c1STitus Rwantare } else { 6373746d5c1STitus Rwantare goto passthough; 6383746d5c1STitus Rwantare } 6393746d5c1STitus Rwantare break; 6403746d5c1STitus Rwantare 6413746d5c1STitus Rwantare case PMBUS_VIN_UV_WARN_LIMIT: /* R/W word */ 6423746d5c1STitus Rwantare if (pmdev->pages[index].page_flags & PB_HAS_VIN) { 6433746d5c1STitus Rwantare pmbus_send16(pmdev, pmdev->pages[index].vin_uv_warn_limit); 6443746d5c1STitus Rwantare } else { 6453746d5c1STitus Rwantare goto passthough; 6463746d5c1STitus Rwantare } 6473746d5c1STitus Rwantare break; 6483746d5c1STitus Rwantare 6493746d5c1STitus Rwantare case PMBUS_VIN_UV_FAULT_LIMIT: /* R/W word */ 6503746d5c1STitus Rwantare if (pmdev->pages[index].page_flags & PB_HAS_VIN) { 6513746d5c1STitus Rwantare pmbus_send16(pmdev, pmdev->pages[index].vin_uv_fault_limit); 6523746d5c1STitus Rwantare } else { 6533746d5c1STitus Rwantare goto passthough; 6543746d5c1STitus Rwantare } 6553746d5c1STitus Rwantare break; 6563746d5c1STitus Rwantare 6573746d5c1STitus Rwantare case PMBUS_VIN_UV_FAULT_RESPONSE: /* R/W byte */ 6583746d5c1STitus Rwantare if (pmdev->pages[index].page_flags & PB_HAS_VIN) { 6593746d5c1STitus Rwantare pmbus_send8(pmdev, pmdev->pages[index].vin_uv_fault_response); 6603746d5c1STitus Rwantare } else { 6613746d5c1STitus Rwantare goto passthough; 6623746d5c1STitus Rwantare } 6633746d5c1STitus Rwantare break; 6643746d5c1STitus Rwantare 6653746d5c1STitus Rwantare case PMBUS_IIN_OC_FAULT_LIMIT: /* R/W word */ 6663746d5c1STitus Rwantare if (pmdev->pages[index].page_flags & PB_HAS_IIN) { 6673746d5c1STitus Rwantare pmbus_send16(pmdev, pmdev->pages[index].iin_oc_fault_limit); 6683746d5c1STitus Rwantare } else { 6693746d5c1STitus Rwantare goto passthough; 6703746d5c1STitus Rwantare } 6713746d5c1STitus Rwantare break; 6723746d5c1STitus Rwantare 6733746d5c1STitus Rwantare case PMBUS_IIN_OC_FAULT_RESPONSE: /* R/W byte */ 6743746d5c1STitus Rwantare if (pmdev->pages[index].page_flags & PB_HAS_IIN) { 6753746d5c1STitus Rwantare pmbus_send8(pmdev, pmdev->pages[index].iin_oc_fault_response); 6763746d5c1STitus Rwantare } else { 6773746d5c1STitus Rwantare goto passthough; 6783746d5c1STitus Rwantare } 6793746d5c1STitus Rwantare break; 6803746d5c1STitus Rwantare 6813746d5c1STitus Rwantare case PMBUS_IIN_OC_WARN_LIMIT: /* R/W word */ 6823746d5c1STitus Rwantare if (pmdev->pages[index].page_flags & PB_HAS_IIN) { 6833746d5c1STitus Rwantare pmbus_send16(pmdev, pmdev->pages[index].iin_oc_warn_limit); 6843746d5c1STitus Rwantare } else { 6853746d5c1STitus Rwantare goto passthough; 6863746d5c1STitus Rwantare } 6873746d5c1STitus Rwantare break; 6883746d5c1STitus Rwantare 6893746d5c1STitus Rwantare case PMBUS_POUT_OP_FAULT_LIMIT: /* R/W word */ 6903746d5c1STitus Rwantare if (pmdev->pages[index].page_flags & PB_HAS_POUT) { 6913746d5c1STitus Rwantare pmbus_send16(pmdev, pmdev->pages[index].pout_op_fault_limit); 6923746d5c1STitus Rwantare } else { 6933746d5c1STitus Rwantare goto passthough; 6943746d5c1STitus Rwantare } 6953746d5c1STitus Rwantare break; 6963746d5c1STitus Rwantare 6973746d5c1STitus Rwantare case PMBUS_POUT_OP_FAULT_RESPONSE: /* R/W byte */ 6983746d5c1STitus Rwantare if (pmdev->pages[index].page_flags & PB_HAS_POUT) { 6993746d5c1STitus Rwantare pmbus_send8(pmdev, pmdev->pages[index].pout_op_fault_response); 7003746d5c1STitus Rwantare } else { 7013746d5c1STitus Rwantare goto passthough; 7023746d5c1STitus Rwantare } 7033746d5c1STitus Rwantare break; 7043746d5c1STitus Rwantare 7053746d5c1STitus Rwantare case PMBUS_POUT_OP_WARN_LIMIT: /* R/W word */ 7063746d5c1STitus Rwantare if (pmdev->pages[index].page_flags & PB_HAS_POUT) { 7073746d5c1STitus Rwantare pmbus_send16(pmdev, pmdev->pages[index].pout_op_warn_limit); 7083746d5c1STitus Rwantare } else { 7093746d5c1STitus Rwantare goto passthough; 7103746d5c1STitus Rwantare } 7113746d5c1STitus Rwantare break; 7123746d5c1STitus Rwantare 7133746d5c1STitus Rwantare case PMBUS_PIN_OP_WARN_LIMIT: /* R/W word */ 7143746d5c1STitus Rwantare if (pmdev->pages[index].page_flags & PB_HAS_PIN) { 7153746d5c1STitus Rwantare pmbus_send16(pmdev, pmdev->pages[index].pin_op_warn_limit); 7163746d5c1STitus Rwantare } else { 7173746d5c1STitus Rwantare goto passthough; 7183746d5c1STitus Rwantare } 7193746d5c1STitus Rwantare break; 7203746d5c1STitus Rwantare 7213746d5c1STitus Rwantare case PMBUS_STATUS_BYTE: /* R/W byte */ 7223746d5c1STitus Rwantare pmbus_send8(pmdev, pmdev->pages[index].status_word & 0xFF); 7233746d5c1STitus Rwantare break; 7243746d5c1STitus Rwantare 7253746d5c1STitus Rwantare case PMBUS_STATUS_WORD: /* R/W word */ 7263746d5c1STitus Rwantare pmbus_send16(pmdev, pmdev->pages[index].status_word); 7273746d5c1STitus Rwantare break; 7283746d5c1STitus Rwantare 7293746d5c1STitus Rwantare case PMBUS_STATUS_VOUT: /* R/W byte */ 7303746d5c1STitus Rwantare if (pmdev->pages[index].page_flags & PB_HAS_VOUT) { 7313746d5c1STitus Rwantare pmbus_send8(pmdev, pmdev->pages[index].status_vout); 7323746d5c1STitus Rwantare } else { 7333746d5c1STitus Rwantare goto passthough; 7343746d5c1STitus Rwantare } 7353746d5c1STitus Rwantare break; 7363746d5c1STitus Rwantare 7373746d5c1STitus Rwantare case PMBUS_STATUS_IOUT: /* R/W byte */ 7383746d5c1STitus Rwantare if (pmdev->pages[index].page_flags & PB_HAS_IOUT) { 7393746d5c1STitus Rwantare pmbus_send8(pmdev, pmdev->pages[index].status_iout); 7403746d5c1STitus Rwantare } else { 7413746d5c1STitus Rwantare goto passthough; 7423746d5c1STitus Rwantare } 7433746d5c1STitus Rwantare break; 7443746d5c1STitus Rwantare 7453746d5c1STitus Rwantare case PMBUS_STATUS_INPUT: /* R/W byte */ 7463746d5c1STitus Rwantare if (pmdev->pages[index].page_flags & PB_HAS_VIN || 7473746d5c1STitus Rwantare pmdev->pages[index].page_flags & PB_HAS_IIN || 7483746d5c1STitus Rwantare pmdev->pages[index].page_flags & PB_HAS_PIN) { 7493746d5c1STitus Rwantare pmbus_send8(pmdev, pmdev->pages[index].status_input); 7503746d5c1STitus Rwantare } else { 7513746d5c1STitus Rwantare goto passthough; 7523746d5c1STitus Rwantare } 7533746d5c1STitus Rwantare break; 7543746d5c1STitus Rwantare 7553746d5c1STitus Rwantare case PMBUS_STATUS_TEMPERATURE: /* R/W byte */ 7563746d5c1STitus Rwantare if (pmdev->pages[index].page_flags & PB_HAS_TEMPERATURE) { 7573746d5c1STitus Rwantare pmbus_send8(pmdev, pmdev->pages[index].status_temperature); 7583746d5c1STitus Rwantare } else { 7593746d5c1STitus Rwantare goto passthough; 7603746d5c1STitus Rwantare } 7613746d5c1STitus Rwantare break; 7623746d5c1STitus Rwantare 7633746d5c1STitus Rwantare case PMBUS_STATUS_CML: /* R/W byte */ 7643746d5c1STitus Rwantare pmbus_send8(pmdev, pmdev->pages[index].status_cml); 7653746d5c1STitus Rwantare break; 7663746d5c1STitus Rwantare 7673746d5c1STitus Rwantare case PMBUS_STATUS_OTHER: /* R/W byte */ 7683746d5c1STitus Rwantare pmbus_send8(pmdev, pmdev->pages[index].status_other); 7693746d5c1STitus Rwantare break; 7703746d5c1STitus Rwantare 77132480293STitus Rwantare case PMBUS_STATUS_MFR_SPECIFIC: /* R/W byte */ 77232480293STitus Rwantare pmbus_send8(pmdev, pmdev->pages[index].status_mfr_specific); 77332480293STitus Rwantare break; 77432480293STitus Rwantare 7753746d5c1STitus Rwantare case PMBUS_READ_EIN: /* Read-Only block 5 bytes */ 7763746d5c1STitus Rwantare if (pmdev->pages[index].page_flags & PB_HAS_EIN) { 7773746d5c1STitus Rwantare pmbus_send(pmdev, pmdev->pages[index].read_ein, 5); 7783746d5c1STitus Rwantare } else { 7793746d5c1STitus Rwantare goto passthough; 7803746d5c1STitus Rwantare } 7813746d5c1STitus Rwantare break; 7823746d5c1STitus Rwantare 7833746d5c1STitus Rwantare case PMBUS_READ_EOUT: /* Read-Only block 5 bytes */ 7843746d5c1STitus Rwantare if (pmdev->pages[index].page_flags & PB_HAS_EOUT) { 7853746d5c1STitus Rwantare pmbus_send(pmdev, pmdev->pages[index].read_eout, 5); 7863746d5c1STitus Rwantare } else { 7873746d5c1STitus Rwantare goto passthough; 7883746d5c1STitus Rwantare } 7893746d5c1STitus Rwantare break; 7903746d5c1STitus Rwantare 7913746d5c1STitus Rwantare case PMBUS_READ_VIN: /* Read-Only word */ 7923746d5c1STitus Rwantare if (pmdev->pages[index].page_flags & PB_HAS_VIN) { 7933746d5c1STitus Rwantare pmbus_send16(pmdev, pmdev->pages[index].read_vin); 7943746d5c1STitus Rwantare } else { 7953746d5c1STitus Rwantare goto passthough; 7963746d5c1STitus Rwantare } 7973746d5c1STitus Rwantare break; 7983746d5c1STitus Rwantare 7993746d5c1STitus Rwantare case PMBUS_READ_IIN: /* Read-Only word */ 8003746d5c1STitus Rwantare if (pmdev->pages[index].page_flags & PB_HAS_IIN) { 8013746d5c1STitus Rwantare pmbus_send16(pmdev, pmdev->pages[index].read_iin); 8023746d5c1STitus Rwantare } else { 8033746d5c1STitus Rwantare goto passthough; 8043746d5c1STitus Rwantare } 8053746d5c1STitus Rwantare break; 8063746d5c1STitus Rwantare 8073746d5c1STitus Rwantare case PMBUS_READ_VOUT: /* Read-Only word */ 8083746d5c1STitus Rwantare if (pmdev->pages[index].page_flags & PB_HAS_VOUT) { 8093746d5c1STitus Rwantare pmbus_send16(pmdev, pmdev->pages[index].read_vout); 8103746d5c1STitus Rwantare } else { 8113746d5c1STitus Rwantare goto passthough; 8123746d5c1STitus Rwantare } 8133746d5c1STitus Rwantare break; 8143746d5c1STitus Rwantare 8153746d5c1STitus Rwantare case PMBUS_READ_IOUT: /* Read-Only word */ 8163746d5c1STitus Rwantare if (pmdev->pages[index].page_flags & PB_HAS_IOUT) { 8173746d5c1STitus Rwantare pmbus_send16(pmdev, pmdev->pages[index].read_iout); 8183746d5c1STitus Rwantare } else { 8193746d5c1STitus Rwantare goto passthough; 8203746d5c1STitus Rwantare } 8213746d5c1STitus Rwantare break; 8223746d5c1STitus Rwantare 8233746d5c1STitus Rwantare case PMBUS_READ_TEMPERATURE_1: /* Read-Only word */ 8243746d5c1STitus Rwantare if (pmdev->pages[index].page_flags & PB_HAS_TEMPERATURE) { 8253746d5c1STitus Rwantare pmbus_send16(pmdev, pmdev->pages[index].read_temperature_1); 8263746d5c1STitus Rwantare } else { 8273746d5c1STitus Rwantare goto passthough; 8283746d5c1STitus Rwantare } 8293746d5c1STitus Rwantare break; 8303746d5c1STitus Rwantare 8313746d5c1STitus Rwantare case PMBUS_READ_TEMPERATURE_2: /* Read-Only word */ 8323746d5c1STitus Rwantare if (pmdev->pages[index].page_flags & PB_HAS_TEMP2) { 8333746d5c1STitus Rwantare pmbus_send16(pmdev, pmdev->pages[index].read_temperature_2); 8343746d5c1STitus Rwantare } else { 8353746d5c1STitus Rwantare goto passthough; 8363746d5c1STitus Rwantare } 8373746d5c1STitus Rwantare break; 8383746d5c1STitus Rwantare 8393746d5c1STitus Rwantare case PMBUS_READ_TEMPERATURE_3: /* Read-Only word */ 8403746d5c1STitus Rwantare if (pmdev->pages[index].page_flags & PB_HAS_TEMP3) { 8413746d5c1STitus Rwantare pmbus_send16(pmdev, pmdev->pages[index].read_temperature_3); 8423746d5c1STitus Rwantare } else { 8433746d5c1STitus Rwantare goto passthough; 8443746d5c1STitus Rwantare } 8453746d5c1STitus Rwantare break; 8463746d5c1STitus Rwantare 8473746d5c1STitus Rwantare case PMBUS_READ_POUT: /* Read-Only word */ 8483746d5c1STitus Rwantare if (pmdev->pages[index].page_flags & PB_HAS_POUT) { 8493746d5c1STitus Rwantare pmbus_send16(pmdev, pmdev->pages[index].read_pout); 8503746d5c1STitus Rwantare } else { 8513746d5c1STitus Rwantare goto passthough; 8523746d5c1STitus Rwantare } 8533746d5c1STitus Rwantare break; 8543746d5c1STitus Rwantare 8553746d5c1STitus Rwantare case PMBUS_READ_PIN: /* Read-Only word */ 8563746d5c1STitus Rwantare if (pmdev->pages[index].page_flags & PB_HAS_PIN) { 8573746d5c1STitus Rwantare pmbus_send16(pmdev, pmdev->pages[index].read_pin); 8583746d5c1STitus Rwantare } else { 8593746d5c1STitus Rwantare goto passthough; 8603746d5c1STitus Rwantare } 8613746d5c1STitus Rwantare break; 8623746d5c1STitus Rwantare 8633746d5c1STitus Rwantare case PMBUS_REVISION: /* Read-Only byte */ 8643746d5c1STitus Rwantare pmbus_send8(pmdev, pmdev->pages[index].revision); 8653746d5c1STitus Rwantare break; 8663746d5c1STitus Rwantare 8673746d5c1STitus Rwantare case PMBUS_MFR_ID: /* R/W block */ 8683746d5c1STitus Rwantare if (pmdev->pages[index].page_flags & PB_HAS_MFR_INFO) { 8693746d5c1STitus Rwantare pmbus_send_string(pmdev, pmdev->pages[index].mfr_id); 8703746d5c1STitus Rwantare } else { 8713746d5c1STitus Rwantare goto passthough; 8723746d5c1STitus Rwantare } 8733746d5c1STitus Rwantare break; 8743746d5c1STitus Rwantare 8753746d5c1STitus Rwantare case PMBUS_MFR_MODEL: /* R/W block */ 8763746d5c1STitus Rwantare if (pmdev->pages[index].page_flags & PB_HAS_MFR_INFO) { 8773746d5c1STitus Rwantare pmbus_send_string(pmdev, pmdev->pages[index].mfr_model); 8783746d5c1STitus Rwantare } else { 8793746d5c1STitus Rwantare goto passthough; 8803746d5c1STitus Rwantare } 8813746d5c1STitus Rwantare break; 8823746d5c1STitus Rwantare 8833746d5c1STitus Rwantare case PMBUS_MFR_REVISION: /* R/W block */ 8843746d5c1STitus Rwantare if (pmdev->pages[index].page_flags & PB_HAS_MFR_INFO) { 8853746d5c1STitus Rwantare pmbus_send_string(pmdev, pmdev->pages[index].mfr_revision); 8863746d5c1STitus Rwantare } else { 8873746d5c1STitus Rwantare goto passthough; 8883746d5c1STitus Rwantare } 8893746d5c1STitus Rwantare break; 8903746d5c1STitus Rwantare 8913746d5c1STitus Rwantare case PMBUS_MFR_LOCATION: /* R/W block */ 8923746d5c1STitus Rwantare if (pmdev->pages[index].page_flags & PB_HAS_MFR_INFO) { 8933746d5c1STitus Rwantare pmbus_send_string(pmdev, pmdev->pages[index].mfr_location); 8943746d5c1STitus Rwantare } else { 8953746d5c1STitus Rwantare goto passthough; 8963746d5c1STitus Rwantare } 8973746d5c1STitus Rwantare break; 8983746d5c1STitus Rwantare 8993746d5c1STitus Rwantare case PMBUS_MFR_VIN_MIN: /* Read-Only word */ 9003746d5c1STitus Rwantare if (pmdev->pages[index].page_flags & PB_HAS_VIN_RATING) { 9013746d5c1STitus Rwantare pmbus_send16(pmdev, pmdev->pages[index].mfr_vin_min); 9023746d5c1STitus Rwantare } else { 9033746d5c1STitus Rwantare goto passthough; 9043746d5c1STitus Rwantare } 9053746d5c1STitus Rwantare break; 9063746d5c1STitus Rwantare 9073746d5c1STitus Rwantare case PMBUS_MFR_VIN_MAX: /* Read-Only word */ 9083746d5c1STitus Rwantare if (pmdev->pages[index].page_flags & PB_HAS_VIN_RATING) { 9093746d5c1STitus Rwantare pmbus_send16(pmdev, pmdev->pages[index].mfr_vin_max); 9103746d5c1STitus Rwantare } else { 9113746d5c1STitus Rwantare goto passthough; 9123746d5c1STitus Rwantare } 9133746d5c1STitus Rwantare break; 9143746d5c1STitus Rwantare 9153746d5c1STitus Rwantare case PMBUS_MFR_IIN_MAX: /* Read-Only word */ 9163746d5c1STitus Rwantare if (pmdev->pages[index].page_flags & PB_HAS_IIN_RATING) { 9173746d5c1STitus Rwantare pmbus_send16(pmdev, pmdev->pages[index].mfr_iin_max); 9183746d5c1STitus Rwantare } else { 9193746d5c1STitus Rwantare goto passthough; 9203746d5c1STitus Rwantare } 9213746d5c1STitus Rwantare break; 9223746d5c1STitus Rwantare 9233746d5c1STitus Rwantare case PMBUS_MFR_PIN_MAX: /* Read-Only word */ 9243746d5c1STitus Rwantare if (pmdev->pages[index].page_flags & PB_HAS_PIN_RATING) { 9253746d5c1STitus Rwantare pmbus_send16(pmdev, pmdev->pages[index].mfr_pin_max); 9263746d5c1STitus Rwantare } else { 9273746d5c1STitus Rwantare goto passthough; 9283746d5c1STitus Rwantare } 9293746d5c1STitus Rwantare break; 9303746d5c1STitus Rwantare 9313746d5c1STitus Rwantare case PMBUS_MFR_VOUT_MIN: /* Read-Only word */ 9323746d5c1STitus Rwantare if (pmdev->pages[index].page_flags & PB_HAS_VOUT_RATING) { 9333746d5c1STitus Rwantare pmbus_send16(pmdev, pmdev->pages[index].mfr_vout_min); 9343746d5c1STitus Rwantare } else { 9353746d5c1STitus Rwantare goto passthough; 9363746d5c1STitus Rwantare } 9373746d5c1STitus Rwantare break; 9383746d5c1STitus Rwantare 9393746d5c1STitus Rwantare case PMBUS_MFR_VOUT_MAX: /* Read-Only word */ 9403746d5c1STitus Rwantare if (pmdev->pages[index].page_flags & PB_HAS_VOUT_RATING) { 9413746d5c1STitus Rwantare pmbus_send16(pmdev, pmdev->pages[index].mfr_vout_max); 9423746d5c1STitus Rwantare } else { 9433746d5c1STitus Rwantare goto passthough; 9443746d5c1STitus Rwantare } 9453746d5c1STitus Rwantare break; 9463746d5c1STitus Rwantare 9473746d5c1STitus Rwantare case PMBUS_MFR_IOUT_MAX: /* Read-Only word */ 9483746d5c1STitus Rwantare if (pmdev->pages[index].page_flags & PB_HAS_IOUT_RATING) { 9493746d5c1STitus Rwantare pmbus_send16(pmdev, pmdev->pages[index].mfr_iout_max); 9503746d5c1STitus Rwantare } else { 9513746d5c1STitus Rwantare goto passthough; 9523746d5c1STitus Rwantare } 9533746d5c1STitus Rwantare break; 9543746d5c1STitus Rwantare 9553746d5c1STitus Rwantare case PMBUS_MFR_POUT_MAX: /* Read-Only word */ 9563746d5c1STitus Rwantare if (pmdev->pages[index].page_flags & PB_HAS_POUT_RATING) { 9573746d5c1STitus Rwantare pmbus_send16(pmdev, pmdev->pages[index].mfr_pout_max); 9583746d5c1STitus Rwantare } else { 9593746d5c1STitus Rwantare goto passthough; 9603746d5c1STitus Rwantare } 9613746d5c1STitus Rwantare break; 9623746d5c1STitus Rwantare 9633746d5c1STitus Rwantare case PMBUS_MFR_MAX_TEMP_1: /* R/W word */ 9643746d5c1STitus Rwantare if (pmdev->pages[index].page_flags & PB_HAS_TEMP_RATING) { 9653746d5c1STitus Rwantare pmbus_send16(pmdev, pmdev->pages[index].mfr_max_temp_1); 9663746d5c1STitus Rwantare } else { 9673746d5c1STitus Rwantare goto passthough; 9683746d5c1STitus Rwantare } 9693746d5c1STitus Rwantare break; 9703746d5c1STitus Rwantare 9713746d5c1STitus Rwantare case PMBUS_MFR_MAX_TEMP_2: /* R/W word */ 9723746d5c1STitus Rwantare if (pmdev->pages[index].page_flags & PB_HAS_TEMP_RATING) { 9733746d5c1STitus Rwantare pmbus_send16(pmdev, pmdev->pages[index].mfr_max_temp_2); 9743746d5c1STitus Rwantare } else { 9753746d5c1STitus Rwantare goto passthough; 9763746d5c1STitus Rwantare } 9773746d5c1STitus Rwantare break; 9783746d5c1STitus Rwantare 9793746d5c1STitus Rwantare case PMBUS_MFR_MAX_TEMP_3: /* R/W word */ 9803746d5c1STitus Rwantare if (pmdev->pages[index].page_flags & PB_HAS_TEMP_RATING) { 9813746d5c1STitus Rwantare pmbus_send16(pmdev, pmdev->pages[index].mfr_max_temp_3); 9823746d5c1STitus Rwantare } else { 9833746d5c1STitus Rwantare goto passthough; 9843746d5c1STitus Rwantare } 9853746d5c1STitus Rwantare break; 9863746d5c1STitus Rwantare 9873746d5c1STitus Rwantare case PMBUS_CLEAR_FAULTS: /* Send Byte */ 9883746d5c1STitus Rwantare case PMBUS_PAGE_PLUS_WRITE: /* Block Write-only */ 9893746d5c1STitus Rwantare case PMBUS_STORE_DEFAULT_ALL: /* Send Byte */ 9903746d5c1STitus Rwantare case PMBUS_RESTORE_DEFAULT_ALL: /* Send Byte */ 9913746d5c1STitus Rwantare case PMBUS_STORE_DEFAULT_CODE: /* Write-only Byte */ 9923746d5c1STitus Rwantare case PMBUS_RESTORE_DEFAULT_CODE: /* Write-only Byte */ 9933746d5c1STitus Rwantare case PMBUS_STORE_USER_ALL: /* Send Byte */ 9943746d5c1STitus Rwantare case PMBUS_RESTORE_USER_ALL: /* Send Byte */ 9953746d5c1STitus Rwantare case PMBUS_STORE_USER_CODE: /* Write-only Byte */ 9963746d5c1STitus Rwantare case PMBUS_RESTORE_USER_CODE: /* Write-only Byte */ 9973746d5c1STitus Rwantare case PMBUS_QUERY: /* Write-Only */ 9983746d5c1STitus Rwantare qemu_log_mask(LOG_GUEST_ERROR, 9993746d5c1STitus Rwantare "%s: reading from write only register 0x%02x\n", 10003746d5c1STitus Rwantare __func__, pmdev->code); 10013746d5c1STitus Rwantare break; 10023746d5c1STitus Rwantare 10033746d5c1STitus Rwantare passthough: 10043746d5c1STitus Rwantare default: 10053746d5c1STitus Rwantare /* Pass through read request if not handled */ 10063746d5c1STitus Rwantare if (pmdc->receive_byte) { 10073746d5c1STitus Rwantare ret = pmdc->receive_byte(pmdev); 10083746d5c1STitus Rwantare } 10093746d5c1STitus Rwantare break; 10103746d5c1STitus Rwantare } 10113746d5c1STitus Rwantare 10123746d5c1STitus Rwantare if (pmdev->out_buf_len != 0) { 10133746d5c1STitus Rwantare ret = pmbus_out_buf_pop(pmdev); 10143746d5c1STitus Rwantare return ret; 10153746d5c1STitus Rwantare } 10163746d5c1STitus Rwantare 10173746d5c1STitus Rwantare return ret; 10183746d5c1STitus Rwantare } 10193746d5c1STitus Rwantare 10203746d5c1STitus Rwantare /* 10213746d5c1STitus Rwantare * PMBus clear faults command applies to all status registers, existing faults 10223746d5c1STitus Rwantare * should separately get re-asserted. 10233746d5c1STitus Rwantare */ 10243746d5c1STitus Rwantare static void pmbus_clear_faults(PMBusDevice *pmdev) 10253746d5c1STitus Rwantare { 10263746d5c1STitus Rwantare for (uint8_t i = 0; i < pmdev->num_pages; i++) { 10273746d5c1STitus Rwantare pmdev->pages[i].status_word = 0; 10283746d5c1STitus Rwantare pmdev->pages[i].status_vout = 0; 10293746d5c1STitus Rwantare pmdev->pages[i].status_iout = 0; 10303746d5c1STitus Rwantare pmdev->pages[i].status_input = 0; 10313746d5c1STitus Rwantare pmdev->pages[i].status_temperature = 0; 10323746d5c1STitus Rwantare pmdev->pages[i].status_cml = 0; 10333746d5c1STitus Rwantare pmdev->pages[i].status_other = 0; 10343746d5c1STitus Rwantare pmdev->pages[i].status_mfr_specific = 0; 10353746d5c1STitus Rwantare pmdev->pages[i].status_fans_1_2 = 0; 10363746d5c1STitus Rwantare pmdev->pages[i].status_fans_3_4 = 0; 10373746d5c1STitus Rwantare } 10383746d5c1STitus Rwantare 10393746d5c1STitus Rwantare } 10403746d5c1STitus Rwantare 10413746d5c1STitus Rwantare /* 10423746d5c1STitus Rwantare * PMBus operation is used to turn On and Off PSUs 10433746d5c1STitus Rwantare * Therefore, default value for the Operation should be PB_OP_ON or 0x80 10443746d5c1STitus Rwantare */ 10453746d5c1STitus Rwantare static void pmbus_operation(PMBusDevice *pmdev) 10463746d5c1STitus Rwantare { 10473746d5c1STitus Rwantare uint8_t index = pmdev->page; 10483746d5c1STitus Rwantare if ((pmdev->pages[index].operation & PB_OP_ON) == 0) { 10493746d5c1STitus Rwantare pmdev->pages[index].read_vout = 0; 10503746d5c1STitus Rwantare pmdev->pages[index].read_iout = 0; 10513746d5c1STitus Rwantare pmdev->pages[index].read_pout = 0; 10523746d5c1STitus Rwantare return; 10533746d5c1STitus Rwantare } 10543746d5c1STitus Rwantare 10553746d5c1STitus Rwantare if (pmdev->pages[index].operation & (PB_OP_ON | PB_OP_MARGIN_HIGH)) { 10563746d5c1STitus Rwantare pmdev->pages[index].read_vout = pmdev->pages[index].vout_margin_high; 10573746d5c1STitus Rwantare } 10583746d5c1STitus Rwantare 10593746d5c1STitus Rwantare if (pmdev->pages[index].operation & (PB_OP_ON | PB_OP_MARGIN_LOW)) { 10603746d5c1STitus Rwantare pmdev->pages[index].read_vout = pmdev->pages[index].vout_margin_low; 10613746d5c1STitus Rwantare } 10623746d5c1STitus Rwantare pmbus_check_limits(pmdev); 10633746d5c1STitus Rwantare } 10643746d5c1STitus Rwantare 10653746d5c1STitus Rwantare static int pmbus_write_data(SMBusDevice *smd, uint8_t *buf, uint8_t len) 10663746d5c1STitus Rwantare { 10673746d5c1STitus Rwantare PMBusDevice *pmdev = PMBUS_DEVICE(smd); 10683746d5c1STitus Rwantare PMBusDeviceClass *pmdc = PMBUS_DEVICE_GET_CLASS(pmdev); 10693746d5c1STitus Rwantare int ret = 0; 10703746d5c1STitus Rwantare uint8_t index; 10713746d5c1STitus Rwantare 10723746d5c1STitus Rwantare if (len == 0) { 10733746d5c1STitus Rwantare qemu_log_mask(LOG_GUEST_ERROR, "%s: writing empty data\n", __func__); 107438870253STitus Rwantare return PMBUS_ERR_BYTE; 10753746d5c1STitus Rwantare } 10763746d5c1STitus Rwantare 10773746d5c1STitus Rwantare if (!pmdev->pages) { /* allocate memory for pages on first use */ 10783746d5c1STitus Rwantare pmbus_pages_alloc(pmdev); 10793746d5c1STitus Rwantare } 10803746d5c1STitus Rwantare 10813746d5c1STitus Rwantare pmdev->in_buf_len = len; 10823746d5c1STitus Rwantare pmdev->in_buf = buf; 10833746d5c1STitus Rwantare 10843746d5c1STitus Rwantare pmdev->code = buf[0]; /* PMBus command code */ 10853746d5c1STitus Rwantare if (len == 1) { /* Single length writes are command codes only */ 10863746d5c1STitus Rwantare return 0; 10873746d5c1STitus Rwantare } 10883746d5c1STitus Rwantare 10893746d5c1STitus Rwantare if (pmdev->code == PMBUS_PAGE) { 10903746d5c1STitus Rwantare pmdev->page = pmbus_receive8(pmdev); 10913746d5c1STitus Rwantare return 0; 10923746d5c1STitus Rwantare } 109338870253STitus Rwantare 10943746d5c1STitus Rwantare /* loop through all the pages when 0xFF is received */ 10953746d5c1STitus Rwantare if (pmdev->page == PB_ALL_PAGES) { 10963746d5c1STitus Rwantare for (int i = 0; i < pmdev->num_pages; i++) { 10973746d5c1STitus Rwantare pmdev->page = i; 10983746d5c1STitus Rwantare pmbus_write_data(smd, buf, len); 10993746d5c1STitus Rwantare } 11003746d5c1STitus Rwantare pmdev->page = PB_ALL_PAGES; 11013746d5c1STitus Rwantare return 0; 11023746d5c1STitus Rwantare } 11033746d5c1STitus Rwantare 110438870253STitus Rwantare if (pmdev->page > pmdev->num_pages - 1) { 110538870253STitus Rwantare qemu_log_mask(LOG_GUEST_ERROR, 110638870253STitus Rwantare "%s: page %u is out of range\n", 110738870253STitus Rwantare __func__, pmdev->page); 110838870253STitus Rwantare pmdev->page = 0; /* undefined behaviour - reset to page 0 */ 110938870253STitus Rwantare pmbus_cml_error(pmdev); 111038870253STitus Rwantare return PMBUS_ERR_BYTE; 111138870253STitus Rwantare } 111238870253STitus Rwantare 11133746d5c1STitus Rwantare index = pmdev->page; 11143746d5c1STitus Rwantare 11153746d5c1STitus Rwantare switch (pmdev->code) { 11163746d5c1STitus Rwantare case PMBUS_OPERATION: /* R/W byte */ 11173746d5c1STitus Rwantare pmdev->pages[index].operation = pmbus_receive8(pmdev); 11183746d5c1STitus Rwantare pmbus_operation(pmdev); 11193746d5c1STitus Rwantare break; 11203746d5c1STitus Rwantare 11213746d5c1STitus Rwantare case PMBUS_ON_OFF_CONFIG: /* R/W byte */ 11223746d5c1STitus Rwantare pmdev->pages[index].on_off_config = pmbus_receive8(pmdev); 11233746d5c1STitus Rwantare break; 11243746d5c1STitus Rwantare 11253746d5c1STitus Rwantare case PMBUS_CLEAR_FAULTS: /* Send Byte */ 11263746d5c1STitus Rwantare pmbus_clear_faults(pmdev); 11273746d5c1STitus Rwantare break; 11283746d5c1STitus Rwantare 11293746d5c1STitus Rwantare case PMBUS_PHASE: /* R/W byte */ 11303746d5c1STitus Rwantare pmdev->pages[index].phase = pmbus_receive8(pmdev); 11313746d5c1STitus Rwantare break; 11323746d5c1STitus Rwantare 11333746d5c1STitus Rwantare case PMBUS_PAGE_PLUS_WRITE: /* Block Write-only */ 11343746d5c1STitus Rwantare case PMBUS_WRITE_PROTECT: /* R/W byte */ 11353746d5c1STitus Rwantare pmdev->pages[index].write_protect = pmbus_receive8(pmdev); 11363746d5c1STitus Rwantare break; 11373746d5c1STitus Rwantare 11383746d5c1STitus Rwantare case PMBUS_VOUT_MODE: /* R/W byte */ 11393746d5c1STitus Rwantare if (pmdev->pages[index].page_flags & PB_HAS_VOUT_MODE) { 11403746d5c1STitus Rwantare pmdev->pages[index].vout_mode = pmbus_receive8(pmdev); 11413746d5c1STitus Rwantare } else { 11423746d5c1STitus Rwantare goto passthrough; 11433746d5c1STitus Rwantare } 11443746d5c1STitus Rwantare break; 11453746d5c1STitus Rwantare 11463746d5c1STitus Rwantare case PMBUS_VOUT_COMMAND: /* R/W word */ 11473746d5c1STitus Rwantare if (pmdev->pages[index].page_flags & PB_HAS_VOUT) { 11483746d5c1STitus Rwantare pmdev->pages[index].vout_command = pmbus_receive16(pmdev); 11493746d5c1STitus Rwantare } else { 11503746d5c1STitus Rwantare goto passthrough; 11513746d5c1STitus Rwantare } 11523746d5c1STitus Rwantare break; 11533746d5c1STitus Rwantare 11543746d5c1STitus Rwantare case PMBUS_VOUT_TRIM: /* R/W word */ 11553746d5c1STitus Rwantare if (pmdev->pages[index].page_flags & PB_HAS_VOUT) { 11563746d5c1STitus Rwantare pmdev->pages[index].vout_trim = pmbus_receive16(pmdev); 11573746d5c1STitus Rwantare } else { 11583746d5c1STitus Rwantare goto passthrough; 11593746d5c1STitus Rwantare } 11603746d5c1STitus Rwantare break; 11613746d5c1STitus Rwantare 11623746d5c1STitus Rwantare case PMBUS_VOUT_CAL_OFFSET: /* R/W word */ 11633746d5c1STitus Rwantare if (pmdev->pages[index].page_flags & PB_HAS_VOUT) { 11643746d5c1STitus Rwantare pmdev->pages[index].vout_cal_offset = pmbus_receive16(pmdev); 11653746d5c1STitus Rwantare } else { 11663746d5c1STitus Rwantare goto passthrough; 11673746d5c1STitus Rwantare } 11683746d5c1STitus Rwantare break; 11693746d5c1STitus Rwantare 11703746d5c1STitus Rwantare case PMBUS_VOUT_MAX: /* R/W word */ 11713746d5c1STitus Rwantare if (pmdev->pages[index].page_flags & PB_HAS_VOUT) { 11723746d5c1STitus Rwantare pmdev->pages[index].vout_max = pmbus_receive16(pmdev); 11733746d5c1STitus Rwantare } else { 11743746d5c1STitus Rwantare goto passthrough; 11753746d5c1STitus Rwantare } 11763746d5c1STitus Rwantare break; 11773746d5c1STitus Rwantare 11783746d5c1STitus Rwantare case PMBUS_VOUT_MARGIN_HIGH: /* R/W word */ 11793746d5c1STitus Rwantare if (pmdev->pages[index].page_flags & PB_HAS_VOUT_MARGIN) { 11803746d5c1STitus Rwantare pmdev->pages[index].vout_margin_high = pmbus_receive16(pmdev); 11813746d5c1STitus Rwantare } else { 11823746d5c1STitus Rwantare goto passthrough; 11833746d5c1STitus Rwantare } 11843746d5c1STitus Rwantare break; 11853746d5c1STitus Rwantare 11863746d5c1STitus Rwantare case PMBUS_VOUT_MARGIN_LOW: /* R/W word */ 11873746d5c1STitus Rwantare if (pmdev->pages[index].page_flags & PB_HAS_VOUT_MARGIN) { 11883746d5c1STitus Rwantare pmdev->pages[index].vout_margin_low = pmbus_receive16(pmdev); 11893746d5c1STitus Rwantare } else { 11903746d5c1STitus Rwantare goto passthrough; 11913746d5c1STitus Rwantare } 11923746d5c1STitus Rwantare break; 11933746d5c1STitus Rwantare 11943746d5c1STitus Rwantare case PMBUS_VOUT_TRANSITION_RATE: /* R/W word */ 11953746d5c1STitus Rwantare if (pmdev->pages[index].page_flags & PB_HAS_VOUT) { 11963746d5c1STitus Rwantare pmdev->pages[index].vout_transition_rate = pmbus_receive16(pmdev); 11973746d5c1STitus Rwantare } else { 11983746d5c1STitus Rwantare goto passthrough; 11993746d5c1STitus Rwantare } 12003746d5c1STitus Rwantare break; 12013746d5c1STitus Rwantare 12023746d5c1STitus Rwantare case PMBUS_VOUT_DROOP: /* R/W word */ 12033746d5c1STitus Rwantare if (pmdev->pages[index].page_flags & PB_HAS_VOUT) { 12043746d5c1STitus Rwantare pmdev->pages[index].vout_droop = pmbus_receive16(pmdev); 12053746d5c1STitus Rwantare } else { 12063746d5c1STitus Rwantare goto passthrough; 12073746d5c1STitus Rwantare } 12083746d5c1STitus Rwantare break; 12093746d5c1STitus Rwantare 12103746d5c1STitus Rwantare case PMBUS_VOUT_SCALE_LOOP: /* R/W word */ 12113746d5c1STitus Rwantare if (pmdev->pages[index].page_flags & PB_HAS_VOUT) { 12123746d5c1STitus Rwantare pmdev->pages[index].vout_scale_loop = pmbus_receive16(pmdev); 12133746d5c1STitus Rwantare } else { 12143746d5c1STitus Rwantare goto passthrough; 12153746d5c1STitus Rwantare } 12163746d5c1STitus Rwantare break; 12173746d5c1STitus Rwantare 12183746d5c1STitus Rwantare case PMBUS_VOUT_SCALE_MONITOR: /* R/W word */ 12193746d5c1STitus Rwantare if (pmdev->pages[index].page_flags & PB_HAS_VOUT) { 12203746d5c1STitus Rwantare pmdev->pages[index].vout_scale_monitor = pmbus_receive16(pmdev); 12213746d5c1STitus Rwantare } else { 12223746d5c1STitus Rwantare goto passthrough; 12233746d5c1STitus Rwantare } 12243746d5c1STitus Rwantare break; 12253746d5c1STitus Rwantare 122632480293STitus Rwantare case PMBUS_VOUT_MIN: /* R/W word */ 122732480293STitus Rwantare if (pmdev->pages[index].page_flags & PB_HAS_VOUT_RATING) { 122832480293STitus Rwantare pmdev->pages[index].vout_min = pmbus_receive16(pmdev); 122932480293STitus Rwantare } else { 123032480293STitus Rwantare goto passthrough; 123132480293STitus Rwantare } 123232480293STitus Rwantare break; 123332480293STitus Rwantare 12343746d5c1STitus Rwantare case PMBUS_POUT_MAX: /* R/W word */ 12353746d5c1STitus Rwantare if (pmdev->pages[index].page_flags & PB_HAS_VOUT) { 12363746d5c1STitus Rwantare pmdev->pages[index].pout_max = pmbus_receive16(pmdev); 12373746d5c1STitus Rwantare } else { 12383746d5c1STitus Rwantare goto passthrough; 12393746d5c1STitus Rwantare } 12403746d5c1STitus Rwantare break; 12413746d5c1STitus Rwantare 12423746d5c1STitus Rwantare case PMBUS_VIN_ON: /* R/W word */ 12433746d5c1STitus Rwantare if (pmdev->pages[index].page_flags & PB_HAS_VIN) { 12443746d5c1STitus Rwantare pmdev->pages[index].vin_on = pmbus_receive16(pmdev); 12453746d5c1STitus Rwantare } else { 12463746d5c1STitus Rwantare goto passthrough; 12473746d5c1STitus Rwantare } 12483746d5c1STitus Rwantare break; 12493746d5c1STitus Rwantare 12503746d5c1STitus Rwantare case PMBUS_VIN_OFF: /* R/W word */ 12513746d5c1STitus Rwantare if (pmdev->pages[index].page_flags & PB_HAS_VIN) { 12523746d5c1STitus Rwantare pmdev->pages[index].vin_off = pmbus_receive16(pmdev); 12533746d5c1STitus Rwantare } else { 12543746d5c1STitus Rwantare goto passthrough; 12553746d5c1STitus Rwantare } 12563746d5c1STitus Rwantare break; 12573746d5c1STitus Rwantare 12583746d5c1STitus Rwantare case PMBUS_IOUT_CAL_GAIN: /* R/W word */ 12593746d5c1STitus Rwantare if (pmdev->pages[index].page_flags & PB_HAS_IOUT_GAIN) { 12603746d5c1STitus Rwantare pmdev->pages[index].iout_cal_gain = pmbus_receive16(pmdev); 12613746d5c1STitus Rwantare } else { 12623746d5c1STitus Rwantare goto passthrough; 12633746d5c1STitus Rwantare } 12643746d5c1STitus Rwantare break; 12653746d5c1STitus Rwantare 12663746d5c1STitus Rwantare case PMBUS_VOUT_OV_FAULT_LIMIT: /* R/W word */ 12673746d5c1STitus Rwantare if (pmdev->pages[index].page_flags & PB_HAS_VOUT) { 12683746d5c1STitus Rwantare pmdev->pages[index].vout_ov_fault_limit = pmbus_receive16(pmdev); 12693746d5c1STitus Rwantare } else { 12703746d5c1STitus Rwantare goto passthrough; 12713746d5c1STitus Rwantare } 12723746d5c1STitus Rwantare break; 12733746d5c1STitus Rwantare 12743746d5c1STitus Rwantare case PMBUS_VOUT_OV_FAULT_RESPONSE: /* R/W byte */ 12753746d5c1STitus Rwantare if (pmdev->pages[index].page_flags & PB_HAS_VOUT) { 12763746d5c1STitus Rwantare pmdev->pages[index].vout_ov_fault_response = pmbus_receive8(pmdev); 12773746d5c1STitus Rwantare } else { 12783746d5c1STitus Rwantare goto passthrough; 12793746d5c1STitus Rwantare } 12803746d5c1STitus Rwantare break; 12813746d5c1STitus Rwantare 12823746d5c1STitus Rwantare case PMBUS_VOUT_OV_WARN_LIMIT: /* R/W word */ 12833746d5c1STitus Rwantare if (pmdev->pages[index].page_flags & PB_HAS_VOUT) { 12843746d5c1STitus Rwantare pmdev->pages[index].vout_ov_warn_limit = pmbus_receive16(pmdev); 12853746d5c1STitus Rwantare } else { 12863746d5c1STitus Rwantare goto passthrough; 12873746d5c1STitus Rwantare } 12883746d5c1STitus Rwantare break; 12893746d5c1STitus Rwantare 12903746d5c1STitus Rwantare case PMBUS_VOUT_UV_WARN_LIMIT: /* R/W word */ 12913746d5c1STitus Rwantare if (pmdev->pages[index].page_flags & PB_HAS_VOUT) { 12923746d5c1STitus Rwantare pmdev->pages[index].vout_uv_warn_limit = pmbus_receive16(pmdev); 12933746d5c1STitus Rwantare } else { 12943746d5c1STitus Rwantare goto passthrough; 12953746d5c1STitus Rwantare } 12963746d5c1STitus Rwantare break; 12973746d5c1STitus Rwantare 12983746d5c1STitus Rwantare case PMBUS_VOUT_UV_FAULT_LIMIT: /* R/W word */ 12993746d5c1STitus Rwantare if (pmdev->pages[index].page_flags & PB_HAS_VOUT) { 13003746d5c1STitus Rwantare pmdev->pages[index].vout_uv_fault_limit = pmbus_receive16(pmdev); 13013746d5c1STitus Rwantare } else { 13023746d5c1STitus Rwantare goto passthrough; 13033746d5c1STitus Rwantare } 13043746d5c1STitus Rwantare break; 13053746d5c1STitus Rwantare 13063746d5c1STitus Rwantare case PMBUS_VOUT_UV_FAULT_RESPONSE: /* R/W byte */ 13073746d5c1STitus Rwantare if (pmdev->pages[index].page_flags & PB_HAS_VOUT) { 13083746d5c1STitus Rwantare pmdev->pages[index].vout_uv_fault_response = pmbus_receive8(pmdev); 13093746d5c1STitus Rwantare } else { 13103746d5c1STitus Rwantare goto passthrough; 13113746d5c1STitus Rwantare } 13123746d5c1STitus Rwantare break; 13133746d5c1STitus Rwantare 13143746d5c1STitus Rwantare case PMBUS_IOUT_OC_FAULT_LIMIT: /* R/W word */ 13153746d5c1STitus Rwantare if (pmdev->pages[index].page_flags & PB_HAS_IOUT) { 13163746d5c1STitus Rwantare pmdev->pages[index].iout_oc_fault_limit = pmbus_receive16(pmdev); 13173746d5c1STitus Rwantare } else { 13183746d5c1STitus Rwantare goto passthrough; 13193746d5c1STitus Rwantare } 13203746d5c1STitus Rwantare break; 13213746d5c1STitus Rwantare 13223746d5c1STitus Rwantare case PMBUS_IOUT_OC_FAULT_RESPONSE: /* R/W byte */ 13233746d5c1STitus Rwantare if (pmdev->pages[index].page_flags & PB_HAS_IOUT) { 13243746d5c1STitus Rwantare pmdev->pages[index].iout_oc_fault_response = pmbus_receive8(pmdev); 13253746d5c1STitus Rwantare } else { 13263746d5c1STitus Rwantare goto passthrough; 13273746d5c1STitus Rwantare } 13283746d5c1STitus Rwantare break; 13293746d5c1STitus Rwantare 13303746d5c1STitus Rwantare case PMBUS_IOUT_OC_LV_FAULT_LIMIT: /* R/W word */ 13313746d5c1STitus Rwantare if (pmdev->pages[index].page_flags & PB_HAS_IOUT) { 13323746d5c1STitus Rwantare pmdev->pages[index].iout_oc_lv_fault_limit = pmbus_receive16(pmdev); 13333746d5c1STitus Rwantare } else { 13343746d5c1STitus Rwantare goto passthrough; 13353746d5c1STitus Rwantare } 13363746d5c1STitus Rwantare break; 13373746d5c1STitus Rwantare 13383746d5c1STitus Rwantare case PMBUS_IOUT_OC_LV_FAULT_RESPONSE: /* R/W byte */ 13393746d5c1STitus Rwantare if (pmdev->pages[index].page_flags & PB_HAS_IOUT) { 13403746d5c1STitus Rwantare pmdev->pages[index].iout_oc_lv_fault_response 13413746d5c1STitus Rwantare = pmbus_receive8(pmdev); 13423746d5c1STitus Rwantare } else { 13433746d5c1STitus Rwantare goto passthrough; 13443746d5c1STitus Rwantare } 13453746d5c1STitus Rwantare break; 13463746d5c1STitus Rwantare 13473746d5c1STitus Rwantare case PMBUS_IOUT_OC_WARN_LIMIT: /* R/W word */ 13483746d5c1STitus Rwantare if (pmdev->pages[index].page_flags & PB_HAS_IOUT) { 13493746d5c1STitus Rwantare pmdev->pages[index].iout_oc_warn_limit = pmbus_receive16(pmdev); 13503746d5c1STitus Rwantare } else { 13513746d5c1STitus Rwantare goto passthrough; 13523746d5c1STitus Rwantare } 13533746d5c1STitus Rwantare break; 13543746d5c1STitus Rwantare 13553746d5c1STitus Rwantare case PMBUS_IOUT_UC_FAULT_LIMIT: /* R/W word */ 13563746d5c1STitus Rwantare if (pmdev->pages[index].page_flags & PB_HAS_IOUT) { 13573746d5c1STitus Rwantare pmdev->pages[index].iout_uc_fault_limit = pmbus_receive16(pmdev); 13583746d5c1STitus Rwantare } else { 13593746d5c1STitus Rwantare goto passthrough; 13603746d5c1STitus Rwantare } 13613746d5c1STitus Rwantare break; 13623746d5c1STitus Rwantare 13633746d5c1STitus Rwantare case PMBUS_IOUT_UC_FAULT_RESPONSE: /* R/W byte */ 13643746d5c1STitus Rwantare if (pmdev->pages[index].page_flags & PB_HAS_IOUT) { 13653746d5c1STitus Rwantare pmdev->pages[index].iout_uc_fault_response = pmbus_receive8(pmdev); 13663746d5c1STitus Rwantare } else { 13673746d5c1STitus Rwantare goto passthrough; 13683746d5c1STitus Rwantare } 13693746d5c1STitus Rwantare break; 13703746d5c1STitus Rwantare 13713746d5c1STitus Rwantare case PMBUS_OT_FAULT_LIMIT: /* R/W word */ 13723746d5c1STitus Rwantare if (pmdev->pages[index].page_flags & PB_HAS_TEMPERATURE) { 13733746d5c1STitus Rwantare pmdev->pages[index].ot_fault_limit = pmbus_receive16(pmdev); 13743746d5c1STitus Rwantare } else { 13753746d5c1STitus Rwantare goto passthrough; 13763746d5c1STitus Rwantare } 13773746d5c1STitus Rwantare break; 13783746d5c1STitus Rwantare 13793746d5c1STitus Rwantare case PMBUS_OT_FAULT_RESPONSE: /* R/W byte */ 13803746d5c1STitus Rwantare if (pmdev->pages[index].page_flags & PB_HAS_TEMPERATURE) { 13813746d5c1STitus Rwantare pmdev->pages[index].ot_fault_response = pmbus_receive8(pmdev); 13823746d5c1STitus Rwantare } else { 13833746d5c1STitus Rwantare goto passthrough; 13843746d5c1STitus Rwantare } 13853746d5c1STitus Rwantare break; 13863746d5c1STitus Rwantare 13873746d5c1STitus Rwantare case PMBUS_OT_WARN_LIMIT: /* R/W word */ 13883746d5c1STitus Rwantare if (pmdev->pages[index].page_flags & PB_HAS_TEMPERATURE) { 13893746d5c1STitus Rwantare pmdev->pages[index].ot_warn_limit = pmbus_receive16(pmdev); 13903746d5c1STitus Rwantare } else { 13913746d5c1STitus Rwantare goto passthrough; 13923746d5c1STitus Rwantare } 13933746d5c1STitus Rwantare break; 13943746d5c1STitus Rwantare 13953746d5c1STitus Rwantare case PMBUS_UT_WARN_LIMIT: /* R/W word */ 13963746d5c1STitus Rwantare if (pmdev->pages[index].page_flags & PB_HAS_TEMPERATURE) { 13973746d5c1STitus Rwantare pmdev->pages[index].ut_warn_limit = pmbus_receive16(pmdev); 13983746d5c1STitus Rwantare } else { 13993746d5c1STitus Rwantare goto passthrough; 14003746d5c1STitus Rwantare } 14013746d5c1STitus Rwantare break; 14023746d5c1STitus Rwantare 14033746d5c1STitus Rwantare case PMBUS_UT_FAULT_LIMIT: /* R/W word */ 14043746d5c1STitus Rwantare if (pmdev->pages[index].page_flags & PB_HAS_TEMPERATURE) { 14053746d5c1STitus Rwantare pmdev->pages[index].ut_fault_limit = pmbus_receive16(pmdev); 14063746d5c1STitus Rwantare } else { 14073746d5c1STitus Rwantare goto passthrough; 14083746d5c1STitus Rwantare } 14093746d5c1STitus Rwantare break; 14103746d5c1STitus Rwantare 14113746d5c1STitus Rwantare case PMBUS_UT_FAULT_RESPONSE: /* R/W byte */ 14123746d5c1STitus Rwantare if (pmdev->pages[index].page_flags & PB_HAS_TEMPERATURE) { 14133746d5c1STitus Rwantare pmdev->pages[index].ut_fault_response = pmbus_receive8(pmdev); 14143746d5c1STitus Rwantare } else { 14153746d5c1STitus Rwantare goto passthrough; 14163746d5c1STitus Rwantare } 14173746d5c1STitus Rwantare break; 14183746d5c1STitus Rwantare 14193746d5c1STitus Rwantare case PMBUS_VIN_OV_FAULT_LIMIT: /* R/W word */ 14203746d5c1STitus Rwantare if (pmdev->pages[index].page_flags & PB_HAS_VIN) { 14213746d5c1STitus Rwantare pmdev->pages[index].vin_ov_fault_limit = pmbus_receive16(pmdev); 14223746d5c1STitus Rwantare } else { 14233746d5c1STitus Rwantare goto passthrough; 14243746d5c1STitus Rwantare } 14253746d5c1STitus Rwantare break; 14263746d5c1STitus Rwantare 14273746d5c1STitus Rwantare case PMBUS_VIN_OV_FAULT_RESPONSE: /* R/W byte */ 14283746d5c1STitus Rwantare if (pmdev->pages[index].page_flags & PB_HAS_VIN) { 14293746d5c1STitus Rwantare pmdev->pages[index].vin_ov_fault_response = pmbus_receive8(pmdev); 14303746d5c1STitus Rwantare } else { 14313746d5c1STitus Rwantare goto passthrough; 14323746d5c1STitus Rwantare } 14333746d5c1STitus Rwantare break; 14343746d5c1STitus Rwantare 14353746d5c1STitus Rwantare case PMBUS_VIN_OV_WARN_LIMIT: /* R/W word */ 14363746d5c1STitus Rwantare if (pmdev->pages[index].page_flags & PB_HAS_VIN) { 14373746d5c1STitus Rwantare pmdev->pages[index].vin_ov_warn_limit = pmbus_receive16(pmdev); 14383746d5c1STitus Rwantare } else { 14393746d5c1STitus Rwantare goto passthrough; 14403746d5c1STitus Rwantare } 14413746d5c1STitus Rwantare break; 14423746d5c1STitus Rwantare 14433746d5c1STitus Rwantare case PMBUS_VIN_UV_WARN_LIMIT: /* R/W word */ 14443746d5c1STitus Rwantare if (pmdev->pages[index].page_flags & PB_HAS_VIN) { 14453746d5c1STitus Rwantare pmdev->pages[index].vin_uv_warn_limit = pmbus_receive16(pmdev); 14463746d5c1STitus Rwantare } else { 14473746d5c1STitus Rwantare goto passthrough; 14483746d5c1STitus Rwantare } 14493746d5c1STitus Rwantare break; 14503746d5c1STitus Rwantare 14513746d5c1STitus Rwantare case PMBUS_VIN_UV_FAULT_LIMIT: /* R/W word */ 14523746d5c1STitus Rwantare if (pmdev->pages[index].page_flags & PB_HAS_VIN) { 14533746d5c1STitus Rwantare pmdev->pages[index].vin_uv_fault_limit = pmbus_receive16(pmdev); 14543746d5c1STitus Rwantare } else { 14553746d5c1STitus Rwantare goto passthrough; 14563746d5c1STitus Rwantare } 14573746d5c1STitus Rwantare break; 14583746d5c1STitus Rwantare 14593746d5c1STitus Rwantare case PMBUS_VIN_UV_FAULT_RESPONSE: /* R/W byte */ 14603746d5c1STitus Rwantare if (pmdev->pages[index].page_flags & PB_HAS_VIN) { 14613746d5c1STitus Rwantare pmdev->pages[index].vin_uv_fault_response = pmbus_receive8(pmdev); 14623746d5c1STitus Rwantare } else { 14633746d5c1STitus Rwantare goto passthrough; 14643746d5c1STitus Rwantare } 14653746d5c1STitus Rwantare break; 14663746d5c1STitus Rwantare 14673746d5c1STitus Rwantare case PMBUS_IIN_OC_FAULT_LIMIT: /* R/W word */ 14683746d5c1STitus Rwantare if (pmdev->pages[index].page_flags & PB_HAS_IIN) { 14693746d5c1STitus Rwantare pmdev->pages[index].iin_oc_fault_limit = pmbus_receive16(pmdev); 14703746d5c1STitus Rwantare } else { 14713746d5c1STitus Rwantare goto passthrough; 14723746d5c1STitus Rwantare } 14733746d5c1STitus Rwantare break; 14743746d5c1STitus Rwantare 14753746d5c1STitus Rwantare case PMBUS_IIN_OC_FAULT_RESPONSE: /* R/W byte */ 14763746d5c1STitus Rwantare if (pmdev->pages[index].page_flags & PB_HAS_IIN) { 14773746d5c1STitus Rwantare pmdev->pages[index].iin_oc_fault_response = pmbus_receive8(pmdev); 14783746d5c1STitus Rwantare } else { 14793746d5c1STitus Rwantare goto passthrough; 14803746d5c1STitus Rwantare } 14813746d5c1STitus Rwantare break; 14823746d5c1STitus Rwantare 14833746d5c1STitus Rwantare case PMBUS_IIN_OC_WARN_LIMIT: /* R/W word */ 14843746d5c1STitus Rwantare if (pmdev->pages[index].page_flags & PB_HAS_IIN) { 14853746d5c1STitus Rwantare pmdev->pages[index].iin_oc_warn_limit = pmbus_receive16(pmdev); 14863746d5c1STitus Rwantare } else { 14873746d5c1STitus Rwantare goto passthrough; 14883746d5c1STitus Rwantare } 14893746d5c1STitus Rwantare break; 14903746d5c1STitus Rwantare 14913746d5c1STitus Rwantare case PMBUS_POUT_OP_FAULT_LIMIT: /* R/W word */ 14923746d5c1STitus Rwantare if (pmdev->pages[index].page_flags & PB_HAS_VOUT) { 14933746d5c1STitus Rwantare pmdev->pages[index].pout_op_fault_limit = pmbus_receive16(pmdev); 14943746d5c1STitus Rwantare } else { 14953746d5c1STitus Rwantare goto passthrough; 14963746d5c1STitus Rwantare } 14973746d5c1STitus Rwantare break; 14983746d5c1STitus Rwantare 14993746d5c1STitus Rwantare case PMBUS_POUT_OP_FAULT_RESPONSE: /* R/W byte */ 15003746d5c1STitus Rwantare if (pmdev->pages[index].page_flags & PB_HAS_VOUT) { 15013746d5c1STitus Rwantare pmdev->pages[index].pout_op_fault_response = pmbus_receive8(pmdev); 15023746d5c1STitus Rwantare } else { 15033746d5c1STitus Rwantare goto passthrough; 15043746d5c1STitus Rwantare } 15053746d5c1STitus Rwantare break; 15063746d5c1STitus Rwantare 15073746d5c1STitus Rwantare case PMBUS_POUT_OP_WARN_LIMIT: /* R/W word */ 15083746d5c1STitus Rwantare if (pmdev->pages[index].page_flags & PB_HAS_VOUT) { 15093746d5c1STitus Rwantare pmdev->pages[index].pout_op_warn_limit = pmbus_receive16(pmdev); 15103746d5c1STitus Rwantare } else { 15113746d5c1STitus Rwantare goto passthrough; 15123746d5c1STitus Rwantare } 15133746d5c1STitus Rwantare break; 15143746d5c1STitus Rwantare 15153746d5c1STitus Rwantare case PMBUS_PIN_OP_WARN_LIMIT: /* R/W word */ 15163746d5c1STitus Rwantare if (pmdev->pages[index].page_flags & PB_HAS_PIN) { 15173746d5c1STitus Rwantare pmdev->pages[index].pin_op_warn_limit = pmbus_receive16(pmdev); 15183746d5c1STitus Rwantare } else { 15193746d5c1STitus Rwantare goto passthrough; 15203746d5c1STitus Rwantare } 15213746d5c1STitus Rwantare break; 15223746d5c1STitus Rwantare 15233746d5c1STitus Rwantare case PMBUS_STATUS_BYTE: /* R/W byte */ 15243746d5c1STitus Rwantare pmdev->pages[index].status_word = pmbus_receive8(pmdev); 15253746d5c1STitus Rwantare break; 15263746d5c1STitus Rwantare 15273746d5c1STitus Rwantare case PMBUS_STATUS_WORD: /* R/W word */ 15283746d5c1STitus Rwantare pmdev->pages[index].status_word = pmbus_receive16(pmdev); 15293746d5c1STitus Rwantare break; 15303746d5c1STitus Rwantare 15313746d5c1STitus Rwantare case PMBUS_STATUS_VOUT: /* R/W byte */ 15323746d5c1STitus Rwantare if (pmdev->pages[index].page_flags & PB_HAS_VOUT) { 15333746d5c1STitus Rwantare pmdev->pages[index].status_vout = pmbus_receive8(pmdev); 15343746d5c1STitus Rwantare } else { 15353746d5c1STitus Rwantare goto passthrough; 15363746d5c1STitus Rwantare } 15373746d5c1STitus Rwantare break; 15383746d5c1STitus Rwantare 15393746d5c1STitus Rwantare case PMBUS_STATUS_IOUT: /* R/W byte */ 15403746d5c1STitus Rwantare if (pmdev->pages[index].page_flags & PB_HAS_IOUT) { 15413746d5c1STitus Rwantare pmdev->pages[index].status_iout = pmbus_receive8(pmdev); 15423746d5c1STitus Rwantare } else { 15433746d5c1STitus Rwantare goto passthrough; 15443746d5c1STitus Rwantare } 15453746d5c1STitus Rwantare break; 15463746d5c1STitus Rwantare 15473746d5c1STitus Rwantare case PMBUS_STATUS_INPUT: /* R/W byte */ 15483746d5c1STitus Rwantare pmdev->pages[index].status_input = pmbus_receive8(pmdev); 15493746d5c1STitus Rwantare break; 15503746d5c1STitus Rwantare 15513746d5c1STitus Rwantare case PMBUS_STATUS_TEMPERATURE: /* R/W byte */ 15523746d5c1STitus Rwantare if (pmdev->pages[index].page_flags & PB_HAS_TEMPERATURE) { 15533746d5c1STitus Rwantare pmdev->pages[index].status_temperature = pmbus_receive8(pmdev); 15543746d5c1STitus Rwantare } else { 15553746d5c1STitus Rwantare goto passthrough; 15563746d5c1STitus Rwantare } 15573746d5c1STitus Rwantare break; 15583746d5c1STitus Rwantare 15593746d5c1STitus Rwantare case PMBUS_STATUS_CML: /* R/W byte */ 15603746d5c1STitus Rwantare pmdev->pages[index].status_cml = pmbus_receive8(pmdev); 15613746d5c1STitus Rwantare break; 15623746d5c1STitus Rwantare 15633746d5c1STitus Rwantare case PMBUS_STATUS_OTHER: /* R/W byte */ 15643746d5c1STitus Rwantare pmdev->pages[index].status_other = pmbus_receive8(pmdev); 15653746d5c1STitus Rwantare break; 15663746d5c1STitus Rwantare 156732480293STitus Rwantare case PMBUS_STATUS_MFR_SPECIFIC: /* R/W byte */ 156832480293STitus Rwantare pmdev->pages[index].status_mfr_specific = pmbus_receive8(pmdev); 156932480293STitus Rwantare break; 157032480293STitus Rwantare 15713746d5c1STitus Rwantare case PMBUS_PAGE_PLUS_READ: /* Block Read-only */ 15723746d5c1STitus Rwantare case PMBUS_CAPABILITY: /* Read-Only byte */ 15733746d5c1STitus Rwantare case PMBUS_COEFFICIENTS: /* Read-only block 5 bytes */ 15743746d5c1STitus Rwantare case PMBUS_READ_EIN: /* Read-Only block 5 bytes */ 15753746d5c1STitus Rwantare case PMBUS_READ_EOUT: /* Read-Only block 5 bytes */ 15763746d5c1STitus Rwantare case PMBUS_READ_VIN: /* Read-Only word */ 15773746d5c1STitus Rwantare case PMBUS_READ_IIN: /* Read-Only word */ 15783746d5c1STitus Rwantare case PMBUS_READ_VCAP: /* Read-Only word */ 15793746d5c1STitus Rwantare case PMBUS_READ_VOUT: /* Read-Only word */ 15803746d5c1STitus Rwantare case PMBUS_READ_IOUT: /* Read-Only word */ 15813746d5c1STitus Rwantare case PMBUS_READ_TEMPERATURE_1: /* Read-Only word */ 15823746d5c1STitus Rwantare case PMBUS_READ_TEMPERATURE_2: /* Read-Only word */ 15833746d5c1STitus Rwantare case PMBUS_READ_TEMPERATURE_3: /* Read-Only word */ 15843746d5c1STitus Rwantare case PMBUS_READ_FAN_SPEED_1: /* Read-Only word */ 15853746d5c1STitus Rwantare case PMBUS_READ_FAN_SPEED_2: /* Read-Only word */ 15863746d5c1STitus Rwantare case PMBUS_READ_FAN_SPEED_3: /* Read-Only word */ 15873746d5c1STitus Rwantare case PMBUS_READ_FAN_SPEED_4: /* Read-Only word */ 15883746d5c1STitus Rwantare case PMBUS_READ_DUTY_CYCLE: /* Read-Only word */ 15893746d5c1STitus Rwantare case PMBUS_READ_FREQUENCY: /* Read-Only word */ 15903746d5c1STitus Rwantare case PMBUS_READ_POUT: /* Read-Only word */ 15913746d5c1STitus Rwantare case PMBUS_READ_PIN: /* Read-Only word */ 15923746d5c1STitus Rwantare case PMBUS_REVISION: /* Read-Only byte */ 15933746d5c1STitus Rwantare case PMBUS_APP_PROFILE_SUPPORT: /* Read-Only block-read */ 15943746d5c1STitus Rwantare case PMBUS_MFR_VIN_MIN: /* Read-Only word */ 15953746d5c1STitus Rwantare case PMBUS_MFR_VIN_MAX: /* Read-Only word */ 15963746d5c1STitus Rwantare case PMBUS_MFR_IIN_MAX: /* Read-Only word */ 15973746d5c1STitus Rwantare case PMBUS_MFR_PIN_MAX: /* Read-Only word */ 15983746d5c1STitus Rwantare case PMBUS_MFR_VOUT_MIN: /* Read-Only word */ 15993746d5c1STitus Rwantare case PMBUS_MFR_VOUT_MAX: /* Read-Only word */ 16003746d5c1STitus Rwantare case PMBUS_MFR_IOUT_MAX: /* Read-Only word */ 16013746d5c1STitus Rwantare case PMBUS_MFR_POUT_MAX: /* Read-Only word */ 16023746d5c1STitus Rwantare case PMBUS_MFR_TAMBIENT_MAX: /* Read-Only word */ 16033746d5c1STitus Rwantare case PMBUS_MFR_TAMBIENT_MIN: /* Read-Only word */ 16043746d5c1STitus Rwantare case PMBUS_MFR_EFFICIENCY_LL: /* Read-Only block 14 bytes */ 16053746d5c1STitus Rwantare case PMBUS_MFR_EFFICIENCY_HL: /* Read-Only block 14 bytes */ 16063746d5c1STitus Rwantare case PMBUS_MFR_PIN_ACCURACY: /* Read-Only byte */ 16073746d5c1STitus Rwantare case PMBUS_IC_DEVICE_ID: /* Read-Only block-read */ 16083746d5c1STitus Rwantare case PMBUS_IC_DEVICE_REV: /* Read-Only block-read */ 16093746d5c1STitus Rwantare qemu_log_mask(LOG_GUEST_ERROR, 16103746d5c1STitus Rwantare "%s: writing to read-only register 0x%02x\n", 16113746d5c1STitus Rwantare __func__, pmdev->code); 16123746d5c1STitus Rwantare break; 16133746d5c1STitus Rwantare 16143746d5c1STitus Rwantare passthrough: 16153746d5c1STitus Rwantare /* Unimplimented registers get passed to the device */ 16163746d5c1STitus Rwantare default: 16173746d5c1STitus Rwantare if (pmdc->write_data) { 16183746d5c1STitus Rwantare ret = pmdc->write_data(pmdev, buf, len); 16193746d5c1STitus Rwantare } 16203746d5c1STitus Rwantare break; 16213746d5c1STitus Rwantare } 16223746d5c1STitus Rwantare pmbus_check_limits(pmdev); 16233746d5c1STitus Rwantare pmdev->in_buf_len = 0; 16243746d5c1STitus Rwantare return ret; 16253746d5c1STitus Rwantare } 16263746d5c1STitus Rwantare 16273746d5c1STitus Rwantare int pmbus_page_config(PMBusDevice *pmdev, uint8_t index, uint64_t flags) 16283746d5c1STitus Rwantare { 16293746d5c1STitus Rwantare if (!pmdev->pages) { /* allocate memory for pages on first use */ 16303746d5c1STitus Rwantare pmbus_pages_alloc(pmdev); 16313746d5c1STitus Rwantare } 16323746d5c1STitus Rwantare 16333746d5c1STitus Rwantare /* The 0xFF page is special for commands applying to all pages */ 16343746d5c1STitus Rwantare if (index == PB_ALL_PAGES) { 16353746d5c1STitus Rwantare for (int i = 0; i < pmdev->num_pages; i++) { 16363746d5c1STitus Rwantare pmdev->pages[i].page_flags = flags; 16373746d5c1STitus Rwantare } 16383746d5c1STitus Rwantare return 0; 16393746d5c1STitus Rwantare } 16403746d5c1STitus Rwantare 16413746d5c1STitus Rwantare if (index > pmdev->num_pages - 1) { 16423746d5c1STitus Rwantare qemu_log_mask(LOG_GUEST_ERROR, 16433746d5c1STitus Rwantare "%s: index %u is out of range\n", 16443746d5c1STitus Rwantare __func__, index); 16453746d5c1STitus Rwantare return -1; 16463746d5c1STitus Rwantare } 16473746d5c1STitus Rwantare 16483746d5c1STitus Rwantare pmdev->pages[index].page_flags = flags; 16493746d5c1STitus Rwantare 16503746d5c1STitus Rwantare return 0; 16513746d5c1STitus Rwantare } 16523746d5c1STitus Rwantare 16533746d5c1STitus Rwantare /* TODO: include pmbus page info in vmstate */ 16543746d5c1STitus Rwantare const VMStateDescription vmstate_pmbus_device = { 16553746d5c1STitus Rwantare .name = TYPE_PMBUS_DEVICE, 16563746d5c1STitus Rwantare .version_id = 0, 16573746d5c1STitus Rwantare .minimum_version_id = 0, 16583746d5c1STitus Rwantare .fields = (VMStateField[]) { 16593746d5c1STitus Rwantare VMSTATE_SMBUS_DEVICE(smb, PMBusDevice), 16603746d5c1STitus Rwantare VMSTATE_UINT8(num_pages, PMBusDevice), 16613746d5c1STitus Rwantare VMSTATE_UINT8(code, PMBusDevice), 16623746d5c1STitus Rwantare VMSTATE_UINT8(page, PMBusDevice), 16633746d5c1STitus Rwantare VMSTATE_UINT8(capability, PMBusDevice), 16643746d5c1STitus Rwantare VMSTATE_END_OF_LIST() 16653746d5c1STitus Rwantare } 16663746d5c1STitus Rwantare }; 16673746d5c1STitus Rwantare 16683746d5c1STitus Rwantare static void pmbus_device_finalize(Object *obj) 16693746d5c1STitus Rwantare { 16703746d5c1STitus Rwantare PMBusDevice *pmdev = PMBUS_DEVICE(obj); 16713746d5c1STitus Rwantare g_free(pmdev->pages); 16723746d5c1STitus Rwantare } 16733746d5c1STitus Rwantare 16743746d5c1STitus Rwantare static void pmbus_device_class_init(ObjectClass *klass, void *data) 16753746d5c1STitus Rwantare { 16763746d5c1STitus Rwantare SMBusDeviceClass *k = SMBUS_DEVICE_CLASS(klass); 16773746d5c1STitus Rwantare 16783746d5c1STitus Rwantare k->quick_cmd = pmbus_quick_cmd; 16793746d5c1STitus Rwantare k->write_data = pmbus_write_data; 16803746d5c1STitus Rwantare k->receive_byte = pmbus_receive_byte; 16813746d5c1STitus Rwantare } 16823746d5c1STitus Rwantare 16833746d5c1STitus Rwantare static const TypeInfo pmbus_device_type_info = { 16843746d5c1STitus Rwantare .name = TYPE_PMBUS_DEVICE, 16853746d5c1STitus Rwantare .parent = TYPE_SMBUS_DEVICE, 16863746d5c1STitus Rwantare .instance_size = sizeof(PMBusDevice), 16873746d5c1STitus Rwantare .instance_finalize = pmbus_device_finalize, 16883746d5c1STitus Rwantare .abstract = true, 16893746d5c1STitus Rwantare .class_size = sizeof(PMBusDeviceClass), 16903746d5c1STitus Rwantare .class_init = pmbus_device_class_init, 16913746d5c1STitus Rwantare }; 16923746d5c1STitus Rwantare 16933746d5c1STitus Rwantare static void pmbus_device_register_types(void) 16943746d5c1STitus Rwantare { 16953746d5c1STitus Rwantare type_register_static(&pmbus_device_type_info); 16963746d5c1STitus Rwantare } 16973746d5c1STitus Rwantare 16983746d5c1STitus Rwantare type_init(pmbus_device_register_types) 1699