diff options
Diffstat (limited to 'hw/openpic.c')
-rw-r--r-- | hw/openpic.c | 77 |
1 files changed, 71 insertions, 6 deletions
diff --git a/hw/openpic.c b/hw/openpic.c index 35a7fe3..66179c2 100644 --- a/hw/openpic.c +++ b/hw/openpic.c @@ -162,6 +162,12 @@ 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. @@ -178,6 +184,8 @@ typedef struct IRQSource { 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; @@ -422,7 +430,7 @@ static void openpic_set_irq(void *opaque, int n_IRQ, int level) 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) { @@ -455,6 +463,19 @@ 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++) { @@ -530,10 +551,36 @@ static inline void write_IRQreg_idr(OpenPICState *opp, int n_IRQ, uint32_t val) 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); @@ -976,7 +1023,7 @@ static uint32_t openpic_cpu_read_internal(void *opaque, hwaddr addr, retval = IVPR_VECTOR(opp, src->ivpr); } IRQ_resetbit(&dst->raised, n_IRQ); - if (!(src->ivpr & IVPR_SENSE_MASK)) { + if (!src->level) { /* edge-sensitive IRQ */ src->ivpr &= ~IVPR_ACTIVITY_MASK; src->pending = 0; @@ -984,7 +1031,7 @@ static uint32_t openpic_cpu_read_internal(void *opaque, hwaddr addr, 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)) { + if (src->idr && !src->level) { /* trigger on CPUs that didn't know about it yet */ openpic_set_irq(opp, n_IRQ, 1); openpic_set_irq(opp, n_IRQ, 0); @@ -1282,7 +1329,25 @@ static int openpic_init(SysBusDevice *dev) opp->brr1 = FSL_BRR1_IPID | FSL_BRR1_IPMJ | FSL_BRR1_IPMN; 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; |