From: Mikail Sadic <mikail.sadic@ibm.com>
To: clg@kaod.org, peter.maydell@linaro.org
Cc: Mikail Sadic <mikail.sadic@ibm.com>,
pbonzini@redhat.com, ninad@linux.ibm.com, titusr@google.com,
jeuk20.kim@samsung.com, philmd@mailo.com,
steven_lee@aspeedtech.com, leetroy@gmail.com,
jamin_lin@aspeedtech.com, kane_chen@aspeedtech.com,
andrew@codeconstruct.com.au, joel@jms.id.au,
calebs@linux.ibm.com, milesg@linux.ibm.com, qemu-arm@nongnu.org,
qemu-devel@nongnu.org
Subject: [PATCH v3 5/8] hw/sensor: Add UCD90320 model
Date: Mon, 10 Aug 2026 13:57:43 -0500 [thread overview]
Message-ID: <20260810185748.1253-6-mikail.sadic@ibm.com> (raw)
In-Reply-To: <20260810185748.1253-1-mikail.sadic@ibm.com>
Add a PMBus device model for the TI-UCD90320 24-rail power
sequencer. Configures 24 pages with linear vout mode and responds to
the vendor-specific UCD9000_DEVICE_ID, NUM_PAGES, MONITOR_CONFIG, and
MFR_STATUS commands, allowing the Linux ucd9000 driver to bind and
create hwmon sysfs entries.
Signed-off-by: Mikail Sadic <mikail.sadic@ibm.com>
---
MAINTAINERS | 1 +
docs/specs/index.rst | 1 +
docs/specs/ucd90320.rst | 36 +++++++++
hw/sensor/ucd90320.c | 169 ++++++++++++++++++++++++++++++++++++++++
hw/arm/Kconfig | 1 +
hw/sensor/Kconfig | 4 +
hw/sensor/meson.build | 1 +
7 files changed, 213 insertions(+)
create mode 100644 docs/specs/ucd90320.rst
create mode 100644 hw/sensor/ucd90320.c
diff --git a/MAINTAINERS b/MAINTAINERS
index 902db77218..ec415cb8e4 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -4063,6 +4063,7 @@ F: hw/i2c/pmbus_device.c
F: hw/sensor/adm1272.c
F: hw/sensor/isl_pmbus_vr.c
F: hw/sensor/max34451.c
+F: hw/sensor/ucd90320.c
F: include/hw/i2c/pmbus_device.h
F: include/hw/sensor/isl_pmbus_vr.h
F: tests/qtest/adm1272-test.c
diff --git a/docs/specs/index.rst b/docs/specs/index.rst
index b7909a108a..4de65e2fdf 100644
--- a/docs/specs/index.rst
+++ b/docs/specs/index.rst
@@ -39,4 +39,5 @@ guest hardware that is specific to QEMU.
riscv-iommu
riscv-aia
aspeed-intc
+ ucd90320
iommu-testdev
diff --git a/docs/specs/ucd90320.rst b/docs/specs/ucd90320.rst
new file mode 100644
index 0000000000..fdc0878c75
--- /dev/null
+++ b/docs/specs/ucd90320.rst
@@ -0,0 +1,36 @@
+Texas Instruments UCD90320 Power Sequencer
+==========================================
+
+The UCD90320 is a 24-rail PMBus power sequencer. QEMU models it as a PMBus
+device (``"ucd90320"``) so the Linux ``ucd9000`` driver can bind and create
+``hwmon`` sysfs entries.
+
+The model configures 24 PMBus pages (one per rail) with ``VOUT_MODE = 0x00``
+(linear, exponent 0). ``READ_VOUT`` and ``MFR_STATUS`` return zero on all
+pages.
+
+Vendor-specific commands handled:
+
++------+---------------------------+--------------------------------------------+
+| Code | Name | Response |
++======+===========================+============================================+
+| 0xD5 | ``UCD9000_MONITOR_CONFIG``| Block: one byte ``0x00`` |
++------+---------------------------+--------------------------------------------+
+| 0xD6 | ``UCD9000_NUM_PAGES`` | Byte: ``24`` |
++------+---------------------------+--------------------------------------------+
+| 0xF3 | ``UCD9000_MFR_STATUS`` | Block: four bytes ``0x00 0x00 0x00 0x00`` |
++------+---------------------------+--------------------------------------------+
+| 0xFD | ``UCD9000_DEVICE_ID`` | Block: ASCII string ``"UCD90320"`` |
++------+---------------------------+--------------------------------------------+
+
+``DEVICE_ID`` uses the vendor-specific code ``0xFD`` rather than the standard
+``PMBUS_IC_DEVICE_ID`` (``0xAD``), matching the Linux ``ucd9000`` probe
+sequence.
+
+The UCD90320 is instantiated automatically in the ``huygens-bmc`` machine on
+I2C bus 5 at address ``0x11``. To instantiate on a different Aspeed machine:
+
+.. code-block:: c
+
+ i2c_slave_create_simple(aspeed_i2c_get_bus(&soc->i2c, 5),
+ "ucd90320", 0x11);
diff --git a/hw/sensor/ucd90320.c b/hw/sensor/ucd90320.c
new file mode 100644
index 0000000000..6d4f828448
--- /dev/null
+++ b/hw/sensor/ucd90320.c
@@ -0,0 +1,169 @@
+/*
+ * Texas Instruments UCD90320 24-Rail PMBus Power Sequencer
+ *
+ * Copyright 2026 IBM Corp.
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#include "qemu/osdep.h"
+#include "hw/i2c/pmbus_device.h"
+#include "migration/vmstate.h"
+#include "qemu/log.h"
+#include "qemu/module.h"
+
+#define TYPE_UCD90320 "ucd90320"
+
+/* UCD90320 has 24 sequenced power-rail pages */
+#define UCD90320_NUM_PAGES 24
+
+/* Vendor-specific command codes (not in the standard PMBus register table) */
+#define UCD9000_MONITOR_CONFIG 0xd5
+#define UCD9000_NUM_PAGES 0xd6
+#define UCD9000_MFR_STATUS 0xf3
+#define UCD9000_DEVICE_ID 0xfd
+
+#define UCD90320_DEVICE_ID_LEN 8
+#define UCD90320_MFR_STATUS_LEN 4
+
+typedef struct UCD90320State {
+ PMBusDevice parent;
+
+ /* Reset values for the vendor-specific block-read commands. */
+ uint8_t device_id[UCD90320_DEVICE_ID_LEN];
+ uint8_t monitor_config;
+ uint8_t mfr_status[UCD90320_MFR_STATUS_LEN];
+} UCD90320State;
+
+#define UCD90320(obj) OBJECT_CHECK(UCD90320State, (obj), TYPE_UCD90320)
+
+static void ucd90320_send_block(PMBusDevice *pmdev,
+ const uint8_t *data, uint8_t len)
+{
+ int i;
+
+ pmdev->out_buf[len + pmdev->out_buf_len] = len;
+ for (i = len - 1; i >= 0; i--) {
+ pmdev->out_buf[i + pmdev->out_buf_len] = data[len - 1 - i];
+ }
+ pmdev->out_buf_len += len + 1;
+}
+
+static uint8_t ucd90320_read_byte(PMBusDevice *pmdev)
+{
+ UCD90320State *s = UCD90320(pmdev);
+
+ switch (pmdev->code) {
+ case UCD9000_DEVICE_ID:
+ ucd90320_send_block(pmdev, s->device_id, sizeof(s->device_id));
+ pmbus_idle(pmdev);
+ return 0;
+ case UCD9000_NUM_PAGES:
+ pmbus_send8(pmdev, UCD90320_NUM_PAGES);
+ pmbus_idle(pmdev);
+ return 0;
+
+ case UCD9000_MONITOR_CONFIG:
+ ucd90320_send_block(pmdev, &s->monitor_config,
+ sizeof(s->monitor_config));
+ pmbus_idle(pmdev);
+ return 0;
+ case UCD9000_MFR_STATUS:
+ ucd90320_send_block(pmdev, s->mfr_status, sizeof(s->mfr_status));
+ pmbus_idle(pmdev);
+ return 0;
+ default:
+ qemu_log_mask(LOG_GUEST_ERROR,
+ "%s: reading from unsupported register: 0x%02x\n",
+ __func__, pmdev->code);
+ break;
+ }
+ return 0xFF;
+}
+
+static int ucd90320_write_data(PMBusDevice *pmdev, const uint8_t *buf,
+ uint8_t len)
+{
+ if (len == 0) {
+ qemu_log_mask(LOG_GUEST_ERROR, "%s: writing empty data\n", __func__);
+ return -1;
+ }
+
+ pmdev->code = buf[0];
+
+ if (len == 1) {
+ return 0;
+ }
+
+ return 0;
+}
+
+static void ucd90320_exit_reset(Object *obj, ResetType type)
+{
+ PMBusDevice *pmdev = PMBUS_DEVICE(obj);
+ UCD90320State *s = UCD90320(obj);
+
+ pmdev->capability = 0x20; /* PEC supported */
+
+ for (int i = 0; i < UCD90320_NUM_PAGES; i++) {
+ pmdev->pages[i].operation = 0x80; /* on */
+ pmdev->pages[i].on_off_config = 0x1a;
+ pmdev->pages[i].vout_mode = 0x00; /* linear mode, exponent=0 */
+ pmdev->pages[i].read_vout = 0; /* rails off, pgood=0 */
+ }
+
+ memcpy(s->device_id, "UCD90320", sizeof(s->device_id));
+ s->monitor_config = 0x00;
+ memset(s->mfr_status, 0x00, sizeof(s->mfr_status));
+}
+
+static void ucd90320_init(Object *obj)
+{
+ PMBusDevice *pmdev = PMBUS_DEVICE(obj);
+ uint64_t flags = PB_HAS_VOUT | PB_HAS_VOUT_MODE |
+ PB_HAS_STATUS_MFR_SPECIFIC;
+
+ for (int i = 0; i < UCD90320_NUM_PAGES; i++) {
+ pmbus_page_config(pmdev, i, flags);
+ }
+}
+
+static const VMStateDescription vmstate_ucd90320 = {
+ .name = TYPE_UCD90320,
+ .version_id = 0,
+ .minimum_version_id = 0,
+ .fields = (const VMStateField[]) {
+ VMSTATE_PMBUS_DEVICE(parent, UCD90320State),
+ VMSTATE_UINT8_ARRAY(device_id, UCD90320State, UCD90320_DEVICE_ID_LEN),
+ VMSTATE_UINT8(monitor_config, UCD90320State),
+ VMSTATE_UINT8_ARRAY(mfr_status, UCD90320State,
+ UCD90320_MFR_STATUS_LEN),
+ VMSTATE_END_OF_LIST()
+ }
+};
+
+static void ucd90320_class_init(ObjectClass *klass, const void *data)
+{
+ ResettableClass *rc = RESETTABLE_CLASS(klass);
+ DeviceClass *dc = DEVICE_CLASS(klass);
+ PMBusDeviceClass *k = PMBUS_DEVICE_CLASS(klass);
+
+ dc->desc = "Texas Instruments UCD90320 24-Rail Power Sequencer";
+ dc->vmsd = &vmstate_ucd90320;
+ k->write_data = ucd90320_write_data;
+ k->receive_byte = ucd90320_read_byte;
+ k->device_num_pages = UCD90320_NUM_PAGES;
+ rc->phases.exit = ucd90320_exit_reset;
+}
+
+static const TypeInfo ucd90320_types[] = {
+ {
+ .name = TYPE_UCD90320,
+ .parent = TYPE_PMBUS_DEVICE,
+ .instance_size = sizeof(UCD90320State),
+ .instance_init = ucd90320_init,
+ .class_init = ucd90320_class_init,
+ },
+};
+
+DEFINE_TYPES(ucd90320_types)
diff --git a/hw/arm/Kconfig b/hw/arm/Kconfig
index 82e0bc2e70..ac44740547 100644
--- a/hw/arm/Kconfig
+++ b/hw/arm/Kconfig
@@ -555,6 +555,7 @@ config ASPEED_SOC
select LED
select PMBUS
select MAX31785
+ select UCD90320
select FSI_APB2OPB_ASPEED
select AT24C
select PCI_EXPRESS
diff --git a/hw/sensor/Kconfig b/hw/sensor/Kconfig
index bc6331b4ab..135ffa7d5d 100644
--- a/hw/sensor/Kconfig
+++ b/hw/sensor/Kconfig
@@ -43,3 +43,7 @@ config ISL_PMBUS_VR
config MAX31785
bool
depends on PMBUS
+
+config UCD90320
+ bool
+ depends on PMBUS
diff --git a/hw/sensor/meson.build b/hw/sensor/meson.build
index 420fdc3359..c0583161d3 100644
--- a/hw/sensor/meson.build
+++ b/hw/sensor/meson.build
@@ -8,3 +8,4 @@ system_ss.add(when: 'CONFIG_MAX34451', if_true: files('max34451.c'))
system_ss.add(when: 'CONFIG_LSM303DLHC_MAG', if_true: files('lsm303dlhc_mag.c'))
system_ss.add(when: 'CONFIG_ISL_PMBUS_VR', if_true: files('isl_pmbus_vr.c'))
system_ss.add(when: 'CONFIG_MAX31785', if_true: files('max31785.c'))
+system_ss.add(when: 'CONFIG_UCD90320', if_true: files('ucd90320.c'))
--
2.53.0
next prev parent reply other threads:[~2026-08-10 18:59 UTC|newest]
Thread overview: 14+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-08-10 18:57 [PATCH v3 0/8] Add IBM Huygens BMC machine for AST2700 Mikail Sadic
2026-08-10 18:57 ` [PATCH v3 1/8] fsi/cfam: Add common CFAM base class Mikail Sadic
2026-08-11 13:36 ` Miles Glenn
2026-08-10 18:57 ` [PATCH v3 2/8] fsi/cfam: Add CFAM-S model Mikail Sadic
2026-08-10 18:57 ` [PATCH v3 3/8] arm/aspeed: Wire AST2700 FSI controllers to APB-to-OPB bridges Mikail Sadic
2026-08-11 4:32 ` Cédric Le Goater
2026-08-10 18:57 ` [PATCH v3 4/8] i2c/aspeed: Fix DMA receive first-byte handling for block reads Mikail Sadic
2026-08-11 4:30 ` Cédric Le Goater
2026-08-11 7:33 ` Jamin Lin
2026-08-10 18:57 ` Mikail Sadic [this message]
2026-08-10 18:57 ` [PATCH v3 6/8] ufs: Make the logical block size configurable and answer absent LUNs Mikail Sadic
2026-08-10 18:57 ` [PATCH v3 7/8] ufs/aspeed: Add AST2700 UFS host controller Mikail Sadic
2026-08-10 18:57 ` [PATCH v3 8/8] arm/aspeed: Add AST2700 Huygens machine Mikail Sadic
2026-08-11 4:35 ` Cédric Le Goater
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=20260810185748.1253-6-mikail.sadic@ibm.com \
--to=mikail.sadic@ibm.com \
--cc=andrew@codeconstruct.com.au \
--cc=calebs@linux.ibm.com \
--cc=clg@kaod.org \
--cc=jamin_lin@aspeedtech.com \
--cc=jeuk20.kim@samsung.com \
--cc=joel@jms.id.au \
--cc=kane_chen@aspeedtech.com \
--cc=leetroy@gmail.com \
--cc=milesg@linux.ibm.com \
--cc=ninad@linux.ibm.com \
--cc=pbonzini@redhat.com \
--cc=peter.maydell@linaro.org \
--cc=philmd@mailo.com \
--cc=qemu-arm@nongnu.org \
--cc=qemu-devel@nongnu.org \
--cc=steven_lee@aspeedtech.com \
--cc=titusr@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.