[PATCH v7 12/22] hw/i3c/dw-i3c: Add IRQ MMIO behavior

Jamin Lin
2026-02-25T02:16:43Z

Signed-off-by: Joe Komlodi <komlodi@google.com>
Reviewed-by: Patrick Venture <venture@google.com>
Reviewed-by: Hao Wu <wuhaotsh@google.com>
Reviewed-by: Jamin Lin <jamin_lin@aspeedtech.com>
Signed-off-by: Jamin Lin <jamin_lin@aspeedtech.com>
---
hw/i3c/dw-i3c.c | 56 +++++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 56 insertions(+)

diff --git a/hw/i3c/dw-i3c.c b/hw/i3c/dw-i3c.c
index 7ca99fb87e..0b99c7edfb 100644
--- a/hw/i3c/dw-i3c.c
+++ b/hw/i3c/dw-i3c.c
@@ -17,6 +17,7 @@
#include "qapi/error.h"
#include "migration/vmstate.h"
#include "trace.h"
+#include "hw/core/irq.h"

REG32(DEVICE_CTRL, 0x00)
FIELD(DEVICE_CTRL, I3C_BROADCAST_ADDR_INC, 0, 1)
@@ -335,6 +336,46 @@ static const uint32_t dw_i3c_ro[DW_I3C_NR_REGS] = {
[R_SLAVE_CONFIG] = 0xffffffff,
};

+static void dw_i3c_update_irq(DWI3C *s)
+{
+ bool level = !!(s->regs[R_INTR_SIGNAL_EN] & s->regs[R_INTR_STATUS]);
+ qemu_set_irq(s->irq, level);
+}
+
+static uint32_t dw_i3c_intr_status_r(DWI3C *s)
+{
+ /* Only return the status whose corresponding EN bits are set. */
+ return s->regs[R_INTR_STATUS] & s->regs[R_INTR_STATUS_EN];
+}
+
+static void dw_i3c_intr_status_w(DWI3C *s, uint32_t val)
+{
+ /* INTR_STATUS[13:5] is w1c, other bits are RO. */
+ val &= 0x3fe0;
+ s->regs[R_INTR_STATUS] &= ~val;
+
+ dw_i3c_update_irq(s);
+}
+
+static void dw_i3c_intr_status_en_w(DWI3C *s, uint32_t val)
+{
+ s->regs[R_INTR_STATUS_EN] = val;
+ dw_i3c_update_irq(s);
+}
+
+static void dw_i3c_intr_signal_en_w(DWI3C *s, uint32_t val)
+{
+ s->regs[R_INTR_SIGNAL_EN] = val;
+ dw_i3c_update_irq(s);
+}
+
+static void dw_i3c_intr_force_w(DWI3C *s, uint32_t val)
+{
+ /* INTR_FORCE is WO, just set the corresponding INTR_STATUS bits. */
+ s->regs[R_INTR_STATUS] = val;
+ dw_i3c_update_irq(s);
+}
+
static uint64_t dw_i3c_read(void *opaque, hwaddr offset, unsigned size)
{
DWI3C *s = DW_I3C(opaque);
@@ -348,6 +389,9 @@ static uint64_t dw_i3c_read(void *opaque, hwaddr offset, unsigned size)
case R_INTR_FORCE:
value = 0;
break;
+ case R_INTR_STATUS:
+ value = dw_i3c_intr_status_r(s);
+ break;
default:
value = s->regs[addr];
break;
@@ -392,6 +436,18 @@ static void dw_i3c_write(void *opaque, hwaddr offset, uint64_t value,
break;
case R_RESET_CTRL:
break;
+ case R_INTR_STATUS:
+ dw_i3c_intr_status_w(s, val32);
+ break;
+ case R_INTR_STATUS_EN:
+ dw_i3c_intr_status_en_w(s, val32);
+ break;
+ case R_INTR_SIGNAL_EN:
+ dw_i3c_intr_signal_en_w(s, val32);
+ break;
+ case R_INTR_FORCE:
+ dw_i3c_intr_force_w(s, val32);
+ break;
default:
s->regs[addr] = val32;
break;
--
2.43.0