[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[PATCH v2 0/5] Add support for PMBus in QEMU
From: |
Titus Rwantare |
Subject: |
[PATCH v2 0/5] Add support for PMBus in QEMU |
Date: |
Wed, 5 May 2021 10:00:56 -0700 |
Hello,
This patch series adds an interface to start supporting PMBus devices in QEMU.
I’ve included two PMBus devices: MAX34451 and ADM1272.
PMBus is a variant of SMBus meant for digital management of power supplies.
PMBus adds to the SMBus standard by defining a number of constants and commands
used by compliant devices. The specification for PMBus can be found at:
https://pmbus.org/specification-archives/
Currently, the goal for these devices is to emulate basic functionality by
reading and writing registers. Timing, and some logical operation is not
implemented. This implementation supports nearly all available registers for
PMBus including:
- Voltage inputs and outputs
- Current inputs and outputs
- Temperature sensors
Unimplimented registers get passed through to the device model, and device
models can opt out of using the standard registers with flags. The included
devices make use of these fields and illustrate how to interface with the pmbus
class.
Datasheets for sensors:
https://datasheets.maximintegrated.com/en/ds/MAX34451.pdf
https://www.analog.com/media/en/technical-documentation/data-sheets/ADM1272.pdf
Since v1:
- addressed Joel's comments
- split out tests into their own patches
Thanks for reviewing,
Titus Rwantare
Titus Rwantare (5):
hw/i2c: add support for PMBus
hw/misc: add ADM1272 device
tests/qtest: add tests for ADM1272 device model
hw/misc: add MAX34451 device
tests/qtest: add tests for MAX34451 device model
include/hw/i2c/pmbus_device.h | 506 +++++++++++
hw/i2c/pmbus_device.c | 1596 +++++++++++++++++++++++++++++++++
hw/misc/adm1272.c | 543 +++++++++++
hw/misc/max34451.c | 719 +++++++++++++++
tests/qtest/adm1272-test.c | 445 +++++++++
tests/qtest/max34451-test.c | 336 +++++++
hw/arm/Kconfig | 3 +
hw/i2c/Kconfig | 4 +
hw/i2c/meson.build | 1 +
hw/misc/Kconfig | 8 +
hw/misc/meson.build | 2 +
tests/qtest/meson.build | 2 +
12 files changed, 4165 insertions(+)
create mode 100644 include/hw/i2c/pmbus_device.h
create mode 100644 hw/i2c/pmbus_device.c
create mode 100644 hw/misc/adm1272.c
create mode 100644 hw/misc/max34451.c
create mode 100644 tests/qtest/adm1272-test.c
create mode 100644 tests/qtest/max34451-test.c
Range-diff against v1:
1: b584203d74 ! 1: cac7aa27ec hw/i2c: add support for PMBus
@@ include/hw/i2c/pmbus_device.h (new)
+ *
+ * Copyright 2021 Google LLC
+ *
-+ * This program is free software; you can redistribute it and/or modify it
-+ * under the terms of the GNU General Public License as published by the
-+ * Free Software Foundation; either version 2 of the License, or
-+ * (at your option) any later version.
-+ *
-+ * This program is distributed in the hope that it will be useful, but
WITHOUT
-+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
-+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
-+ * for more details.
++ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#ifndef HW_PMBUS_DEVICE_H
@@ include/hw/i2c/pmbus_device.h (new)
+ uint8_t out_buf[SMBUS_DATA_MAX_LEN];
+};
+
-+typedef struct PMBusBlock {
-+ uint8_t *buf;
-+ uint8_t len;
-+} PMBusBlock;
-+
+/**
+ * Direct mode coefficients
+ * @var m - mantissa
@@ include/hw/i2c/pmbus_device.h (new)
+ * Assumes that the bytes in the block are already ordered correctly,
+ * also assumes the length has been prepended to the block if necessary
+ * | low_byte | ... | high_byte |
-+ * @param pmdev - maintains state of the PMBus device
++ * @param state - maintains state of the PMBus device
+ * @param data - byte array to be sent by device
+ * @param len - number
+ */
-+void pmbus_send_block(PMBusDevice *state, PMBusBlock block);
++void pmbus_send(PMBusDevice *state, const uint8_t *data, uint16_t len);
+void pmbus_send8(PMBusDevice *state, uint8_t data);
+void pmbus_send16(PMBusDevice *state, uint16_t data);
+void pmbus_send32(PMBusDevice *state, uint32_t data);
@@ include/hw/i2c/pmbus_device.h (new)
+uint16_t pmbus_receive16(PMBusDevice *pmdev);
+uint32_t pmbus_receive32(PMBusDevice *pmdev);
+uint64_t pmbus_receive64(PMBusDevice *pmdev);
-+PMBusBlock pmbus_receive_block(PMBusDevice *pmdev);
+
+/**
+ * PMBus page config must be called before any page is first used.
@@ hw/i2c/pmbus_device.c (new)
+ *
+ * Copyright 2021 Google LLC
+ *
-+ * This program is free software; you can redistribute it and/or modify it
-+ * under the terms of the GNU General Public License as published by the
-+ * Free Software Foundation; either version 2 of the License, or
-+ * (at your option) any later version.
-+ *
-+ * This program is distributed in the hope that it will be useful, but
WITHOUT
-+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
-+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
-+ * for more details.
++ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
++
+#include "qemu/osdep.h"
+#include <math.h>
+#include <string.h>
@@ hw/i2c/pmbus_device.c (new)
+ return x;
+}
+
-+static void pmbus_send(PMBusDevice *pmdev, const uint8_t *data, uint16_t
len)
++void pmbus_send(PMBusDevice *pmdev, const uint8_t *data, uint16_t len)
+{
+ if (pmdev->out_buf_len + len > SMBUS_DATA_MAX_LEN) {
+ qemu_log_mask(LOG_GUEST_ERROR,
@@ hw/i2c/pmbus_device.c (new)
+static void pmbus_send_uint(PMBusDevice *pmdev, uint64_t data, uint8_t
size)
+{
+ uint8_t bytes[8];
++ g_assert(size <= 8);
++
+ for (int i = 0; i < size; i++) {
+ bytes[i] = data & 0xFF;
+ data = data >> 8;
@@ hw/i2c/pmbus_device.c (new)
+ pmbus_send(pmdev, bytes, size);
+}
+
-+void pmbus_send_block(PMBusDevice *pmdev, PMBusBlock block)
-+{
-+ pmbus_send(pmdev, block.buf, block.len);
-+}
-+
+void pmbus_send8(PMBusDevice *pmdev, uint8_t data)
+{
+ pmbus_send_uint(pmdev, data, 1);
@@ hw/i2c/pmbus_device.c (new)
+void pmbus_send_string(PMBusDevice *pmdev, const char *data)
+{
+ size_t len = strlen(data);
++ g_assert(len > 0);
+ g_assert(len + pmdev->out_buf_len < SMBUS_DATA_MAX_LEN);
+ pmdev->out_buf[len + pmdev->out_buf_len] = len;
+
@@ hw/i2c/pmbus_device.c (new)
+ "%s: length mismatch. Expected 1 byte, got %d
bytes\n",
+ __func__, pmdev->in_buf_len - 1);
+ }
-+ return (uint8_t) pmbus_receive_uint(pmdev->in_buf, pmdev->in_buf_len);
++ return pmbus_receive_uint(pmdev->in_buf, pmdev->in_buf_len);
+}
+
+uint16_t pmbus_receive16(PMBusDevice *pmdev)
@@ hw/i2c/pmbus_device.c (new)
+ "%s: length mismatch. Expected 2 bytes, got %d
bytes\n",
+ __func__, pmdev->in_buf_len - 1);
+ }
-+ return (uint16_t) pmbus_receive_uint(pmdev->in_buf,
pmdev->in_buf_len);
++ return pmbus_receive_uint(pmdev->in_buf, pmdev->in_buf_len);
+}
+
+uint32_t pmbus_receive32(PMBusDevice *pmdev)
@@ hw/i2c/pmbus_device.c (new)
+ "%s: length mismatch. Expected 4 bytes, got %d
bytes\n",
+ __func__, pmdev->in_buf_len - 1);
+ }
-+ return (uint32_t) pmbus_receive_uint(pmdev->in_buf,
pmdev->in_buf_len);
++ return pmbus_receive_uint(pmdev->in_buf, pmdev->in_buf_len);
+}
+
+uint64_t pmbus_receive64(PMBusDevice *pmdev)
@@ hw/i2c/pmbus_device.c (new)
+ "%s: length mismatch. Expected 8 bytes, got %d
bytes\n",
+ __func__, pmdev->in_buf_len - 1);
+ }
-+ return (uint64_t) pmbus_receive_uint(pmdev->in_buf,
pmdev->in_buf_len);
-+}
-+
-+PMBusBlock pmbus_receive_block(PMBusDevice *pmdev)
-+{
-+ PMBusBlock data = { pmdev->in_buf, pmdev->in_buf_len };
-+ return data;
++ return pmbus_receive_uint(pmdev->in_buf, pmdev->in_buf_len);
+}
+
+static uint8_t pmbus_out_buf_pop(PMBusDevice *pmdev)
2: 33b0608a8c ! 2: a130a338b4 hw/misc: add ADM1272 device
@@ hw/misc/adm1272.c (new)
+ *
+ * Copyright 2021 Google LLC
+ *
-+ * This program is free software; you can redistribute it and/or modify it
-+ * under the terms of the GNU General Public License as published by the
-+ * Free Software Foundation; either version 2 of the License, or
-+ * (at your option) any later version.
-+ *
-+ * This program is distributed in the hope that it will be useful, but
WITHOUT
-+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
-+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
-+ * for more details.
++ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#include "qemu/osdep.h"
@@ hw/misc/adm1272.c (new)
+
+type_init(adm1272_register_types)
- ## tests/qtest/adm1272-test.c (new) ##
-@@
-+/*
-+ * QTests for the ADM1272 hotswap controller
-+ *
-+ * Copyright 2021 Google LLC
-+ *
-+ * This program is free software; you can redistribute it and/or modify it
-+ * under the terms of the GNU General Public License as published by the
-+ * Free Software Foundation; either version 2 of the License, or
-+ * (at your option) any later version.
-+ *
-+ * This program is distributed in the hope that it will be useful, but
WITHOUT
-+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
-+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
-+ * for more details.
-+ */
-+
-+#include "qemu/osdep.h"
-+#include <math.h>
-+#include "hw/i2c/pmbus_device.h"
-+#include "libqtest-single.h"
-+#include "libqos/qgraph.h"
-+#include "libqos/i2c.h"
-+#include "qapi/qmp/qdict.h"
-+#include "qapi/qmp/qnum.h"
-+#include "qemu/bitops.h"
-+
-+#define TEST_ID "adm1272-test"
-+#define TEST_ADDR (0x10)
-+
-+#define ADM1272_RESTART_TIME 0xCC
-+#define ADM1272_MFR_PEAK_IOUT 0xD0
-+#define ADM1272_MFR_PEAK_VIN 0xD1
-+#define ADM1272_MFR_PEAK_VOUT 0xD2
-+#define ADM1272_MFR_PMON_CONTROL 0xD3
-+#define ADM1272_MFR_PMON_CONFIG 0xD4
-+#define ADM1272_MFR_ALERT1_CONFIG 0xD5
-+#define ADM1272_MFR_ALERT2_CONFIG 0xD6
-+#define ADM1272_MFR_PEAK_TEMPERATURE 0xD7
-+#define ADM1272_MFR_DEVICE_CONFIG 0xD8
-+#define ADM1272_MFR_POWER_CYCLE 0xD9
-+#define ADM1272_MFR_PEAK_PIN 0xDA
-+#define ADM1272_MFR_READ_PIN_EXT 0xDB
-+#define ADM1272_MFR_READ_EIN_EXT 0xDC
-+
-+#define ADM1272_HYSTERESIS_LOW 0xF2
-+#define ADM1272_HYSTERESIS_HIGH 0xF3
-+#define ADM1272_STATUS_HYSTERESIS 0xF4
-+#define ADM1272_STATUS_GPIO 0xF5
-+#define ADM1272_STRT_UP_IOUT_LIM 0xF6
-+
-+/* Defaults */
-+#define ADM1272_OPERATION_DEFAULT 0x80
-+#define ADM1272_CAPABILITY_DEFAULT 0xB0
-+#define ADM1272_CAPABILITY_NO_PEC 0x30
-+#define ADM1272_DIRECT_MODE 0x40
-+#define ADM1272_HIGH_LIMIT_DEFAULT 0x0FFF
-+#define ADM1272_PIN_OP_DEFAULT 0x7FFF
-+#define ADM1272_PMBUS_REVISION_DEFAULT 0x22
-+#define ADM1272_MFR_ID_DEFAULT "ADI"
-+#define ADM1272_MODEL_DEFAULT "ADM1272-A1"
-+#define ADM1272_MFR_DEFAULT_REVISION "25"
-+#define ADM1272_DEFAULT_DATE "160301"
-+#define ADM1272_RESTART_TIME_DEFAULT 0x64
-+#define ADM1272_PMON_CONTROL_DEFAULT 0x1
-+#define ADM1272_PMON_CONFIG_DEFAULT 0x3F35
-+#define ADM1272_DEVICE_CONFIG_DEFAULT 0x8
-+#define ADM1272_HYSTERESIS_HIGH_DEFAULT 0xFFFF
-+#define ADM1272_STRT_UP_IOUT_LIM_DEFAULT 0x000F
-+#define ADM1272_VOLT_DEFAULT 12000
-+#define ADM1272_IOUT_DEFAULT 25000
-+#define ADM1272_PWR_DEFAULT 300 /* 12V 25A */
-+#define ADM1272_SHUNT 300 /* micro-ohms */
-+#define ADM1272_VOLTAGE_COEFF_DEFAULT 1
-+#define ADM1272_CURRENT_COEFF_DEFAULT 3
-+#define ADM1272_PWR_COEFF_DEFAULT 7
-+#define ADM1272_IOUT_OFFSET 0x5000
-+#define ADM1272_IOUT_OFFSET 0x5000
-+
-+static const PMBusCoefficients adm1272_coefficients[] = {
-+ [0] = { 6770, 0, -2 }, /* voltage, vrange 60V */
-+ [1] = { 4062, 0, -2 }, /* voltage, vrange 100V */
-+ [2] = { 1326, 20480, -1 }, /* current, vsense range 15mV */
-+ [3] = { 663, 20480, -1 }, /* current, vsense range 30mV */
-+ [4] = { 3512, 0, -2 }, /* power, vrange 60V, irange 15mV */
-+ [5] = { 21071, 0, -3 }, /* power, vrange 100V, irange 15mV */
-+ [6] = { 17561, 0, -3 }, /* power, vrange 60V, irange 30mV */
-+ [7] = { 10535, 0, -3 }, /* power, vrange 100V, irange 30mV */
-+ [8] = { 42, 31871, -1 }, /* temperature */
-+};
-+
-+uint16_t pmbus_data2direct_mode(PMBusCoefficients c, uint32_t value)
-+{
-+ /* R is usually negative to fit large readings into 16 bits */
-+ uint16_t y = (c.m * value + c.b) * pow(10, c.R);
-+ return y;
-+}
-+
-+uint32_t pmbus_direct_mode2data(PMBusCoefficients c, uint16_t value)
-+{
-+ /* X = (Y * 10^-R - b) / m */
-+ uint32_t x = (value / pow(10, c.R) - c.b) / c.m;
-+ return x;
-+}
-+
-+
-+static uint16_t adm1272_millivolts_to_direct(uint32_t value)
-+{
-+ PMBusCoefficients c =
adm1272_coefficients[ADM1272_VOLTAGE_COEFF_DEFAULT];
-+ c.b = c.b * 1000;
-+ c.R = c.R - 3;
-+ return pmbus_data2direct_mode(c, value);
-+}
-+
-+static uint32_t adm1272_direct_to_millivolts(uint16_t value)
-+{
-+ PMBusCoefficients c =
adm1272_coefficients[ADM1272_VOLTAGE_COEFF_DEFAULT];
-+ c.b = c.b * 1000;
-+ c.R = c.R - 3;
-+ return pmbus_direct_mode2data(c, value);
-+}
-+
-+static uint16_t adm1272_milliamps_to_direct(uint32_t value)
-+{
-+ PMBusCoefficients c =
adm1272_coefficients[ADM1272_CURRENT_COEFF_DEFAULT];
-+ /* Y = (m * r_sense * x - b) * 10^R */
-+ c.m = c.m * ADM1272_SHUNT / 1000; /* micro-ohms */
-+ c.b = c.b * 1000;
-+ c.R = c.R - 3;
-+ return pmbus_data2direct_mode(c, value);
-+}
-+
-+static uint32_t adm1272_direct_to_milliamps(uint16_t value)
-+{
-+ PMBusCoefficients c =
adm1272_coefficients[ADM1272_CURRENT_COEFF_DEFAULT];
-+ c.m = c.m * ADM1272_SHUNT / 1000;
-+ c.b = c.b * 1000;
-+ c.R = c.R - 3;
-+ return pmbus_direct_mode2data(c, value);
-+}
-+
-+static uint16_t adm1272_watts_to_direct(uint32_t value)
-+{
-+ PMBusCoefficients c = adm1272_coefficients[ADM1272_PWR_COEFF_DEFAULT];
-+ c.m = c.m * ADM1272_SHUNT / 1000;
-+ return pmbus_data2direct_mode(c, value);
-+}
-+
-+static uint32_t adm1272_direct_to_watts(uint16_t value)
-+{
-+ PMBusCoefficients c = adm1272_coefficients[ADM1272_PWR_COEFF_DEFAULT];
-+ c.m = c.m * ADM1272_SHUNT / 1000;
-+ return pmbus_direct_mode2data(c, value);
-+}
-+
-+static uint16_t qmp_adm1272_get(const char *id, const char *property)
-+{
-+ QDict *response;
-+ uint64_t ret;
-+
-+ response = qmp("{ 'execute': 'qom-get', 'arguments': { 'path': %s, "
-+ "'property': %s } }", id, property);
-+ g_assert(qdict_haskey(response, "return"));
-+ ret = qnum_get_uint(qobject_to(QNum, qdict_get(response, "return")));
-+ qobject_unref(response);
-+ return ret;
-+}
-+
-+static void qmp_adm1272_set(const char *id,
-+ const char *property,
-+ uint16_t value)
-+{
-+ QDict *response;
-+
-+ response = qmp("{ 'execute': 'qom-set', 'arguments': { 'path': %s, "
-+ "'property': %s, 'value': %u } }", id, property,
value);
-+ g_assert(qdict_haskey(response, "return"));
-+ qobject_unref(response);
-+}
-+
-+/* PMBus commands are little endian vs i2c_set16 in i2c.h which is big
endian */
-+static uint16_t adm1272_i2c_get16(QI2CDevice *i2cdev, uint8_t reg)
-+{
-+ uint8_t resp[2];
-+ i2c_read_block(i2cdev, reg, resp, sizeof(resp));
-+ return (resp[1] << 8) | resp[0];
-+}
-+
-+/* PMBus commands are little endian vs i2c_set16 in i2c.h which is big
endian */
-+static void adm1272_i2c_set16(QI2CDevice *i2cdev, uint8_t reg, uint16_t
value)
-+{
-+ uint8_t data[2];
-+
-+ data[0] = value & 255;
-+ data[1] = value >> 8;
-+ i2c_write_block(i2cdev, reg, data, sizeof(data));
-+}
-+
-+static void test_defaults(void *obj, void *data, QGuestAllocator *alloc)
-+{
-+ uint16_t value, i2c_value;
-+ int16_t err;
-+ QI2CDevice *i2cdev = (QI2CDevice *)obj;
-+ value = qmp_adm1272_get(TEST_ID, "vout");
-+ err = ADM1272_VOLT_DEFAULT - value;
-+ g_assert_cmpuint(abs(err), <, ADM1272_VOLT_DEFAULT / 20);
-+
-+ i2c_value = i2c_get8(i2cdev, PMBUS_OPERATION);
-+ g_assert_cmphex(i2c_value, ==, ADM1272_OPERATION_DEFAULT);
-+
-+ i2c_value = i2c_get8(i2cdev, PMBUS_VOUT_MODE);
-+ g_assert_cmphex(i2c_value, ==, ADM1272_DIRECT_MODE);
-+
-+ i2c_value = adm1272_i2c_get16(i2cdev, PMBUS_VOUT_OV_WARN_LIMIT);
-+ g_assert_cmphex(i2c_value, ==, ADM1272_HIGH_LIMIT_DEFAULT);
-+
-+ i2c_value = adm1272_i2c_get16(i2cdev, PMBUS_VOUT_UV_WARN_LIMIT);
-+ g_assert_cmphex(i2c_value, ==, 0);
-+
-+ i2c_value = adm1272_i2c_get16(i2cdev, PMBUS_IOUT_OC_WARN_LIMIT);
-+ g_assert_cmphex(i2c_value, ==, ADM1272_HIGH_LIMIT_DEFAULT);
-+
-+ i2c_value = adm1272_i2c_get16(i2cdev, PMBUS_OT_FAULT_LIMIT);
-+ g_assert_cmphex(i2c_value, ==, ADM1272_HIGH_LIMIT_DEFAULT);
-+
-+ i2c_value = adm1272_i2c_get16(i2cdev, PMBUS_OT_WARN_LIMIT);
-+ g_assert_cmphex(i2c_value, ==, ADM1272_HIGH_LIMIT_DEFAULT);
-+
-+ i2c_value = adm1272_i2c_get16(i2cdev, PMBUS_VIN_OV_WARN_LIMIT);
-+ g_assert_cmphex(i2c_value, ==, ADM1272_HIGH_LIMIT_DEFAULT);
-+
-+ i2c_value = adm1272_i2c_get16(i2cdev, PMBUS_VIN_UV_WARN_LIMIT);
-+ g_assert_cmphex(i2c_value, ==, 0);
-+
-+ i2c_value = adm1272_i2c_get16(i2cdev, PMBUS_PIN_OP_WARN_LIMIT);
-+ g_assert_cmphex(i2c_value, ==, ADM1272_PIN_OP_DEFAULT);
-+
-+ i2c_value = i2c_get8(i2cdev, PMBUS_REVISION);
-+ g_assert_cmphex(i2c_value, ==, ADM1272_PMBUS_REVISION_DEFAULT);
-+
-+ i2c_value = i2c_get8(i2cdev, ADM1272_MFR_PMON_CONTROL);
-+ g_assert_cmphex(i2c_value, ==, ADM1272_PMON_CONTROL_DEFAULT);
-+
-+ i2c_value = adm1272_i2c_get16(i2cdev, ADM1272_MFR_PMON_CONFIG);
-+ g_assert_cmphex(i2c_value, ==, ADM1272_PMON_CONFIG_DEFAULT);
-+
-+ i2c_value = adm1272_i2c_get16(i2cdev, ADM1272_MFR_DEVICE_CONFIG);
-+ g_assert_cmphex(i2c_value, ==, ADM1272_DEVICE_CONFIG_DEFAULT);
-+
-+ i2c_value = adm1272_i2c_get16(i2cdev, ADM1272_HYSTERESIS_HIGH);
-+ g_assert_cmphex(i2c_value, ==, ADM1272_HYSTERESIS_HIGH_DEFAULT);
-+
-+ i2c_value = adm1272_i2c_get16(i2cdev, ADM1272_STRT_UP_IOUT_LIM);
-+ g_assert_cmphex(i2c_value, ==, ADM1272_STRT_UP_IOUT_LIM_DEFAULT);
-+}
-+
-+/* test qmp access */
-+static void test_tx_rx(void *obj, void *data, QGuestAllocator *alloc)
-+{
-+ uint16_t i2c_value, value, i2c_voltage, i2c_pwr, lossy_value;
-+ QI2CDevice *i2cdev = (QI2CDevice *)obj;
-+
-+ /* converting to direct mode is lossy - we generate the same loss
here */
-+ lossy_value =
-+ adm1272_direct_to_millivolts(adm1272_millivolts_to_direct(1000));
-+ qmp_adm1272_set(TEST_ID, "vin", 1000);
-+ value = qmp_adm1272_get(TEST_ID, "vin");
-+ i2c_value = adm1272_i2c_get16(i2cdev, PMBUS_READ_VIN);
-+ i2c_voltage = adm1272_direct_to_millivolts(i2c_value);
-+ g_assert_cmpuint(value, ==, i2c_voltage);
-+ g_assert_cmpuint(i2c_voltage, ==, lossy_value);
-+
-+ lossy_value =
-+ adm1272_direct_to_millivolts(adm1272_millivolts_to_direct(1500));
-+ qmp_adm1272_set(TEST_ID, "vout", 1500);
-+ value = qmp_adm1272_get(TEST_ID, "vout");
-+ i2c_value = adm1272_i2c_get16(i2cdev, PMBUS_READ_VOUT);
-+ i2c_voltage = adm1272_direct_to_millivolts(i2c_value);
-+ g_assert_cmpuint(value, ==, i2c_voltage);
-+ g_assert_cmpuint(i2c_voltage, ==, lossy_value);
-+
-+ lossy_value =
-+ adm1272_direct_to_milliamps(adm1272_milliamps_to_direct(1600));
-+ qmp_adm1272_set(TEST_ID, "iout", 1600);
-+ value = qmp_adm1272_get(TEST_ID, "iout");
-+ i2c_value = adm1272_i2c_get16(i2cdev, PMBUS_READ_IOUT);
-+ i2c_value = adm1272_direct_to_milliamps(i2c_value);
-+ g_assert_cmphex(value, ==, i2c_value);
-+ g_assert_cmphex(i2c_value, ==, lossy_value);
-+
-+ lossy_value =
-+ adm1272_direct_to_watts(adm1272_watts_to_direct(320));
-+ qmp_adm1272_set(TEST_ID, "pin", 320);
-+ value = qmp_adm1272_get(TEST_ID, "pin");
-+ i2c_value = adm1272_i2c_get16(i2cdev, PMBUS_READ_PIN);
-+ i2c_pwr = adm1272_direct_to_watts(i2c_value);
-+ g_assert_cmphex(value, ==, i2c_pwr);
-+ g_assert_cmphex(i2c_pwr, ==, lossy_value);
-+}
-+
-+/* test r/w registers */
-+static void test_rw_regs(void *obj, void *data, QGuestAllocator *alloc)
-+{
-+ uint16_t i2c_value;
-+ QI2CDevice *i2cdev = (QI2CDevice *)obj;
-+
-+ adm1272_i2c_set16(i2cdev, PMBUS_VOUT_OV_WARN_LIMIT, 0xABCD);
-+ i2c_value = adm1272_i2c_get16(i2cdev, PMBUS_VOUT_OV_WARN_LIMIT);
-+ g_assert_cmphex(i2c_value, ==, 0xABCD);
-+
-+ adm1272_i2c_set16(i2cdev, PMBUS_VOUT_UV_WARN_LIMIT, 0xCDEF);
-+ i2c_value = adm1272_i2c_get16(i2cdev, PMBUS_VOUT_UV_WARN_LIMIT);
-+ g_assert_cmphex(i2c_value, ==, 0xCDEF);
-+
-+ adm1272_i2c_set16(i2cdev, PMBUS_IOUT_OC_WARN_LIMIT, 0x1234);
-+ i2c_value = adm1272_i2c_get16(i2cdev, PMBUS_IOUT_OC_WARN_LIMIT);
-+ g_assert_cmphex(i2c_value, ==, 0x1234);
-+
-+ adm1272_i2c_set16(i2cdev, PMBUS_OT_FAULT_LIMIT, 0x5678);
-+ i2c_value = adm1272_i2c_get16(i2cdev, PMBUS_OT_FAULT_LIMIT);
-+ g_assert_cmphex(i2c_value, ==, 0x5678);
-+
-+ adm1272_i2c_set16(i2cdev, PMBUS_OT_WARN_LIMIT, 0xABDC);
-+ i2c_value = adm1272_i2c_get16(i2cdev, PMBUS_OT_WARN_LIMIT);
-+ g_assert_cmphex(i2c_value, ==, 0xABDC);
-+
-+ adm1272_i2c_set16(i2cdev, PMBUS_VIN_OV_WARN_LIMIT, 0xCDEF);
-+ i2c_value = adm1272_i2c_get16(i2cdev, PMBUS_VIN_OV_WARN_LIMIT);
-+ g_assert_cmphex(i2c_value, ==, 0xCDEF);
-+
-+ adm1272_i2c_set16(i2cdev, PMBUS_VIN_UV_WARN_LIMIT, 0x2345);
-+ i2c_value = adm1272_i2c_get16(i2cdev, PMBUS_VIN_UV_WARN_LIMIT);
-+ g_assert_cmphex(i2c_value, ==, 0x2345);
-+
-+ i2c_set8(i2cdev, ADM1272_RESTART_TIME, 0xF8);
-+ i2c_value = i2c_get8(i2cdev, ADM1272_RESTART_TIME);
-+ g_assert_cmphex(i2c_value, ==, 0xF8);
-+
-+ i2c_set8(i2cdev, ADM1272_MFR_PMON_CONTROL, 0);
-+ i2c_value = i2c_get8(i2cdev, ADM1272_MFR_PMON_CONTROL);
-+ g_assert_cmpuint(i2c_value, ==, 0);
-+
-+ adm1272_i2c_set16(i2cdev, ADM1272_MFR_PMON_CONFIG, 0xDEF0);
-+ i2c_value = adm1272_i2c_get16(i2cdev, ADM1272_MFR_PMON_CONFIG);
-+ g_assert_cmphex(i2c_value, ==, 0xDEF0);
-+
-+ adm1272_i2c_set16(i2cdev, ADM1272_MFR_ALERT1_CONFIG, 0x0123);
-+ i2c_value = adm1272_i2c_get16(i2cdev, ADM1272_MFR_ALERT1_CONFIG);
-+ g_assert_cmphex(i2c_value, ==, 0x0123);
-+
-+ adm1272_i2c_set16(i2cdev, ADM1272_MFR_ALERT2_CONFIG, 0x9876);
-+ i2c_value = adm1272_i2c_get16(i2cdev, ADM1272_MFR_ALERT2_CONFIG);
-+ g_assert_cmphex(i2c_value, ==, 0x9876);
-+
-+ adm1272_i2c_set16(i2cdev, ADM1272_MFR_DEVICE_CONFIG, 0x3456);
-+ i2c_value = adm1272_i2c_get16(i2cdev, ADM1272_MFR_DEVICE_CONFIG);
-+ g_assert_cmphex(i2c_value, ==, 0x3456);
-+
-+ adm1272_i2c_set16(i2cdev, ADM1272_HYSTERESIS_LOW, 0xCABA);
-+ i2c_value = adm1272_i2c_get16(i2cdev, ADM1272_HYSTERESIS_LOW);
-+ g_assert_cmphex(i2c_value, ==, 0xCABA);
-+
-+ adm1272_i2c_set16(i2cdev, ADM1272_HYSTERESIS_HIGH, 0x6789);
-+ i2c_value = adm1272_i2c_get16(i2cdev, ADM1272_HYSTERESIS_HIGH);
-+ g_assert_cmphex(i2c_value, ==, 0x6789);
-+
-+ adm1272_i2c_set16(i2cdev, ADM1272_STRT_UP_IOUT_LIM, 0x9876);
-+ i2c_value = adm1272_i2c_get16(i2cdev, ADM1272_STRT_UP_IOUT_LIM);
-+ g_assert_cmphex(i2c_value, ==, 0x9876);
-+
-+ adm1272_i2c_set16(i2cdev, PMBUS_OPERATION, 0xA);
-+ i2c_value = i2c_get8(i2cdev, PMBUS_OPERATION);
-+ g_assert_cmphex(i2c_value, ==, 0xA);
-+}
-+
-+/* test read-only registers */
-+static void test_ro_regs(void *obj, void *data, QGuestAllocator *alloc)
-+{
-+ uint16_t i2c_init_value, i2c_value;
-+ QI2CDevice *i2cdev = (QI2CDevice *)obj;
-+
-+ i2c_init_value = adm1272_i2c_get16(i2cdev, PMBUS_READ_VIN);
-+ adm1272_i2c_set16(i2cdev, PMBUS_READ_VIN, 0xBEEF);
-+ i2c_value = adm1272_i2c_get16(i2cdev, PMBUS_READ_VIN);
-+ g_assert_cmphex(i2c_init_value, ==, i2c_value);
-+
-+ i2c_init_value = adm1272_i2c_get16(i2cdev, PMBUS_READ_VOUT);
-+ adm1272_i2c_set16(i2cdev, PMBUS_READ_VOUT, 0x1234);
-+ i2c_value = adm1272_i2c_get16(i2cdev, PMBUS_READ_VOUT);
-+ g_assert_cmphex(i2c_init_value, ==, i2c_value);
-+
-+ i2c_init_value = adm1272_i2c_get16(i2cdev, PMBUS_READ_IOUT);
-+ adm1272_i2c_set16(i2cdev, PMBUS_READ_IOUT, 0x6547);
-+ i2c_value = adm1272_i2c_get16(i2cdev, PMBUS_READ_IOUT);
-+ g_assert_cmphex(i2c_init_value, ==, i2c_value);
-+
-+ i2c_init_value = adm1272_i2c_get16(i2cdev, PMBUS_READ_TEMPERATURE_1);
-+ adm1272_i2c_set16(i2cdev, PMBUS_READ_TEMPERATURE_1, 0x1597);
-+ i2c_value = adm1272_i2c_get16(i2cdev, PMBUS_READ_TEMPERATURE_1);
-+ g_assert_cmphex(i2c_init_value, ==, i2c_value);
-+
-+ i2c_init_value = adm1272_i2c_get16(i2cdev, PMBUS_READ_PIN);
-+ adm1272_i2c_set16(i2cdev, PMBUS_READ_PIN, 0xDEAD);
-+ i2c_value = adm1272_i2c_get16(i2cdev, PMBUS_READ_PIN);
-+ g_assert_cmphex(i2c_init_value, ==, i2c_value);
-+}
-+
-+/* test voltage fault handling */
-+static void test_voltage_faults(void *obj, void *data, QGuestAllocator
*alloc)
-+{
-+ uint16_t i2c_value;
-+ uint8_t i2c_byte;
-+ QI2CDevice *i2cdev = (QI2CDevice *)obj;
-+
-+ adm1272_i2c_set16(i2cdev, PMBUS_VOUT_OV_WARN_LIMIT,
-+ adm1272_millivolts_to_direct(5000));
-+ qmp_adm1272_set(TEST_ID, "vout", 5100);
-+
-+ i2c_value = adm1272_i2c_get16(i2cdev, PMBUS_STATUS_WORD);
-+ i2c_byte = i2c_get8(i2cdev, PMBUS_STATUS_VOUT);
-+ g_assert_true((i2c_value & PB_STATUS_VOUT) != 0);
-+ g_assert_true((i2c_byte & PB_STATUS_VOUT_OV_WARN) != 0);
-+
-+ qmp_adm1272_set(TEST_ID, "vout", 4500);
-+ i2c_set8(i2cdev, PMBUS_CLEAR_FAULTS, 0);
-+ i2c_byte = i2c_get8(i2cdev, PMBUS_STATUS_VOUT);
-+ g_assert_true((i2c_byte & PB_STATUS_VOUT_OV_WARN) == 0);
-+
-+ adm1272_i2c_set16(i2cdev, PMBUS_VOUT_UV_WARN_LIMIT,
-+ adm1272_millivolts_to_direct(4600));
-+ i2c_value = adm1272_i2c_get16(i2cdev, PMBUS_STATUS_WORD);
-+ i2c_byte = i2c_get8(i2cdev, PMBUS_STATUS_VOUT);
-+ g_assert_true((i2c_value & PB_STATUS_VOUT) != 0);
-+ g_assert_true((i2c_byte & PB_STATUS_VOUT_UV_WARN) != 0);
-+
-+}
-+
-+static void adm1272_register_nodes(void)
-+{
-+ QOSGraphEdgeOptions opts = {
-+ .extra_device_opts = "id=" TEST_ID ",address=0x10"
-+ };
-+ add_qi2c_address(&opts, &(QI2CAddress) { TEST_ADDR });
-+
-+ qos_node_create_driver("adm1272", i2c_device_create);
-+ qos_node_consumes("adm1272", "i2c-bus", &opts);
-+
-+ qos_add_test("test_defaults", "adm1272", test_defaults, NULL);
-+ qos_add_test("test_tx_rx", "adm1272", test_tx_rx, NULL);
-+ qos_add_test("test_rw_regs", "adm1272", test_rw_regs, NULL);
-+ qos_add_test("test_ro_regs", "adm1272", test_ro_regs, NULL);
-+ qos_add_test("test_ov_faults", "adm1272", test_voltage_faults, NULL);
-+}
-+libqos_init(adm1272_register_nodes);
-
## hw/arm/Kconfig ##
@@ hw/arm/Kconfig: config XLNX_VERSAL
config NPCM7XX
@@ hw/misc/meson.build: softmmu_ss.add(when: 'CONFIG_SGA', if_true:
files('sga.c'))
softmmu_ss.add(when: 'CONFIG_UNIMP', if_true: files('unimp.c'))
softmmu_ss.add(when: 'CONFIG_EMPTY_SLOT', if_true: files('empty_slot.c'))
softmmu_ss.add(when: 'CONFIG_LED', if_true: files('led.c'))
-
- ## tests/qtest/meson.build ##
-@@ tests/qtest/meson.build: qtests_s390x = \
- qos_test_ss = ss.source_set()
- qos_test_ss.add(
- 'ac97-test.c',
-+ 'adm1272-test.c',
- 'ds1338-test.c',
- 'e1000-test.c',
- 'e1000e-test.c',
-: ---------- > 3: 7d6d55d65e tests/qtest: add tests for ADM1272 device model
3: 7bf96fed69 ! 4: 5d69decd3a hw/misc: add MAX34451 device
@@ hw/misc/max34451.c (new)
+ *
+ * Copyright 2021 Google LLC
+ *
-+ * This program is free software; you can redistribute it and/or modify it
-+ * under the terms of the GNU General Public License as published by the
-+ * Free Software Foundation; either version 2 of the License, or
-+ * (at your option) any later version.
-+ *
-+ * This program is distributed in the hope that it will be useful, but
WITHOUT
-+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
-+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
-+ * for more details.
++ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#include "qemu/osdep.h"
@@ hw/misc/max34451.c (new)
+
+type_init(max34451_register_types)
- ## tests/qtest/max34451-test.c (new) ##
-@@
-+/*
-+ * QTests for the MAX34451 device
-+ *
-+ * Copyright 2021 Google LLC
-+ *
-+ * This program is free software; you can redistribute it and/or modify it
-+ * under the terms of the GNU General Public License as published by the
-+ * Free Software Foundation; either version 2 of the License, or
-+ * (at your option) any later version.
-+ *
-+ * This program is distributed in the hope that it will be useful, but
WITHOUT
-+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
-+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
-+ * for more details.
-+ */
-+
-+#include "qemu/osdep.h"
-+#include "hw/i2c/pmbus_device.h"
-+#include "libqtest-single.h"
-+#include "libqos/qgraph.h"
-+#include "libqos/i2c.h"
-+#include "qapi/qmp/qdict.h"
-+#include "qapi/qmp/qnum.h"
-+#include "qemu/bitops.h"
-+
-+#define TEST_ID "max34451-test"
-+#define TEST_ADDR (0x4e)
-+
-+#define MAX34451_MFR_VOUT_PEAK 0xD4
-+#define MAX34451_MFR_IOUT_PEAK 0xD5
-+#define MAX34451_MFR_TEMPERATURE_PEAK 0xD6
-+#define MAX34451_MFR_VOUT_MIN 0xD7
-+
-+#define DEFAULT_VOUT 0
-+#define DEFAULT_UV_LIMIT 0
-+#define DEFAULT_TEMPERATURE 2500
-+#define DEFAULT_SCALE 0x7FFF
-+#define DEFAULT_OV_LIMIT 0x7FFF
-+#define DEFAULT_OC_LIMIT 0x7FFF
-+#define DEFAULT_OT_LIMIT 0x7FFF
-+#define DEFAULT_VMIN 0x7FFF
-+#define DEFAULT_TON_FAULT_LIMIT 0xFFFF
-+#define DEFAULT_CHANNEL_CONFIG 0x20
-+#define DEFAULT_TEXT 0x20
-+
-+#define MAX34451_NUM_PWR_DEVICES 16
-+#define MAX34451_NUM_TEMP_DEVICES 5
-+
-+
-+static uint16_t qmp_max34451_get(const char *id, const char *property)
-+{
-+ QDict *response;
-+ uint16_t ret;
-+ response = qmp("{ 'execute': 'qom-get', 'arguments': { 'path': %s, "
-+ "'property': %s } }", id, property);
-+ g_assert(qdict_haskey(response, "return"));
-+ ret = qnum_get_uint(qobject_to(QNum, qdict_get(response, "return")));
-+ qobject_unref(response);
-+ return ret;
-+}
-+
-+static void qmp_max34451_set(const char *id,
-+ const char *property,
-+ uint16_t value)
-+{
-+ QDict *response;
-+
-+ response = qmp("{ 'execute': 'qom-set', 'arguments': { 'path': %s, "
-+ "'property': %s, 'value': %u } }",
-+ id, property, value);
-+ g_assert(qdict_haskey(response, "return"));
-+ qobject_unref(response);
-+}
-+
-+/* PMBus commands are little endian vs i2c_set16 in i2c.h which is big
endian */
-+static uint16_t max34451_i2c_get16(QI2CDevice *i2cdev, uint8_t reg)
-+{
-+ uint8_t resp[2];
-+ i2c_read_block(i2cdev, reg, resp, sizeof(resp));
-+ return (resp[1] << 8) | resp[0];
-+}
-+
-+/* PMBus commands are little endian vs i2c_set16 in i2c.h which is big
endian */
-+static void max34451_i2c_set16(QI2CDevice *i2cdev, uint8_t reg, uint16_t
value)
-+{
-+ uint8_t data[2];
-+
-+ data[0] = value & 255;
-+ data[1] = value >> 8;
-+ i2c_write_block(i2cdev, reg, data, sizeof(data));
-+}
-+
-+/* Test default values */
-+static void test_defaults(void *obj, void *data, QGuestAllocator *alloc)
-+{
-+ uint16_t value, i2c_value;
-+ QI2CDevice *i2cdev = (QI2CDevice *)obj;
-+ char *path;
-+
-+ /* Default temperatures and temperature fault limits */
-+ for (int i = 0; i < MAX34451_NUM_TEMP_DEVICES; i++) {
-+ path = g_strdup_printf("temperature[%d]", i);
-+ value = qmp_max34451_get(TEST_ID, path);
-+ g_assert_cmpuint(value, ==, DEFAULT_TEMPERATURE);
-+ g_free(path);
-+
-+ /* Temperature sensors start on page 16 */
-+ i2c_set8(i2cdev, PMBUS_PAGE, i + 16);
-+ i2c_value = max34451_i2c_get16(i2cdev, PMBUS_READ_TEMPERATURE_1);
-+ g_assert_cmpuint(i2c_value, ==, DEFAULT_TEMPERATURE);
-+
-+ i2c_value = max34451_i2c_get16(i2cdev, PMBUS_OT_FAULT_LIMIT);
-+ g_assert_cmpuint(i2c_value, ==, DEFAULT_OT_LIMIT);
-+
-+ i2c_value = max34451_i2c_get16(i2cdev, PMBUS_OT_WARN_LIMIT);
-+ g_assert_cmpuint(i2c_value, ==, DEFAULT_OT_LIMIT);
-+ }
-+
-+ /* Default voltages and fault limits */
-+ for (int i = 0; i < MAX34451_NUM_PWR_DEVICES; i++) {
-+ path = g_strdup_printf("vout[%d]", i);
-+ value = qmp_max34451_get(TEST_ID, path);
-+ g_assert_cmpuint(value, ==, DEFAULT_VOUT);
-+ g_free(path);
-+
-+ i2c_set8(i2cdev, PMBUS_PAGE, i);
-+ i2c_value = max34451_i2c_get16(i2cdev, PMBUS_READ_VOUT);
-+ g_assert_cmpuint(i2c_value, ==, DEFAULT_VOUT);
-+
-+ i2c_value = max34451_i2c_get16(i2cdev, PMBUS_VOUT_OV_FAULT_LIMIT);
-+ g_assert_cmpuint(i2c_value, ==, DEFAULT_OV_LIMIT);
-+
-+ i2c_value = max34451_i2c_get16(i2cdev, PMBUS_VOUT_OV_WARN_LIMIT);
-+ g_assert_cmpuint(i2c_value, ==, DEFAULT_OV_LIMIT);
-+
-+ i2c_value = max34451_i2c_get16(i2cdev, PMBUS_VOUT_UV_WARN_LIMIT);
-+ g_assert_cmpuint(i2c_value, ==, DEFAULT_UV_LIMIT);
-+
-+ i2c_value = max34451_i2c_get16(i2cdev, PMBUS_VOUT_UV_FAULT_LIMIT);
-+ g_assert_cmpuint(i2c_value, ==, DEFAULT_UV_LIMIT);
-+
-+ i2c_value = max34451_i2c_get16(i2cdev, MAX34451_MFR_VOUT_MIN);
-+ g_assert_cmpuint(i2c_value, ==, DEFAULT_VMIN);
-+ }
-+
-+ i2c_value = i2c_get8(i2cdev, PMBUS_VOUT_MODE);
-+ g_assert_cmphex(i2c_value, ==, 0x40); /* DIRECT mode */
-+
-+ i2c_value = i2c_get8(i2cdev, PMBUS_REVISION);
-+ g_assert_cmphex(i2c_value, ==, 0x11); /* Rev 1.1 */
-+}
-+
-+/* Test setting temperature */
-+static void test_temperature(void *obj, void *data, QGuestAllocator
*alloc)
-+{
-+ uint16_t value, i2c_value;
-+ QI2CDevice *i2cdev = (QI2CDevice *)obj;
-+ char *path;
-+
-+ for (int i = 0; i < MAX34451_NUM_TEMP_DEVICES; i++) {
-+ path = g_strdup_printf("temperature[%d]", i);
-+ qmp_max34451_set(TEST_ID, path, 0xBE00 + i);
-+ value = qmp_max34451_get(TEST_ID, path);
-+ g_assert_cmphex(value, ==, 0xBE00 + i);
-+ g_free(path);
-+ }
-+
-+ /* compare qmp read with i2c read separately */
-+ for (int i = 0; i < MAX34451_NUM_TEMP_DEVICES; i++) {
-+ /* temperature[0] is on page 16 */
-+ i2c_set8(i2cdev, PMBUS_PAGE, i + 16);
-+ i2c_value = max34451_i2c_get16(i2cdev, PMBUS_READ_TEMPERATURE_1);
-+ g_assert_cmphex(i2c_value, ==, 0xBE00 + i);
-+
-+ i2c_value = max34451_i2c_get16(i2cdev,
MAX34451_MFR_TEMPERATURE_PEAK);
-+ g_assert_cmphex(i2c_value, ==, 0xBE00 + i);
-+ }
-+}
-+
-+/* Test setting voltage */
-+static void test_voltage(void *obj, void *data, QGuestAllocator *alloc)
-+{
-+ uint16_t value, i2c_value;
-+ QI2CDevice *i2cdev = (QI2CDevice *)obj;
-+ char *path;
-+
-+ for (int i = 0; i < MAX34451_NUM_PWR_DEVICES; i++) {
-+ path = g_strdup_printf("vout[%d]", i);
-+ qmp_max34451_set(TEST_ID, path, 3000 + i);
-+ value = qmp_max34451_get(TEST_ID, path);
-+ g_assert_cmpuint(value, ==, 3000 + i);
-+ g_free(path);
-+ }
-+
-+ /* compare qmp read with i2c read separately */
-+ for (int i = 0; i < MAX34451_NUM_PWR_DEVICES; i++) {
-+ i2c_set8(i2cdev, PMBUS_PAGE, i);
-+ i2c_value = max34451_i2c_get16(i2cdev, PMBUS_READ_VOUT);
-+ g_assert_cmpuint(i2c_value, ==, 3000 + i);
-+
-+ i2c_value = max34451_i2c_get16(i2cdev, MAX34451_MFR_VOUT_PEAK);
-+ g_assert_cmpuint(i2c_value, ==, 3000 + i);
-+
-+ i2c_value = max34451_i2c_get16(i2cdev, MAX34451_MFR_VOUT_MIN);
-+ g_assert_cmpuint(i2c_value, ==, 3000 + i);
-+ }
-+}
-+
-+/* Test setting some read/write registers */
-+static void test_rw_regs(void *obj, void *data, QGuestAllocator *alloc)
-+{
-+ uint16_t i2c_value;
-+ QI2CDevice *i2cdev = (QI2CDevice *)obj;
-+
-+ i2c_set8(i2cdev, PMBUS_PAGE, 11);
-+ i2c_value = i2c_get8(i2cdev, PMBUS_PAGE);
-+ g_assert_cmpuint(i2c_value, ==, 11);
-+
-+ i2c_set8(i2cdev, PMBUS_OPERATION, 1);
-+ i2c_value = i2c_get8(i2cdev, PMBUS_OPERATION);
-+ g_assert_cmpuint(i2c_value, ==, 1);
-+
-+ max34451_i2c_set16(i2cdev, PMBUS_VOUT_MARGIN_HIGH, 5000);
-+ i2c_value = max34451_i2c_get16(i2cdev, PMBUS_VOUT_MARGIN_HIGH);
-+ g_assert_cmpuint(i2c_value, ==, 5000);
-+
-+ max34451_i2c_set16(i2cdev, PMBUS_VOUT_MARGIN_LOW, 4000);
-+ i2c_value = max34451_i2c_get16(i2cdev, PMBUS_VOUT_MARGIN_LOW);
-+ g_assert_cmpuint(i2c_value, ==, 4000);
-+
-+ max34451_i2c_set16(i2cdev, PMBUS_VOUT_OV_FAULT_LIMIT, 5500);
-+ i2c_value = max34451_i2c_get16(i2cdev, PMBUS_VOUT_OV_FAULT_LIMIT);
-+ g_assert_cmpuint(i2c_value, ==, 5500);
-+
-+ max34451_i2c_set16(i2cdev, PMBUS_VOUT_OV_WARN_LIMIT, 5600);
-+ i2c_value = max34451_i2c_get16(i2cdev, PMBUS_VOUT_OV_WARN_LIMIT);
-+ g_assert_cmpuint(i2c_value, ==, 5600);
-+
-+ max34451_i2c_set16(i2cdev, PMBUS_VOUT_UV_FAULT_LIMIT, 5700);
-+ i2c_value = max34451_i2c_get16(i2cdev, PMBUS_VOUT_UV_FAULT_LIMIT);
-+ g_assert_cmpuint(i2c_value, ==, 5700);
-+
-+ max34451_i2c_set16(i2cdev, PMBUS_VOUT_UV_WARN_LIMIT, 5800);
-+ i2c_value = max34451_i2c_get16(i2cdev, PMBUS_VOUT_UV_WARN_LIMIT);
-+ g_assert_cmpuint(i2c_value, ==, 5800);
-+
-+ max34451_i2c_set16(i2cdev, PMBUS_POWER_GOOD_ON, 5900);
-+ i2c_value = max34451_i2c_get16(i2cdev, PMBUS_POWER_GOOD_ON);
-+ g_assert_cmpuint(i2c_value, ==, 5900);
-+
-+ max34451_i2c_set16(i2cdev, PMBUS_POWER_GOOD_OFF, 6100);
-+ i2c_value = max34451_i2c_get16(i2cdev, PMBUS_POWER_GOOD_OFF);
-+ g_assert_cmpuint(i2c_value, ==, 6100);
-+}
-+
-+/* Test that Read only registers can't be written */
-+static void test_ro_regs(void *obj, void *data, QGuestAllocator *alloc)
-+{
-+ uint16_t i2c_value, i2c_init_value;
-+ QI2CDevice *i2cdev = (QI2CDevice *)obj;
-+
-+ i2c_set8(i2cdev, PMBUS_PAGE, 1); /* move to page 1 */
-+ i2c_init_value = i2c_get8(i2cdev, PMBUS_CAPABILITY);
-+ i2c_set8(i2cdev, PMBUS_CAPABILITY, 0xF9);
-+ i2c_value = i2c_get8(i2cdev, PMBUS_CAPABILITY);
-+ g_assert_cmpuint(i2c_init_value, ==, i2c_value);
-+
-+ i2c_init_value = max34451_i2c_get16(i2cdev, PMBUS_READ_VOUT);
-+ max34451_i2c_set16(i2cdev, PMBUS_READ_VOUT, 0xDEAD);
-+ i2c_value = max34451_i2c_get16(i2cdev, PMBUS_READ_VOUT);
-+ g_assert_cmpuint(i2c_init_value, ==, i2c_value);
-+ g_assert_cmphex(i2c_value, !=, 0xDEAD);
-+
-+ i2c_set8(i2cdev, PMBUS_PAGE, 16); /* move to page 16 */
-+ i2c_init_value = max34451_i2c_get16(i2cdev, PMBUS_READ_TEMPERATURE_1);
-+ max34451_i2c_set16(i2cdev, PMBUS_READ_TEMPERATURE_1, 0xABBA);
-+ i2c_value = max34451_i2c_get16(i2cdev, PMBUS_READ_TEMPERATURE_1);
-+ g_assert_cmpuint(i2c_init_value, ==, i2c_value);
-+ g_assert_cmphex(i2c_value, !=, 0xABBA);
-+}
-+
-+/* test over voltage faults */
-+static void test_ov_faults(void *obj, void *data, QGuestAllocator *alloc)
-+{
-+ uint16_t i2c_value;
-+ uint8_t i2c_byte;
-+ QI2CDevice *i2cdev = (QI2CDevice *)obj;
-+ char *path;
-+ /* Test ov fault reporting */
-+ for (int i = 0; i < MAX34451_NUM_PWR_DEVICES; i++) {
-+ path = g_strdup_printf("vout[%d]", i);
-+ i2c_set8(i2cdev, PMBUS_PAGE, i);
-+ max34451_i2c_set16(i2cdev, PMBUS_VOUT_OV_FAULT_LIMIT, 5000);
-+ qmp_max34451_set(TEST_ID, path, 5100);
-+ g_free(path);
-+
-+ i2c_value = max34451_i2c_get16(i2cdev, PMBUS_STATUS_WORD);
-+ i2c_byte = i2c_get8(i2cdev, PMBUS_STATUS_VOUT);
-+ g_assert_true((i2c_value & PB_STATUS_VOUT) != 0);
-+ g_assert_true((i2c_byte & PB_STATUS_VOUT_OV_FAULT) != 0);
-+ }
-+}
-+
-+/* test over temperature faults */
-+static void test_ot_faults(void *obj, void *data, QGuestAllocator *alloc)
-+{
-+ uint16_t i2c_value;
-+ uint8_t i2c_byte;
-+ QI2CDevice *i2cdev = (QI2CDevice *)obj;
-+ char *path;
-+
-+ for (int i = 0; i < MAX34451_NUM_TEMP_DEVICES; i++) {
-+ path = g_strdup_printf("temperature[%d]", i);
-+ i2c_set8(i2cdev, PMBUS_PAGE, i + 16);
-+ max34451_i2c_set16(i2cdev, PMBUS_OT_FAULT_LIMIT, 6000);
-+ qmp_max34451_set(TEST_ID, path, 6100);
-+ g_free(path);
-+
-+ i2c_value = max34451_i2c_get16(i2cdev, PMBUS_STATUS_WORD);
-+ i2c_byte = i2c_get8(i2cdev, PMBUS_STATUS_TEMPERATURE);
-+ g_assert_true((i2c_value & PB_STATUS_TEMPERATURE) != 0);
-+ g_assert_true((i2c_byte & PB_STATUS_OT_FAULT) != 0);
-+ }
-+}
-+
-+static void max34451_register_nodes(void)
-+{
-+ QOSGraphEdgeOptions opts = {
-+ .extra_device_opts = "id=" TEST_ID ",address=0x4e"
-+ };
-+ add_qi2c_address(&opts, &(QI2CAddress) { TEST_ADDR });
-+
-+ qos_node_create_driver("max34451", i2c_device_create);
-+ qos_node_consumes("max34451", "i2c-bus", &opts);
-+
-+ qos_add_test("test_defaults", "max34451", test_defaults, NULL);
-+ qos_add_test("test_temperature", "max34451", test_temperature, NULL);
-+ qos_add_test("test_voltage", "max34451", test_voltage, NULL);
-+ qos_add_test("test_rw_regs", "max34451", test_rw_regs, NULL);
-+ qos_add_test("test_ro_regs", "max34451", test_ro_regs, NULL);
-+ qos_add_test("test_ov_faults", "max34451", test_ov_faults, NULL);
-+ qos_add_test("test_ot_faults", "max34451", test_ot_faults, NULL);
-+}
-+libqos_init(max34451_register_nodes);
-
## hw/arm/Kconfig ##
@@ hw/arm/Kconfig: config NPCM7XX
select ADM1272
@@ hw/misc/meson.build: softmmu_ss.add(when: 'CONFIG_TMP105', if_true:
files('tmp10
softmmu_ss.add(when: 'CONFIG_UNIMP', if_true: files('unimp.c'))
softmmu_ss.add(when: 'CONFIG_EMPTY_SLOT', if_true: files('empty_slot.c'))
softmmu_ss.add(when: 'CONFIG_LED', if_true: files('led.c'))
-
- ## tests/qtest/meson.build ##
-@@ tests/qtest/meson.build: qos_test_ss.add(
- 'eepro100-test.c',
- 'es1370-test.c',
- 'ipoctal232-test.c',
-+ 'max34451-test.c',
- 'megasas-test.c',
- 'ne2000-test.c',
- 'tulip-test.c',
-: ---------- > 5: d782fc570f tests/qtest: add tests for MAX34451 device model
--
2.31.1.527.g47e6f16901-goog
- [PATCH v2 0/5] Add support for PMBus in QEMU,
Titus Rwantare <=
- [PATCH v2 1/5] hw/i2c: add support for PMBus, Titus Rwantare, 2021/05/05
- [PATCH v2 3/5] tests/qtest: add tests for ADM1272 device model, Titus Rwantare, 2021/05/05
- [PATCH v2 2/5] hw/misc: add ADM1272 device, Titus Rwantare, 2021/05/05
- [PATCH v2 4/5] hw/misc: add MAX34451 device, Titus Rwantare, 2021/05/05
- [PATCH v2 5/5] tests/qtest: add tests for MAX34451 device model, Titus Rwantare, 2021/05/05