Reviewed-by: Glenn Miles <[email protected]> Thanks,
Glenn On Mon, 2026-10-12 at 16:32 +0200, Emmanuel Blot via qemu development wrote: > Add a model for the Texas Instruments INA230, a 36V, 16-bit I2C > current-shunt and power monitor. > > The device measures a differential shunt voltage and a bus voltage; the > current and power registers are not measured directly but derived by the > device from the shunt voltage and the guest-programmed Calibration > register. The alert is modelled. > > Shunt and bus voltages can be injected through the "shunt-voltage" and > "bus-voltage" QOM properties (in nV and uV respectively). > > Note: Conversion is instantaneous: the averaging and conversion-time > fields are stored but have no timing effect. > > Signed-off-by: Emmanuel Blot <[email protected]> > --- > hw/arm/Kconfig | 1 + > hw/sensor/Kconfig | 4 + > hw/sensor/ina230.c | 597 > +++++++++++++++++++++++++++++++++++++++++++++ > hw/sensor/meson.build | 1 + > hw/sensor/trace-events | 7 + > include/hw/sensor/ina230.h | 14 ++ > 6 files changed, 624 insertions(+) > > diff --git a/hw/arm/Kconfig b/hw/arm/Kconfig > index 4445f16f45..116d1c64ac 100644 > --- a/hw/arm/Kconfig > +++ b/hw/arm/Kconfig > @@ -539,6 +539,7 @@ config ASPEED_SOC > select TMP105 > select TMP421 > select EMC141X > + select INA230 > select OR_IRQ > select UNIMP > select LED > diff --git a/hw/sensor/Kconfig b/hw/sensor/Kconfig > index b459ac2240..da519159ac 100644 > --- a/hw/sensor/Kconfig > +++ b/hw/sensor/Kconfig > @@ -2,6 +2,10 @@ config ADC128D818 > bool > depends on I2C > > +config INA230 > + bool > + depends on I2C > + > config TMP105 > bool > depends on I2C > diff --git a/hw/sensor/ina230.c b/hw/sensor/ina230.c > new file mode 100644 > index 0000000000..988d12a6c5 > --- /dev/null > +++ b/hw/sensor/ina230.c > @@ -0,0 +1,597 @@ > +/* > + * Texas Instruments INA230 36V, 16-bit current/voltage/power monitor > + * with an I2C interface. > + * > + * Copyright (c) 2026 Meta Platforms, Inc. and affiliates. > + * > + * SPDX-License-Identifier: GPL-2.0-or-later > + * > + * The INA230 measures a differential shunt voltage and a bus voltage. > + * Current and power are not measured directly: they are derived by the > + * device from the measured shunt voltage. > + * > + * Limitations: > + * - Conversion timing is instantaneous. A conversion runs synchronously > when an > + * input, the calibration or the configuration changes; the per-channel > + * conversion times (VSH_CT/VBUS_CT), the averaging count (AVG) and > continuous > + * re-conversion on a timer are not modelled. A read always returns the > value > + * derived from the current inputs and CVRF is set once a conversion has > run. > + */ > + > +#include "qemu/osdep.h" > +#include "qemu/log.h" > +#include "qapi/error.h" > +#include "qapi/visitor.h" > +#include "qom/object.h" > +#include "hw/sensor/ina230.h" > +#include "hw/core/irq.h" > +#include "hw/core/qdev-properties.h" > +#include "hw/core/registerfields.h" > +#include "hw/i2c/i2c.h" > +#include "migration/vmstate.h" > +#include "trace.h" > + > +#define INA230_REG_CONFIG 0x00 > +#define INA230_REG_SHUNT 0x01 > +#define INA230_REG_BUS 0x02 > +#define INA230_REG_POWER 0x03 > +#define INA230_REG_CURRENT 0x04 > +#define INA230_REG_CALIBRATION 0x05 > +#define INA230_REG_MASK_ENABLE 0x06 > +#define INA230_REG_ALERT_LIMIT 0x07 > +#define INA230_REG_DIE_ID 0xFF > + > +FIELD(INA230_CONFIG, MODE, 0, 3) > +FIELD(INA230_CONFIG, VSH_CT, 3, 3) > +FIELD(INA230_CONFIG, VBUS_CT, 6, 3) > +FIELD(INA230_CONFIG, AVG, 9, 3) > +FIELD(INA230_CONFIG, RST, 15, 1) > + > +#define INA230_CONFIG_POR 0x4127 > + > +FIELD(INA230_MASK_ENABLE, LEN, 0, 1) /* alert latch enable */ > +FIELD(INA230_MASK_ENABLE, APOL, 1, 1) /* alert polarity */ > +FIELD(INA230_MASK_ENABLE, OVF, 2, 1) /* math overflow flag (status) */ > +FIELD(INA230_MASK_ENABLE, CVRF, 3, 1) /* conversion ready flag (status) */ > +FIELD(INA230_MASK_ENABLE, AFF, 4, 1) /* alert function flag (status) */ > +FIELD(INA230_MASK_ENABLE, CNVR, 10, 1) /* conversion ready alert */ > +FIELD(INA230_MASK_ENABLE, POL, 11, 1) /* power over-limit */ > +FIELD(INA230_MASK_ENABLE, BUL, 12, 1) /* bus under-limit */ > +FIELD(INA230_MASK_ENABLE, BOL, 13, 1) /* bus over-limit */ > +FIELD(INA230_MASK_ENABLE, SUL, 14, 1) /* shunt under-limit */ > +FIELD(INA230_MASK_ENABLE, SOL, 15, 1) /* shunt over-limit */ > + > +/* Writable Mask/Enable bits: alert-function selects + APOL + LEN */ > +#define INA230_ME_WMASK (R_INA230_MASK_ENABLE_SOL_MASK | \ > + R_INA230_MASK_ENABLE_SUL_MASK | \ > + R_INA230_MASK_ENABLE_BOL_MASK | \ > + R_INA230_MASK_ENABLE_BUL_MASK | \ > + R_INA230_MASK_ENABLE_POL_MASK | \ > + R_INA230_MASK_ENABLE_CNVR_MASK | \ > + R_INA230_MASK_ENABLE_APOL_MASK | \ > + R_INA230_MASK_ENABLE_LEN_MASK) > +/* Read-only status bits: AFF, CVRF, OVF */ > +#define INA230_ME_STATUS_MASK (R_INA230_MASK_ENABLE_AFF_MASK | \ > + R_INA230_MASK_ENABLE_CVRF_MASK | \ > + R_INA230_MASK_ENABLE_OVF_MASK) > + > +FIELD(INA230_CALIBRATION, FS, 0, 15) > +#define INA230_CAL_MASK R_INA230_CALIBRATION_FS_MASK > + > +#define INA230_SHUNT_LSB_NV 2500 /* 2.5 uV per LSB */ > +#define INA230_BUS_LSB_UV 1250 /* 1.25 mV per LSB */ > +#define INA230_SHUNT_MIN_NV (-81920000) /* -32768 * 2.5 uV */ > +#define INA230_SHUNT_MAX_NV 81917500 /* 32767 * 2.5 uV */ > +/* > + * A real rail should not drive the pin past 36 V, so bus codes above 0x7080 > + * are intentionally unreachable in the model. > + */ > +#define INA230_BUS_MAX_UV 36000000 /* bus pin rated 0..36 V */ > + > +#define INA230_CURRENT_DIV 2048 > +#define INA230_POWER_DIV 20000 > + > +#define INA230_DIE_ID_VAL 0x2310 > + > +OBJECT_DECLARE_SIMPLE_TYPE(INA230State, INA230) > + > +struct INA230State { > + I2CSlave parent_obj; > + > + qemu_irq alert; /* ALERT open-drain output */ > + > + /* I2C transfer state */ > + uint8_t ptr; /* register pointer */ > + uint8_t len; /* bytes received in current transfer */ > + uint8_t wr_hi; /* pending write MSB */ > + uint8_t rx_byte; /* 0: return MSB next, 1: return LSB next */ > + uint16_t rx_word; /* value latched at the start of a read */ > + > + /* Register file */ > + uint16_t config; > + uint16_t calibration; > + uint16_t mask_enable; > + uint16_t alert_limit; > + > + /* Derived measurement registers */ > + uint16_t shunt_reg; > + uint16_t bus_reg; > + uint16_t current_reg; > + uint16_t power_reg; > + > + /* Injected physical inputs */ > + int32_t shunt_nv; /* shunt voltage, nanovolts (signed: bidirectional) */ > + uint32_t bus_uv; /* bus voltage, microvolts (never negative) */ > + > + /* > + * In latch mode AFF and the ALERT pin stay asserted until the > + * Configuration register is written or the Mask/Enable register is read > + * (datasheet 7.3.3). > + */ > + bool alert_latched; > + > + char *description; > +}; > + > +/* > + * Evaluate the selected alert function. @shunt_fresh and @bus_fresh tell > which > + * of the measurement registers hold a fresh value; a function fed by a > channel > + * that did not convert cannot assert. > + */ > +static bool ina230_limit_alert(const INA230State *s, bool shunt_fresh, > + bool bus_fresh) > +{ > + uint16_t me = s->mask_enable; > + int16_t shunt = (int16_t)s->shunt_reg; > + int16_t limit_s = (int16_t)s->alert_limit; > + uint16_t limit_u = s->alert_limit; > + > + if (FIELD_EX16(me, INA230_MASK_ENABLE, SOL)) { > + return shunt_fresh && shunt > limit_s; > + } > + if (FIELD_EX16(me, INA230_MASK_ENABLE, SUL)) { > + return shunt_fresh && shunt < limit_s; > + } > + if (FIELD_EX16(me, INA230_MASK_ENABLE, BOL)) { > + return bus_fresh && s->bus_reg > limit_u; > + } > + if (FIELD_EX16(me, INA230_MASK_ENABLE, BUL)) { > + return bus_fresh && s->bus_reg < limit_u; > + } > + if (FIELD_EX16(me, INA230_MASK_ENABLE, POL)) { > + return bus_fresh && s->power_reg > limit_u; > + } > + return false; > +} > + > +static void ina230_refresh_alert(INA230State *s) > +{ > + bool latch = FIELD_EX16(s->mask_enable, INA230_MASK_ENABLE, LEN); > + bool aff = latch ? s->alert_latched : ina230_limit_alert(s, true, true); > + bool cnvr; > + bool active; > + int level; > + > + s->mask_enable = FIELD_DP16(s->mask_enable, INA230_MASK_ENABLE, AFF, > aff); > + > + cnvr = FIELD_EX16(s->mask_enable, INA230_MASK_ENABLE, CNVR) && > + FIELD_EX16(s->mask_enable, INA230_MASK_ENABLE, CVRF); > + > + active = aff || cnvr; > + > + level = FIELD_EX16(s->mask_enable, INA230_MASK_ENABLE, APOL) ? > + active : !active; > + > + trace_ina230_alert(s->description, level); > + qemu_set_irq(s->alert, level); > +} > + > +static void ina230_sample_alert(INA230State *s, bool shunt_fresh, > + bool bus_fresh) > +{ > + if (FIELD_EX16(s->mask_enable, INA230_MASK_ENABLE, LEN) && > + ina230_limit_alert(s, shunt_fresh, bus_fresh)) { > + s->alert_latched = true; > + } > + ina230_refresh_alert(s); > +} > + > +static void ina230_convert(INA230State *s) > +{ > + uint8_t mode = FIELD_EX16(s->config, INA230_CONFIG, MODE); > + bool shunt_en = mode & 0x1; > + bool bus_en = mode & 0x2; > + int64_t sr, br, cur, pwr; > + bool ovf = false; > + > + if (!shunt_en && !bus_en) { > + return; > + } > + > + if (shunt_en) { > + sr = s->shunt_nv / INA230_SHUNT_LSB_NV; > + sr = MAX(INT16_MIN, MIN(INT16_MAX, sr)); > + s->shunt_reg = (uint16_t)(int16_t)sr; > + > + cur = (int64_t)(int16_t)s->shunt_reg * s->calibration / > + INA230_CURRENT_DIV; > + if (cur > INT16_MAX || cur < INT16_MIN) { > + ovf = true; > + cur = MAX(INT16_MIN, MIN(INT16_MAX, cur)); > + } > + s->current_reg = (uint16_t)(int16_t)cur; > + } > + > + if (bus_en) { > + br = s->bus_uv / INA230_BUS_LSB_UV; > + br = MAX(0, MIN(INT16_MAX, br)); > + s->bus_reg = (uint16_t)br; > + } > + > + /* > + * In bus-only mode the current register is retained from the last shunt > + * conversion rather than measured anew, but the power register is still > + * refreshed. > + */ > + if (bus_en) { > + pwr = (int64_t)(int16_t)s->current_reg * s->bus_reg / > INA230_POWER_DIV; > + /* POWER is an unsigned, positive-only register (datasheet). */ > + if (pwr < 0) { > + pwr = 0; > + } else if (pwr > UINT16_MAX) { > + ovf = true; > + pwr = UINT16_MAX; > + } > + s->power_reg = (uint16_t)pwr; > + } > + > + /* Without a calibration the device reports neither current nor power. */ > + if (s->calibration == 0) { > + s->current_reg = 0; > + s->power_reg = 0; > + } > + > + s->mask_enable = FIELD_DP16(s->mask_enable, INA230_MASK_ENABLE, OVF, > ovf); > + s->mask_enable = FIELD_DP16(s->mask_enable, INA230_MASK_ENABLE, CVRF, 1); > + > + trace_ina230_convert(s->description, s->shunt_reg, s->bus_reg, > + s->current_reg, s->power_reg); > + > + ina230_sample_alert(s, shunt_en, bus_en); > +} > + > +/* > + * A physical input or the calibration changed. > + * - in continuous mode the device keeps converting and immediately tracks > + * the new value > + * - in a triggered mode it performs exactly one conversion per trigger > + */ > +static void ina230_input_update(INA230State *s) > +{ > + if (FIELD_EX16(s->config, INA230_CONFIG, MODE) & 0x4) { > + ina230_convert(s); > + } > +} > + > +static void ina230_reset_regs(INA230State *s) > +{ > + s->config = INA230_CONFIG_POR; > + s->calibration = 0; > + s->mask_enable = 0; > + s->alert_limit = 0; > + s->alert_latched = false; > + > + s->ptr = 0; > + s->len = 0; > + s->wr_hi = 0; > + s->rx_byte = 0; > + s->rx_word = 0; > + > + s->shunt_reg = 0; > + s->bus_reg = 0; > + s->current_reg = 0; > + s->power_reg = 0; > + > + ina230_refresh_alert(s); > +} > + > +static uint16_t ina230_read_reg(INA230State *s, uint8_t reg) > +{ > + uint16_t val; > + > + switch (reg) { > + case INA230_REG_CONFIG: > + val = s->config; > + break; > + case INA230_REG_SHUNT: > + val = s->shunt_reg; > + break; > + case INA230_REG_BUS: > + val = s->bus_reg; > + break; > + case INA230_REG_POWER: > + val = s->power_reg; > + break; > + case INA230_REG_CURRENT: > + val = s->current_reg; > + break; > + case INA230_REG_CALIBRATION: > + val = s->calibration; > + break; > + case INA230_REG_MASK_ENABLE: > + val = s->mask_enable; > + s->mask_enable = FIELD_DP16(s->mask_enable, INA230_MASK_ENABLE, > + CVRF, 0); > + s->alert_latched = false; > + ina230_refresh_alert(s); > + break; > + case INA230_REG_ALERT_LIMIT: > + val = s->alert_limit; > + break; > + case INA230_REG_DIE_ID: > + val = INA230_DIE_ID_VAL; > + break; > + default: > + qemu_log_mask(LOG_GUEST_ERROR, > + "%s: %s: read from undefined register 0x%02x\n", > + __func__, s->description, reg); > + val = 0; > + break; > + } > + > + trace_ina230_read(s->description, reg, val); > + return val; > +} > + > +static void ina230_write_reg(INA230State *s, uint8_t reg, uint16_t val) > +{ > + trace_ina230_write(s->description, reg, val); > + > + switch (reg) { > + case INA230_REG_CONFIG: > + if (val & R_INA230_CONFIG_RST_MASK) { > + trace_ina230_reset(s->description, "reg"); > + ina230_reset_regs(s); > + break; > + } > + s->config = val & ~R_INA230_CONFIG_RST_MASK; > + /* > + * The write itself releases a latched alert, so the release reaches > + * the ALERT pin before the conversion it triggers can relatch it. > + */ > + s->alert_latched = false; > + ina230_refresh_alert(s); > + ina230_convert(s); > + break; > + case INA230_REG_CALIBRATION: > + s->calibration = val & INA230_CAL_MASK; > + ina230_input_update(s); > + break; > + case INA230_REG_MASK_ENABLE: > + s->mask_enable = (val & INA230_ME_WMASK) | > + (s->mask_enable & INA230_ME_STATUS_MASK); > + ina230_refresh_alert(s); > + break; > + case INA230_REG_ALERT_LIMIT: > + s->alert_limit = val; > + ina230_refresh_alert(s); > + break; > + case INA230_REG_SHUNT: > + case INA230_REG_BUS: > + case INA230_REG_POWER: > + case INA230_REG_CURRENT: > + case INA230_REG_DIE_ID: > + 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 ina230_recv(I2CSlave *i2c) > +{ > + INA230State *s = INA230(i2c); > + uint8_t byte; > + > + if (s->rx_byte == 0) { > + s->rx_word = ina230_read_reg(s, s->ptr); > + byte = s->rx_word >> 8; > + s->rx_byte = 1; > + } else { > + byte = s->rx_word & 0xff; > + s->rx_byte = 0; > + } > + > + return byte; > +} > + > +static int ina230_send(I2CSlave *i2c, uint8_t data) > +{ > + INA230State *s = INA230(i2c); > + > + if (s->len == 0) { > + s->ptr = data; > + } else if ((s->len & 1) == 1) { > + s->wr_hi = data; > + } else { > + ina230_write_reg(s, s->ptr, ((uint16_t)s->wr_hi << 8) | data); > + } > + s->len++; > + > + return 0; > +} > + > +static int ina230_event(I2CSlave *i2c, enum i2c_event event) > +{ > + INA230State *s = INA230(i2c); > + > + s->len = 0; > + s->rx_byte = 0; > + > + return 0; > +} > + > +static void ina230_get_shunt(Object *obj, Visitor *v, const char *name, > + void *opaque, Error **errp) > +{ > + INA230State *s = INA230(obj); > + int64_t value = s->shunt_nv; > + > + visit_type_int(v, name, &value, errp); > +} > + > +static void ina230_set_shunt(Object *obj, Visitor *v, const char *name, > + void *opaque, Error **errp) > +{ > + INA230State *s = INA230(obj); > + int64_t value; > + > + if (!visit_type_int(v, name, &value, errp)) { > + return; > + } > + > + if (value < INA230_SHUNT_MIN_NV || value > INA230_SHUNT_MAX_NV) { > + error_setg(errp, > + "%s: shunt-voltage %" PRId64 " out of range (%d..%d nV)", > + s->description, value, > + INA230_SHUNT_MIN_NV, INA230_SHUNT_MAX_NV); > + return; > + } > + > + s->shunt_nv = (int32_t)value; > + ina230_input_update(s); > +} > + > +static void ina230_get_bus(Object *obj, Visitor *v, const char *name, > + void *opaque, Error **errp) > +{ > + INA230State *s = INA230(obj); > + int64_t value = s->bus_uv; > + > + visit_type_int(v, name, &value, errp); > +} > + > +static void ina230_set_bus(Object *obj, Visitor *v, const char *name, > + void *opaque, Error **errp) > +{ > + INA230State *s = INA230(obj); > + int64_t value; > + > + if (!visit_type_int(v, name, &value, errp)) { > + return; > + } > + > + if (value < 0 || value > INA230_BUS_MAX_UV) { > + error_setg(errp, > + "%s: bus-voltage %" PRId64 " out of range (0..%d uV)", > + s->description, value, INA230_BUS_MAX_UV); > + return; > + } > + > + s->bus_uv = (uint32_t)value; > + ina230_input_update(s); > +} > + > +static void ina230_reset_hold(Object *obj, ResetType type) > +{ > + INA230State *s = INA230(obj); > + > + trace_ina230_reset(s->description, "hw"); > + ina230_reset_regs(s); > +} > + > +static int ina230_post_load(void *opaque, int version_id) > +{ > + INA230State *s = opaque; > + > + ina230_refresh_alert(s); > + > + return 0; > +} > + > +static const VMStateDescription ina230_vmstate = { > + .name = "INA230", > + .version_id = 1, > + .minimum_version_id = 1, > + .post_load = ina230_post_load, > + .fields = (const VMStateField[]) { > + VMSTATE_UINT8(ptr, INA230State), > + VMSTATE_UINT8(len, INA230State), > + VMSTATE_UINT8(wr_hi, INA230State), > + VMSTATE_UINT8(rx_byte, INA230State), > + VMSTATE_UINT16(rx_word, INA230State), > + VMSTATE_UINT16(config, INA230State), > + VMSTATE_UINT16(calibration, INA230State), > + VMSTATE_UINT16(mask_enable, INA230State), > + VMSTATE_UINT16(alert_limit, INA230State), > + VMSTATE_UINT16(shunt_reg, INA230State), > + VMSTATE_UINT16(bus_reg, INA230State), > + VMSTATE_UINT16(current_reg, INA230State), > + VMSTATE_UINT16(power_reg, INA230State), > + VMSTATE_INT32(shunt_nv, INA230State), > + VMSTATE_UINT32(bus_uv, INA230State), > + VMSTATE_BOOL(alert_latched, INA230State), > + VMSTATE_I2C_SLAVE(parent_obj, INA230State), > + VMSTATE_END_OF_LIST() > + } > +}; > + > +static void ina230_initfn(Object *obj) > +{ > + object_property_add(obj, "shunt-voltage", "int", ina230_get_shunt, > + ina230_set_shunt, NULL, NULL); > + object_property_set_description(obj, "shunt-voltage", > + "Injected shunt (differential) voltage, " > + "in nanovolts"); > + object_property_add(obj, "bus-voltage", "int", ina230_get_bus, > + ina230_set_bus, NULL, NULL); > + object_property_set_description(obj, "bus-voltage", > + "Injected bus voltage, in microvolts"); > +} > + > +static void ina230_realize(DeviceState *dev, Error **errp) > +{ > + INA230State *s = INA230(dev); > + > + if (!s->description) { > + s->description = g_strdup(object_get_typename(OBJECT(dev))); > + } > + > + qdev_init_gpio_out(dev, &s->alert, 1); > +} > + > +static const Property ina230_properties[] = { > + DEFINE_PROP_STRING("description", INA230State, description), > +}; > + > +static void ina230_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 = ina230_event; > + ic->recv = ina230_recv; > + ic->send = ina230_send; > + dc->realize = ina230_realize; > + rc->phases.hold = ina230_reset_hold; > + dc->vmsd = &ina230_vmstate; > + device_class_set_props(dc, ina230_properties); > +} > + > +static const TypeInfo ina230_types[] = { > + { > + .name = TYPE_INA230, > + .parent = TYPE_I2C_SLAVE, > + .instance_init = ina230_initfn, > + .instance_size = sizeof(INA230State), > + .class_init = ina230_class_init, > + }, > +}; > + > +DEFINE_TYPES(ina230_types) > diff --git a/hw/sensor/meson.build b/hw/sensor/meson.build > index fe36c9ef91..b81f001fdf 100644 > --- a/hw/sensor/meson.build > +++ b/hw/sensor/meson.build > @@ -1,4 +1,5 @@ > system_ss.add(when: 'CONFIG_ADC128D818', if_true: files('adc128d818.c')) > +system_ss.add(when: 'CONFIG_INA230', if_true: files('ina230.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 5a3630f7bb..f809b6b506 100644 > --- a/hw/sensor/trace-events > +++ b/hw/sensor/trace-events > @@ -8,6 +8,13 @@ adc128d818_convert(const char *id, uint8_t channel, uint16_t > value) "%s ch %u va > adc128d818_irq(const char *id, bool level) "%s level %u" > adc128d818_reset(const char *id, const char *source) "%s %s" > > +# ina230.c > +ina230_read(const char *id, uint8_t reg, uint16_t value) "%s reg 0x%02x val > 0x%04x" > +ina230_write(const char *id, uint8_t reg, uint16_t value) "%s reg 0x%02x val > 0x%04x" > +ina230_convert(const char *id, uint16_t shunt, uint16_t bus, uint16_t > current, uint16_t power) "%s shunt 0x%04x bus 0x%04x current 0x%04x power > 0x%04x" > +ina230_alert(const char *id, bool level) "%s level %u" > +ina230_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" > diff --git a/include/hw/sensor/ina230.h b/include/hw/sensor/ina230.h > new file mode 100644 > index 0000000000..4394727d47 > --- /dev/null > +++ b/include/hw/sensor/ina230.h > @@ -0,0 +1,14 @@ > +/* > + * Texas Instruments INA230 current/voltage/power monitor with I2C interface > + * > + * Copyright (c) 2026 Meta Platforms, Inc. and affiliates. > + * > + * SPDX-License-Identifier: GPL-2.0-or-later > + */ > + > +#ifndef HW_SENSOR_INA230_H > +#define HW_SENSOR_INA230_H > + > +#define TYPE_INA230 "ina230" > + > +#endif >
