#define VIR_GENERIC 0x00000000 /* Generic Vendor ID */
#define GCR_RESET 0x80000000
+#define GCR_MODE_PASS 0x00000000
+#define GCR_MODE_MIXED 0x20000000
+#define GCR_MODE_PROXY 0x60000000
#define TBCR_CI 0x80000000 /* count inhibit */
#define TCCR_TOG 0x80000000 /* toggles when decrement to zero */
static int get_current_cpu(void)
{
+ CPUState *cpu_single_cpu;
+
if (!cpu_single_env) {
return -1;
}
- return cpu_single_env->cpu_index;
+ cpu_single_cpu = ENV_GET_CPU(cpu_single_env);
+ return cpu_single_cpu->cpu_index;
}
static uint32_t openpic_cpu_read_internal(void *opaque, hwaddr addr,
static void openpic_cpu_write_internal(void *opaque, hwaddr addr,
uint32_t val, int idx);
+typedef enum IRQType {
+ IRQ_TYPE_NORMAL = 0,
+ IRQ_TYPE_FSLINT, /* FSL internal interrupt -- level only */
+ IRQ_TYPE_FSLSPECIAL, /* FSL timer/IPI interrupt, edge, no polarity */
+} IRQType;
+
typedef struct IRQQueue {
/* Round up to the nearest 64 IRQs so that the queue length
* won't change when moving between 32 and 64 bit hosts.
int last_cpu;
int output; /* IRQ level, e.g. OPENPIC_OUTPUT_INT */
int pending; /* TRUE if IRQ is pending */
+ IRQType type;
+ bool level:1; /* level-triggered */
bool nomask:1; /* critical interrupts ignore mask on some FSL MPICs */
} IRQSource;
IRQQueue raised;
IRQQueue servicing;
qemu_irq *irqs;
+
+ /* Count of IRQ sources asserting on non-INT outputs */
+ uint32_t outputs_active[OPENPIC_OUTPUT_NB];
} IRQDest;
typedef struct OpenPICState {
uint32_t ivpr_reset;
uint32_t idr_reset;
uint32_t brr1;
+ uint32_t mpic_mode_mask;
/* Sub-regions */
MemoryRegion sub_io_mem[5];
static void IRQ_check(OpenPICState *opp, IRQQueue *q)
{
- int next, i;
- int priority;
+ int irq = -1;
+ int next = -1;
+ int priority = -1;
+
+ for (;;) {
+ irq = find_next_bit(q->queue, opp->max_irq, irq + 1);
+ if (irq == opp->max_irq) {
+ break;
+ }
- next = -1;
- priority = -1;
- for (i = 0; i < opp->max_irq; i++) {
- if (IRQ_testbit(q, i)) {
- DPRINTF("IRQ_check: irq %d set ivpr_pr=%d pr=%d\n",
- i, IVPR_PRIORITY(opp->src[i].ivpr), priority);
- if (IVPR_PRIORITY(opp->src[i].ivpr) > priority) {
- next = i;
- priority = IVPR_PRIORITY(opp->src[i].ivpr);
- }
+ DPRINTF("IRQ_check: irq %d set ivpr_pr=%d pr=%d\n",
+ irq, IVPR_PRIORITY(opp->src[irq].ivpr), priority);
+
+ if (IVPR_PRIORITY(opp->src[irq].ivpr) > priority) {
+ next = irq;
+ priority = IVPR_PRIORITY(opp->src[irq].ivpr);
}
}
+
q->next = next;
q->priority = priority;
}
return q->next;
}
-static void IRQ_local_pipe(OpenPICState *opp, int n_CPU, int n_IRQ)
+static void IRQ_local_pipe(OpenPICState *opp, int n_CPU, int n_IRQ,
+ bool active, bool was_active)
{
IRQDest *dst;
IRQSource *src;
dst = &opp->dst[n_CPU];
src = &opp->src[n_IRQ];
+ DPRINTF("%s: IRQ %d active %d was %d\n",
+ __func__, n_IRQ, active, was_active);
+
if (src->output != OPENPIC_OUTPUT_INT) {
+ DPRINTF("%s: output %d irq %d active %d was %d count %d\n",
+ __func__, src->output, n_IRQ, active, was_active,
+ dst->outputs_active[src->output]);
+
/* 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]);
+ if (active) {
+ if (!was_active && dst->outputs_active[src->output]++ == 0) {
+ DPRINTF("%s: Raise OpenPIC output %d cpu %d irq %d\n",
+ __func__, src->output, n_CPU, n_IRQ);
+ qemu_irq_raise(dst->irqs[src->output]);
+ }
+ } else {
+ if (was_active && --dst->outputs_active[src->output] == 0) {
+ DPRINTF("%s: Lower OpenPIC output %d cpu %d irq %d\n",
+ __func__, src->output, n_CPU, n_IRQ);
+ qemu_irq_lower(dst->irqs[src->output]);
+ }
+ }
+
return;
}
priority = IVPR_PRIORITY(src->ivpr);
- if (priority <= dst->ctpr) {
- /* Too low priority */
- DPRINTF("%s: IRQ %d has too low priority on CPU %d\n",
- __func__, n_IRQ, n_CPU);
- return;
- }
- if (IRQ_testbit(&dst->raised, n_IRQ)) {
- /* Interrupt miss */
- DPRINTF("%s: IRQ %d was missed on CPU %d\n",
- __func__, n_IRQ, n_CPU);
- return;
- }
- src->ivpr |= IVPR_ACTIVITY_MASK;
- IRQ_setbit(&dst->raised, n_IRQ);
- if (priority < dst->raised.priority) {
- /* An higher priority IRQ is already raised */
- DPRINTF("%s: IRQ %d is hidden by raised IRQ %d on CPU %d\n",
- __func__, n_IRQ, dst->raised.next, n_CPU);
- return;
+
+ /* Even if the interrupt doesn't have enough priority,
+ * it is still raised, in case ctpr is lowered later.
+ */
+ if (active) {
+ IRQ_setbit(&dst->raised, n_IRQ);
+ } else {
+ IRQ_resetbit(&dst->raised, n_IRQ);
}
+
IRQ_check(opp, &dst->raised);
- if (IRQ_get_next(opp, &dst->servicing) != -1 &&
- priority <= dst->servicing.priority) {
- DPRINTF("%s: IRQ %d is hidden by servicing IRQ %d on CPU %d\n",
- __func__, n_IRQ, dst->servicing.next, n_CPU);
- /* Already servicing a higher priority IRQ */
- return;
+
+ if (active && priority <= dst->ctpr) {
+ DPRINTF("%s: IRQ %d priority %d too low for ctpr %d on CPU %d\n",
+ __func__, n_IRQ, priority, dst->ctpr, n_CPU);
+ active = 0;
+ }
+
+ if (active) {
+ if (IRQ_get_next(opp, &dst->servicing) >= 0 &&
+ priority <= dst->servicing.priority) {
+ DPRINTF("%s: IRQ %d is hidden by servicing IRQ %d on CPU %d\n",
+ __func__, n_IRQ, dst->servicing.next, n_CPU);
+ } else {
+ DPRINTF("%s: Raise OpenPIC INT output cpu %d irq %d/%d\n",
+ __func__, n_CPU, n_IRQ, dst->raised.next);
+ qemu_irq_raise(opp->dst[n_CPU].irqs[OPENPIC_OUTPUT_INT]);
+ }
+ } else {
+ IRQ_get_next(opp, &dst->servicing);
+ if (dst->raised.priority > dst->ctpr &&
+ dst->raised.priority > dst->servicing.priority) {
+ DPRINTF("%s: IRQ %d inactive, IRQ %d prio %d above %d/%d, CPU %d\n",
+ __func__, n_IRQ, dst->raised.next, dst->raised.priority,
+ dst->ctpr, dst->servicing.priority, n_CPU);
+ /* IRQ line stays asserted */
+ } else {
+ DPRINTF("%s: IRQ %d inactive, current prio %d/%d, CPU %d\n",
+ __func__, n_IRQ, dst->ctpr, dst->servicing.priority, n_CPU);
+ qemu_irq_lower(opp->dst[n_CPU].irqs[OPENPIC_OUTPUT_INT]);
+ }
}
- DPRINTF("Raise OpenPIC INT output cpu %d irq %d\n", n_CPU, n_IRQ);
- qemu_irq_raise(opp->dst[n_CPU].irqs[OPENPIC_OUTPUT_INT]);
}
/* update pic state because registers for n_IRQ have changed value */
static void openpic_update_irq(OpenPICState *opp, int n_IRQ)
{
IRQSource *src;
+ bool active, was_active;
int i;
src = &opp->src[n_IRQ];
+ active = src->pending;
- if (!src->pending) {
- /* no irq pending */
- DPRINTF("%s: IRQ %d is not pending\n", __func__, n_IRQ);
- return;
- }
if ((src->ivpr & IVPR_MASK_MASK) && !src->nomask) {
/* Interrupt source is disabled */
DPRINTF("%s: IRQ %d is disabled\n", __func__, n_IRQ);
- return;
+ active = false;
}
- if (IVPR_PRIORITY(src->ivpr) == 0) {
- /* Priority set to zero */
- DPRINTF("%s: IRQ %d has 0 priority\n", __func__, n_IRQ);
+
+ was_active = !!(src->ivpr & IVPR_ACTIVITY_MASK);
+
+ /*
+ * We don't have a similar check for already-active because
+ * ctpr may have changed and we need to withdraw the interrupt.
+ */
+ if (!active && !was_active) {
+ DPRINTF("%s: IRQ %d is already inactive\n", __func__, n_IRQ);
return;
}
- if (src->ivpr & IVPR_ACTIVITY_MASK) {
- /* IRQ already active */
- DPRINTF("%s: IRQ %d is already active\n", __func__, n_IRQ);
- return;
+
+ if (active) {
+ src->ivpr |= IVPR_ACTIVITY_MASK;
+ } else {
+ src->ivpr &= ~IVPR_ACTIVITY_MASK;
}
+
if (src->idr == 0) {
/* No target */
DPRINTF("%s: IRQ %d has no target\n", __func__, n_IRQ);
if (src->idr == (1 << src->last_cpu)) {
/* Only one CPU is allowed to receive this IRQ */
- IRQ_local_pipe(opp, src->last_cpu, n_IRQ);
+ IRQ_local_pipe(opp, src->last_cpu, n_IRQ, active, was_active);
} else if (!(src->ivpr & IVPR_MODE_MASK)) {
/* Directed delivery mode */
for (i = 0; i < opp->nb_cpus; i++) {
if (src->destmask & (1 << i)) {
- IRQ_local_pipe(opp, i, n_IRQ);
+ IRQ_local_pipe(opp, i, n_IRQ, active, was_active);
}
}
} else {
i = 0;
}
if (src->destmask & (1 << i)) {
- IRQ_local_pipe(opp, i, n_IRQ);
+ IRQ_local_pipe(opp, i, n_IRQ, active, was_active);
src->last_cpu = i;
break;
}
OpenPICState *opp = opaque;
IRQSource *src;
+ if (n_IRQ >= MAX_IRQ) {
+ fprintf(stderr, "%s: IRQ %d out of range\n", __func__, n_IRQ);
+ abort();
+ }
+
src = &opp->src[n_IRQ];
DPRINTF("openpic: set irq %d = %d ivpr=0x%08x\n",
n_IRQ, level, src->ivpr);
- if (src->ivpr & IVPR_SENSE_MASK) {
+ if (src->level) {
/* level-sensitive irq */
src->pending = level;
- if (!level) {
- src->ivpr &= ~IVPR_ACTIVITY_MASK;
- }
+ openpic_update_irq(opp, n_IRQ);
} else {
/* edge-sensitive irq */
if (level) {
src->pending = 1;
+ openpic_update_irq(opp, n_IRQ);
+ }
+
+ if (src->output != OPENPIC_OUTPUT_INT) {
+ /* Edge-triggered interrupts shouldn't be used
+ * with non-INT delivery, but just in case,
+ * try to make it do something sane rather than
+ * cause an interrupt storm. This is close to
+ * what you'd probably see happen in real hardware.
+ */
+ src->pending = 0;
+ openpic_update_irq(opp, n_IRQ);
}
}
- openpic_update_irq(opp, n_IRQ);
}
static void openpic_reset(DeviceState *d)
for (i = 0; i < opp->max_irq; i++) {
opp->src[i].ivpr = opp->ivpr_reset;
opp->src[i].idr = opp->idr_reset;
+
+ switch (opp->src[i].type) {
+ case IRQ_TYPE_NORMAL:
+ opp->src[i].level = !!(opp->ivpr_reset & IVPR_SENSE_MASK);
+ break;
+
+ case IRQ_TYPE_FSLINT:
+ opp->src[i].ivpr |= IVPR_POLARITY_MASK;
+ break;
+
+ case IRQ_TYPE_FSLSPECIAL:
+ break;
+ }
}
/* Initialise IRQ destinations */
for (i = 0; i < MAX_CPU; i++) {
static inline void write_IRQreg_ivpr(OpenPICState *opp, int n_IRQ, uint32_t val)
{
- /* NOTE: not fully accurate for special IRQs, but simple and sufficient */
+ uint32_t mask;
+
+ /* NOTE when implementing newer FSL MPIC models: starting with v4.0,
+ * the polarity bit is read-only on internal interrupts.
+ */
+ mask = IVPR_MASK_MASK | IVPR_PRIORITY_MASK | IVPR_SENSE_MASK |
+ IVPR_POLARITY_MASK | opp->vector_mask;
+
/* ACTIVITY bit is read-only */
- opp->src[n_IRQ].ivpr = (opp->src[n_IRQ].ivpr & IVPR_ACTIVITY_MASK) |
- (val & (IVPR_MASK_MASK | IVPR_PRIORITY_MASK | opp->vector_mask));
+ opp->src[n_IRQ].ivpr =
+ (opp->src[n_IRQ].ivpr & IVPR_ACTIVITY_MASK) | (val & mask);
+
+ /* For FSL internal interrupts, The sense bit is reserved and zero,
+ * and the interrupt is always level-triggered. Timers and IPIs
+ * have no sense or polarity bits, and are edge-triggered.
+ */
+ switch (opp->src[n_IRQ].type) {
+ case IRQ_TYPE_NORMAL:
+ opp->src[n_IRQ].level = !!(opp->src[n_IRQ].ivpr & IVPR_SENSE_MASK);
+ break;
+
+ case IRQ_TYPE_FSLINT:
+ opp->src[n_IRQ].ivpr &= ~IVPR_SENSE_MASK;
+ break;
+
+ case IRQ_TYPE_FSLSPECIAL:
+ opp->src[n_IRQ].ivpr &= ~(IVPR_POLARITY_MASK | IVPR_SENSE_MASK);
+ break;
+ }
+
openpic_update_irq(opp, n_IRQ);
DPRINTF("Set IVPR %d to 0x%08x -> 0x%08x\n", n_IRQ, val,
opp->src[n_IRQ].ivpr);
case 0x1020: /* GCR */
if (val & GCR_RESET) {
openpic_reset(&opp->busdev.qdev);
+ } else if (opp->mpic_mode_mask) {
+ CPUArchState *env;
+ int mpic_proxy = 0;
+
+ opp->gcr &= ~opp->mpic_mode_mask;
+ opp->gcr |= val & opp->mpic_mode_mask;
+
+ /* Set external proxy mode */
+ if ((val & opp->mpic_mode_mask) == GCR_MODE_PROXY) {
+ mpic_proxy = 1;
+ }
+ for (env = first_cpu; env != NULL; env = env->next_cpu) {
+ env->mpic_proxy = mpic_proxy;
+ }
}
break;
case 0x1080: /* VIR */
break;
case 0x80: /* CTPR */
dst->ctpr = val & 0x0000000F;
+
+ DPRINTF("%s: set CPU %d ctpr to %d, raised %d servicing %d\n",
+ __func__, idx, dst->ctpr, dst->raised.priority,
+ dst->servicing.priority);
+
+ if (dst->raised.priority <= dst->ctpr) {
+ DPRINTF("%s: Lower OpenPIC INT output cpu %d due to ctpr\n",
+ __func__, idx);
+ qemu_irq_lower(dst->irqs[OPENPIC_OUTPUT_INT]);
+ } else if (dst->raised.priority > dst->servicing.priority) {
+ DPRINTF("%s: Raise OpenPIC INT output cpu %d irq %d\n",
+ __func__, idx, dst->raised.next);
+ qemu_irq_raise(dst->irqs[OPENPIC_OUTPUT_INT]);
+ }
+
break;
case 0x90: /* WHOAMI */
/* Read-only register */
case 0xB0: /* EOI */
DPRINTF("EOI\n");
s_IRQ = IRQ_get_next(opp, &dst->servicing);
+
+ if (s_IRQ < 0) {
+ DPRINTF("%s: EOI with no interrupt in service\n", __func__);
+ break;
+ }
+
IRQ_resetbit(&dst->servicing, s_IRQ);
/* Set up next servicing IRQ */
s_IRQ = IRQ_get_next(opp, &dst->servicing);
openpic_cpu_write_internal(opaque, addr, val, (addr & 0x1f000) >> 12);
}
+
+static uint32_t openpic_iack(OpenPICState *opp, IRQDest *dst, int cpu)
+{
+ IRQSource *src;
+ int retval, irq;
+
+ DPRINTF("Lower OpenPIC INT output\n");
+ qemu_irq_lower(dst->irqs[OPENPIC_OUTPUT_INT]);
+
+ irq = IRQ_get_next(opp, &dst->raised);
+ DPRINTF("IACK: irq=%d\n", irq);
+
+ if (irq == -1) {
+ /* No more interrupt pending */
+ return opp->spve;
+ }
+
+ src = &opp->src[irq];
+ if (!(src->ivpr & IVPR_ACTIVITY_MASK) ||
+ !(IVPR_PRIORITY(src->ivpr) > dst->ctpr)) {
+ fprintf(stderr, "%s: bad raised IRQ %d ctpr %d ivpr 0x%08x\n",
+ __func__, irq, dst->ctpr, src->ivpr);
+ openpic_update_irq(opp, irq);
+ retval = opp->spve;
+ } else {
+ /* IRQ enter servicing state */
+ IRQ_setbit(&dst->servicing, irq);
+ retval = IVPR_VECTOR(opp, src->ivpr);
+ }
+
+ if (!src->level) {
+ /* edge-sensitive IRQ */
+ src->ivpr &= ~IVPR_ACTIVITY_MASK;
+ src->pending = 0;
+ IRQ_resetbit(&dst->raised, irq);
+ }
+
+ if ((irq >= opp->irq_ipi0) && (irq < (opp->irq_ipi0 + MAX_IPI))) {
+ src->idr &= ~(1 << cpu);
+ if (src->idr && !src->level) {
+ /* trigger on CPUs that didn't know about it yet */
+ openpic_set_irq(opp, irq, 1);
+ openpic_set_irq(opp, irq, 0);
+ /* if all CPUs knew about it, set active bit again */
+ src->ivpr |= IVPR_ACTIVITY_MASK;
+ }
+ }
+
+ return retval;
+}
+
static uint32_t openpic_cpu_read_internal(void *opaque, hwaddr addr,
int idx)
{
OpenPICState *opp = opaque;
- IRQSource *src;
IRQDest *dst;
uint32_t retval;
- int n_IRQ;
DPRINTF("%s: cpu %d addr %#" HWADDR_PRIx "\n", __func__, idx, addr);
retval = 0xFFFFFFFF;
retval = idx;
break;
case 0xA0: /* IACK */
- DPRINTF("Lower OpenPIC INT output\n");
- qemu_irq_lower(dst->irqs[OPENPIC_OUTPUT_INT]);
- n_IRQ = IRQ_get_next(opp, &dst->raised);
- DPRINTF("IACK: irq=%d\n", n_IRQ);
- if (n_IRQ == -1) {
- /* No more interrupt pending */
- retval = opp->spve;
- } else {
- src = &opp->src[n_IRQ];
- if (!(src->ivpr & IVPR_ACTIVITY_MASK) ||
- !(IVPR_PRIORITY(src->ivpr) > dst->ctpr)) {
- /* - Spurious level-sensitive IRQ
- * - Priorities has been changed
- * and the pending IRQ isn't allowed anymore
- */
- src->ivpr &= ~IVPR_ACTIVITY_MASK;
- retval = opp->spve;
- } else {
- /* IRQ enter servicing state */
- IRQ_setbit(&dst->servicing, n_IRQ);
- retval = IVPR_VECTOR(opp, src->ivpr);
- }
- IRQ_resetbit(&dst->raised, n_IRQ);
- if (!(src->ivpr & IVPR_SENSE_MASK)) {
- /* edge-sensitive IRQ */
- src->ivpr &= ~IVPR_ACTIVITY_MASK;
- src->pending = 0;
- }
-
- if ((n_IRQ >= opp->irq_ipi0) && (n_IRQ < (opp->irq_ipi0 + MAX_IPI))) {
- src->idr &= ~(1 << idx);
- if (src->idr && !(src->ivpr & IVPR_SENSE_MASK)) {
- /* trigger on CPUs that didn't know about it yet */
- openpic_set_irq(opp, n_IRQ, 1);
- openpic_set_irq(opp, n_IRQ, 0);
- /* if all CPUs knew about it, set active bit again */
- src->ivpr |= IVPR_ACTIVITY_MASK;
- }
- }
- }
+ retval = openpic_iack(opp, dst, idx);
break;
case 0xB0: /* EOI */
retval = 0;
qemu_put_sbe32s(f, &opp->dst[i].ctpr);
openpic_save_IRQ_queue(f, &opp->dst[i].raised);
openpic_save_IRQ_queue(f, &opp->dst[i].servicing);
+ qemu_put_buffer(f, (uint8_t *)&opp->dst[i].outputs_active,
+ sizeof(opp->dst[i].outputs_active));
}
for (i = 0; i < MAX_TMR; i++) {
qemu_get_sbe32s(f, &opp->dst[i].ctpr);
openpic_load_IRQ_queue(f, &opp->dst[i].raised);
openpic_load_IRQ_queue(f, &opp->dst[i].servicing);
+ qemu_get_buffer(f, (uint8_t *)&opp->dst[i].outputs_active,
+ sizeof(opp->dst[i].outputs_active));
}
for (i = 0; i < MAX_TMR; i++) {
opp->irq_tim0 = FSL_MPIC_20_TMR_IRQ;
opp->irq_msi = FSL_MPIC_20_MSI_IRQ;
opp->brr1 = FSL_BRR1_IPID | FSL_BRR1_IPMJ | FSL_BRR1_IPMN;
+ /* XXX really only available as of MPIC 4.0 */
+ opp->mpic_mode_mask = GCR_MODE_PROXY;
+
msi_supported = true;
list = list_be;
+
+ for (i = 0; i < FSL_MPIC_20_MAX_EXT; i++) {
+ opp->src[i].level = false;
+ }
+
+ /* Internal interrupts, including message and MSI */
+ for (i = 16; i < MAX_SRC; i++) {
+ opp->src[i].type = IRQ_TYPE_FSLINT;
+ opp->src[i].level = true;
+ }
+
+ /* timers and IPIs */
+ for (i = MAX_SRC; i < MAX_IRQ; i++) {
+ opp->src[i].type = IRQ_TYPE_FSLSPECIAL;
+ opp->src[i].level = false;
+ }
+
break;
+
case OPENPIC_MODEL_RAVEN:
opp->nb_irqs = RAVEN_MAX_EXT;
opp->vid = VID_REVISION_1_3;
dc->reset = openpic_reset;
}
-static TypeInfo openpic_info = {
+static const TypeInfo openpic_info = {
.name = "openpic",
.parent = TYPE_SYS_BUS_DEVICE,
.instance_size = sizeof(OpenPICState),