From: "Cédric Le Goater" <clg@redhat.com>
To: qemu-arm@nongnu.org, qemu-devel@nongnu.org
Cc: "Emmanuel Blot" <emmanuel.blot@free.fr>,
"Alexander Hansen" <alexander.hansen@9elements.com>,
"Cédric Le Goater" <clg@redhat.com>
Subject: [PULL 27/83] hw/sensor: adc128d818: add 12-bit 8-channel ADC device
Date: Tue, 11 Aug 2026 18:28:42 +0200 [thread overview]
Message-ID: <20260811162938.1403216-28-clg@redhat.com> (raw)
In-Reply-To: <20260811162938.1403216-1-clg@redhat.com>
From: Emmanuel Blot <emmanuel.blot@free.fr>
The ADC128D818 is a TI 12-bit, 8-channel I2C ADC used on several
OpenBMC platforms for voltage and temperature monitoring.
Implement the device with:
- four operating modes
- 12-bit voltage conversion from QOM inputs
- 9-bit temperature conversion from milli-degree Celsius QOM inputs
- switchable internal or external voltage reference
- per-channel high/low limit registers
- interrupt support
- software reset
- one-shot conversion support in shutdown mode
Reviewed-by: Alexander Hansen <alexander.hansen@9elements.com>
Tested-by: Alexander Hansen <alexander.hansen@9elements.com>
Signed-off-by: Emmanuel Blot <emmanuel.blot@free.fr>
Link: https://lore.kernel.org/qemu-devel/20260707091609.97759-2-emmanuel.blot@free.fr
Signed-off-by: Cédric Le Goater <clg@redhat.com>
---
include/hw/sensor/adc128d818.h | 14 +
hw/sensor/adc128d818.c | 696 +++++++++++++++++++++++++++++++++
hw/sensor/Kconfig | 4 +
hw/sensor/meson.build | 1 +
hw/sensor/trace-events | 8 +
5 files changed, 723 insertions(+)
create mode 100644 include/hw/sensor/adc128d818.h
create mode 100644 hw/sensor/adc128d818.c
diff --git a/include/hw/sensor/adc128d818.h b/include/hw/sensor/adc128d818.h
new file mode 100644
index 000000000000..10c34b964645
--- /dev/null
+++ b/include/hw/sensor/adc128d818.h
@@ -0,0 +1,14 @@
+/*
+ * Texas Instruments ADC128D818 12-bit 8-channel ADC with I2C interface
+ *
+ * Copyright (c) 2026 Meta Platforms, Inc. and affiliates.
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#ifndef HW_SENSOR_ADC128D818_H
+#define HW_SENSOR_ADC128D818_H
+
+#define TYPE_ADC128D818 "adc128d818"
+
+#endif
diff --git a/hw/sensor/adc128d818.c b/hw/sensor/adc128d818.c
new file mode 100644
index 000000000000..c65508cba144
--- /dev/null
+++ b/hw/sensor/adc128d818.c
@@ -0,0 +1,696 @@
+/*
+ * Texas Instruments ADC128D818 12-bit 8-channel ADC with I2C interface
+ *
+ * Copyright (c) 2026 Meta Platforms, Inc. and affiliates.
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#include "qemu/osdep.h"
+#include "qemu/log.h"
+#include "qapi/error.h"
+#include "qapi/visitor.h"
+#include "qom/object.h"
+#include "hw/sensor/adc128d818.h"
+#include "hw/core/irq.h"
+#include "hw/core/qdev-properties.h"
+#include "hw/i2c/i2c.h"
+#include "migration/vmstate.h"
+#include "trace.h"
+
+
+/* Register addresses */
+#define REG_CONFIG 0x00
+#define REG_INT_STATUS 0x01
+#define REG_INT_MASK 0x03
+#define REG_CONV_RATE 0x07
+#define REG_CH_DISABLE 0x08
+#define REG_ONE_SHOT 0x09
+#define REG_DEEP_SHUTDOWN 0x0a
+#define REG_ADV_CONFIG 0x0b
+#define REG_BUSY_STATUS 0x0c
+
+/* Channel Reading Registers (16-bit, read-only) */
+#define REG_CH_READING_BASE 0x20
+#define REG_CH_READING_LAST 0x27
+
+/* Limit Registers (8-bit, read/write) */
+#define REG_LIMIT_BASE 0x2a
+#define REG_LIMIT_LAST 0x39
+
+/* ID Registers (read-only) */
+#define REG_MANUFACTURER_ID 0x3e
+#define REG_REVISION_ID 0x3f
+
+/* Configuration Register (0x00) bitfields */
+#define CONFIG_START BIT(0)
+#define CONFIG_INT_ENABLE BIT(1)
+#define CONFIG_INT_CLEAR BIT(3)
+#define CONFIG_INITIALIZATION BIT(7)
+#define CONFIG_WR_MASK \
+ (CONFIG_START | CONFIG_INT_ENABLE | CONFIG_INT_CLEAR)
+
+/* Advanced Configuration Register (0x0B) bitfields */
+#define ADV_CONFIG_EXT_REF_EN BIT(0)
+#define ADV_CONFIG_MODE_SHIFT 1
+#define ADV_CONFIG_MODE_MASK (0x3 << ADV_CONFIG_MODE_SHIFT)
+#define ADV_CONFIG_WR_MASK \
+ (ADV_CONFIG_EXT_REF_EN | ADV_CONFIG_MODE_MASK)
+
+/* Busy Status Register (0x0C) bitfields */
+#define BUSY_STATUS_NOT_READY BIT(1)
+
+/* Conversion Rate Register (0x07) bitfields */
+#define CONV_RATE_MASK 0x01
+
+/* Deep Shutdown Register (0x0A) bitfields */
+#define DEEP_SHUTDOWN_EN 0x01
+
+/* Device constants */
+#define ADC128D818_NUM_CHANNELS 8
+#define ADC128D818_NUM_REGS 0x40
+
+#define ADC128D818_INTERNAL_VREF_MV 2560
+#define ADC128D818_MAX_VDD_MV 5500
+#define ADC128D818_MANUFACTURER_ID_VAL 0x01
+#define ADC128D818_REVISION_ID_VAL 0x09
+
+/* ADC resolution */
+#define ADC128D818_ADC_RESOLUTION 4096
+#define ADC128D818_ADC_MAX 4095
+
+/* Temperature: 0.5 deg C per LSb = 500 milli-degrees per LSb */
+#define ADC128D818_TEMP_LSB_MC 500
+#define ADC128D818_TEMP_RAW_MIN (-256)
+#define ADC128D818_TEMP_RAW_MAX 255
+
+
+OBJECT_DECLARE_SIMPLE_TYPE(ADC128D818State, ADC128D818)
+
+struct ADC128D818State {
+ I2CSlave parent_obj;
+
+ qemu_irq irq;
+
+ uint8_t len;
+ uint8_t pointer;
+ uint8_t rx_byte;
+
+ uint8_t regs[ADC128D818_NUM_REGS];
+ uint16_t channel[ADC128D818_NUM_CHANNELS];
+
+ int16_t ain[ADC128D818_NUM_CHANNELS]; /* mV */
+ int32_t temperature; /* milli-degrees Celsius */
+ uint16_t ext_vref; /* mV, 0 means not connected */
+ bool temp_alarm; /* temperature high-limit alarm latched */
+
+ char *description;
+};
+
+static uint16_t adc128d818_get_vref(const ADC128D818State *s)
+{
+ if (s->regs[REG_ADV_CONFIG] & ADV_CONFIG_EXT_REF_EN) {
+ if (s->ext_vref > 0u) {
+ return s->ext_vref;
+ }
+ qemu_log_mask(LOG_GUEST_ERROR,
+ "%s: %s: external VREF selected but not"
+ " connected, falling back to internal\n",
+ __func__, s->description);
+ }
+
+ return ADC128D818_INTERNAL_VREF_MV;
+}
+
+static uint8_t adc128d818_get_mode(const ADC128D818State *s)
+{
+ return (s->regs[REG_ADV_CONFIG] & ADV_CONFIG_MODE_MASK) >>
+ ADV_CONFIG_MODE_SHIFT;
+}
+
+static bool adc128d818_is_temp_channel(const ADC128D818State *s, unsigned ch)
+{
+ if (ch != 7u) {
+ return false;
+ }
+
+ return adc128d818_get_mode(s) != 1u;
+}
+
+static bool adc128d818_is_reserved_channel(const ADC128D818State *s,
+ unsigned ch)
+{
+ switch (adc128d818_get_mode(s)) {
+ case 2u:
+ return ch >= 4u && ch <= 6u;
+ case 3u:
+ return ch == 6u;
+ default:
+ return false;
+ }
+}
+
+static int16_t adc128d818_channel_voltage(const ADC128D818State *s, unsigned ch)
+{
+ switch (adc128d818_get_mode(s)) {
+ case 2u:
+ switch (ch) {
+ case 0u:
+ return (int16_t)(s->ain[0] - s->ain[1]);
+ case 1u:
+ return (int16_t)(s->ain[3] - s->ain[2]);
+ case 2u:
+ return (int16_t)(s->ain[4] - s->ain[5]);
+ case 3u:
+ return (int16_t)(s->ain[7] - s->ain[6]);
+ default:
+ return 0;
+ }
+ case 3u:
+ switch (ch) {
+ case 4u:
+ return (int16_t)(s->ain[4] - s->ain[5]);
+ case 5u:
+ return (int16_t)(s->ain[7] - s->ain[6]);
+ default:
+ return s->ain[ch];
+ }
+ default:
+ return s->ain[ch];
+ }
+}
+
+static void adc128d818_update_irq(ADC128D818State *s)
+{
+ uint8_t cfg = s->regs[REG_CONFIG];
+ uint8_t active;
+ bool level;
+
+ active = s->regs[REG_INT_STATUS] & ~s->regs[REG_INT_MASK];
+
+ /* INT pin is active-low */
+ level = !((cfg & CONFIG_INT_ENABLE) && !(cfg & CONFIG_INT_CLEAR) &&
+ (active != 0u));
+
+ trace_adc128d818_irq(s->description, level);
+ qemu_set_irq(s->irq, level);
+}
+
+static bool adc128d818_monitoring_active(const ADC128D818State *s)
+{
+ if (s->regs[REG_DEEP_SHUTDOWN] & DEEP_SHUTDOWN_EN) {
+ return false;
+ }
+ if (!(s->regs[REG_CONFIG] & CONFIG_START)) {
+ return false;
+ }
+ if (s->regs[REG_CONFIG] & CONFIG_INT_CLEAR) {
+ return false;
+ }
+
+ return true;
+}
+
+static void adc128d818_check_limits(ADC128D818State *s)
+{
+ uint8_t disabled = s->regs[REG_CH_DISABLE];
+ uint8_t int_status = 0u;
+
+ for (unsigned ch = 0u; ch < ADC128D818_NUM_CHANNELS; ch++) {
+ if ((disabled & (1u << ch)) ||
+ adc128d818_is_reserved_channel(s, ch)) {
+ continue;
+ }
+
+ if (adc128d818_is_temp_channel(s, ch)) {
+ int raw = s->temperature / ADC128D818_TEMP_LSB_MC;
+ int thot;
+ int thyst;
+
+ raw = MAX(ADC128D818_TEMP_RAW_MIN,
+ MIN(ADC128D818_TEMP_RAW_MAX, raw));
+ thot = (int)(int8_t)s->regs[REG_LIMIT_BASE + ch * 2u] * 2;
+ thyst = (int)(int8_t)s->regs[REG_LIMIT_BASE + ch * 2u + 1u] * 2;
+
+ if (raw > thot) {
+ s->temp_alarm = true;
+ } else if (raw <= thyst) {
+ s->temp_alarm = false;
+ }
+ if (s->temp_alarm) {
+ int_status |= (1u << ch);
+ }
+ } else {
+ uint8_t msb = (uint8_t)(s->channel[ch] >> 8u);
+ uint8_t high_lim = s->regs[REG_LIMIT_BASE + ch * 2u];
+ uint8_t low_lim = s->regs[REG_LIMIT_BASE + ch * 2u + 1u];
+
+ if (msb > high_lim || msb <= low_lim) {
+ int_status |= (1u << ch);
+ }
+ }
+ }
+
+ s->regs[REG_INT_STATUS] = int_status;
+ adc128d818_update_irq(s);
+}
+
+static void adc128d818_convert(ADC128D818State *s)
+{
+ uint8_t disabled;
+ uint16_t vref;
+
+ disabled = s->regs[REG_CH_DISABLE];
+ vref = adc128d818_get_vref(s);
+
+ for (unsigned ch = 0u; ch < ADC128D818_NUM_CHANNELS; ch++) {
+ if ((disabled & (1u << ch)) ||
+ adc128d818_is_reserved_channel(s, ch)) {
+ continue;
+ }
+
+ if (adc128d818_is_temp_channel(s, ch)) {
+ int32_t raw = s->temperature / ADC128D818_TEMP_LSB_MC;
+
+ raw =
+ MAX(ADC128D818_TEMP_RAW_MIN, MIN(ADC128D818_TEMP_RAW_MAX, raw));
+ s->channel[ch] = (uint16_t)(((unsigned)raw & 0x1FFu) << 7u);
+ } else {
+ int16_t vin = adc128d818_channel_voltage(s, ch);
+ int32_t dout;
+
+ dout = vin * (int32_t)ADC128D818_ADC_RESOLUTION / vref;
+ dout = MAX(0, MIN((int32_t)ADC128D818_ADC_MAX, dout));
+ s->channel[ch] = (uint16_t)(dout << 4u);
+ }
+
+ trace_adc128d818_convert(s->description, ch, s->channel[ch]);
+ }
+
+ s->regs[REG_BUSY_STATUS] &= ~BUSY_STATUS_NOT_READY;
+
+ adc128d818_check_limits(s);
+}
+
+static uint8_t adc128d818_read_channel(ADC128D818State *s, unsigned ch)
+{
+ uint8_t val;
+
+ if (s->rx_byte == 0u) {
+ val = (uint8_t)(s->channel[ch] >> 8u);
+ trace_adc128d818_read_channel(s->description, ch, s->channel[ch]);
+ } else {
+ val = (uint8_t)(s->channel[ch] & 0xFFu);
+ }
+ s->rx_byte ^= 1u;
+
+ return val;
+}
+
+static uint8_t adc128d818_read_reg(ADC128D818State *s, uint8_t reg)
+{
+ uint8_t val;
+
+ switch (reg) {
+ case REG_INT_STATUS:
+ val = s->regs[REG_INT_STATUS];
+ s->regs[REG_INT_STATUS] = 0x00u;
+ if (adc128d818_monitoring_active(s)) {
+ adc128d818_check_limits(s);
+ } else {
+ adc128d818_update_irq(s);
+ }
+ trace_adc128d818_read(s->description, reg, val);
+ return val;
+ case REG_CONFIG:
+ case REG_INT_MASK:
+ case REG_CONV_RATE:
+ case REG_CH_DISABLE:
+ case REG_ONE_SHOT:
+ case REG_DEEP_SHUTDOWN:
+ case REG_ADV_CONFIG:
+ case REG_BUSY_STATUS:
+ case REG_LIMIT_BASE ... REG_LIMIT_LAST:
+ case REG_MANUFACTURER_ID:
+ case REG_REVISION_ID:
+ trace_adc128d818_read(s->description, reg, s->regs[reg]);
+ return s->regs[reg];
+ case REG_CH_READING_BASE ... REG_CH_READING_LAST:
+ return adc128d818_read_channel(s, reg - REG_CH_READING_BASE);
+ default:
+ qemu_log_mask(LOG_GUEST_ERROR,
+ "%s: %s: read from undefined register 0x%02x\n",
+ __func__, s->description, reg);
+ return 0x00u;
+ }
+}
+
+static void adc128d818_write_reg(ADC128D818State *s, uint8_t reg, uint8_t val);
+
+static void adc128d818_reset_regs(ADC128D818State *s)
+{
+ memset(s->regs, 0, sizeof(s->regs));
+ memset(s->channel, 0, sizeof(s->channel));
+ s->temp_alarm = false;
+
+ s->regs[REG_CONFIG] = 0x08u;
+ s->regs[REG_BUSY_STATUS] = 0x02u;
+ s->regs[REG_MANUFACTURER_ID] = ADC128D818_MANUFACTURER_ID_VAL;
+ s->regs[REG_REVISION_ID] = ADC128D818_REVISION_ID_VAL;
+
+ for (unsigned ch = 0u; ch < ADC128D818_NUM_CHANNELS; ch++) {
+ s->regs[REG_LIMIT_BASE + ch * 2u] = 0xFFu;
+ }
+
+ s->pointer = 0x00u;
+ s->len = 0u;
+ s->rx_byte = 0u;
+
+ adc128d818_update_irq(s);
+}
+
+static void adc128d818_write_reg(ADC128D818State *s, uint8_t reg, uint8_t val)
+{
+ trace_adc128d818_write(s->description, reg, val);
+
+ switch (reg) {
+ case REG_CONFIG:
+ if (val & CONFIG_INITIALIZATION) {
+ trace_adc128d818_reset(s->description, "reg");
+ adc128d818_reset_regs(s);
+ break;
+ }
+ s->regs[REG_CONFIG] = val & CONFIG_WR_MASK;
+ if ((val & CONFIG_START) && !(val & CONFIG_INT_CLEAR) &&
+ !(s->regs[REG_DEEP_SHUTDOWN] & DEEP_SHUTDOWN_EN)) {
+ adc128d818_convert(s);
+ }
+ adc128d818_update_irq(s);
+ break;
+ case REG_INT_MASK:
+ s->regs[REG_INT_MASK] = val;
+ adc128d818_update_irq(s);
+ break;
+ case REG_CONV_RATE:
+ if (s->regs[REG_CONFIG] & CONFIG_START) {
+ qemu_log_mask(LOG_GUEST_ERROR,
+ "%s: %s: CONV_RATE written while running\n",
+ __func__, s->description);
+ break;
+ }
+ s->regs[REG_CONV_RATE] = val & CONV_RATE_MASK;
+ break;
+ case REG_CH_DISABLE:
+ if (s->regs[REG_CONFIG] & CONFIG_START) {
+ qemu_log_mask(LOG_GUEST_ERROR,
+ "%s: %s: CH_DISABLE written while running\n",
+ __func__, s->description);
+ break;
+ }
+ s->regs[REG_CH_DISABLE] = val;
+ memset(s->channel, 0, sizeof(s->channel));
+ s->regs[REG_INT_STATUS] = 0x00u;
+ s->temp_alarm = false;
+ adc128d818_update_irq(s);
+ break;
+ case REG_ONE_SHOT:
+ if (!(s->regs[REG_CONFIG] & CONFIG_START)) {
+ adc128d818_convert(s);
+ }
+ break;
+ case REG_DEEP_SHUTDOWN:
+ if ((val & DEEP_SHUTDOWN_EN) && (s->regs[REG_CONFIG] & CONFIG_START)) {
+ qemu_log_mask(LOG_GUEST_ERROR,
+ "%s: %s: DEEP_SHUTDOWN set while running\n",
+ __func__, s->description);
+ break;
+ }
+ s->regs[REG_DEEP_SHUTDOWN] = val & DEEP_SHUTDOWN_EN;
+ break;
+ case REG_ADV_CONFIG:
+ if (s->regs[REG_CONFIG] & CONFIG_START) {
+ qemu_log_mask(LOG_GUEST_ERROR,
+ "%s: %s: ADV_CONFIG written while running\n",
+ __func__, s->description);
+ break;
+ }
+ s->regs[REG_ADV_CONFIG] = val & ADV_CONFIG_WR_MASK;
+ memset(s->channel, 0, sizeof(s->channel));
+ s->regs[REG_INT_STATUS] = 0x00u;
+ s->temp_alarm = false;
+ adc128d818_update_irq(s);
+ break;
+ case REG_LIMIT_BASE ... REG_LIMIT_LAST:
+ s->regs[reg] = val;
+ break;
+ case REG_INT_STATUS:
+ case REG_BUSY_STATUS:
+ case REG_MANUFACTURER_ID:
+ case REG_REVISION_ID:
+ case REG_CH_READING_BASE ... REG_CH_READING_LAST:
+ qemu_log_mask(LOG_GUEST_ERROR,
+ "%s: %s: write to read-only register 0x%02x\n",
+ __func__, s->description, reg);
+ break;
+ default:
+ qemu_log_mask(LOG_GUEST_ERROR,
+ "%s: %s: write to undefined register 0x%02x\n",
+ __func__, s->description, reg);
+ break;
+ }
+}
+
+static uint8_t adc128d818_recv(I2CSlave *i2c)
+{
+ ADC128D818State *s = ADC128D818(i2c);
+
+ return adc128d818_read_reg(s, s->pointer);
+}
+
+static int adc128d818_send(I2CSlave *i2c, uint8_t data)
+{
+ ADC128D818State *s = ADC128D818(i2c);
+
+ if (s->len == 0u) {
+ s->pointer = data;
+ s->len++;
+ } else {
+ adc128d818_write_reg(s, s->pointer, data);
+ }
+
+ return 0;
+}
+
+static int adc128d818_event(I2CSlave *i2c, enum i2c_event event)
+{
+ ADC128D818State *s = ADC128D818(i2c);
+
+ s->len = 0u;
+ s->rx_byte = 0u;
+
+ return 0;
+}
+
+static void adc128d818_get_ain(Object *obj, Visitor *v, const char *name,
+ void *opaque, Error **errp)
+{
+ ADC128D818State *s = ADC128D818(obj);
+ int64_t value;
+ int ch_num;
+ int rc;
+
+ rc = sscanf(name, "ain%d", &ch_num);
+ if (rc != 1 || ch_num < 0 || ch_num >= (int)ADC128D818_NUM_CHANNELS) {
+ error_setg(errp, "%s: %s: invalid channel '%s'", __func__,
+ s->description, name);
+ return;
+ }
+
+ value = s->ain[ch_num];
+ visit_type_int(v, name, &value, errp);
+}
+
+static void adc128d818_set_ain(Object *obj, Visitor *v, const char *name,
+ void *opaque, Error **errp)
+{
+ ADC128D818State *s = ADC128D818(obj);
+ int64_t value;
+ int ch_num;
+ int rc;
+
+ if (!visit_type_int(v, name, &value, errp)) {
+ return;
+ }
+
+ rc = sscanf(name, "ain%d", &ch_num);
+ if (rc != 1 || ch_num < 0 || ch_num >= (int)ADC128D818_NUM_CHANNELS) {
+ error_setg(errp, "%s: %s: invalid channel '%s'", __func__,
+ s->description, name);
+ return;
+ }
+
+ if (value < INT16_MIN || value > INT16_MAX) {
+ error_setg(errp, "%s: %s: value %" PRId64 " out of range for '%s'",
+ __func__, s->description, value, name);
+ return;
+ }
+
+ s->ain[ch_num] = (int16_t)value;
+
+ if (adc128d818_monitoring_active(s)) {
+ adc128d818_convert(s);
+ }
+}
+
+static void adc128d818_get_temperature(
+ Object *obj, Visitor *v, const char *name, void *opaque, Error **errp)
+{
+ ADC128D818State *s = ADC128D818(obj);
+ int64_t value = s->temperature;
+
+ visit_type_int(v, name, &value, errp);
+}
+
+static void adc128d818_set_temperature(
+ Object *obj, Visitor *v, const char *name, void *opaque, Error **errp)
+{
+ ADC128D818State *s = ADC128D818(obj);
+ int64_t value;
+
+ if (!visit_type_int(v, name, &value, errp)) {
+ return;
+ }
+
+ if (value < INT32_MIN || value > INT32_MAX) {
+ error_setg(errp, "%s: %s: value %" PRId64 " out of range", __func__,
+ s->description, value);
+ return;
+ }
+
+ s->temperature = (int32_t)value;
+
+ if (adc128d818_monitoring_active(s)) {
+ adc128d818_convert(s);
+ }
+}
+
+static const VMStateDescription adc128d818_vmstate = {
+ .name = "ADC128D818",
+ .version_id = 0,
+ .minimum_version_id = 0,
+ .fields = (VMStateField[]) {
+ VMSTATE_UINT8(len, ADC128D818State),
+ VMSTATE_UINT8(pointer, ADC128D818State),
+ VMSTATE_UINT8(rx_byte, ADC128D818State),
+ VMSTATE_UINT8_ARRAY(regs, ADC128D818State,
+ ADC128D818_NUM_REGS),
+ VMSTATE_UINT16_ARRAY(channel, ADC128D818State,
+ ADC128D818_NUM_CHANNELS),
+ VMSTATE_INT16_ARRAY(ain, ADC128D818State,
+ ADC128D818_NUM_CHANNELS),
+ VMSTATE_INT32(temperature, ADC128D818State),
+ VMSTATE_UINT16(ext_vref, ADC128D818State),
+ VMSTATE_BOOL(temp_alarm, ADC128D818State),
+ VMSTATE_I2C_SLAVE(parent_obj, ADC128D818State),
+ VMSTATE_END_OF_LIST()
+ }
+};
+
+static void adc128d818_reset_hold(Object *obj, ResetType type)
+{
+ ADC128D818State *s = ADC128D818(obj);
+
+ trace_adc128d818_reset(s->description, "hw");
+ adc128d818_reset_regs(s);
+}
+
+static void adc128d818_get_ext_vref(
+ Object *obj, Visitor *v, const char *name, void *opaque, Error **errp)
+{
+ ADC128D818State *s = ADC128D818(obj);
+ int64_t value = (int64_t)s->ext_vref;
+
+ visit_type_int(v, name, &value, errp);
+}
+
+static void adc128d818_set_ext_vref(
+ Object *obj, Visitor *v, const char *name, void *opaque, Error **errp)
+{
+ ADC128D818State *s = ADC128D818(obj);
+ int64_t value;
+
+ if (!visit_type_int(v, name, &value, errp)) {
+ return;
+ }
+
+ if (value < 0 || value > ADC128D818_MAX_VDD_MV) {
+ error_setg(errp,
+ "%s: %s: ext-vref-mv %" PRId64 " out of range (0..%u mV)",
+ __func__, s->description, value, ADC128D818_MAX_VDD_MV);
+ return;
+ }
+
+ s->ext_vref = (uint16_t)value;
+
+ if (adc128d818_monitoring_active(s)) {
+ adc128d818_convert(s);
+ }
+}
+
+static void adc128d818_initfn(Object *obj)
+{
+ for (unsigned ch = 0u; ch < ADC128D818_NUM_CHANNELS; ch++) {
+ char *name = g_strdup_printf("ain%u", ch);
+
+ object_property_add(obj, name, "int", adc128d818_get_ain,
+ adc128d818_set_ain, NULL, NULL);
+ g_free(name);
+ }
+
+ object_property_add(obj, "temperature", "int", adc128d818_get_temperature,
+ adc128d818_set_temperature, NULL, NULL);
+ object_property_add(obj, "ext-vref-mv", "int", adc128d818_get_ext_vref,
+ adc128d818_set_ext_vref, NULL, NULL);
+}
+
+static void adc128d818_realize(DeviceState *dev, Error **errp)
+{
+ ADC128D818State *s = ADC128D818(dev);
+
+ if (!s->description) {
+ s->description = g_strdup(object_get_typename(OBJECT(dev)));
+ }
+
+ qdev_init_gpio_out(dev, &s->irq, 1u);
+}
+
+static const Property adc128d818_properties[] = {
+ DEFINE_PROP_STRING("description", ADC128D818State, description),
+};
+
+static void adc128d818_class_init(ObjectClass *klass, const void *data)
+{
+ DeviceClass *dc = DEVICE_CLASS(klass);
+ I2CSlaveClass *ic = I2C_SLAVE_CLASS(klass);
+ ResettableClass *rc = RESETTABLE_CLASS(klass);
+
+ ic->event = adc128d818_event;
+ ic->recv = adc128d818_recv;
+ ic->send = adc128d818_send;
+ dc->realize = adc128d818_realize;
+ rc->phases.hold = adc128d818_reset_hold;
+ dc->vmsd = &adc128d818_vmstate;
+ device_class_set_props(dc, adc128d818_properties);
+}
+
+static const TypeInfo adc128d818_types[] = {
+ {
+ .name = TYPE_ADC128D818,
+ .parent = TYPE_I2C_SLAVE,
+ .instance_init = adc128d818_initfn,
+ .instance_size = sizeof(ADC128D818State),
+ .class_init = adc128d818_class_init,
+ },
+};
+
+DEFINE_TYPES(adc128d818_types)
diff --git a/hw/sensor/Kconfig b/hw/sensor/Kconfig
index bc6331b4abbd..b459ac224076 100644
--- a/hw/sensor/Kconfig
+++ b/hw/sensor/Kconfig
@@ -1,3 +1,7 @@
+config ADC128D818
+ bool
+ depends on I2C
+
config TMP105
bool
depends on I2C
diff --git a/hw/sensor/meson.build b/hw/sensor/meson.build
index 420fdc335927..fe36c9ef9109 100644
--- a/hw/sensor/meson.build
+++ b/hw/sensor/meson.build
@@ -1,3 +1,4 @@
+system_ss.add(when: 'CONFIG_ADC128D818', if_true: files('adc128d818.c'))
system_ss.add(when: 'CONFIG_TMP105', if_true: files('tmp105.c'))
system_ss.add(when: 'CONFIG_TMP421', if_true: files('tmp421.c'))
system_ss.add(when: 'CONFIG_DPS310', if_true: files('dps310.c'))
diff --git a/hw/sensor/trace-events b/hw/sensor/trace-events
index a3fe54fa6dc1..5a3630f7bbd6 100644
--- a/hw/sensor/trace-events
+++ b/hw/sensor/trace-events
@@ -1,5 +1,13 @@
# See docs/devel/tracing.rst for syntax documentation.
+# adc128d818.c
+adc128d818_read(const char *id, uint8_t reg, uint8_t value) "%s reg 0x%02x val 0x%02x"
+adc128d818_read_channel(const char *id, uint8_t channel, uint16_t value) "%s ch %u val 0x%04x"
+adc128d818_write(const char *id, uint8_t reg, uint8_t value) "%s reg 0x%02x val 0x%02x"
+adc128d818_convert(const char *id, uint8_t channel, uint16_t value) "%s ch %u val 0x%04x"
+adc128d818_irq(const char *id, bool level) "%s level %u"
+adc128d818_reset(const char *id, const char *source) "%s %s"
+
# tmp105.c
tmp105_read(uint8_t dev, uint8_t addr) "device: 0x%02x, addr: 0x%02x"
tmp105_write(uint8_t dev, uint8_t addr) "device: 0x%02x, addr 0x%02x"
--
2.55.0
next prev parent reply other threads:[~2026-08-11 16:31 UTC|newest]
Thread overview: 85+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-08-11 16:28 [PULL 00/83] aspeed queue Cédric Le Goater
2026-08-11 16:28 ` [PULL 01/83] hw/arm/aspeed: Add missing PCI_EXPRESS -> PCIE_PORT Kconfig dependency Cédric Le Goater
2026-08-11 16:28 ` [PULL 02/83] hw/arm/aspeed: Add missing Kconfig dependencies on required components Cédric Le Goater
2026-08-11 16:28 ` [PULL 03/83] hw/arm/aspeed: Add missing Kconfig dependencies on optional components Cédric Le Goater
2026-08-11 16:28 ` [PULL 04/83] hw/usb/hcd-ehci: Change descriptor addresses to 64-bit with migration compatibility Cédric Le Goater
2026-08-11 16:28 ` [PULL 05/83] hw/usb/hcd-ehci: Add property to advertise 64-bit addressing capability Cédric Le Goater
2026-08-11 16:28 ` [PULL 06/83] hw/usb/hcd-ehci: Implement 64-bit QH descriptor addressing Cédric Le Goater
2026-08-11 16:28 ` [PULL 07/83] hw/usb/hcd-ehci: Implement 64-bit qTD " Cédric Le Goater
2026-08-11 16:28 ` [PULL 08/83] hw/usb/hcd-ehci: Implement 64-bit iTD " Cédric Le Goater
2026-08-11 16:28 ` [PULL 09/83] hw/usb/hcd-ehci: Implement 64-bit siTD " Cédric Le Goater
2026-08-11 16:28 ` [PULL 10/83] hw/usb/hcd-ehci: Add ctrldssegment-default property Cédric Le Goater
2026-08-11 16:28 ` [PULL 11/83] hw/arm/aspeed_ast27x0: Set EHCI ctrldssegment-default Cédric Le Goater
2026-08-11 16:28 ` [PULL 12/83] hw/arm/aspeed_ast27x0: Enable 64-bit EHCI DMA addressing Cédric Le Goater
2026-08-11 16:28 ` [PULL 13/83] tests/functional/aarch64/test_aspeed_ast2700: Add USB EHCI test for AST2700 A1/A2 Cédric Le Goater
2026-08-11 16:28 ` [PULL 14/83] tests/qtest: aspeed_smc: Introduce read_page_mem_fn for page read helpers Cédric Le Goater
2026-08-11 16:28 ` [PULL 15/83] tests/qtest: aspeed_smc: Add fast-read test coverage Cédric Le Goater
2026-08-11 16:28 ` [PULL 16/83] tests/qtest: aspeed_smc: Add Dual Output Read (DOR) " Cédric Le Goater
2026-08-11 16:28 ` [PULL 17/83] tests/qtest: aspeed_smc: Add Quad Output Read (QOR) " Cédric Le Goater
2026-08-11 16:28 ` [PULL 18/83] hw/misc/aspeed_scu: Introduce Aspeed2700SCUState Cédric Le Goater
2026-08-11 16:28 ` [PULL 19/83] hw/arm/aspeed: Use Aspeed2700SCUState for AST2700 users Cédric Le Goater
2026-08-11 16:28 ` [PULL 20/83] hw/arm/aspeed_ast27x0: Move SCU link into AST27x0 coprocessors Cédric Le Goater
2026-08-11 16:28 ` [PULL 21/83] hw/misc/aspeed_scu: Add separate reset handler for AST2700 SCUIO Cédric Le Goater
2026-08-11 16:28 ` [PULL 22/83] hw/arm/aspeed_ast27x0: Pass realized PSP SoC to SSP/TSP initialization Cédric Le Goater
2026-08-11 16:28 ` [PULL 23/83] hw/arm/ast27x0: Share single SCUIO instance across PSP, SSP, and TSP Cédric Le Goater
2026-08-11 16:28 ` [PULL 24/83] hw/arm/ast27x0: Share FMC controller with SSP " Cédric Le Goater
2026-08-11 16:28 ` [PULL 25/83] hw/ssi/aspeed_smc: Add Data FIFO-based flash access support for AST2700 Cédric Le Goater
2026-08-11 16:28 ` [PULL 26/83] tests/qtest/ast2700-smc-test: Add Data FIFO mode test Cédric Le Goater
2026-08-11 16:28 ` Cédric Le Goater [this message]
2026-08-11 16:28 ` [PULL 28/83] tests/qtest: adc128d818: add test harness and register access Cédric Le Goater
2026-08-11 16:28 ` [PULL 29/83] tests/qtest: adc128d818: test voltage and temperature conversion Cédric Le Goater
2026-08-11 16:28 ` [PULL 30/83] tests/qtest: adc128d818: test limit interrupts Cédric Le Goater
2026-08-11 16:28 ` [PULL 31/83] tests/qtest: adc128d818: test operating modes and power control Cédric Le Goater
2026-08-11 16:28 ` [PULL 32/83] hw/arm/aspeed: anacapa: use ASCII in comments Cédric Le Goater
2026-08-11 16:28 ` [PULL 33/83] hw/arm: anacapa: add ADC128D818 devices Cédric Le Goater
2026-08-11 16:28 ` [PULL 34/83] hw/gpio: pca9552: register types with DEFINE_TYPES() Cédric Le Goater
2026-08-11 16:28 ` [PULL 35/83] hw/gpio: pca9552: move PCA955xState definition out of the header Cédric Le Goater
2026-08-11 16:28 ` [PULL 36/83] hw/gpio: pca9552: rename I2CSlave member to parent_obj Cédric Le Goater
2026-08-11 16:28 ` [PULL 37/83] hw/gpio: pca9552: default description to the instantiated type name Cédric Le Goater
2026-08-11 16:28 ` [PULL 38/83] hw/gpio: pca9552: declare pca9555 device as an alias of pca9535 device Cédric Le Goater
2026-08-11 16:28 ` [PULL 39/83] hw/gpio: pca9552: use the Resettable interface instead of legacy reset Cédric Le Goater
2026-08-11 16:28 ` [PULL 40/83] hw/gpio: pca9552: apply input polarity inversion on read Cédric Le Goater
2026-08-11 16:28 ` [PULL 41/83] hw/gpio: pca9552: conform GPIO command handling to the datasheet Cédric Le Goater
2026-08-11 16:28 ` [PULL 42/83] hw/gpio: pca9552: expose GPIO pins as pin%d QOM properties Cédric Le Goater
2026-08-11 16:28 ` [PULL 43/83] tests/qtest: add PCA9555 register access tests Cédric Le Goater
2026-08-11 16:28 ` [PULL 44/83] tests/qtest: pca9555: test output-to-input reflection and pull-ups Cédric Le Goater
2026-08-11 16:29 ` [PULL 45/83] tests/qtest: pca9555: test polarity inversion Cédric Le Goater
2026-08-11 16:29 ` [PULL 46/83] tests/qtest: pca9555: test auto-increment and command wrapping Cédric Le Goater
2026-08-11 16:29 ` [PULL 47/83] tests/qtest: pca9552: test behaviour specific to the LED variant Cédric Le Goater
2026-08-11 16:29 ` [PULL 48/83] hw/gpio: pca9554: add PCA9536 support Cédric Le Goater
2026-08-11 16:29 ` [PULL 49/83] hw/gpio: pca9554: add hw-dir property honoring the configured pin direction Cédric Le Goater
2026-08-11 16:29 ` [PULL 50/83] hw/gpio: pca9554: reflect push-pull outputs in the input register Cédric Le Goater
2026-08-11 16:29 ` [PULL 51/83] hw/gpio: pca9554: expose pin%d as a string property Cédric Le Goater
2026-08-11 16:29 ` [PULL 52/83] tests/qtest: add PCA9554 register access tests Cédric Le Goater
2026-08-11 16:29 ` [PULL 53/83] tests/qtest: pca9554: test output-to-input reflection and pull-ups Cédric Le Goater
2026-08-11 16:29 ` [PULL 54/83] tests/qtest: pca9554: test polarity inversion Cédric Le Goater
2026-08-11 16:29 ` [PULL 55/83] tests/qtest: pca9554: test absence of command auto-increment Cédric Le Goater
2026-08-11 16:29 ` [PULL 56/83] tests/qtest: pca9554: test the PCA9536 4-bit variant Cédric Le Goater
2026-08-11 16:29 ` [PULL 57/83] hw/arm: catalina: model PCA9555 IO expanders with their own type Cédric Le Goater
2026-08-11 16:29 ` [PULL 58/83] hw/arm: catalina: add NIC and FIO temperature sensors Cédric Le Goater
2026-08-11 16:29 ` [PULL 59/83] hw/i2c/aspeed_i2c: Support the AST2700 master buffer mode Cédric Le Goater
2026-08-11 16:29 ` [PULL 60/83] tests/functional/aarch64/test_aspeed_ast2700a2: Update ASPEED SDK v11.03 Cédric Le Goater
2026-08-11 16:29 ` [PULL 61/83] tests/functional/aarch64/test_aspeed_ast2700a1: " Cédric Le Goater
2026-08-11 16:29 ` [PULL 62/83] tests/functional/aarch64/test_aspeed_ast2700fc: " Cédric Le Goater
2026-08-11 16:29 ` [PULL 63/83] tests/functional/arm/test_aspeed_ast2600_sdk: " Cédric Le Goater
2026-08-11 16:29 ` [PULL 64/83] tests/functional/arm/test_aspeed_ast2500_sdk: " Cédric Le Goater
2026-08-11 16:29 ` [PULL 65/83] tests/functional/arm/test_aspeed_ast1030: Update ASPEED Zephyr SDK v03.08 Cédric Le Goater
2026-08-11 16:29 ` [PULL 66/83] tests/functional/arm/test_aspeed_ast1060: Update ASPEED ZEPHYR PROJECT v03.07 Cédric Le Goater
2026-08-11 16:29 ` [PULL 67/83] hw/arm/aspeed: avoid sign mismatch on sscanf for uart property Cédric Le Goater
2026-08-11 16:29 ` [PULL 68/83] hw/misc/aspeed_hace: Support the crypto command in direct access mode Cédric Le Goater
2026-08-11 16:29 ` [PULL 69/83] tests/qtest/aspeed-hace: Test the crypto command on the AST2500 Cédric Le Goater
2026-08-11 16:29 ` [PULL 70/83] hw/misc/aspeed_hace: Support scatter-gather mode for the crypto command Cédric Le Goater
2026-08-11 16:29 ` [PULL 71/83] hw/misc/aspeed_hace: Support the CTR " Cédric Le Goater
2026-08-11 16:29 ` [PULL 72/83] tests/qtest/aspeed-hace: Test the crypto command on the AST2600 Cédric Le Goater
2026-08-11 16:29 ` [PULL 73/83] tests/qtest/aspeed-hace: Test the crypto command on the AST1030 Cédric Le Goater
2026-08-11 16:29 ` [PULL 74/83] crypto/cipher: Add GCM to QCryptoCipherMode Cédric Le Goater
2026-08-11 16:29 ` [PULL 75/83] crypto/cipher: Add setaad/gettag for AEAD modes Cédric Le Goater
2026-08-11 16:29 ` [PULL 76/83] crypto/cipher-gcrypt: Implement AES-GCM Cédric Le Goater
2026-08-11 16:29 ` [PULL 77/83] crypto/cipher-nettle: " Cédric Le Goater
2026-08-11 16:29 ` [PULL 78/83] crypto/cipher-gnutls: " Cédric Le Goater
2026-08-11 16:29 ` [PULL 79/83] tests/unit/test-crypto-cipher: Test AES-GCM mode Cédric Le Goater
2026-08-11 16:29 ` [PULL 80/83] hw/misc/aspeed_hace: Support 64-bit DMA for the crypto command Cédric Le Goater
2026-08-11 16:29 ` [PULL 81/83] hw/misc/aspeed_hace: Support the AES-GCM mode " Cédric Le Goater
2026-08-11 16:29 ` [PULL 82/83] hw/misc/aspeed_hace: Enable the crypto command on the AST2700 Cédric Le Goater
2026-08-11 16:29 ` [PULL 83/83] tests/qtest/aspeed-hace: Test " Cédric Le Goater
2026-08-12 16:15 ` [PULL 00/83] aspeed queue Richard Henderson
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=20260811162938.1403216-28-clg@redhat.com \
--to=clg@redhat.com \
--cc=alexander.hansen@9elements.com \
--cc=emmanuel.blot@free.fr \
--cc=qemu-arm@nongnu.org \
--cc=qemu-devel@nongnu.org \
/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.