Hi Brian,

+Peter for commit 3d7680fb18c ("bitops.h: Define bit operations
on 'uint32_t' arrays").

On 15/7/26 20:56, Brian Cain wrote:
From: Sid Manning <[email protected]>

The Hexagon DSP requires an L2VIC to route up to 1024
external interrupt sources through 4 VID output groups into
the core's 8 interrupt inputs.  This device model implements
the register interface, interrupt steering, and edge/level
type handling needed by the sysemu machine models.

Co-authored-by: Matheus Tavares Bernardino <[email protected]>
Co-authored-by: Damien Hedde <[email protected]>
Signed-off-by: Brian Cain <[email protected]>
---
  MAINTAINERS                    |   3 +
  docs/devel/hexagon-l2vic.rst   |  55 ++++
  docs/devel/index-internals.rst |   1 +
  include/hw/intc/hex-l2vic.h    |  87 ++++++
  target/hexagon/cpu.h           |   2 +
  hw/intc/hex-l2vic.c            | 532 +++++++++++++++++++++++++++++++++
  target/hexagon/cpu.c           |   2 +
  target/hexagon/op_helper.c     |  21 +-
  hw/hexagon/Kconfig             |   1 +
  hw/intc/Kconfig                |   3 +
  hw/intc/meson.build            |   2 +
  hw/intc/trace-events           |   4 +
  12 files changed, 712 insertions(+), 1 deletion(-)
  create mode 100644 docs/devel/hexagon-l2vic.rst
  create mode 100644 include/hw/intc/hex-l2vic.h
  create mode 100644 hw/intc/hex-l2vic.c

diff --git a/MAINTAINERS b/MAINTAINERS
index ecb8cfdc41e..7281a281632 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -250,6 +250,8 @@ M: Brian Cain <[email protected]>
  R: Pierrick Bouvier <[email protected]>
  S: Supported
  F: target/hexagon/
+F: hw/intc/hex-l2vic.c
+F: include/hw/intc/hex-l2vic.h
  X: target/hexagon/idef-parser/
  X: target/hexagon/gen_idef_parser_funcs.py
  F: linux-user/hexagon/
@@ -262,6 +264,7 @@ F: gdbstub/gdb-xml/hexagon*.xml
  F: docs/system/target-hexagon.rst
  F: docs/system/hexagon/
  F: docs/devel/hexagon-sys.rst
+F: docs/devel/hexagon-l2vic.rst
  T: git https://github.com/qualcomm/qemu.git hex-next
Hexagon idef-parser
diff --git a/docs/devel/hexagon-l2vic.rst b/docs/devel/hexagon-l2vic.rst
new file mode 100644
index 00000000000..9cb2a86871e
--- /dev/null
+++ b/docs/devel/hexagon-l2vic.rst
@@ -0,0 +1,55 @@
+.. SPDX-License-Identifier: GPL-2.0-or-later
+
+Hexagon L2 Vectored Interrupt Controller
+========================================
+
+
+.. code-block:: none
+
+                +-------------+                  +----------------------+
+                |    l2vic    |                  |     hexagon core     |
+                |             |                  |                      |
+    IRQ in ---->|             |                  |                      |
+    IRQ in ---->|    VID0    -|----------------->| irq2                 |
+      ...  ---->|             |                  |   |                  |
+    IRQ in ---->|             |                  |   v                  |
+                |     ...     |                  | <int steering>       |
+                |             |                  |  /   |    |    \     |
+    IRQ in ---->|             |                  | t0   t1   t2   t3 ...|
+    IRQ in ---->|    VIDN    -|                  |                      |
+      ...  ---->|             |                  |                      |
+    IRQ in ---->|             |                  | Global SREG File     |
+                |             |                  |                      |
+                |    State    |                  |                      |
+                | [      ] <--|==================|==> [ VID  ]          |
+                | [      ] <--|==================|==> [ VID1 ]          |
+                |             |                  |                      |
+                +-------------+                  +----------------------+
+
+L2VIC/Core Integration
+----------------------
+
+* hexagon core supports 8 external interrupt sources
+* l2vic supports 1024 input interrupts mapped among 4 output interrupts
+* l2vic has four output signals: { VID0, VID1, VID2, VID3 }
+* l2vic device has a bank of registers per-VID that can be used to query
+  the status or assert new interrupts.
+* Interrupts are 'steered' to threads based on { thread priority, 'EX' state,
+  thread interrupt mask, thread interrupt enable, global interrupt enable,
+  etc. }.
+* Any hardware thread could conceivably handle any input interrupt, dependent
+  on state.
+* The system register transfer instruction can read the VID0-VID3 values from
+  the l2vic when reading from hexagon core system registers "VID" and "VID1".
+* When l2vic VID0 has multiple active interrupts, it pulses the VID0 output
+  IRQ and stores the IRQ number for the VID0 register field.  Only after this
+  interrupt is cleared can the l2vic pulse the VID0 output IRQ again and 
provide
+  the next interrupt number on the VID0 register.
+* The ``ciad`` instruction clears the l2vic input interrupt and un-disables the
+  core interrupt.  If some/an l2vic VID0 interrupt is pending when this occurs,
+  the next interrupt should fire and any subsequent reads of the VID register
+  should reflect the newly raised interrupt.
+* In QEMU, on an external interrupt or an unmasked-pending interrupt,
+  all vCPUs are triggered (has_work==true) and each will grab the IO lock
+  while considering the steering logic to determine whether they're the thread
+  that must handle the interrupt.
diff --git a/docs/devel/index-internals.rst b/docs/devel/index-internals.rst
index b89bab9b306..84985973490 100644
--- a/docs/devel/index-internals.rst
+++ b/docs/devel/index-internals.rst
@@ -15,6 +15,7 @@ Details about QEMU's various subsystems including how to add 
features to them.
     clocks
     ebpf_rss
     hexagon-sys
+   hexagon-l2vic
     migration/index
     multi-process
     reset
diff --git a/include/hw/intc/hex-l2vic.h b/include/hw/intc/hex-l2vic.h
new file mode 100644
index 00000000000..f6519bcd6c2
--- /dev/null
+++ b/include/hw/intc/hex-l2vic.h
@@ -0,0 +1,87 @@
+/*
+ * QEMU L2VIC Interrupt Controller
+ *
+ * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries.
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#ifndef HW_INTC_HEX_L2VIC_H
+#define HW_INTC_HEX_L2VIC_H
+
+#include "qom/object.h"
+
+#define L2VIC_VID_GRP_0 0x0 /* Read */
+#define L2VIC_VID_GRP_1 0x4 /* Read */
+#define L2VIC_VID_GRP_2 0x8 /* Read */
+#define L2VIC_VID_GRP_3 0xC /* Read */
+#define L2VIC_INT_ENABLEn 0x100 /* Read/Write */
+#define L2VIC_INT_ENABLE_CLEARn 0x180 /* Write */
+#define L2VIC_INT_ENABLE_SETn 0x200 /* Write */
+#define L2VIC_INT_TYPEn 0x280 /* Read/Write */
+#define L2VIC_INT_STATUSn 0x380 /* Read */
+#define L2VIC_INT_CLEARn 0x400 /* Write */
+#define L2VIC_SOFT_INTn 0x480 /* Write */
+#define L2VIC_INT_PENDINGn 0x500 /* Read */
+#define L2VIC_INT_GRPn_0 0x600 /* Read/Write */
+#define L2VIC_INT_GRPn_1 0x680 /* Read/Write */
+#define L2VIC_INT_GRPn_2 0x700 /* Read/Write */
+#define L2VIC_INT_GRPn_3 0x780 /* Read/Write */
+
+#define L2VIC_INTERRUPT_MAX 1024
+/*
+ * Note about l2vic groups:
+ * Each interrupt to L2VIC can be configured to associate with one of
+ * four groups.
+ * Group 0 interrupts go to IRQ2 via VID 0 (SSR: 0xC2, the default)
+ * Group 1 interrupts go to IRQ3 via VID 1 (SSR: 0xC3)
+ * Group 2 interrupts go to IRQ4 via VID 2 (SSR: 0xC4)
+ * Group 3 interrupts go to IRQ5 via VID 3 (SSR: 0xC5)
+ */
+
+#define TYPE_HEX_L2VIC "hex-l2vic"
+/*
+ * L2VIC Interface for CPU/GlobalReg interaction
+ */
+#define TYPE_HEX_L2VIC_INTERFACE "hex-l2vic-if"
+
+typedef struct HexL2VicInterface HexL2VicInterface;
+
+typedef struct HexL2VicInterfaceClass {
+    InterfaceClass parent_class;
+
+    /* Read VID register for given group */
+    uint32_t (*read_vid)(HexL2VicInterface *l2vic, uint32_t group);
+
+    /* Update VID register value */
+    void (*update_vid)(HexL2VicInterface *l2vic, uint32_t group,
+                        uint32_t value);
+
+    /* Clear interrupt using CIAD instruction */
+    void (*clear_interrupt)(HexL2VicInterface *l2vic);
+} HexL2VicInterfaceClass;
+
+DECLARE_OBJ_CHECKERS(HexL2VicInterface, HexL2VicInterfaceClass,
+                     HEX_L2VIC_INTERFACE, TYPE_HEX_L2VIC_INTERFACE);
+
+/* Convenience functions for interface users */
+static inline uint32_t l2vic_read_vid(HexL2VicInterface *l2vic,
+                                       uint32_t group)
+{
+    HexL2VicInterfaceClass *k = HEX_L2VIC_INTERFACE_GET_CLASS(l2vic);
+    return k->read_vid(l2vic, group);
+}
+
+static inline void l2vic_update_vid(HexL2VicInterface *l2vic, uint32_t group,
+                                     uint32_t value)
+{
+    HexL2VicInterfaceClass *k = HEX_L2VIC_INTERFACE_GET_CLASS(l2vic);
+    k->update_vid(l2vic, group, value);
+}
+
+static inline void l2vic_clear_interrupt(HexL2VicInterface *l2vic)
+{
+    HexL2VicInterfaceClass *k = HEX_L2VIC_INTERFACE_GET_CLASS(l2vic);
+    k->clear_interrupt(l2vic);
+}
+
+#endif /* HW_INTC_HEX_L2VIC_H */
diff --git a/target/hexagon/cpu.h b/target/hexagon/cpu.h
index 7694fd91fa8..f6c3c639325 100644
--- a/target/hexagon/cpu.h
+++ b/target/hexagon/cpu.h
@@ -39,6 +39,7 @@ typedef struct HexagonGlobalRegState HexagonGlobalRegState;
  #include "qemu/bitmap.h"
#include "target/hexagon/reg_fields.h"
+#include "hw/intc/hex-l2vic.h"
#define NUM_PREGS 4
  #define TOTAL_PER_THREAD_REGS 64
@@ -198,6 +199,7 @@ struct ArchCPU {
      uint32_t boot_addr;
      HexagonGlobalRegState *globalregs;
      uint32_t htid;
+    HexL2VicInterface *l2vic;
  #endif
  };
diff --git a/hw/intc/hex-l2vic.c b/hw/intc/hex-l2vic.c
new file mode 100644
index 00000000000..6b2dc6bb091
--- /dev/null
+++ b/hw/intc/hex-l2vic.c
@@ -0,0 +1,532 @@
+/*
+ * QEMU L2VIC Interrupt Controller
+ *
+ * Arm PrimeCell PL190 Vector Interrupt Controller was used as a reference.
+ * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries.
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#include "qemu/osdep.h"
+#include "hw/core/irq.h"
+#include "hw/core/sysbus.h"
+#include "migration/vmstate.h"
+#include "qemu/lockable.h"
+#include "qemu/log.h"
+#include "qemu/module.h"
+#include "qemu/bitmap.h"
+#include "qemu/bitops.h"
+#include "hw/intc/hex-l2vic.h"
+#include "trace.h"
+
+static void bitmap32_write_word(uint32_t *bitmap, int word_offset, uint32_t 
val)
+{
+    bitmap[word_offset] = val;
+}
+
+static void bitmap32_clear_word(uint32_t *bitmap, int word_offset,
+                                uint32_t mask)
+{
+    bitmap[word_offset] &= ~mask;
+}
+
+static void bitmap32_set_word(uint32_t *bitmap, int word_offset, uint32_t mask)
+{
+    bitmap[word_offset] |= mask;
+}
+
+static uint32_t bitmap32_read_word(uint32_t *bitmap, int word_offset)
+{
+    return bitmap[word_offset];
+}
+
+OBJECT_DECLARE_SIMPLE_TYPE(HexL2VICState, HEX_L2VIC)
+
+#define SLICE_MAX (L2VIC_INTERRUPT_MAX / 32)
+#define L2VIC_REG_RANGE_SIZE 0x80
+
+typedef struct HexL2VICState {
+    SysBusDevice parent_obj;
+
+    QemuMutex active;

Can you describe what is protected by this mutex?

+    MemoryRegion iomem;
+    MemoryRegion fast_iomem;
+    /*
+     * offset 0:vid group 0 etc, 10 bits in each group
+     * are used:
+     */
+    uint32_t vid_group[4];
+    uint32_t vid0;
+    /* Enable interrupt source */
+    DECLARE_BITMAP32(int_enable, L2VIC_INTERRUPT_MAX) QEMU_ALIGNED(16);

This bitmap alignment here is dubious. I suppose it relates to my other
comment on find_first_bit32(). If the callee requires alignment, then
the QEMU_ALIGNED() attribute should be used at the definition IMO, not
here. Peter, WDYT?

+    /* Present for debugging, not used */
+    DECLARE_BITMAP32(int_pending, L2VIC_INTERRUPT_MAX) QEMU_ALIGNED(16);
+    /* Which enabled interrupt is active */
+    DECLARE_BITMAP32(int_status, L2VIC_INTERRUPT_MAX) QEMU_ALIGNED(16);

Apparently int_status is mutex-protected.

+    /* Edge or Level interrupt */
+    DECLARE_BITMAP32(int_type, L2VIC_INTERRUPT_MAX) QEMU_ALIGNED(16);
+    /* L2 interrupt group 0-3 0x600-0x7FF */
+    DECLARE_BITMAP32(int_group_n0, L2VIC_INTERRUPT_MAX) QEMU_ALIGNED(16);
+    DECLARE_BITMAP32(int_group_n1, L2VIC_INTERRUPT_MAX) QEMU_ALIGNED(16);
+    DECLARE_BITMAP32(int_group_n2, L2VIC_INTERRUPT_MAX) QEMU_ALIGNED(16);
+    DECLARE_BITMAP32(int_group_n3, L2VIC_INTERRUPT_MAX) QEMU_ALIGNED(16);
+    qemu_irq irq[8];
+} HexL2VICState;
+
+typedef enum {
+    L2VIC_OP_WRITE,
+    L2VIC_OP_CLEAR,
+    L2VIC_OP_SET,
+} L2VicWriteOp;
+
+typedef struct {
+    hwaddr base;
+    size_t state_offset;
+    L2VicWriteOp write_op;
+    bool write_only;
+} L2VicRegRange;
+
+static const L2VicRegRange l2vic_reg_ranges[] = {
+    { L2VIC_INT_ENABLEn, offsetof(HexL2VICState, int_enable),
+      L2VIC_OP_WRITE, false },
+    { L2VIC_INT_ENABLE_CLEARn, offsetof(HexL2VICState, int_enable),
+      L2VIC_OP_CLEAR, true },
+    { L2VIC_INT_ENABLE_SETn, offsetof(HexL2VICState, int_enable),
+      L2VIC_OP_SET, true },
+    { L2VIC_INT_TYPEn, offsetof(HexL2VICState, int_type),
+      L2VIC_OP_WRITE, false },
+    { L2VIC_INT_STATUSn, offsetof(HexL2VICState, int_status),
+      L2VIC_OP_WRITE, false },
+    { L2VIC_INT_CLEARn, offsetof(HexL2VICState, int_status),
+      L2VIC_OP_CLEAR, true },
+    { L2VIC_SOFT_INTn, offsetof(HexL2VICState, int_enable),
+      L2VIC_OP_SET, true },
+    { L2VIC_INT_PENDINGn, offsetof(HexL2VICState, int_pending),
+      L2VIC_OP_WRITE, false },
+    { L2VIC_INT_GRPn_0, offsetof(HexL2VICState, int_group_n0),
+      L2VIC_OP_WRITE, false },
+    { L2VIC_INT_GRPn_1, offsetof(HexL2VICState, int_group_n1),
+      L2VIC_OP_WRITE, false },
+    { L2VIC_INT_GRPn_2, offsetof(HexL2VICState, int_group_n2),
+      L2VIC_OP_WRITE, false },
+    { L2VIC_INT_GRPn_3, offsetof(HexL2VICState, int_group_n3),
+      L2VIC_OP_WRITE, false },
+};
+
+static uint32_t *l2vic_state_bitmap(HexL2VICState *s, size_t state_offset)
+{
+    return (uint32_t *)((char *)s + state_offset);
+}
+
+static bool l2vic_reg_read_range(HexL2VICState *s, hwaddr offset,
+                                 uint64_t *value)
+{
+    int i;
+
+    for (i = 0; i < ARRAY_SIZE(l2vic_reg_ranges); i++) {
+        const L2VicRegRange *r = &l2vic_reg_ranges[i];
+
+        if (offset >= r->base &&
+            offset < r->base + L2VIC_REG_RANGE_SIZE) {
+            if (r->write_only) {
+                *value = 0;
+            } else {
+                uint32_t *bitmap = l2vic_state_bitmap(s, r->state_offset);
+                *value = bitmap32_read_word(bitmap,
+                                            (offset - r->base) >> 2);
+            }
+            return true;
+        }
+    }
+    return false;
+}
+
+static bool l2vic_reg_write_range(HexL2VICState *s, hwaddr offset,
+                                  uint32_t val)
+{
+    int i;
+
+    for (i = 0; i < ARRAY_SIZE(l2vic_reg_ranges); i++) {
+        const L2VicRegRange *r = &l2vic_reg_ranges[i];
+
+        if (offset >= r->base &&
+            offset < r->base + L2VIC_REG_RANGE_SIZE) {
+            uint32_t *bitmap = l2vic_state_bitmap(s, r->state_offset);
+            int word = (offset - r->base) >> 2;
+
+            switch (r->write_op) {
+            case L2VIC_OP_WRITE:
+                bitmap32_write_word(bitmap, word, val);
+                break;
+            case L2VIC_OP_CLEAR:
+                bitmap32_clear_word(bitmap, word, val);
+                break;
+            case L2VIC_OP_SET:
+                bitmap32_set_word(bitmap, word, val);
+                break;
+            default:
+                g_assert_not_reached();
+            }
+            return true;
+        }
+    }
+    return false;
+}
+
+/*
+ * Find out if this irq is associated with a group other than
+ * the default group
+ */
+static uint32_t *get_int_group(HexL2VICState *s, int irq)
+{
+    int n = irq & 0x1f;
+    if (n < 8) {
+        return s->int_group_n0;
+    }
+    if (n < 16) {
+        return s->int_group_n1;
+    }
+    if (n < 24) {
+        return s->int_group_n2;
+    }
+    return s->int_group_n3;
+}
+
+static int find_slice(int irq)
+{
+    return irq / 32;
+}
+
+static int get_vid(HexL2VICState *s, int irq)
+{
+    uint32_t *group = get_int_group(s, irq);
+    uint32_t slice = group[find_slice(irq)];
+    /* Mask with 0x7 to remove the GRP:EN bit */
+    uint32_t val = slice >> ((irq & 0x7) * 4);
+    if (val & 0x8) {
+        return val & 0x7;
+    } else {
+        return 0;
+    }
+}
+
+static inline bool vid_active(HexL2VICState *s)
+{
+    /* scan all 1024 bits in int_status array */
+    const uint32_t size = L2VIC_INTERRUPT_MAX;
+    const uint32_t active_irq = find_first_bit32(s->int_status, size);

Is int_status protected here?

+    return active_irq != size;
+}
+
+static bool l2vic_update(HexL2VICState *s, int irq)
+{
+    bool pending;
+    bool enable;
+
+    if (vid_active(s)) {
+        return true;
+    }
+
+    pending = test_bit32(irq, s->int_pending);
+    enable = test_bit32(irq, s->int_enable);
+    if (pending && enable) {
+        int vid = get_vid(s, irq);
+        set_bit32(irq, s->int_status);

Is int_status protected here? Do we want to use the
set_bit32_atomic() alternative?

+        clear_bit32(irq, s->int_pending);
+        /*
+         * Only auto-disable for edge-triggered interrupts (type=1).
+         * Level-triggered interrupts (type=0, the default) keep their
+         * enable bit set across deliveries -- the firmware enables once
+         * and expects the interrupt to remain enabled.
+         */
+        if (test_bit32(irq, s->int_type)) {
+            clear_bit32(irq, s->int_enable);
+        }
+        s->vid0 = irq;
+        s->vid_group[vid] = irq;
+
+        qemu_irq_pulse(s->irq[vid + 2]);
+        trace_hex_l2vic_delivered(irq, vid);
+        return true;
+    }
+    return false;
+}
+
+static void l2vic_update_all(HexL2VICState *s)
+{
+    for (int i = 0; i < L2VIC_INTERRUPT_MAX; i++) {
+        if (l2vic_update(s, i)) {
+            /* once vid is active, no-one else can set it until ciad */
+            return;
+        }
+    }
+}
+
+static void l2vic_set_irq(void *opaque, int irq, int level)
+{
+    HexL2VICState *s = (HexL2VICState *)opaque;
+
+    QEMU_LOCK_GUARD(&s->active);

Ah, here you protect int_pending, described as "Present for debugging,
not used".

+    if (level) {
+        set_bit32(irq, s->int_pending);
+    }
+    l2vic_update(s, irq);
+}
+
+static void l2vic_write(void *opaque, hwaddr offset, uint64_t val,
+                        unsigned size)
+{
+    HexL2VICState *s = (HexL2VICState *)opaque;
+
+    QEMU_LOCK_GUARD(&s->active);

Here you protect the whole device via MMIO access.

+    trace_hex_l2vic_reg_write((unsigned)offset, (uint32_t)val);
+
+    if (!l2vic_reg_write_range(s, offset, val)) {
+        qemu_log_mask(LOG_UNIMP,
+                      "%s: offset 0x%" HWADDR_PRIx " unimplemented\n",
+                      __func__, offset);
+    }
+
+    /* SOFT_INT also sets pending for edge-triggered interrupts */
+    if (offset >= L2VIC_SOFT_INTn &&
+        offset < L2VIC_SOFT_INTn + L2VIC_REG_RANGE_SIZE && val) {
+        int irq = ctz32((uint32_t)val);
+        irq += ((offset - L2VIC_SOFT_INTn) >> 2) * 32;
+
+        if (test_bit32(irq, s->int_type)) {
+            set_bit32(irq, s->int_pending);
+        }
+    }
+
+    l2vic_update_all(s);
+}
+
+static uint64_t l2vic_read(void *opaque, hwaddr offset, unsigned size)
+{
+    uint64_t value;
+    HexL2VICState *s = (HexL2VICState *)opaque;
+
+    QEMU_LOCK_GUARD(&s->active);

Yet another whole device via MMIO access guard. What are the other
uses you want to protect?

+
+    if (offset <= L2VIC_VID_GRP_3) {
+        value = s->vid_group[offset >> 2];
+    } else if (!l2vic_reg_read_range(s, offset, &value)) {
+        value = 0;
+        qemu_log_mask(LOG_GUEST_ERROR,
+                      "L2VIC: %s: offset 0x%" HWADDR_PRIx "\n", __func__,
+                      offset);
+    }
+
+    trace_hex_l2vic_reg_read((unsigned)offset, (uint32_t)value);
+    return value;
+}
+
+static const MemoryRegionOps l2vic_ops = {
+    .read = l2vic_read,
+    .write = l2vic_write,
+    .endianness = DEVICE_LITTLE_ENDIAN,
+    .valid.min_access_size = 4,
+    .valid.max_access_size = 4,
+    .valid.unaligned = false,
+};
+
+#define FASTL2VIC_ENABLE 0x0
+#define FASTL2VIC_DISABLE 0x1
+#define FASTL2VIC_INT 0x2
+
+static void fastl2vic_write(void *opaque, hwaddr offset, uint64_t val,
+                            unsigned size)
+{
+    if (offset == 0) {
+        uint32_t cmd = (val >> 16) & 0x3;
+        uint32_t irq = val & 0x3ff;
+        uint32_t slice = (irq / 32) * 4;
+        val = 1 << (irq % 32);
+
+        if (cmd == FASTL2VIC_ENABLE) {
+            l2vic_write(opaque, L2VIC_INT_ENABLE_SETn + slice, val, size);
+        } else if (cmd == FASTL2VIC_DISABLE) {
+            l2vic_write(opaque, L2VIC_INT_ENABLE_CLEARn + slice, val, size);
+        } else if (cmd == FASTL2VIC_INT) {
+            l2vic_write(opaque, L2VIC_SOFT_INTn + slice, val, size);
+        } else {
+            qemu_log_mask(LOG_GUEST_ERROR,
+                          "%s: invalid write cmd %" PRId32 "\n",
+                          __func__, cmd);
+        }
+        return;
+    }
+    qemu_log_mask(LOG_GUEST_ERROR, "%s: invalid write offset 0x%08" HWADDR_PRIx
+            "\n", __func__, offset);
+}
+
+static const MemoryRegionOps fastl2vic_ops = {
+    .write = fastl2vic_write,
+    .endianness = DEVICE_LITTLE_ENDIAN,
+    .valid.min_access_size = 4,
+    .valid.max_access_size = 4,
+    .valid.unaligned = false,
+};
+
+/* L2VIC Interface Implementation */
+static uint32_t l2vic_interface_read_vid_impl(HexL2VicInterface *iface,
+                                               uint32_t group)
+{
+    HexL2VICState *s = HEX_L2VIC(iface);
+    uint32_t result = 0;
+
+    QEMU_LOCK_GUARD(&s->active);

Ah, another whole device via MMIO access guard.

+    if (group == 0) {
+        /* VID register: VID1 (bits 16-31), VID0 (bits 0-15) */
+        result = deposit32(result, 0, 16, s->vid_group[0]);
+        result = deposit32(result, 16, 16, s->vid_group[1]);
+    } else if (group == 1) {
+        /* VID1 register: VID3 (bits 16-31), VID2 (bits 0-15) */
+        result = deposit32(result, 0, 16, s->vid_group[2]);
+        result = deposit32(result, 16, 16, s->vid_group[3]);
+    }
+    return result;
+}
+
+static void l2vic_interface_update_vid_impl(HexL2VicInterface *iface,
+                                            uint32_t group, uint32_t value)
+{
+    HexL2VICState *s = HEX_L2VIC(iface);
+
+    QEMU_LOCK_GUARD(&s->active);
+
+    if (group == 0) {
+        /* VID register: unpack VID0 and VID1 */
+        s->vid_group[0] = extract32(value, 0, 16);
+        s->vid_group[1] = extract32(value, 16, 16);
+    } else if (group == 1) {
+        /* VID1 register: unpack VID2 and VID3 */
+        s->vid_group[2] = extract32(value, 0, 16);
+        s->vid_group[3] = extract32(value, 16, 16);
+    }
+
+    l2vic_update_all(s);
+}
+
+static void l2vic_interface_clear_interrupt_impl(HexL2VicInterface *iface)
+{
+    HexL2VICState *s = HEX_L2VIC(iface);
+
+    QEMU_LOCK_GUARD(&s->active);

OK, this is apparently a non-MMIO call via the HexL2VicInterfaceClass.
Is that why we need this mutex? So far I can not see any consumer of
this interface in this series.

+    if (s->vid0 < L2VIC_INTERRUPT_MAX) {
+        clear_bit32(s->vid0, s->int_status);
+    }
+    l2vic_update_all(s);
+}
+
+static void l2vic_reset_hold(Object *obj, ResetType type G_GNUC_UNUSED)
+{
+    HexL2VICState *s = HEX_L2VIC(obj);
+
+    QEMU_LOCK_GUARD(&s->active);
+    memset(s->int_enable, 0, sizeof(s->int_enable));
+    memset(s->int_pending, 0, sizeof(s->int_pending));
+    memset(s->int_status, 0, sizeof(s->int_status));
+    memset(s->int_type, 0, sizeof(s->int_type));
+    memset(s->int_group_n0, 0, sizeof(s->int_group_n0));
+    memset(s->int_group_n1, 0, sizeof(s->int_group_n1));
+    memset(s->int_group_n2, 0, sizeof(s->int_group_n2));
+    memset(s->int_group_n3, 0, sizeof(s->int_group_n3));
+    memset(s->vid_group, 0, sizeof(s->vid_group));
+    s->vid0 = 0;
+
+    l2vic_update_all(s);
+}
+
+static void reset_irq_handler(void *opaque, int irq, int level)
+{
+    Object *obj = OBJECT(opaque);
+
+    if (level) {
+        l2vic_reset_hold(obj, RESET_TYPE_COLD);
+    }
+}
+
+static void l2vic_init(Object *obj)
+{
+    DeviceState *dev = DEVICE(obj);
+    HexL2VICState *s = HEX_L2VIC(obj);
+    SysBusDevice *sbd = SYS_BUS_DEVICE(obj);
+    int i;
+
+    memory_region_init_io(&s->iomem, obj, &l2vic_ops, s, "l2vic", 0x1000);
+    sysbus_init_mmio(sbd, &s->iomem);
+    memory_region_init_io(&s->fast_iomem, obj, &fastl2vic_ops, s, "fast",
+                          0x10000);
+    sysbus_init_mmio(sbd, &s->fast_iomem);
+
+    qdev_init_gpio_in(dev, l2vic_set_irq, L2VIC_INTERRUPT_MAX);
+    qdev_init_gpio_in_named(dev, reset_irq_handler, "reset", 1);
+    for (i = 0; i < 8; i++) {
+        sysbus_init_irq(sbd, &s->irq[i]);
+    }
+    qemu_mutex_init(&s->active);
+}
+
+static const VMStateDescription vmstate_l2vic = {
+    .name = "l2vic",
+    .version_id = 1,
+    .minimum_version_id = 1,
+    .fields =
+        (VMStateField[]){
+            VMSTATE_UINT32_ARRAY(vid_group, HexL2VICState, 4),
+            VMSTATE_UINT32(vid0, HexL2VICState),
+            VMSTATE_UINT32_ARRAY(int_enable, HexL2VICState, SLICE_MAX),
+            VMSTATE_UINT32_ARRAY(int_type, HexL2VICState, SLICE_MAX),
+            VMSTATE_UINT32_ARRAY(int_status, HexL2VICState, SLICE_MAX),
+            VMSTATE_UINT32_ARRAY(int_pending, HexL2VICState, SLICE_MAX),
+            VMSTATE_UINT32_ARRAY(int_group_n0, HexL2VICState, SLICE_MAX),
+            VMSTATE_UINT32_ARRAY(int_group_n1, HexL2VICState, SLICE_MAX),
+            VMSTATE_UINT32_ARRAY(int_group_n2, HexL2VICState, SLICE_MAX),
+            VMSTATE_UINT32_ARRAY(int_group_n3, HexL2VICState, SLICE_MAX),
+            VMSTATE_END_OF_LIST() }
+};
+
+static void l2vic_interface_class_init(ObjectClass *klass, const void *data)
+{
+    HexL2VicInterfaceClass *k = HEX_L2VIC_INTERFACE_CLASS(klass);
+
+    k->read_vid = l2vic_interface_read_vid_impl;
+    k->update_vid = l2vic_interface_update_vid_impl;
+    k->clear_interrupt = l2vic_interface_clear_interrupt_impl;
+}
+
+static void l2vic_class_init(ObjectClass *klass, const void *data)
+{
+    DeviceClass *dc = DEVICE_CLASS(klass);
+    ResettableClass *rc = RESETTABLE_CLASS(klass);
+
+    dc->vmsd = &vmstate_l2vic;
+    rc->phases.hold = l2vic_reset_hold;
+}
+
+static const TypeInfo l2vic_interface_info = {
+    .name = TYPE_HEX_L2VIC_INTERFACE,
+    .parent = TYPE_INTERFACE,
+    .class_size = sizeof(HexL2VicInterfaceClass),
+    .class_init = l2vic_interface_class_init,
+};
+
+static const TypeInfo l2vic_info = {
+    .name = TYPE_HEX_L2VIC,
+    .parent = TYPE_SYS_BUS_DEVICE,
+    .instance_size = sizeof(HexL2VICState),
+    .instance_init = l2vic_init,
+    .class_init = l2vic_class_init,
+    .interfaces = (InterfaceInfo[]) {
+        { TYPE_HEX_L2VIC_INTERFACE },
+        { }
+    },
+};
+
+static void l2vic_register_types(void)
+{
+    type_register_static(&l2vic_interface_info);
+    type_register_static(&l2vic_info);
+}
+
+type_init(l2vic_register_types)

Prefer DEFINE_TYPES() for multiple TypeInfo.


Reply via email to