Model CIU3 interrupt sources, ISC and IDT registers, destination
selection and CP0 interrupt delivery. Route UART and mailbox sources
through CIU3 while keeping the legacy CIU register block separate.
---
 hw/mips/octeon.c          | 483 ++++++++++++++++++++++++++++++++++++++
 hw/mips/octeon_internal.h |  15 ++
 2 files changed, 498 insertions(+)

diff --git a/hw/mips/octeon.c b/hw/mips/octeon.c
index eeca7ca67e..567be0905a 100644
--- a/hw/mips/octeon.c
+++ b/hw/mips/octeon.c
@@ -11,6 +11,7 @@
 #include "qemu/datadir.h"
 #include "qapi/error.h"
 #include "hw/core/clock.h"
+#include "hw/core/irq.h"
 #include "hw/core/boards.h"
 #include "hw/core/qdev-clock.h"
 #include "hw/core/qdev-properties.h"
@@ -74,6 +75,28 @@ OBJECT_DECLARE_SIMPLE_TYPE(OcteonIntcState, OCTEON_INTC)
 #define OCTEON_CIU3_PP_RST_PENDING  0x110
 #define OCTEON_CIU3_NMI             0x160
 #define OCTEON_CIU3_FUSE            0x1a0
+#define OCTEON_CIU3_IDT_CTL(idt)    ((idt) * 8 + 0x110000U)
+#define OCTEON_CIU3_IDT_PP(idt)     ((idt) * 32 + 0x120000U)
+#define OCTEON_CIU3_IDT_IO(idt)     ((idt) * 8 + 0x130000U)
+#define OCTEON_CIU3_DEST_PP_INT(cpu) ((cpu) * 8 + 0x200000U)
+#define OCTEON_CIU3_DEST_PP_INT_INTSN_SHIFT 32
+#define OCTEON_CIU3_DEST_PP_INT_INTR 0x0000000000000001ULL
+#define OCTEON_CIU3_IDT_CTL_IP_NUM  0x7
+#define OCTEON_CIU3_CP0_IRQ_BASE    2
+#define OCTEON_CIU3_ISC_CTL_BASE    0x80000000U
+#define OCTEON_CIU3_ISC_W1C_BASE    0x90000000U
+#define OCTEON_CIU3_ISC_W1S_BASE    0xa0000000U
+#define OCTEON_CIU3_ISC_CTL_IDT     0x0000000000ff0000ULL
+#define OCTEON_CIU3_ISC_CTL_IDT_SHIFT 16
+#define OCTEON_CIU3_ISC_CTL_IMP     0x0000000000008000ULL
+#define OCTEON_CIU3_ISC_CTL_EN      0x0000000000000002ULL
+#define OCTEON_CIU3_ISC_CTL_RAW     0x0000000000000001ULL
+#define OCTEON_CIU3_SRC_LEVEL       0x00000001U
+#define OCTEON_CIU3_SRC_RAW         0x00000002U
+#define OCTEON_CIU3_NINTSN          (1U << 20)
+#define OCTEON_CIU3_ISC_SIZE        (OCTEON_CIU3_NINTSN * 8)
+#define OCTEON_CIU3_MBOX_INTSN(cpu) ((cpu) + 0x4000U)
+#define OCTEON_CIU3_UART0_INTSN     0x8000
 
 /*
  * QEMU-only backing for per-core CVMSEG. Keep it outside guest DRAM;
@@ -126,6 +149,13 @@ struct OcteonIntcState {
     OcteonPeripheralState parent_obj;
     MemoryRegion ciu3;
     uint64_t ciu3_pp_rst;
+    uint32_t ciu3_src_state[OCTEON_CIU3_SRC_COUNT];
+    uint64_t ciu3_src_ctl[OCTEON_CIU3_SRC_COUNT];
+    uint64_t ciu3_idt_ctl[OCTEON_CIU3_IDT_COUNT];
+    uint64_t ciu3_idt_pp[OCTEON_CIU3_IDT_COUNT];
+    uint64_t ciu3_idt_io[OCTEON_CIU3_IDT_COUNT];
+    uint16_t ciu3_src_cursor[OCTEON_MAX_CPUS][OCTEON_CIU3_CP0_IRQ_COUNT];
+    uint8_t ciu3_irq_line[OCTEON_MAX_CPUS][OCTEON_CIU3_CP0_IRQ_COUNT];
 };
 
 uint64_t octeon_read64(uint64_t value, hwaddr addr, unsigned size)
@@ -189,6 +219,286 @@ static void octeon_validate_clocks(OcteonMachineState 
*oms)
     }
 }
 
+static bool octeon_ciu3_source_from_intsn(uint32_t intsn,
+                                          unsigned int *source)
+{
+    if (intsn == OCTEON_CIU3_UART0_INTSN) {
+        *source = OCTEON_CIU3_SRC_UART0;
+        return true;
+    }
+    if (intsn >= OCTEON_CIU3_MBOX_INTSN(0) &&
+        intsn < OCTEON_CIU3_MBOX_INTSN(OCTEON_MAX_CPUS)) {
+        *source = OCTEON_CIU3_SRC_MBOX0 +
+                  intsn - OCTEON_CIU3_MBOX_INTSN(0);
+        return true;
+    }
+
+    return false;
+}
+
+static uint32_t octeon_ciu3_source_intsn(unsigned int source)
+{
+    switch (source) {
+    case OCTEON_CIU3_SRC_UART0:
+        return OCTEON_CIU3_UART0_INTSN;
+    }
+
+    if (source >= OCTEON_CIU3_SRC_MBOX0 &&
+        source < OCTEON_CIU3_SRC_COUNT) {
+        return OCTEON_CIU3_MBOX_INTSN(source - OCTEON_CIU3_SRC_MBOX0);
+    }
+
+    g_assert_not_reached();
+}
+
+static bool octeon_ciu3_source_is_level(unsigned int source)
+{
+    switch (source) {
+    case OCTEON_CIU3_SRC_UART0:
+        return true;
+    }
+
+    return false;
+}
+
+static bool octeon_ciu3_set_level(OcteonState *s, unsigned int source,
+                                  int level)
+{
+    uint32_t old;
+    uint32_t new;
+    uint32_t cmp = qatomic_read(&s->intc->ciu3_src_state[source]);
+
+    do {
+        old = cmp;
+        if (level) {
+            new = old | OCTEON_CIU3_SRC_LEVEL | OCTEON_CIU3_SRC_RAW;
+        } else {
+            new = old & ~(OCTEON_CIU3_SRC_LEVEL | OCTEON_CIU3_SRC_RAW);
+        }
+        if (new == old) {
+            return false;
+        }
+        cmp = qatomic_cmpxchg(&s->intc->ciu3_src_state[source], old, new);
+    } while (old != cmp);
+
+    return true;
+}
+
+static void octeon_ciu3_clear_raw(OcteonState *s, unsigned int source)
+{
+    uint32_t old;
+    uint32_t new;
+    uint32_t cmp = qatomic_read(&s->intc->ciu3_src_state[source]);
+
+    do {
+        old = cmp;
+        if (octeon_ciu3_source_is_level(source) &&
+            (old & OCTEON_CIU3_SRC_LEVEL)) {
+            new = old | OCTEON_CIU3_SRC_RAW;
+        } else {
+            new = old & ~OCTEON_CIU3_SRC_RAW;
+        }
+        if (new == old) {
+            return;
+        }
+        cmp = qatomic_cmpxchg(&s->intc->ciu3_src_state[source], old, new);
+    } while (old != cmp);
+}
+
+static bool octeon_ciu3_isc_decode(hwaddr reg, uint32_t base,
+                                   uint32_t *intsn)
+{
+    if (reg < base || reg >= base + OCTEON_CIU3_ISC_SIZE) {
+        return false;
+    }
+
+    *intsn = (reg - base) >> 3;
+    return true;
+}
+
+static uint64_t octeon_ciu3_isc_read(OcteonState *s, uint32_t intsn)
+{
+    unsigned int src;
+    uint64_t value;
+
+    if (!octeon_ciu3_source_from_intsn(intsn, &src)) {
+        return 0;
+    }
+
+    value = qatomic_read(&s->intc->ciu3_src_ctl[src]) | 
OCTEON_CIU3_ISC_CTL_IMP;
+    if (qatomic_read(&s->intc->ciu3_src_state[src]) & OCTEON_CIU3_SRC_RAW) {
+        value |= OCTEON_CIU3_ISC_CTL_RAW;
+    }
+    return value;
+}
+
+static bool octeon_ciu3_source_pending(OcteonState *s, unsigned int cpu_index,
+                                       unsigned int irq, unsigned int src)
+{
+    uint64_t ctl = qatomic_read(&s->intc->ciu3_src_ctl[src]);
+    unsigned int idt;
+    unsigned int output;
+
+    if (!(qatomic_read(&s->intc->ciu3_src_state[src]) & OCTEON_CIU3_SRC_RAW) ||
+        !(ctl & OCTEON_CIU3_ISC_CTL_EN)) {
+        return false;
+    }
+
+    idt = (ctl & OCTEON_CIU3_ISC_CTL_IDT) >>
+          OCTEON_CIU3_ISC_CTL_IDT_SHIFT;
+    if (idt >= OCTEON_CIU3_IDT_COUNT) {
+        return false;
+    }
+
+    output = qatomic_read(&s->intc->ciu3_idt_ctl[idt]) &
+             OCTEON_CIU3_IDT_CTL_IP_NUM;
+    if (output >= OCTEON_CIU3_CP0_IRQ_COUNT ||
+        irq != OCTEON_CIU3_CP0_IRQ_BASE + output) {
+        return false;
+    }
+
+    return qatomic_read(&s->intc->ciu3_idt_pp[idt]) & (1ULL << cpu_index);
+}
+
+static bool octeon_ciu3_pending_intsn(OcteonState *s, unsigned int cpu_index,
+                                      unsigned int irq, uint32_t *intsn)
+{
+    unsigned int src;
+
+    for (src = 0; src < OCTEON_CIU3_SRC_COUNT; src++) {
+        if (octeon_ciu3_source_pending(s, cpu_index, irq, src)) {
+            *intsn = octeon_ciu3_source_intsn(src);
+            return true;
+        }
+    }
+
+    return false;
+}
+
+static bool octeon_ciu3_pending_intsn_from(OcteonState *s,
+                                           unsigned int cpu_index,
+                                           unsigned int irq,
+                                           unsigned int first_src,
+                                           uint32_t *intsn,
+                                           unsigned int *source)
+{
+    unsigned int i;
+    unsigned int src;
+
+    for (i = 0; i < OCTEON_CIU3_SRC_COUNT; i++) {
+        src = (first_src + i) % OCTEON_CIU3_SRC_COUNT;
+        if (octeon_ciu3_source_pending(s, cpu_index, irq, src)) {
+            *intsn = octeon_ciu3_source_intsn(src);
+            *source = src;
+            return true;
+        }
+    }
+
+    return false;
+}
+
+static bool octeon_ciu3_pending_dest_intsn(OcteonState *s,
+                                           unsigned int cpu_index,
+                                           uint32_t *intsn)
+{
+    unsigned int irq;
+    unsigned int irq_index;
+    unsigned int src;
+
+    for (irq = OCTEON_CIU3_CP0_IRQ_BASE;
+         irq < OCTEON_CIU3_CP0_IRQ_BASE + OCTEON_CIU3_CP0_IRQ_COUNT;
+         irq++) {
+        irq_index = irq - OCTEON_CIU3_CP0_IRQ_BASE;
+        if (octeon_ciu3_pending_intsn_from(s, cpu_index, irq,
+                qatomic_read(&s->intc->ciu3_src_cursor[cpu_index][irq_index]),
+                intsn, &src)) {
+            qatomic_set(&s->intc->ciu3_src_cursor[cpu_index][irq_index],
+                        (src + 1) % OCTEON_CIU3_SRC_COUNT);
+            return true;
+        }
+    }
+
+    return false;
+}
+
+static void octeon_ciu3_set_cpu_irq(OcteonState *s, unsigned int cpu_index,
+                                    unsigned int irq, bool level)
+{
+    CPUMIPSState *env = &s->cpu[cpu_index].cpu->env;
+    unsigned int irq_index = irq - OCTEON_CIU3_CP0_IRQ_BASE;
+    uint8_t new_level = level;
+    uint8_t old_level;
+
+    old_level = qatomic_xchg(&s->intc->ciu3_irq_line[cpu_index][irq_index],
+                             new_level);
+    if (old_level == new_level) {
+        return;
+    }
+
+    qemu_set_irq(env->irq[irq], level);
+}
+
+static void octeon_intc_update_cpu(OcteonState *s, unsigned int cpu_index)
+{
+    uint32_t intsn;
+    unsigned int irq;
+    bool level;
+
+    if (cpu_index >= s->cpu_count || !s->cpu[cpu_index].cpu) {
+        return;
+    }
+
+    if (qatomic_read(&s->intc->ciu3_pp_rst) & (1ULL << cpu_index)) {
+        for (irq = OCTEON_CIU3_CP0_IRQ_BASE;
+             irq < OCTEON_CIU3_CP0_IRQ_BASE + OCTEON_CIU3_CP0_IRQ_COUNT;
+             irq++) {
+            octeon_ciu3_set_cpu_irq(s, cpu_index, irq, false);
+        }
+        return;
+    }
+
+    for (irq = OCTEON_CIU3_CP0_IRQ_BASE;
+         irq < OCTEON_CIU3_CP0_IRQ_BASE + OCTEON_CIU3_CP0_IRQ_COUNT;
+         irq++) {
+        level = false;
+        if (octeon_ciu3_pending_intsn(s, cpu_index, irq, &intsn)) {
+            level = true;
+        }
+
+        octeon_ciu3_set_cpu_irq(s, cpu_index, irq, level);
+    }
+}
+
+static void octeon_intc_request_cpu_update(OcteonState *s,
+                                           unsigned int cpu_index)
+{
+    if (cpu_index >= s->cpu_count || !s->cpu[cpu_index].cpu) {
+        return;
+    }
+
+    octeon_intc_update_cpu(s, cpu_index);
+}
+
+static void octeon_intc_update_cpu_mask(OcteonState *s, uint64_t cpu_mask)
+{
+    unsigned int cpu;
+
+    for (cpu = 0; cpu < s->cpu_count; cpu++) {
+        if (cpu_mask & (1ULL << cpu)) {
+            octeon_intc_request_cpu_update(s, cpu);
+        }
+    }
+}
+
+static void octeon_intc_update_all_cpus(OcteonState *s)
+{
+    unsigned int cpu;
+
+    for (cpu = 0; cpu < s->cpu_count; cpu++) {
+        octeon_intc_request_cpu_update(s, cpu);
+    }
+}
+
 static uint64_t octeon_present_cpu_mask(OcteonState *s)
 {
     return (1ULL << s->cpu_count) - 1;
@@ -199,6 +509,24 @@ static uint64_t octeon_secondary_cpu_mask(OcteonState *s)
     return octeon_present_cpu_mask(s) & ~1ULL;
 }
 
+static uint64_t octeon_ciu3_ctl_cpu_mask(OcteonState *s, uint64_t ctl)
+{
+    unsigned int idt = (ctl & OCTEON_CIU3_ISC_CTL_IDT) >>
+                       OCTEON_CIU3_ISC_CTL_IDT_SHIFT;
+
+    if (idt >= OCTEON_CIU3_IDT_COUNT) {
+        return 0;
+    }
+
+    return qatomic_read(&s->intc->ciu3_idt_pp[idt]);
+}
+
+static uint64_t octeon_ciu3_source_cpu_mask(OcteonState *s, unsigned int src)
+{
+    return octeon_ciu3_ctl_cpu_mask(
+        s, qatomic_read(&s->intc->ciu3_src_ctl[src]));
+}
+
 static void octeon_cpu_park_now(MIPSCPU *cpu)
 {
     CPUMIPSState *env = &cpu->env;
@@ -268,6 +596,9 @@ static uint64_t octeon_ciu3_read(void *opaque, hwaddr addr, 
unsigned size)
     OcteonState *s = opaque;
     hwaddr reg = addr & ~7ULL;
     uint64_t value;
+    uint32_t intsn;
+    unsigned int cpu;
+    unsigned int idt;
 
     if (reg == OCTEON_CIU3_PP_RST) {
         return octeon_read64(qatomic_read(&s->intc->ciu3_pp_rst), addr, size);
@@ -286,6 +617,51 @@ static uint64_t octeon_ciu3_read(void *opaque, hwaddr 
addr, unsigned size)
         return octeon_read64(value, addr, size);
     }
 
+    if (reg >= OCTEON_CIU3_IDT_CTL(0) &&
+        reg < OCTEON_CIU3_IDT_CTL(OCTEON_CIU3_IDT_COUNT)) {
+        idt = (reg - OCTEON_CIU3_IDT_CTL(0)) >> 3;
+        return octeon_read64(qatomic_read(&s->intc->ciu3_idt_ctl[idt]),
+                             addr, size);
+    }
+
+    if (reg >= OCTEON_CIU3_IDT_PP(0) &&
+        reg < OCTEON_CIU3_IDT_PP(OCTEON_CIU3_IDT_COUNT)) {
+        idt = (reg - OCTEON_CIU3_IDT_PP(0)) >> 5;
+        return octeon_read64(qatomic_read(&s->intc->ciu3_idt_pp[idt]),
+                             addr, size);
+    }
+
+    if (reg >= OCTEON_CIU3_IDT_IO(0) &&
+        reg < OCTEON_CIU3_IDT_IO(OCTEON_CIU3_IDT_COUNT)) {
+        idt = (reg - OCTEON_CIU3_IDT_IO(0)) >> 3;
+        return octeon_read64(qatomic_read(&s->intc->ciu3_idt_io[idt]),
+                             addr, size);
+    }
+
+    if (reg >= OCTEON_CIU3_DEST_PP_INT(0) &&
+        reg < OCTEON_CIU3_DEST_PP_INT(OCTEON_MAX_CPUS)) {
+        cpu = (reg - OCTEON_CIU3_DEST_PP_INT(0)) >> 3;
+        value = 0;
+        if (cpu < s->cpu_count) {
+            if (!(qatomic_read(&s->intc->ciu3_pp_rst) & (1ULL << cpu)) &&
+                octeon_ciu3_pending_dest_intsn(s, cpu, &intsn)) {
+                value = OCTEON_CIU3_DEST_PP_INT_INTR |
+                        ((uint64_t)intsn <<
+                         OCTEON_CIU3_DEST_PP_INT_INTSN_SHIFT);
+            }
+        }
+        return octeon_read64(value, addr, size);
+    }
+
+    if (octeon_ciu3_isc_decode(reg, OCTEON_CIU3_ISC_CTL_BASE, &intsn)) {
+        return octeon_read64(octeon_ciu3_isc_read(s, intsn), addr, size);
+    }
+
+    if (octeon_ciu3_isc_decode(reg, OCTEON_CIU3_ISC_W1C_BASE, &intsn) ||
+        octeon_ciu3_isc_decode(reg, OCTEON_CIU3_ISC_W1S_BASE, &intsn)) {
+        return octeon_read64(octeon_ciu3_isc_read(s, intsn), addr, size);
+    }
+
     return 0;
 }
 
@@ -295,7 +671,10 @@ static void octeon_ciu3_write(void *opaque, hwaddr addr,
     OcteonState *s = opaque;
     hwaddr reg = addr & ~7ULL;
     uint64_t old;
+    uint32_t intsn;
     unsigned int cpu;
+    unsigned int idt;
+    unsigned int src;
 
     if (reg == OCTEON_CIU3_PP_RST) {
         value = octeon_atomic_write64(&s->intc->ciu3_pp_rst, addr, value, size,
@@ -314,13 +693,89 @@ static void octeon_ciu3_write(void *opaque, hwaddr addr,
                 octeon_ciu3_start_cpu(s, cpu);
             }
         }
+        octeon_intc_update_all_cpus(s);
         return;
     }
 
     if (reg == OCTEON_CIU3_NMI) {
         octeon_ciu3_nmi(s, octeon_write64(0, addr, value, size));
+        octeon_intc_update_all_cpus(s);
+        return;
+    }
+
+    if (reg >= OCTEON_CIU3_IDT_CTL(0) &&
+        reg < OCTEON_CIU3_IDT_CTL(OCTEON_CIU3_IDT_COUNT)) {
+        idt = (reg - OCTEON_CIU3_IDT_CTL(0)) >> 3;
+        octeon_atomic_write64(&s->intc->ciu3_idt_ctl[idt], addr, value, size,
+                              UINT64_MAX, 0, NULL);
+        octeon_intc_update_cpu_mask(
+            s, qatomic_read(&s->intc->ciu3_idt_pp[idt]));
+        return;
+    }
+
+    if (reg >= OCTEON_CIU3_IDT_PP(0) &&
+        reg < OCTEON_CIU3_IDT_PP(OCTEON_CIU3_IDT_COUNT)) {
+        idt = (reg - OCTEON_CIU3_IDT_PP(0)) >> 5;
+        value = octeon_atomic_write64(&s->intc->ciu3_idt_pp[idt], addr, value,
+                                      size, UINT64_MAX, 0, &old);
+        octeon_intc_update_cpu_mask(s, old | value);
+        return;
+    }
+
+    if (reg >= OCTEON_CIU3_IDT_IO(0) &&
+        reg < OCTEON_CIU3_IDT_IO(OCTEON_CIU3_IDT_COUNT)) {
+        idt = (reg - OCTEON_CIU3_IDT_IO(0)) >> 3;
+        octeon_atomic_write64(&s->intc->ciu3_idt_io[idt], addr, value, size,
+                              UINT64_MAX, 0, NULL);
+        octeon_intc_update_cpu_mask(
+            s, qatomic_read(&s->intc->ciu3_idt_pp[idt]));
+        return;
+    }
+
+    if (octeon_ciu3_isc_decode(reg, OCTEON_CIU3_ISC_CTL_BASE, &intsn)) {
+        if (octeon_ciu3_source_from_intsn(intsn, &src)) {
+            value = octeon_atomic_write64(&s->intc->ciu3_src_ctl[src], addr,
+                                          value, size,
+                                          ~OCTEON_CIU3_ISC_CTL_RAW,
+                                          0, &old);
+            octeon_intc_update_cpu_mask(s,
+                                        octeon_ciu3_ctl_cpu_mask(s, old) |
+                                        octeon_ciu3_ctl_cpu_mask(s, value));
+        }
+        return;
+    }
+
+    if (octeon_ciu3_isc_decode(reg, OCTEON_CIU3_ISC_W1C_BASE, &intsn)) {
+        if (octeon_ciu3_source_from_intsn(intsn, &src)) {
+            old = qatomic_read(&s->intc->ciu3_src_ctl[src]);
+            if (value & OCTEON_CIU3_ISC_CTL_EN) {
+                qatomic_and(&s->intc->ciu3_src_ctl[src],
+                            ~OCTEON_CIU3_ISC_CTL_EN);
+            }
+            if (value & OCTEON_CIU3_ISC_CTL_RAW) {
+                octeon_ciu3_clear_raw(s, src);
+            }
+            octeon_intc_update_cpu_mask(s,
+                                        octeon_ciu3_ctl_cpu_mask(s, old) |
+                                        octeon_ciu3_source_cpu_mask(s, src));
+        }
         return;
     }
+
+    if (octeon_ciu3_isc_decode(reg, OCTEON_CIU3_ISC_W1S_BASE, &intsn)) {
+        if (octeon_ciu3_source_from_intsn(intsn, &src)) {
+            old = qatomic_read(&s->intc->ciu3_src_ctl[src]);
+            if (value & OCTEON_CIU3_ISC_CTL_EN) {
+                qatomic_or(&s->intc->ciu3_src_ctl[src], 
OCTEON_CIU3_ISC_CTL_EN);
+            }
+            if (value & OCTEON_CIU3_ISC_CTL_RAW) {
+                qatomic_or(&s->intc->ciu3_src_state[src], OCTEON_CIU3_SRC_RAW);
+            }
+            octeon_intc_update_cpu_mask(s,
+                                        octeon_ciu3_ctl_cpu_mask(s, old) |
+                                        octeon_ciu3_source_cpu_mask(s, src));
+        }
+    }
 }
 
 static const MemoryRegionOps octeon_ciu3_ops = {
@@ -333,6 +788,26 @@ static const MemoryRegionOps octeon_ciu3_ops = {
     },
 };
 
+void octeon_irq_set(void *opaque, int irq, int level)
+{
+    OcteonState *s = opaque;
+    unsigned int src;
+
+    switch (irq) {
+    case OCTEON_IRQ_UART:
+        src = OCTEON_CIU3_SRC_UART0;
+        if (!octeon_ciu3_set_level(s, src, level)) {
+            return;
+        }
+        break;
+    default:
+        return;
+    }
+
+    octeon_intc_update_cpu_mask(s, octeon_ciu3_source_cpu_mask(s, src));
+}
+
+
 static void octeon_cpu_after_reset(OcteonCPUState *cs)
 {
     CPUMIPSState *env = &cs->cpu->env;
@@ -568,6 +1043,14 @@ static void octeon_intc_reset_hold(Object *obj, ResetType 
type)
     }
 
     qatomic_set(&intc->ciu3_pp_rst, octeon_secondary_cpu_mask(s));
+    memset(intc->ciu3_src_state, 0, sizeof(intc->ciu3_src_state));
+    memset(intc->ciu3_src_ctl, 0, sizeof(intc->ciu3_src_ctl));
+    memset(intc->ciu3_idt_ctl, 0, sizeof(intc->ciu3_idt_ctl));
+    memset(intc->ciu3_idt_pp, 0, sizeof(intc->ciu3_idt_pp));
+    memset(intc->ciu3_idt_io, 0, sizeof(intc->ciu3_idt_io));
+    memset(intc->ciu3_src_cursor, 0, sizeof(intc->ciu3_src_cursor));
+    memset(intc->ciu3_irq_line, 0, sizeof(intc->ciu3_irq_line));
+    octeon_intc_update_all_cpus(s);
 }
 
 static void octeon_peripheral_class_init(ObjectClass *klass,
diff --git a/hw/mips/octeon_internal.h b/hw/mips/octeon_internal.h
index 884eed8994..fef88419b5 100644
--- a/hw/mips/octeon_internal.h
+++ b/hw/mips/octeon_internal.h
@@ -14,6 +14,20 @@
 #include "target/mips/cpu.h"
 
 #define OCTEON_MAX_CPUS             16
+#define OCTEON_CIU3_CP0_IRQ_COUNT   4
+#define OCTEON_CIU3_IDT_COUNT       (OCTEON_MAX_CPUS * 4)
+
+typedef enum OcteonIRQ {
+    OCTEON_IRQ_UART,
+    OCTEON_IRQ_COUNT,
+} OcteonIRQ;
+
+typedef enum OcteonCiu3Source {
+    OCTEON_CIU3_SRC_UART0,
+    OCTEON_CIU3_SRC_MBOX0,
+    OCTEON_CIU3_SRC_COUNT = OCTEON_CIU3_SRC_MBOX0 + OCTEON_MAX_CPUS,
+} OcteonCiu3Source;
+
 typedef struct OcteonState OcteonState;
 typedef struct OcteonRstState OcteonRstState;
 typedef struct OcteonIntcState OcteonIntcState;
@@ -48,5 +62,6 @@ struct OcteonState {
 uint64_t octeon_read64(uint64_t value, hwaddr addr, unsigned size);
 uint64_t octeon_write64(uint64_t old, hwaddr addr,
                         uint64_t value, unsigned size);
+void octeon_irq_set(void *opaque, int irq, int level);
 
 #endif
-- 
2.54.0


Reply via email to