Introduce the INA238 qtest: the qgraph node, the QMP injection helpers and the register map. Cover the reset defaults, the software reset, and the Configuration register write mask.
Signed-off-by: Emmanuel Blot <[email protected]> --- tests/qtest/ina238-test.c | 177 ++++++++++++++++++++++++++++++++++++++++++++++ tests/qtest/meson.build | 1 + 2 files changed, 178 insertions(+) diff --git a/tests/qtest/ina238-test.c b/tests/qtest/ina238-test.c new file mode 100644 index 0000000000..6e59ed30b5 --- /dev/null +++ b/tests/qtest/ina238-test.c @@ -0,0 +1,177 @@ +/* + * QTest testcase for the INA238 current/voltage/power monitor + * + * Copyright (c) 2026 Meta Platforms, Inc. and affiliates. + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "qemu/osdep.h" +#include "libqos/i2c.h" +#include "libqos/qgraph.h" +#include "libqtest-single.h" +#include "qobject/qdict.h" + +#define INA238_TEST_ID "ina238-test" +#define INA238_TEST_ADDR 0x40 + +/* Register pointer addresses */ +#define REG_CONFIG 0x00 +#define REG_ADC_CONFIG 0x01 +#define REG_SHUNT_CAL 0x02 +#define REG_VSHUNT 0x04 +#define REG_VBUS 0x05 +#define REG_DIETEMP 0x06 +#define REG_CURRENT 0x07 +#define REG_POWER 0x08 +#define REG_DIAG_ALRT 0x0B +#define REG_SOVL 0x0C +#define REG_SUVL 0x0D +#define REG_BOVL 0x0E +#define REG_BUVL 0x0F +#define REG_TEMP_LIMIT 0x10 +#define REG_PWR_LIMIT 0x11 +#define REG_MANUFACTURER_ID 0x3E +#define REG_DEVICE_ID 0x3F + +/* Configuration register bits */ +#define CONFIG_RST 0x8000 +#define CONFIG_ADCRANGE 0x0010 + +/* DIAG_ALRT register bits */ +#define DIAG_MEMSTAT 0x0001 +#define DIAG_CNVRF 0x0002 +#define DIAG_POL 0x0004 +#define DIAG_BUSUL 0x0008 +#define DIAG_BUSOL 0x0010 +#define DIAG_SHNTUL 0x0020 +#define DIAG_SHNTOL 0x0040 +#define DIAG_TMPOL 0x0080 +#define DIAG_MATHOF 0x0200 +#define DIAG_APOL 0x1000 +#define DIAG_SLOWALERT 0x2000 +#define DIAG_CNVR 0x4000 +#define DIAG_ALATCH 0x8000 + +/* Reset values */ +#define CONFIG_POR 0x0000 +#define ADC_CONFIG_POR 0xFB68 +#define SHUNT_CAL_POR 0x1000 +#define DIAG_ALRT_POR 0x0001 +#define SOVL_POR 0x7FFF +#define SUVL_POR 0x8000 +#define BOVL_POR 0x7FFF +#define BUVL_POR 0x0000 +#define TEMP_LIMIT_POR 0x7FF0 +#define PWR_LIMIT_POR 0xFFFF +#define MANUFACTURER_ID_VAL 0x5449 +#define DEVICE_ID_VAL 0x2381 + +/* Read the 24-bit POWER register (MSB first) */ +static uint32_t i2c_get24(QI2CDevice *dev, uint8_t reg) +{ + uint8_t resp[3]; + + i2c_read_block(dev, reg, resp, sizeof(resp)); + return (resp[0] << 16) | (resp[1] << 8) | resp[2]; +} + +/* QMP helpers for injecting the physical inputs */ + +static void qmp_ina238_set(const char *property, int value) +{ + QDict *resp; + + resp = qmp("{ 'execute': 'qom-set', 'arguments':" + " { 'path': %s, 'property': %s, 'value': %d } }", + INA238_TEST_ID, property, value); + g_assert(qdict_haskey(resp, "return")); + qobject_unref(resp); +} + +/* Power-on-reset default values and the ID registers */ +static void test_defaults(void *obj, void *data, QGuestAllocator *alloc) +{ + QI2CDevice *dev = (QI2CDevice *)obj; + + g_assert_cmphex(i2c_get16(dev, REG_CONFIG), ==, CONFIG_POR); + g_assert_cmphex(i2c_get16(dev, REG_ADC_CONFIG), ==, ADC_CONFIG_POR); + g_assert_cmphex(i2c_get16(dev, REG_SHUNT_CAL), ==, SHUNT_CAL_POR); + g_assert_cmphex(i2c_get16(dev, REG_VSHUNT), ==, 0x0000); + g_assert_cmphex(i2c_get16(dev, REG_VBUS), ==, 0x0000); + g_assert_cmphex(i2c_get16(dev, REG_DIETEMP), ==, 0x0000); + g_assert_cmphex(i2c_get16(dev, REG_CURRENT), ==, 0x0000); + g_assert_cmphex(i2c_get24(dev, REG_POWER), ==, 0x000000); + g_assert_cmphex(i2c_get16(dev, REG_DIAG_ALRT), ==, DIAG_ALRT_POR); + g_assert_cmphex(i2c_get16(dev, REG_SOVL), ==, SOVL_POR); + g_assert_cmphex(i2c_get16(dev, REG_SUVL), ==, SUVL_POR); + g_assert_cmphex(i2c_get16(dev, REG_BOVL), ==, BOVL_POR); + g_assert_cmphex(i2c_get16(dev, REG_BUVL), ==, BUVL_POR); + g_assert_cmphex(i2c_get16(dev, REG_TEMP_LIMIT), ==, TEMP_LIMIT_POR); + g_assert_cmphex(i2c_get16(dev, REG_PWR_LIMIT), ==, PWR_LIMIT_POR); + g_assert_cmphex(i2c_get16(dev, REG_MANUFACTURER_ID), ==, + MANUFACTURER_ID_VAL); + g_assert_cmphex(i2c_get16(dev, REG_DEVICE_ID), ==, DEVICE_ID_VAL); +} + +/* Software reset via the Configuration RST bit */ +static void test_soft_reset(void *obj, void *data, QGuestAllocator *alloc) +{ + QI2CDevice *dev = (QI2CDevice *)obj; + + i2c_set16(dev, REG_SHUNT_CAL, 0x1234); + i2c_set16(dev, REG_SOVL, 0x0500); + i2c_set16(dev, REG_TEMP_LIMIT, 0x0A00); + i2c_set16(dev, REG_DIAG_ALRT, DIAG_ALATCH); + + qmp_ina238_set("shunt-voltage", 97200000); + qmp_ina238_set("bus-voltage", 48000000); + qmp_ina238_set("die-temperature", 25000); + + g_assert_cmphex(i2c_get16(dev, REG_SHUNT_CAL), ==, 0x1234); + g_assert_cmphex(i2c_get16(dev, REG_SOVL), ==, 0x0500); + g_assert_cmphex(i2c_get16(dev, REG_VSHUNT), ==, 0x4BF0); + g_assert_cmpuint(i2c_get16(dev, REG_CURRENT), !=, 0); + + i2c_set16(dev, REG_CONFIG, CONFIG_RST); + + g_assert_cmphex(i2c_get16(dev, REG_CONFIG), ==, CONFIG_POR); + g_assert_cmphex(i2c_get16(dev, REG_SHUNT_CAL), ==, SHUNT_CAL_POR); + g_assert_cmphex(i2c_get16(dev, REG_SOVL), ==, SOVL_POR); + g_assert_cmphex(i2c_get16(dev, REG_TEMP_LIMIT), ==, TEMP_LIMIT_POR); + g_assert_cmphex(i2c_get16(dev, REG_DIAG_ALRT), ==, DIAG_ALRT_POR); + + g_assert_cmphex(i2c_get16(dev, REG_VSHUNT), ==, 0x0000); + g_assert_cmphex(i2c_get16(dev, REG_VBUS), ==, 0x0000); + g_assert_cmphex(i2c_get16(dev, REG_DIETEMP), ==, 0x0000); + g_assert_cmphex(i2c_get16(dev, REG_CURRENT), ==, 0x0000); + g_assert_cmpuint(i2c_get24(dev, REG_POWER), ==, 0); +} + +/* CONFIG keeps only the writable ADCRANGE/CONVDLY bits and self-clears RST */ +static void test_config_wmask(void *obj, void *data, QGuestAllocator *alloc) +{ + QI2CDevice *dev = (QI2CDevice *)obj; + + i2c_set16(dev, REG_CONFIG, CONFIG_ADCRANGE | 0x0040); + g_assert_cmphex(i2c_get16(dev, REG_CONFIG), ==, CONFIG_ADCRANGE | 0x0040); + + i2c_set16(dev, REG_CONFIG, CONFIG_RST); + g_assert_cmphex(i2c_get16(dev, REG_CONFIG), ==, CONFIG_POR); +} + +static void ina238_register_nodes(void) +{ + QOSGraphEdgeOptions opts = { + .extra_device_opts = "id=" INA238_TEST_ID ",address=0x40" + }; + add_qi2c_address(&opts, &(QI2CAddress) { INA238_TEST_ADDR }); + + qos_node_create_driver("ina238", i2c_device_create); + qos_node_consumes("ina238", "i2c-bus", &opts); + + qos_add_test("defaults", "ina238", test_defaults, NULL); + qos_add_test("soft-reset", "ina238", test_soft_reset, NULL); + qos_add_test("config-wmask", "ina238", test_config_wmask, NULL); +} +libqos_init(ina238_register_nodes); diff --git a/tests/qtest/meson.build b/tests/qtest/meson.build index 97480d92aa..e941d78194 100644 --- a/tests/qtest/meson.build +++ b/tests/qtest/meson.build @@ -325,6 +325,7 @@ qos_test_ss.add( 'eepro100-test.c', 'es1370-test.c', 'ina230-test.c', + 'ina238-test.c', 'lsm303dlhc-mag-test.c', 'isl_pmbus_vr-test.c', 'max34451-test.c', -- 2.50.1
