aboutsummaryrefslogtreecommitdiff
path: root/hw/openpic.c
diff options
context:
space:
mode:
Diffstat (limited to 'hw/openpic.c')
-rw-r--r--hw/openpic.c110
1 files changed, 76 insertions, 34 deletions
diff --git a/hw/openpic.c b/hw/openpic.c
index 6362497237..fe6cf67956 100644
--- a/hw/openpic.c
+++ b/hw/openpic.c
@@ -189,7 +189,9 @@ typedef struct IRQQueue {
typedef struct IRQSource {
uint32_t ivpr; /* IRQ vector/priority register */
uint32_t idr; /* IRQ destination register */
+ uint32_t destmask; /* bitmap of CPU destinations */
int last_cpu;
+ int output; /* IRQ level, e.g. OPENPIC_OUTPUT_INT */
int pending; /* TRUE if IRQ is pending */
} IRQSource;
@@ -264,8 +266,6 @@ typedef struct OpenPICState {
uint32_t irq_msi;
} OpenPICState;
-static void openpic_irq_raise(OpenPICState *opp, int n_CPU, IRQSource *src);
-
static inline void IRQ_setbit(IRQQueue *q, int n_IRQ)
{
q->pending++;
@@ -330,6 +330,19 @@ static void IRQ_local_pipe(OpenPICState *opp, int n_CPU, int n_IRQ)
dst = &opp->dst[n_CPU];
src = &opp->src[n_IRQ];
+
+ if (src->output != OPENPIC_OUTPUT_INT) {
+ /* On Freescale MPIC, critical interrupts ignore priority,
+ * IACK, EOI, etc. Before MPIC v4.1 they also ignore
+ * masking.
+ */
+ src->ivpr |= IVPR_ACTIVITY_MASK;
+ DPRINTF("%s: Raise OpenPIC output %d cpu %d irq %d\n",
+ __func__, src->output, n_CPU, n_IRQ);
+ qemu_irq_raise(opp->dst[n_CPU].irqs[src->output]);
+ return;
+ }
+
priority = IVPR_PRIORITY(src->ivpr);
if (priority <= dst->ctpr) {
/* Too low priority */
@@ -360,7 +373,7 @@ static void IRQ_local_pipe(OpenPICState *opp, int n_CPU, int n_IRQ)
return;
}
DPRINTF("Raise OpenPIC INT output cpu %d irq %d\n", n_CPU, n_IRQ);
- openpic_irq_raise(opp, n_CPU, src);
+ qemu_irq_raise(opp->dst[n_CPU].irqs[OPENPIC_OUTPUT_INT]);
}
/* update pic state because registers for n_IRQ have changed value */
@@ -403,7 +416,7 @@ static void openpic_update_irq(OpenPICState *opp, int n_IRQ)
} else if (!(src->ivpr & IVPR_MODE_MASK)) {
/* Directed delivery mode */
for (i = 0; i < opp->nb_cpus; i++) {
- if (src->idr & (1 << i)) {
+ if (src->destmask & (1 << i)) {
IRQ_local_pipe(opp, i, n_IRQ);
}
}
@@ -413,7 +426,7 @@ static void openpic_update_irq(OpenPICState *opp, int n_IRQ)
if (i == opp->nb_cpus) {
i = 0;
}
- if (src->idr & (1 << i)) {
+ if (src->destmask & (1 << i)) {
IRQ_local_pipe(opp, i, n_IRQ);
src->last_cpu = i;
break;
@@ -493,12 +506,45 @@ static inline uint32_t read_IRQreg_ivpr(OpenPICState *opp, int n_IRQ)
static inline void write_IRQreg_idr(OpenPICState *opp, int n_IRQ, uint32_t val)
{
- uint32_t tmp;
+ IRQSource *src = &opp->src[n_IRQ];
+ uint32_t normal_mask = (1UL << opp->nb_cpus) - 1;
+ uint32_t crit_mask = 0;
+ uint32_t mask = normal_mask;
+ int crit_shift = IDR_EP_SHIFT - opp->nb_cpus;
+ int i;
+
+ if (opp->flags & OPENPIC_FLAG_IDR_CRIT) {
+ crit_mask = mask << crit_shift;
+ mask |= crit_mask | IDR_EP;
+ }
+
+ src->idr = val & mask;
+ DPRINTF("Set IDR %d to 0x%08x\n", n_IRQ, src->idr);
+
+ if (opp->flags & OPENPIC_FLAG_IDR_CRIT) {
+ if (src->idr & crit_mask) {
+ if (src->idr & normal_mask) {
+ DPRINTF("%s: IRQ configured for multiple output types, using "
+ "critical\n", __func__);
+ }
+
+ src->output = OPENPIC_OUTPUT_CINT;
+ src->destmask = 0;
+
+ for (i = 0; i < opp->nb_cpus; i++) {
+ int n_ci = IDR_CI0_SHIFT - i;
- tmp = val & (IDR_EP | IDR_CI);
- tmp |= val & ((1ULL << MAX_CPU) - 1);
- opp->src[n_IRQ].idr = tmp;
- DPRINTF("Set IDR %d to 0x%08x\n", n_IRQ, opp->src[n_IRQ].idr);
+ if (src->idr & (1UL << n_ci)) {
+ src->destmask |= 1UL << i;
+ }
+ }
+ } else {
+ src->output = OPENPIC_OUTPUT_INT;
+ src->destmask = src->idr & normal_mask;
+ }
+ } else {
+ src->destmask = src->idr;
+ }
}
static inline void write_IRQreg_ivpr(OpenPICState *opp, int n_IRQ, uint32_t val)
@@ -878,7 +924,7 @@ static void openpic_cpu_write_internal(void *opaque, hwaddr addr,
IVPR_PRIORITY(src->ivpr) > dst->servicing.priority)) {
DPRINTF("Raise OpenPIC INT output cpu %d irq %d\n",
idx, n_IRQ);
- openpic_irq_raise(opp, idx, src);
+ qemu_irq_raise(opp->dst[idx].irqs[OPENPIC_OUTPUT_INT]);
}
break;
default:
@@ -1101,13 +1147,6 @@ static void openpic_save(QEMUFile* f, void *opaque)
qemu_put_be32s(f, &opp->spve);
qemu_put_be32s(f, &opp->tfrr);
- for (i = 0; i < opp->max_irq; i++) {
- qemu_put_be32s(f, &opp->src[i].ivpr);
- qemu_put_be32s(f, &opp->src[i].idr);
- qemu_put_sbe32s(f, &opp->src[i].last_cpu);
- qemu_put_sbe32s(f, &opp->src[i].pending);
- }
-
qemu_put_be32s(f, &opp->nb_cpus);
for (i = 0; i < opp->nb_cpus; i++) {
@@ -1120,6 +1159,13 @@ static void openpic_save(QEMUFile* f, void *opaque)
qemu_put_be32s(f, &opp->timers[i].tccr);
qemu_put_be32s(f, &opp->timers[i].tbcr);
}
+
+ for (i = 0; i < opp->max_irq; i++) {
+ qemu_put_be32s(f, &opp->src[i].ivpr);
+ qemu_put_be32s(f, &opp->src[i].idr);
+ qemu_put_sbe32s(f, &opp->src[i].last_cpu);
+ qemu_put_sbe32s(f, &opp->src[i].pending);
+ }
}
static void openpic_load_IRQ_queue(QEMUFile* f, IRQQueue *q)
@@ -1148,13 +1194,6 @@ static int openpic_load(QEMUFile* f, void *opaque, int version_id)
qemu_get_be32s(f, &opp->spve);
qemu_get_be32s(f, &opp->tfrr);
- for (i = 0; i < opp->max_irq; i++) {
- qemu_get_be32s(f, &opp->src[i].ivpr);
- qemu_get_be32s(f, &opp->src[i].idr);
- qemu_get_sbe32s(f, &opp->src[i].last_cpu);
- qemu_get_sbe32s(f, &opp->src[i].pending);
- }
-
qemu_get_be32s(f, &opp->nb_cpus);
for (i = 0; i < opp->nb_cpus; i++) {
@@ -1168,18 +1207,21 @@ static int openpic_load(QEMUFile* f, void *opaque, int version_id)
qemu_get_be32s(f, &opp->timers[i].tbcr);
}
- return 0;
-}
+ for (i = 0; i < opp->max_irq; i++) {
+ uint32_t val;
-static void openpic_irq_raise(OpenPICState *opp, int n_CPU, IRQSource *src)
-{
- int n_ci = IDR_CI0_SHIFT - n_CPU;
+ val = qemu_get_be32(f);
+ write_IRQreg_idr(opp, i, val);
+ val = qemu_get_be32(f);
+ write_IRQreg_ivpr(opp, i, val);
- if ((opp->flags & OPENPIC_FLAG_IDR_CRIT) && (src->idr & (1 << n_ci))) {
- qemu_irq_raise(opp->dst[n_CPU].irqs[OPENPIC_OUTPUT_CINT]);
- } else {
- qemu_irq_raise(opp->dst[n_CPU].irqs[OPENPIC_OUTPUT_INT]);
+ qemu_get_be32s(f, &opp->src[i].ivpr);
+ qemu_get_be32s(f, &opp->src[i].idr);
+ qemu_get_sbe32s(f, &opp->src[i].last_cpu);
+ qemu_get_sbe32s(f, &opp->src[i].pending);
}
+
+ return 0;
}
typedef struct MemReg {