X-Git-Url: https://repo.jachan.dev/qemu.git/blobdiff_plain/c1d75727931e30da1b2c28887f21278812cae57c..eee4baef4ac273b99e06ef5b12ac74bbc94fab62:/hw/intc/openpic.c diff --git a/hw/intc/openpic.c b/hw/intc/openpic.c index 08e0e19c59..811cee9b26 100644 --- a/hw/intc/openpic.c +++ b/hw/intc/openpic.c @@ -33,6 +33,7 @@ * Serial interrupts, as implemented in Raven chipset are not supported yet. * */ +#include "qemu/osdep.h" #include "hw/hw.h" #include "hw/ppc/mac.h" #include "hw/pci/pci.h" @@ -40,8 +41,12 @@ #include "hw/ppc/ppc_e500.h" #include "hw/sysbus.h" #include "hw/pci/msi.h" +#include "qapi/error.h" #include "qemu/bitops.h" #include "qapi/qmp/qerror.h" +#include "qemu/log.h" +#include "qemu/timer.h" +#include "qemu/error-report.h" //#define DEBUG_OPENPIC @@ -51,16 +56,13 @@ static const int debug_openpic = 1; static const int debug_openpic = 0; #endif +static int get_current_cpu(void); #define DPRINTF(fmt, ...) do { \ if (debug_openpic) { \ - printf(fmt , ## __VA_ARGS__); \ + info_report("Core%d: " fmt, get_current_cpu(), ## __VA_ARGS__); \ } \ } while (0) -#define MAX_CPU 32 -#define MAX_MSI 8 -#define VID 0x03 /* MPIC version ID */ - /* OpenPIC capability flags */ #define OPENPIC_FLAG_IDR_CRIT (1 << 0) #define OPENPIC_FLAG_ILR (2 << 0) @@ -79,25 +81,6 @@ static const int debug_openpic = 0; #define OPENPIC_CPU_REG_START 0x20000 #define OPENPIC_CPU_REG_SIZE 0x100 + ((MAX_CPU - 1) * 0x1000) -/* Raven */ -#define RAVEN_MAX_CPU 2 -#define RAVEN_MAX_EXT 48 -#define RAVEN_MAX_IRQ 64 -#define RAVEN_MAX_TMR OPENPIC_MAX_TMR -#define RAVEN_MAX_IPI OPENPIC_MAX_IPI - -/* Interrupt definitions */ -#define RAVEN_FE_IRQ (RAVEN_MAX_EXT) /* Internal functional IRQ */ -#define RAVEN_ERR_IRQ (RAVEN_MAX_EXT + 1) /* Error IRQ */ -#define RAVEN_TMR_IRQ (RAVEN_MAX_EXT + 2) /* First timer IRQ */ -#define RAVEN_IPI_IRQ (RAVEN_TMR_IRQ + RAVEN_MAX_TMR) /* First IPI IRQ */ -/* First doorbell IRQ */ -#define RAVEN_DBL_IRQ (RAVEN_IPI_IRQ + (RAVEN_MAX_CPU * RAVEN_MAX_IPI)) - -typedef struct FslMpicInfo { - int max_ext; -} FslMpicInfo; - static FslMpicInfo fsl_mpic_20 = { .max_ext = 12, }; @@ -114,6 +97,7 @@ static FslMpicInfo fsl_mpic_42 = { #define VID_REVISION_1_3 3 #define VIR_GENERIC 0x00000000 /* Generic Vendor ID */ +#define VIR_MPIC2A 0x00004614 /* IBM MPIC-2A */ #define GCR_RESET 0x80000000 #define GCR_MODE_PASS 0x00000000 @@ -156,7 +140,7 @@ static int inttgt_to_output(int inttgt) } } - fprintf(stderr, "%s: unsupported inttgt %d\n", __func__, inttgt); + error_report("%s: unsupported inttgt %d", __func__, inttgt); return OPENPIC_OUTPUT_INT; } @@ -192,114 +176,22 @@ static uint32_t openpic_cpu_read_internal(void *opaque, hwaddr addr, int idx); 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. - */ - unsigned long queue[BITS_TO_LONGS((OPENPIC_MAX_IRQ + 63) & ~63)]; - int next; - int priority; -} 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 */ - IRQType type; - bool level:1; /* level-triggered */ - bool nomask:1; /* critical interrupts ignore mask on some FSL MPICs */ -} IRQSource; - -#define IVPR_MASK_SHIFT 31 -#define IVPR_MASK_MASK (1U << IVPR_MASK_SHIFT) -#define IVPR_ACTIVITY_SHIFT 30 -#define IVPR_ACTIVITY_MASK (1U << IVPR_ACTIVITY_SHIFT) -#define IVPR_MODE_SHIFT 29 -#define IVPR_MODE_MASK (1U << IVPR_MODE_SHIFT) -#define IVPR_POLARITY_SHIFT 23 -#define IVPR_POLARITY_MASK (1U << IVPR_POLARITY_SHIFT) -#define IVPR_SENSE_SHIFT 22 -#define IVPR_SENSE_MASK (1U << IVPR_SENSE_SHIFT) - -#define IVPR_PRIORITY_MASK (0xFU << 16) -#define IVPR_PRIORITY(_ivprr_) ((int)(((_ivprr_) & IVPR_PRIORITY_MASK) >> 16)) -#define IVPR_VECTOR(opp, _ivprr_) ((_ivprr_) & (opp)->vector_mask) - -/* IDR[EP/CI] are only for FSL MPIC prior to v4.0 */ -#define IDR_EP 0x80000000 /* external pin */ -#define IDR_CI 0x40000000 /* critical interrupt */ - -typedef struct IRQDest { - int32_t ctpr; /* CPU current task priority */ - IRQQueue raised; - IRQQueue servicing; - qemu_irq *irqs; - - /* Count of IRQ sources asserting on non-INT outputs */ - uint32_t outputs_active[OPENPIC_OUTPUT_NB]; -} IRQDest; - -#define OPENPIC(obj) OBJECT_CHECK(OpenPICState, (obj), TYPE_OPENPIC) - -typedef struct OpenPICState { - /*< private >*/ - SysBusDevice parent_obj; - /*< public >*/ - - MemoryRegion mem; - - /* Behavior control */ - FslMpicInfo *fsl; - uint32_t model; - uint32_t flags; - uint32_t nb_irqs; - uint32_t vid; - uint32_t vir; /* Vendor identification register */ - uint32_t vector_mask; - uint32_t tfrr_reset; - uint32_t ivpr_reset; - uint32_t idr_reset; - uint32_t brr1; - uint32_t mpic_mode_mask; - - /* Sub-regions */ - MemoryRegion sub_io_mem[6]; - - /* Global registers */ - uint32_t frr; /* Feature reporting register */ - uint32_t gcr; /* Global configuration register */ - uint32_t pir; /* Processor initialization register */ - uint32_t spve; /* Spurious vector register */ - uint32_t tfrr; /* Timer frequency reporting register */ - /* Source registers */ - IRQSource src[OPENPIC_MAX_IRQ]; - /* Local registers per output pin */ - IRQDest dst[MAX_CPU]; - uint32_t nb_cpus; - /* Timer registers */ - struct { - uint32_t tccr; /* Global timer current count register */ - uint32_t tbcr; /* Global timer base count register */ - } timers[OPENPIC_MAX_TMR]; - /* Shared MSI registers */ - struct { - uint32_t msir; /* Shared Message Signaled Interrupt Register */ - } msi[MAX_MSI]; - uint32_t max_irq; - uint32_t irq_ipi0; - uint32_t irq_tim0; - uint32_t irq_msi; -} OpenPICState; +static void openpic_reset(DeviceState *d); + +/* Convert between openpic clock ticks and nanosecs. In the hardware the clock + frequency is driven by board inputs to the PIC which the PIC would then + divide by 4 or 8. For now hard code to 25MZ. +*/ +#define OPENPIC_TIMER_FREQ_MHZ 25 +#define OPENPIC_TIMER_NS_PER_TICK (1000 / OPENPIC_TIMER_FREQ_MHZ) +static inline uint64_t ns_to_ticks(uint64_t ns) +{ + return ns / OPENPIC_TIMER_NS_PER_TICK; +} +static inline uint64_t ticks_to_ns(uint64_t ticks) +{ + return ticks * OPENPIC_TIMER_NS_PER_TICK; +} static inline void IRQ_setbit(IRQQueue *q, int n_IRQ) { @@ -323,7 +215,7 @@ static void IRQ_check(OpenPICState *opp, IRQQueue *q) break; } - DPRINTF("IRQ_check: irq %d set ivpr_pr=%d pr=%d\n", + DPRINTF("IRQ_check: irq %d set ivpr_pr=%d pr=%d", irq, IVPR_PRIORITY(opp->src[irq].ivpr), priority); if (IVPR_PRIORITY(opp->src[irq].ivpr) > priority) { @@ -354,11 +246,11 @@ static void IRQ_local_pipe(OpenPICState *opp, int n_CPU, int n_IRQ, dst = &opp->dst[n_CPU]; src = &opp->src[n_IRQ]; - DPRINTF("%s: IRQ %d active %d was %d\n", + DPRINTF("%s: IRQ %d active %d was %d", __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", + DPRINTF("%s: output %d irq %d active %d was %d count %d", __func__, src->output, n_IRQ, active, was_active, dst->outputs_active[src->output]); @@ -368,13 +260,13 @@ static void IRQ_local_pipe(OpenPICState *opp, int n_CPU, int n_IRQ, */ if (active) { if (!was_active && dst->outputs_active[src->output]++ == 0) { - DPRINTF("%s: Raise OpenPIC output %d cpu %d irq %d\n", + DPRINTF("%s: Raise OpenPIC output %d cpu %d irq %d", __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", + DPRINTF("%s: Lower OpenPIC output %d cpu %d irq %d", __func__, src->output, n_CPU, n_IRQ); qemu_irq_lower(dst->irqs[src->output]); } @@ -397,7 +289,7 @@ static void IRQ_local_pipe(OpenPICState *opp, int n_CPU, int n_IRQ, IRQ_check(opp, &dst->raised); if (active && priority <= dst->ctpr) { - DPRINTF("%s: IRQ %d priority %d too low for ctpr %d on CPU %d\n", + DPRINTF("%s: IRQ %d priority %d too low for ctpr %d on CPU %d", __func__, n_IRQ, priority, dst->ctpr, n_CPU); active = 0; } @@ -405,10 +297,10 @@ static void IRQ_local_pipe(OpenPICState *opp, int n_CPU, int n_IRQ, 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", + DPRINTF("%s: IRQ %d is hidden by servicing IRQ %d on CPU %d", __func__, n_IRQ, dst->servicing.next, n_CPU); } else { - DPRINTF("%s: Raise OpenPIC INT output cpu %d irq %d/%d\n", + DPRINTF("%s: Raise OpenPIC INT output cpu %d irq %d/%d", __func__, n_CPU, n_IRQ, dst->raised.next); qemu_irq_raise(opp->dst[n_CPU].irqs[OPENPIC_OUTPUT_INT]); } @@ -416,12 +308,12 @@ static void IRQ_local_pipe(OpenPICState *opp, int n_CPU, int n_IRQ, 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", + DPRINTF("%s: IRQ %d inactive, IRQ %d prio %d above %d/%d, CPU %d", __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", + DPRINTF("%s: IRQ %d inactive, current prio %d/%d, CPU %d", __func__, n_IRQ, dst->ctpr, dst->servicing.priority, n_CPU); qemu_irq_lower(opp->dst[n_CPU].irqs[OPENPIC_OUTPUT_INT]); } @@ -440,7 +332,7 @@ static void openpic_update_irq(OpenPICState *opp, int n_IRQ) if ((src->ivpr & IVPR_MASK_MASK) && !src->nomask) { /* Interrupt source is disabled */ - DPRINTF("%s: IRQ %d is disabled\n", __func__, n_IRQ); + DPRINTF("%s: IRQ %d is disabled", __func__, n_IRQ); active = false; } @@ -451,7 +343,7 @@ static void openpic_update_irq(OpenPICState *opp, int n_IRQ) * 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); + DPRINTF("%s: IRQ %d is already inactive", __func__, n_IRQ); return; } @@ -463,7 +355,7 @@ static void openpic_update_irq(OpenPICState *opp, int n_IRQ) if (src->destmask == 0) { /* No target */ - DPRINTF("%s: IRQ %d has no target\n", __func__, n_IRQ); + DPRINTF("%s: IRQ %d has no target", __func__, n_IRQ); return; } @@ -498,12 +390,12 @@ static void openpic_set_irq(void *opaque, int n_IRQ, int level) IRQSource *src; if (n_IRQ >= OPENPIC_MAX_IRQ) { - fprintf(stderr, "%s: IRQ %d out of range\n", __func__, n_IRQ); + error_report("%s: IRQ %d out of range", __func__, n_IRQ); abort(); } src = &opp->src[n_IRQ]; - DPRINTF("openpic: set irq %d = %d ivpr=0x%08x\n", + DPRINTF("openpic: set irq %d = %d ivpr=0x%08x", n_IRQ, level, src->ivpr); if (src->level) { /* level-sensitive irq */ @@ -529,55 +421,6 @@ static void openpic_set_irq(void *opaque, int n_IRQ, int level) } } -static void openpic_reset(DeviceState *d) -{ - OpenPICState *opp = OPENPIC(d); - int i; - - opp->gcr = GCR_RESET; - /* Initialise controller registers */ - opp->frr = ((opp->nb_irqs - 1) << FRR_NIRQ_SHIFT) | - ((opp->nb_cpus - 1) << FRR_NCPU_SHIFT) | - (opp->vid << FRR_VID_SHIFT); - - opp->pir = 0; - opp->spve = -1 & opp->vector_mask; - opp->tfrr = opp->tfrr_reset; - /* Initialise IRQ sources */ - 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++) { - opp->dst[i].ctpr = 15; - memset(&opp->dst[i].raised, 0, sizeof(IRQQueue)); - opp->dst[i].raised.next = -1; - memset(&opp->dst[i].servicing, 0, sizeof(IRQQueue)); - opp->dst[i].servicing.next = -1; - } - /* Initialise timers */ - for (i = 0; i < OPENPIC_MAX_TMR; i++) { - opp->timers[i].tccr = 0; - opp->timers[i].tbcr = TBCR_CI; - } - /* Go out of RESET state */ - opp->gcr = 0; -} - static inline uint32_t read_IRQreg_idr(OpenPICState *opp, int n_IRQ) { return opp->src[n_IRQ].idr; @@ -612,13 +455,13 @@ static inline void write_IRQreg_idr(OpenPICState *opp, int n_IRQ, uint32_t val) } src->idr = val & mask; - DPRINTF("Set IDR %d to 0x%08x\n", n_IRQ, src->idr); + DPRINTF("Set IDR %d to 0x%08x", 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__); + "critical", __func__); } src->output = OPENPIC_OUTPUT_CINT; @@ -648,7 +491,7 @@ static inline void write_IRQreg_ilr(OpenPICState *opp, int n_IRQ, uint32_t val) IRQSource *src = &opp->src[n_IRQ]; src->output = inttgt_to_output(val & ILR_INTTGT_MASK); - DPRINTF("Set ILR %d to 0x%08x, output %d\n", n_IRQ, src->idr, + DPRINTF("Set ILR %d to 0x%08x, output %d", n_IRQ, src->idr, src->output); /* TODO: on MPIC v4.0 only, set nomask for non-INT */ @@ -688,7 +531,7 @@ static inline void write_IRQreg_ivpr(OpenPICState *opp, int n_IRQ, uint32_t val) } openpic_update_irq(opp, n_IRQ); - DPRINTF("Set IVPR %d to 0x%08x -> 0x%08x\n", n_IRQ, val, + DPRINTF("Set IVPR %d to 0x%08x -> 0x%08x", n_IRQ, val, opp->src[n_IRQ].ivpr); } @@ -719,7 +562,7 @@ static void openpic_gbl_write(void *opaque, hwaddr addr, uint64_t val, IRQDest *dst; int idx; - DPRINTF("%s: addr %#" HWADDR_PRIx " <= %08" PRIx64 "\n", + DPRINTF("%s: addr %#" HWADDR_PRIx " <= %08" PRIx64, __func__, addr, val); if (addr & 0xF) { return; @@ -747,11 +590,11 @@ static void openpic_gbl_write(void *opaque, hwaddr addr, uint64_t val, case 0x1090: /* PIR */ for (idx = 0; idx < opp->nb_cpus; idx++) { if ((val & (1 << idx)) && !(opp->pir & (1 << idx))) { - DPRINTF("Raise OpenPIC RESET output for CPU %d\n", idx); + DPRINTF("Raise OpenPIC RESET output for CPU %d", idx); dst = &opp->dst[idx]; qemu_irq_raise(dst->irqs[OPENPIC_OUTPUT_RESET]); } else if (!(val & (1 << idx)) && (opp->pir & (1 << idx))) { - DPRINTF("Lower OpenPIC RESET output for CPU %d\n", idx); + DPRINTF("Lower OpenPIC RESET output for CPU %d", idx); dst = &opp->dst[idx]; qemu_irq_lower(dst->irqs[OPENPIC_OUTPUT_RESET]); } @@ -781,7 +624,7 @@ static uint64_t openpic_gbl_read(void *opaque, hwaddr addr, unsigned len) OpenPICState *opp = opaque; uint32_t retval; - DPRINTF("%s: addr %#" HWADDR_PRIx "\n", __func__, addr); + DPRINTF("%s: addr %#" HWADDR_PRIx, __func__, addr); retval = 0xFFFFFFFF; if (addr & 0xF) { return retval; @@ -828,42 +671,103 @@ static uint64_t openpic_gbl_read(void *opaque, hwaddr addr, unsigned len) default: break; } - DPRINTF("%s: => 0x%08x\n", __func__, retval); + DPRINTF("%s: => 0x%08x", __func__, retval); return retval; } +static void openpic_tmr_set_tmr(OpenPICTimer *tmr, uint32_t val, bool enabled); + +static void qemu_timer_cb(void *opaque) +{ + OpenPICTimer *tmr = opaque; + OpenPICState *opp = tmr->opp; + uint32_t n_IRQ = tmr->n_IRQ; + uint32_t val = tmr->tbcr & ~TBCR_CI; + uint32_t tog = ((tmr->tccr & TCCR_TOG) ^ TCCR_TOG); /* invert toggle. */ + + DPRINTF("%s n_IRQ=%d", __func__, n_IRQ); + /* Reload current count from base count and setup timer. */ + tmr->tccr = val | tog; + openpic_tmr_set_tmr(tmr, val, /*enabled=*/true); + /* Raise the interrupt. */ + opp->src[n_IRQ].destmask = read_IRQreg_idr(opp, n_IRQ); + openpic_set_irq(opp, n_IRQ, 1); + openpic_set_irq(opp, n_IRQ, 0); +} + +/* If enabled is true, arranges for an interrupt to be raised val clocks into + the future, if enabled is false cancels the timer. */ +static void openpic_tmr_set_tmr(OpenPICTimer *tmr, uint32_t val, bool enabled) +{ + uint64_t ns = ticks_to_ns(val & ~TCCR_TOG); + /* A count of zero causes a timer to be set to expire immediately. This + effectively stops the simulation since the timer is constantly expiring + which prevents guest code execution, so we don't honor that + configuration. On real hardware, this situation would generate an + interrupt on every clock cycle if the interrupt was unmasked. */ + if ((ns == 0) || !enabled) { + tmr->qemu_timer_active = false; + tmr->tccr = tmr->tccr & TCCR_TOG; + timer_del(tmr->qemu_timer); /* set timer to never expire. */ + } else { + tmr->qemu_timer_active = true; + uint64_t now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); + tmr->origin_time = now; + timer_mod(tmr->qemu_timer, now + ns); /* set timer expiration. */ + } +} + +/* Returns the currrent tccr value, i.e., timer value (in clocks) with + appropriate TOG. */ +static uint64_t openpic_tmr_get_timer(OpenPICTimer *tmr) +{ + uint64_t retval; + if (!tmr->qemu_timer_active) { + retval = tmr->tccr; + } else { + uint64_t now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); + uint64_t used = now - tmr->origin_time; /* nsecs */ + uint32_t used_ticks = (uint32_t)ns_to_ticks(used); + uint32_t count = (tmr->tccr & ~TCCR_TOG) - used_ticks; + retval = (uint32_t)((tmr->tccr & TCCR_TOG) | (count & ~TCCR_TOG)); + } + return retval; +} + static void openpic_tmr_write(void *opaque, hwaddr addr, uint64_t val, - unsigned len) + unsigned len) { OpenPICState *opp = opaque; int idx; - addr += 0x10f0; - - DPRINTF("%s: addr %#" HWADDR_PRIx " <= %08" PRIx64 "\n", - __func__, addr, val); + DPRINTF("%s: addr %#" HWADDR_PRIx " <= %08" PRIx64, + __func__, (addr + 0x10f0), val); if (addr & 0xF) { return; } - if (addr == 0x10f0) { + if (addr == 0) { /* TFRR */ opp->tfrr = val; return; } - + addr -= 0x10; /* correct for TFRR */ idx = (addr >> 6) & 0x3; - addr = addr & 0x30; switch (addr & 0x30) { case 0x00: /* TCCR */ break; case 0x10: /* TBCR */ - if ((opp->timers[idx].tccr & TCCR_TOG) != 0 && - (val & TBCR_CI) == 0 && - (opp->timers[idx].tbcr & TBCR_CI) != 0) { - opp->timers[idx].tccr &= ~TCCR_TOG; + /* Did the enable status change? */ + if ((opp->timers[idx].tbcr & TBCR_CI) != (val & TBCR_CI)) { + /* Did "Count Inhibit" transition from 1 to 0? */ + if ((val & TBCR_CI) == 0) { + opp->timers[idx].tccr = val & ~TCCR_TOG; + } + openpic_tmr_set_tmr(&opp->timers[idx], + (val & ~TBCR_CI), + /*enabled=*/((val & TBCR_CI) == 0)); } opp->timers[idx].tbcr = val; break; @@ -882,33 +786,34 @@ static uint64_t openpic_tmr_read(void *opaque, hwaddr addr, unsigned len) uint32_t retval = -1; int idx; - DPRINTF("%s: addr %#" HWADDR_PRIx "\n", __func__, addr); + DPRINTF("%s: addr %#" HWADDR_PRIx, __func__, addr + 0x10f0); if (addr & 0xF) { goto out; } - idx = (addr >> 6) & 0x3; - if (addr == 0x0) { + if (addr == 0) { /* TFRR */ retval = opp->tfrr; goto out; } + addr -= 0x10; /* correct for TFRR */ + idx = (addr >> 6) & 0x3; switch (addr & 0x30) { case 0x00: /* TCCR */ - retval = opp->timers[idx].tccr; + retval = openpic_tmr_get_timer(&opp->timers[idx]); break; case 0x10: /* TBCR */ retval = opp->timers[idx].tbcr; break; - case 0x20: /* TIPV */ + case 0x20: /* TVPR */ retval = read_IRQreg_ivpr(opp, opp->irq_tim0 + idx); break; - case 0x30: /* TIDE (TIDR) */ + case 0x30: /* TDR */ retval = read_IRQreg_idr(opp, opp->irq_tim0 + idx); break; } out: - DPRINTF("%s: => 0x%08x\n", __func__, retval); + DPRINTF("%s: => 0x%08x", __func__, retval); return retval; } @@ -919,7 +824,7 @@ static void openpic_src_write(void *opaque, hwaddr addr, uint64_t val, OpenPICState *opp = opaque; int idx; - DPRINTF("%s: addr %#" HWADDR_PRIx " <= %08" PRIx64 "\n", + DPRINTF("%s: addr %#" HWADDR_PRIx " <= %08" PRIx64, __func__, addr, val); addr = addr & 0xffff; @@ -944,7 +849,7 @@ static uint64_t openpic_src_read(void *opaque, uint64_t addr, unsigned len) uint32_t retval; int idx; - DPRINTF("%s: addr %#" HWADDR_PRIx "\n", __func__, addr); + DPRINTF("%s: addr %#" HWADDR_PRIx, __func__, addr); retval = 0xFFFFFFFF; addr = addr & 0xffff; @@ -962,7 +867,7 @@ static uint64_t openpic_src_read(void *opaque, uint64_t addr, unsigned len) break; } - DPRINTF("%s: => 0x%08x\n", __func__, retval); + DPRINTF("%s: => 0x%08x", __func__, retval); return retval; } @@ -973,7 +878,7 @@ static void openpic_msi_write(void *opaque, hwaddr addr, uint64_t val, int idx = opp->irq_msi; int srs, ibs; - DPRINTF("%s: addr %#" HWADDR_PRIx " <= 0x%08" PRIx64 "\n", + DPRINTF("%s: addr %#" HWADDR_PRIx " <= 0x%08" PRIx64, __func__, addr, val); if (addr & 0xF) { return; @@ -999,7 +904,7 @@ static uint64_t openpic_msi_read(void *opaque, hwaddr addr, unsigned size) uint64_t r = 0; int i, srs; - DPRINTF("%s: addr %#" HWADDR_PRIx "\n", __func__, addr); + DPRINTF("%s: addr %#" HWADDR_PRIx, __func__, addr); if (addr & 0xF) { return -1; } @@ -1034,7 +939,7 @@ static uint64_t openpic_summary_read(void *opaque, hwaddr addr, unsigned size) { uint64_t r = 0; - DPRINTF("%s: addr %#" HWADDR_PRIx "\n", __func__, addr); + DPRINTF("%s: addr %#" HWADDR_PRIx, __func__, addr); /* TODO: EISR/EIMR */ @@ -1044,7 +949,7 @@ static uint64_t openpic_summary_read(void *opaque, hwaddr addr, unsigned size) static void openpic_summary_write(void *opaque, hwaddr addr, uint64_t val, unsigned size) { - DPRINTF("%s: addr %#" HWADDR_PRIx " <= 0x%08" PRIx64 "\n", + DPRINTF("%s: addr %#" HWADDR_PRIx " <= 0x%08" PRIx64, __func__, addr, val); /* TODO: EISR/EIMR */ @@ -1058,10 +963,10 @@ static void openpic_cpu_write_internal(void *opaque, hwaddr addr, IRQDest *dst; int s_IRQ, n_IRQ; - DPRINTF("%s: cpu %d addr %#" HWADDR_PRIx " <= 0x%08x\n", __func__, idx, + DPRINTF("%s: cpu %d addr %#" HWADDR_PRIx " <= 0x%08x", __func__, idx, addr, val); - if (idx < 0) { + if (idx < 0 || idx >= opp->nb_cpus) { return; } @@ -1084,16 +989,16 @@ static void openpic_cpu_write_internal(void *opaque, hwaddr addr, case 0x80: /* CTPR */ dst->ctpr = val & 0x0000000F; - DPRINTF("%s: set CPU %d ctpr to %d, raised %d servicing %d\n", + DPRINTF("%s: set CPU %d ctpr to %d, raised %d servicing %d", __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", + DPRINTF("%s: Lower OpenPIC INT output cpu %d due to ctpr", __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", + DPRINTF("%s: Raise OpenPIC INT output cpu %d irq %d", __func__, idx, dst->raised.next); qemu_irq_raise(dst->irqs[OPENPIC_OUTPUT_INT]); } @@ -1106,11 +1011,11 @@ static void openpic_cpu_write_internal(void *opaque, hwaddr addr, /* Read-only register */ break; case 0xB0: /* EOI */ - DPRINTF("EOI\n"); + DPRINTF("EOI"); s_IRQ = IRQ_get_next(opp, &dst->servicing); if (s_IRQ < 0) { - DPRINTF("%s: EOI with no interrupt in service\n", __func__); + DPRINTF("%s: EOI with no interrupt in service", __func__); break; } @@ -1123,7 +1028,7 @@ static void openpic_cpu_write_internal(void *opaque, hwaddr addr, if (n_IRQ != -1 && (s_IRQ == -1 || IVPR_PRIORITY(src->ivpr) > dst->servicing.priority)) { - DPRINTF("Raise OpenPIC INT output cpu %d irq %d\n", + DPRINTF("Raise OpenPIC INT output cpu %d irq %d", idx, n_IRQ); qemu_irq_raise(opp->dst[idx].irqs[OPENPIC_OUTPUT_INT]); } @@ -1145,11 +1050,11 @@ static uint32_t openpic_iack(OpenPICState *opp, IRQDest *dst, int cpu) IRQSource *src; int retval, irq; - DPRINTF("Lower OpenPIC INT output\n"); + DPRINTF("Lower OpenPIC INT output"); qemu_irq_lower(dst->irqs[OPENPIC_OUTPUT_INT]); irq = IRQ_get_next(opp, &dst->raised); - DPRINTF("IACK: irq=%d\n", irq); + DPRINTF("IACK: irq=%d", irq); if (irq == -1) { /* No more interrupt pending */ @@ -1159,7 +1064,7 @@ static uint32_t openpic_iack(OpenPICState *opp, IRQDest *dst, int cpu) 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", + error_report("%s: bad raised IRQ %d ctpr %d ivpr 0x%08x", __func__, irq, dst->ctpr, src->ivpr); openpic_update_irq(opp, irq); retval = opp->spve; @@ -1176,7 +1081,10 @@ static uint32_t openpic_iack(OpenPICState *opp, IRQDest *dst, int cpu) IRQ_resetbit(&dst->raised, irq); } - if ((irq >= opp->irq_ipi0) && (irq < (opp->irq_ipi0 + OPENPIC_MAX_IPI))) { + /* Timers and IPIs support multicast. */ + if (((irq >= opp->irq_ipi0) && (irq < (opp->irq_ipi0 + OPENPIC_MAX_IPI))) || + ((irq >= opp->irq_tim0) && (irq < (opp->irq_tim0 + OPENPIC_MAX_TMR)))) { + DPRINTF("irq is IPI or TMR"); src->destmask &= ~(1 << cpu); if (src->destmask && !src->level) { /* trigger on CPUs that didn't know about it yet */ @@ -1197,10 +1105,10 @@ static uint32_t openpic_cpu_read_internal(void *opaque, hwaddr addr, IRQDest *dst; uint32_t retval; - DPRINTF("%s: cpu %d addr %#" HWADDR_PRIx "\n", __func__, idx, addr); + DPRINTF("%s: cpu %d addr %#" HWADDR_PRIx, __func__, idx, addr); retval = 0xFFFFFFFF; - if (idx < 0) { + if (idx < 0 || idx >= opp->nb_cpus) { return retval; } @@ -1225,7 +1133,7 @@ static uint32_t openpic_cpu_read_internal(void *opaque, hwaddr addr, default: break; } - DPRINTF("%s: => 0x%08x\n", __func__, retval); + DPRINTF("%s: => 0x%08x", __func__, retval); return retval; } @@ -1335,130 +1243,59 @@ static const MemoryRegionOps openpic_summary_ops_be = { }, }; -static void openpic_save_IRQ_queue(QEMUFile* f, IRQQueue *q) -{ - unsigned int i; - - for (i = 0; i < ARRAY_SIZE(q->queue); i++) { - /* Always put the lower half of a 64-bit long first, in case we - * restore on a 32-bit host. The least significant bits correspond - * to lower IRQ numbers in the bitmap. - */ - qemu_put_be32(f, (uint32_t)q->queue[i]); -#if LONG_MAX > 0x7FFFFFFF - qemu_put_be32(f, (uint32_t)(q->queue[i] >> 32)); -#endif - } - - qemu_put_sbe32s(f, &q->next); - qemu_put_sbe32s(f, &q->priority); -} - -static void openpic_save(QEMUFile* f, void *opaque) +static void openpic_reset(DeviceState *d) { - OpenPICState *opp = (OpenPICState *)opaque; - unsigned int i; - - qemu_put_be32s(f, &opp->gcr); - qemu_put_be32s(f, &opp->vir); - qemu_put_be32s(f, &opp->pir); - qemu_put_be32s(f, &opp->spve); - qemu_put_be32s(f, &opp->tfrr); - - qemu_put_be32s(f, &opp->nb_cpus); - - for (i = 0; i < opp->nb_cpus; i++) { - 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)); - } + OpenPICState *opp = OPENPIC(d); + int i; - for (i = 0; i < OPENPIC_MAX_TMR; i++) { - qemu_put_be32s(f, &opp->timers[i].tccr); - qemu_put_be32s(f, &opp->timers[i].tbcr); - } + opp->gcr = GCR_RESET; + /* Initialise controller registers */ + opp->frr = ((opp->nb_irqs - 1) << FRR_NIRQ_SHIFT) | + ((opp->nb_cpus - 1) << FRR_NCPU_SHIFT) | + (opp->vid << FRR_VID_SHIFT); + opp->pir = 0; + opp->spve = -1 & opp->vector_mask; + opp->tfrr = opp->tfrr_reset; + /* Initialise IRQ sources */ 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_get_be32s(f, &opp->src[i].destmask); - 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) -{ - unsigned int i; - - for (i = 0; i < ARRAY_SIZE(q->queue); i++) { - unsigned long val; - - val = qemu_get_be32(f); -#if LONG_MAX > 0x7FFFFFFF - val <<= 32; - val |= qemu_get_be32(f); -#endif - - q->queue[i] = val; - } - - qemu_get_sbe32s(f, &q->next); - qemu_get_sbe32s(f, &q->priority); -} - -static int openpic_load(QEMUFile* f, void *opaque, int version_id) -{ - OpenPICState *opp = (OpenPICState *)opaque; - unsigned int i, nb_cpus; + opp->src[i].ivpr = opp->ivpr_reset; + switch (opp->src[i].type) { + case IRQ_TYPE_NORMAL: + opp->src[i].level = !!(opp->ivpr_reset & IVPR_SENSE_MASK); + break; - if (version_id != 1) { - return -EINVAL; - } + case IRQ_TYPE_FSLINT: + opp->src[i].ivpr |= IVPR_POLARITY_MASK; + break; - qemu_get_be32s(f, &opp->gcr); - qemu_get_be32s(f, &opp->vir); - qemu_get_be32s(f, &opp->pir); - qemu_get_be32s(f, &opp->spve); - qemu_get_be32s(f, &opp->tfrr); + case IRQ_TYPE_FSLSPECIAL: + break; + } - qemu_get_be32s(f, &nb_cpus); - if (opp->nb_cpus != nb_cpus) { - return -EINVAL; + write_IRQreg_idr(opp, i, opp->idr_reset); } - assert(nb_cpus > 0 && nb_cpus <= MAX_CPU); - + /* Initialise IRQ destinations */ for (i = 0; i < opp->nb_cpus; 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)); + opp->dst[i].ctpr = 15; + opp->dst[i].raised.next = -1; + opp->dst[i].raised.priority = 0; + bitmap_clear(opp->dst[i].raised.queue, 0, IRQQUEUE_SIZE_BITS); + opp->dst[i].servicing.next = -1; + opp->dst[i].servicing.priority = 0; + bitmap_clear(opp->dst[i].servicing.queue, 0, IRQQUEUE_SIZE_BITS); } - + /* Initialise timers */ for (i = 0; i < OPENPIC_MAX_TMR; i++) { - qemu_get_be32s(f, &opp->timers[i].tccr); - qemu_get_be32s(f, &opp->timers[i].tbcr); - } - - for (i = 0; i < opp->max_irq; i++) { - uint32_t val; - - val = qemu_get_be32(f); - write_IRQreg_idr(opp, i, val); - val = qemu_get_be32(f); - write_IRQreg_ivpr(opp, i, val); - - qemu_get_be32s(f, &opp->src[i].ivpr); - qemu_get_be32s(f, &opp->src[i].idr); - qemu_get_be32s(f, &opp->src[i].destmask); - qemu_get_sbe32s(f, &opp->src[i].last_cpu); - qemu_get_sbe32s(f, &opp->src[i].pending); + opp->timers[i].tccr = 0; + opp->timers[i].tbcr = TBCR_CI; + if (opp->timers[i].qemu_timer_active) { + timer_del(opp->timers[i].qemu_timer); /* Inhibit timer */ + opp->timers[i].qemu_timer_active = false; + } } - - return 0; + /* Go out of RESET state */ + opp->gcr = 0; } typedef struct MemReg { @@ -1490,7 +1327,7 @@ static void fsl_common_init(OpenPICState *opp) opp->irq_msi = 224; - msi_supported = true; + msi_nonbroken = true; for (i = 0; i < opp->fsl->max_ext; i++) { opp->src[i].level = false; } @@ -1506,6 +1343,15 @@ static void fsl_common_init(OpenPICState *opp) opp->src[i].type = IRQ_TYPE_FSLSPECIAL; opp->src[i].level = false; } + + for (i = 0; i < OPENPIC_MAX_TMR; i++) { + opp->timers[i].n_IRQ = opp->irq_tim0 + i; + opp->timers[i].qemu_timer_active = false; + opp->timers[i].qemu_timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, + &qemu_timer_cb, + &opp->timers[i]); + opp->timers[i].opp = opp; + } } static void map_list(OpenPICState *opp, const MemReg *list, int *count) @@ -1524,6 +1370,110 @@ static void map_list(OpenPICState *opp, const MemReg *list, int *count) } } +static const VMStateDescription vmstate_openpic_irq_queue = { + .name = "openpic_irq_queue", + .version_id = 0, + .minimum_version_id = 0, + .fields = (VMStateField[]) { + VMSTATE_BITMAP(queue, IRQQueue, 0, queue_size), + VMSTATE_INT32(next, IRQQueue), + VMSTATE_INT32(priority, IRQQueue), + VMSTATE_END_OF_LIST() + } +}; + +static const VMStateDescription vmstate_openpic_irqdest = { + .name = "openpic_irqdest", + .version_id = 0, + .minimum_version_id = 0, + .fields = (VMStateField[]) { + VMSTATE_INT32(ctpr, IRQDest), + VMSTATE_STRUCT(raised, IRQDest, 0, vmstate_openpic_irq_queue, + IRQQueue), + VMSTATE_STRUCT(servicing, IRQDest, 0, vmstate_openpic_irq_queue, + IRQQueue), + VMSTATE_UINT32_ARRAY(outputs_active, IRQDest, OPENPIC_OUTPUT_NB), + VMSTATE_END_OF_LIST() + } +}; + +static const VMStateDescription vmstate_openpic_irqsource = { + .name = "openpic_irqsource", + .version_id = 0, + .minimum_version_id = 0, + .fields = (VMStateField[]) { + VMSTATE_UINT32(ivpr, IRQSource), + VMSTATE_UINT32(idr, IRQSource), + VMSTATE_UINT32(destmask, IRQSource), + VMSTATE_INT32(last_cpu, IRQSource), + VMSTATE_INT32(pending, IRQSource), + VMSTATE_END_OF_LIST() + } +}; + +static const VMStateDescription vmstate_openpic_timer = { + .name = "openpic_timer", + .version_id = 0, + .minimum_version_id = 0, + .fields = (VMStateField[]) { + VMSTATE_UINT32(tccr, OpenPICTimer), + VMSTATE_UINT32(tbcr, OpenPICTimer), + VMSTATE_END_OF_LIST() + } +}; + +static const VMStateDescription vmstate_openpic_msi = { + .name = "openpic_msi", + .version_id = 0, + .minimum_version_id = 0, + .fields = (VMStateField[]) { + VMSTATE_UINT32(msir, OpenPICMSI), + VMSTATE_END_OF_LIST() + } +}; + +static int openpic_post_load(void *opaque, int version_id) +{ + OpenPICState *opp = (OpenPICState *)opaque; + int i; + + /* Update internal ivpr and idr variables */ + for (i = 0; i < opp->max_irq; i++) { + write_IRQreg_idr(opp, i, opp->src[i].idr); + write_IRQreg_ivpr(opp, i, opp->src[i].ivpr); + } + + return 0; +} + +static const VMStateDescription vmstate_openpic = { + .name = "openpic", + .version_id = 3, + .minimum_version_id = 3, + .post_load = openpic_post_load, + .fields = (VMStateField[]) { + VMSTATE_UINT32(gcr, OpenPICState), + VMSTATE_UINT32(vir, OpenPICState), + VMSTATE_UINT32(pir, OpenPICState), + VMSTATE_UINT32(spve, OpenPICState), + VMSTATE_UINT32(tfrr, OpenPICState), + VMSTATE_UINT32(max_irq, OpenPICState), + VMSTATE_STRUCT_VARRAY_UINT32(src, OpenPICState, max_irq, 0, + vmstate_openpic_irqsource, IRQSource), + VMSTATE_UINT32_EQUAL(nb_cpus, OpenPICState, NULL), + VMSTATE_STRUCT_VARRAY_UINT32(dst, OpenPICState, nb_cpus, 0, + vmstate_openpic_irqdest, IRQDest), + VMSTATE_STRUCT_ARRAY(timers, OpenPICState, OPENPIC_MAX_TMR, 0, + vmstate_openpic_timer, OpenPICTimer), + VMSTATE_STRUCT_ARRAY(msi, OpenPICState, MAX_MSI, 0, + vmstate_openpic_msi, OpenPICMSI), + VMSTATE_UINT32(irq_ipi0, OpenPICState), + VMSTATE_UINT32(irq_tim0, OpenPICState), + VMSTATE_UINT32(irq_msi, OpenPICState), + VMSTATE_END_OF_LIST() + } +}; + static void openpic_init(Object *obj) { OpenPICState *opp = OPENPIC(obj); @@ -1568,9 +1518,9 @@ static void openpic_realize(DeviceState *dev, Error **errp) }; if (opp->nb_cpus > MAX_CPU) { - error_set(errp, QERR_PROPERTY_VALUE_OUT_OF_RANGE, - TYPE_OPENPIC, "nb_cpus", (uint64_t)opp->nb_cpus, - (uint64_t)0, (uint64_t)MAX_CPU); + error_setg(errp, QERR_PROPERTY_VALUE_OUT_OF_RANGE, + TYPE_OPENPIC, "nb_cpus", (uint64_t)opp->nb_cpus, + (uint64_t)0, (uint64_t)MAX_CPU); return; } @@ -1621,19 +1571,43 @@ static void openpic_realize(DeviceState *dev, Error **errp) return; } + map_list(opp, list_le, &list_count); + break; + + case OPENPIC_MODEL_KEYLARGO: + opp->nb_irqs = KEYLARGO_MAX_EXT; + opp->vid = VID_REVISION_1_2; + opp->vir = VIR_GENERIC; + opp->vector_mask = 0xFF; + opp->tfrr_reset = 4160000; + opp->ivpr_reset = IVPR_MASK_MASK | IVPR_MODE_MASK; + opp->idr_reset = 0; + opp->max_irq = KEYLARGO_MAX_IRQ; + opp->irq_ipi0 = KEYLARGO_IPI_IRQ; + opp->irq_tim0 = KEYLARGO_TMR_IRQ; + opp->brr1 = -1; + opp->mpic_mode_mask = GCR_MODE_MIXED; + + if (opp->nb_cpus != 1) { + error_setg(errp, "Only UP supported today"); + return; + } + map_list(opp, list_le, &list_count); break; } for (i = 0; i < opp->nb_cpus; i++) { - opp->dst[i].irqs = g_new(qemu_irq, OPENPIC_OUTPUT_NB); + opp->dst[i].irqs = g_new0(qemu_irq, OPENPIC_OUTPUT_NB); for (j = 0; j < OPENPIC_OUTPUT_NB; j++) { sysbus_init_irq(d, &opp->dst[i].irqs[j]); } - } - register_savevm(dev, "openpic", 0, 2, - openpic_save, openpic_load, opp); + opp->dst[i].raised.queue_size = IRQQUEUE_SIZE_BITS; + opp->dst[i].raised.queue = bitmap_new(IRQQUEUE_SIZE_BITS); + opp->dst[i].servicing.queue_size = IRQQUEUE_SIZE_BITS; + opp->dst[i].servicing.queue = bitmap_new(IRQQUEUE_SIZE_BITS); + } sysbus_init_mmio(d, &opp->mem); qdev_init_gpio_in(dev, openpic_set_irq, opp->max_irq); @@ -1652,6 +1626,8 @@ static void openpic_class_init(ObjectClass *oc, void *data) dc->realize = openpic_realize; dc->props = openpic_properties; dc->reset = openpic_reset; + dc->vmsd = &vmstate_openpic; + set_bit(DEVICE_CATEGORY_MISC, dc->categories); } static const TypeInfo openpic_info = {