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