From: Titus Rwantare <titusr@google.com>
To: peter.maydell@linaro.org
Cc: qemu-arm@nongnu.org, qemu-devel@nongnu.org, kfting@nuvoton.com,
imaginos32@gmail.com, wuhaotsh@google.com, philmd@mailo.com,
fanjason@google.com, Titus Rwantare <titusr@google.com>
Subject: [PATCH 5/8] hw/sensor: switch adm1266 to millivolts vout
Date: Wed, 29 Jul 2026 23:13:20 +0000 [thread overview]
Message-ID: <20260729231325.3808993-6-titusr@google.com> (raw)
In-Reply-To: <20260729231325.3808993-1-titusr@google.com>
Enables storing fractional voltages for the ADM1266 over QMP
Signed-off-by: Titus Rwantare <titusr@google.com>
---
hw/sensor/adm1266.c | 27 ++++---
tests/qtest/adm1266-test.c | 150 +++++++++++++++++++++++++++++++++++++
2 files changed, 167 insertions(+), 10 deletions(-)
diff --git a/hw/sensor/adm1266.c b/hw/sensor/adm1266.c
index 2979557309..80960dc1c4 100644
--- a/hw/sensor/adm1266.c
+++ b/hw/sensor/adm1266.c
@@ -263,32 +263,39 @@ static int adm1266_write_data(PMBusDevice *pmdev, const uint8_t *buf,
static void adm1266_get(Object *obj, Visitor *v, const char *name, void *opaque,
Error **errp)
{
- uint16_t value;
+ uint32_t value, index;
PMBusDevice *pmdev = PMBUS_DEVICE(obj);
PMBusVoutMode *mode = (PMBusVoutMode *)&pmdev->pages[0].vout_mode;
- if (strcmp(name, "vout") == 0) {
- value = pmbus_linear_mode2data(*(uint16_t *)opaque, mode->exp);
+ if (strncmp(name, "vout[", 5) == 0) {
+ sscanf(name, "vout[%u]", &index);
+ mode = (PMBusVoutMode *)&pmdev->pages[index].vout_mode;
+ value = pmbus_linear_mode2milliunits(*(uint16_t *)opaque, mode->exp);
} else {
value = *(uint16_t *)opaque;
}
- visit_type_uint16(v, name, &value, errp);
+ visit_type_uint32(v, name, &value, errp);
}
static void adm1266_set(Object *obj, Visitor *v, const char *name, void *opaque,
Error **errp)
{
uint16_t *internal = opaque;
- uint16_t value;
+ uint32_t value, index;
PMBusDevice *pmdev = PMBUS_DEVICE(obj);
- PMBusVoutMode *mode = (PMBusVoutMode *)&pmdev->pages[0].vout_mode;
+ PMBusVoutMode *mode;
- if (!visit_type_uint16(v, name, &value, errp)) {
+ if (!visit_type_uint32(v, name, &value, errp)) {
return;
}
-
- *internal = pmbus_data2linear_mode(value, mode->exp);
+ if (strncmp(name, "vout[", 5) == 0) {
+ sscanf(name, "vout[%u]", &index);
+ mode = (PMBusVoutMode *)&pmdev->pages[index].vout_mode;
+ *internal = pmbus_milliunits2linear_mode(value, mode->exp);
+ } else {
+ *internal = value;
+ }
pmbus_check_limits(pmdev);
}
@@ -311,7 +318,7 @@ static void adm1266_init(Object *obj)
for (int i = 0; i < ADM1266_NUM_PAGES; i++) {
pmbus_page_config(pmdev, i, flags);
- object_property_add(obj, "vout[*]", "uint16",
+ object_property_add(obj, "vout[*]", "uint32",
adm1266_get,
adm1266_set, NULL, &pmdev->pages[i].read_vout);
}
diff --git a/tests/qtest/adm1266-test.c b/tests/qtest/adm1266-test.c
index fa8bbc5795..fd3d8079b6 100644
--- a/tests/qtest/adm1266-test.c
+++ b/tests/qtest/adm1266-test.c
@@ -16,6 +16,7 @@
#include "qobject/qdict.h"
#include "qobject/qnum.h"
#include "qemu/bitops.h"
+#include "qemu/bswap.h"
#define TEST_ID "adm1266-test"
#define TEST_ADDR (0x12)
@@ -45,6 +46,57 @@
#define TEST_STRING_B "b sample"
#define TEST_STRING_C "rev c"
+#define ADM1266_NUM_PAGES 17
+#define ADM1266_MAX_VALUE 65535000
+
+typedef union {
+ uint8_t raw;
+ PMBusVoutMode mode;
+} ADM1266VoutMode;
+
+static uint32_t qmp_adm1266_get(const char *id, const char *property)
+{
+ QDict *response;
+ uint32_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_adm1266_set(const char *id,
+ const char *property,
+ uint32_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"));
+}
+
+static uint64_t adm1266_linear_mode2milliunits(uint16_t value, int exp)
+{
+ /* D = L * 2^e */
+ uint64_t val = value;
+ uint64_t ret;
+
+ if (exp < 0) {
+ ret = DIV_ROUND_CLOSEST((val * 1000), 1ULL << (-exp));
+ } else {
+ ret = (val << exp) * 1000;
+ }
+
+ if (ret > UINT32_MAX) {
+ return UINT32_MAX;
+ }
+
+ return ret;
+}
+
static void compare_string(QI2CDevice *i2cdev, uint8_t reg,
const char *test_str)
{
@@ -67,6 +119,98 @@ static void write_and_compare_string(QI2CDevice *i2cdev, uint8_t reg,
compare_string(i2cdev, reg, test_str);
}
+static void test_vout_milliunits(void *obj, void *data, QGuestAllocator *alloc)
+{
+ uint16_t i2c_value, value;
+ uint64_t i2c_milliunits;
+ QI2CDevice *i2cdev = (QI2CDevice *)obj;
+ char *path;
+ ADM1266VoutMode m;
+
+ /* set a different value in millivolts for each page */
+ for (int i = 0; i < ADM1266_NUM_PAGES; i++) {
+ path = g_strdup_printf("vout[%d]", i);
+ qmp_adm1266_set(TEST_ID, path, (1000 * (i + 1)));
+ }
+
+ for (int i = 0; i < ADM1266_NUM_PAGES; i++) {
+ i2c_set8(i2cdev, PMBUS_PAGE, i);
+
+ m.raw = i2c_get8(i2cdev, PMBUS_VOUT_MODE);
+ i2c_value = bswap16(i2c_get16(i2cdev, PMBUS_READ_VOUT));
+ i2c_milliunits = adm1266_linear_mode2milliunits(i2c_value, m.mode.exp);
+ g_assert_cmpuint(i2c_milliunits, ==, (1000 * (i + 1)));
+
+ path = g_strdup_printf("vout[%d]", i);
+ value = qmp_adm1266_get(TEST_ID, path);
+ g_assert_cmpuint(value, ==, (1000 * (i + 1)));
+ }
+}
+
+/*
+ * Note that the exponent determines the dynamic range, large exponents can not
+ * be used with values that need to be incremented in small steps
+ */
+static void test_vout_mode_exponent(void *obj, void *data,
+ QGuestAllocator *alloc)
+{
+ uint16_t i2c_value, value, expected;
+ uint64_t i2c_milliunits;
+ QI2CDevice *i2cdev = (QI2CDevice *)obj;
+ ADM1266VoutMode m;
+ char *path;
+
+ /* set a different exponent per page and a different value */
+ for (int i = 0; i < ADM1266_NUM_PAGES; i++) {
+ i2c_set8(i2cdev, PMBUS_PAGE, i);
+ expected = 1000 * (i * 2);
+ m.mode.exp = i - 14;
+ i2c_set8(i2cdev, PMBUS_VOUT_MODE, m.raw);
+ path = g_strdup_printf("vout[%d]", i);
+ qmp_adm1266_set(TEST_ID, path, expected);
+ }
+
+ for (int i = 0; i < ADM1266_NUM_PAGES; i++) {
+ i2c_set8(i2cdev, PMBUS_PAGE, i);
+ expected = 1000 * (i * 2);
+ /* check correct value from i2c*/
+ m.raw = i2c_get8(i2cdev, PMBUS_VOUT_MODE);
+ i2c_value = bswap16(i2c_get16(i2cdev, PMBUS_READ_VOUT));
+ i2c_milliunits = adm1266_linear_mode2milliunits(i2c_value, m.mode.exp);
+ g_assert_cmpuint(i2c_milliunits, ==, expected);
+
+ /* check correct value from qmp*/
+ path = g_strdup_printf("vout[%d]", i);
+ value = qmp_adm1266_get(TEST_ID, path);
+ g_assert_cmpuint(value, ==, expected);
+ }
+}
+
+static void test_vout_clamp_to_max(void *obj, void *data,
+ QGuestAllocator *alloc)
+{
+ uint16_t i2c_value;
+ uint32_t value;
+ QI2CDevice *i2cdev = (QI2CDevice *)obj;
+ char *path;
+
+ for (int i = 0; i < ADM1266_NUM_PAGES; i++) {
+ path = g_strdup_printf("vout[%d]", i);
+ qmp_adm1266_set(TEST_ID, path, 90000000);
+ }
+
+ for (int i = 0; i < ADM1266_NUM_PAGES; i++) {
+ i2c_set8(i2cdev, PMBUS_PAGE, i);
+
+ i2c_value = bswap16(i2c_get16(i2cdev, PMBUS_READ_VOUT));
+ g_assert_cmpuint(i2c_value, ==, UINT16_MAX);
+
+ path = g_strdup_printf("vout[%d]", i);
+ value = qmp_adm1266_get(TEST_ID, path);
+ g_assert_cmpuint(value, ==, ADM1266_MAX_VALUE);
+ }
+}
+
static void test_defaults(void *obj, void *data, QGuestAllocator *alloc)
{
uint16_t i2c_value;
@@ -128,6 +272,12 @@ static void adm1266_register_nodes(void)
qos_add_test("test_defaults", "adm1266", test_defaults, NULL);
qos_add_test("test_partial_reads", "adm1266", test_partial_reads, NULL);
qos_add_test("test_rw_regs", "adm1266", test_rw_regs, NULL);
+ qos_add_test("test_vout_milliunits", "adm1266",
+ test_vout_milliunits, NULL);
+ qos_add_test("test_vout_mode_exponent", "adm1266",
+ test_vout_mode_exponent, NULL);
+ qos_add_test("test_vout_clamp_to_max", "adm1266",
+ test_vout_clamp_to_max, NULL);
}
libqos_init(adm1266_register_nodes);
--
2.55.0.508.g3f0d502094-goog
next prev parent reply other threads:[~2026-07-29 23:15 UTC|newest]
Thread overview: 9+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-07-29 23:13 [PATCH 0/8] hw/i2c: PMBus updates and adm1266 fixes Titus Rwantare
2026-07-29 23:13 ` [PATCH 1/8] osdep: add DIV_ROUND_CLOSEST Titus Rwantare
2026-07-29 23:13 ` [PATCH 2/8] hw/i2c: pmbus: add milliunits linear mode functions Titus Rwantare
2026-07-29 23:13 ` [PATCH 3/8] hw/i2c: smbus: increase MAX_DATA_LEN Titus Rwantare
2026-07-29 23:13 ` [PATCH 4/8] hw/sensor: update adm1266 block transfers Titus Rwantare
2026-07-29 23:13 ` Titus Rwantare [this message]
2026-07-29 23:13 ` [PATCH 6/8] hw/i2c: fix VOUT_MODE representation on little-endian machines Titus Rwantare
2026-07-29 23:13 ` [PATCH 7/8] hw/sensor: adm1266: expose vout_mode over QMP Titus Rwantare
2026-07-29 23:13 ` [PATCH 8/8] hw/sensor: adm1266: set default VOUT mode Titus Rwantare
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20260729231325.3808993-6-titusr@google.com \
--to=titusr@google.com \
--cc=fanjason@google.com \
--cc=imaginos32@gmail.com \
--cc=kfting@nuvoton.com \
--cc=peter.maydell@linaro.org \
--cc=philmd@mailo.com \
--cc=qemu-arm@nongnu.org \
--cc=qemu-devel@nongnu.org \
--cc=wuhaotsh@google.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.