* Serial interrupts, as implemented in Raven chipset are not supported yet.
*
*/
-#include "hw/hw.h"
+
+#include "qemu/osdep.h"
+#include "hw/irq.h"
#include "hw/ppc/mac.h"
#include "hw/pci/pci.h"
#include "hw/ppc/openpic.h"
#include "hw/ppc/ppc_e500.h"
+#include "hw/qdev-properties.h"
#include "hw/sysbus.h"
+#include "migration/vmstate.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/module.h"
+#include "qemu/timer.h"
+#include "qemu/error-report.h"
//#define DEBUG_OPENPIC
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)
#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,
};
#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
}
}
- fprintf(stderr, "%s: unsupported inttgt %d\n", __func__, inttgt);
+ error_report("%s: unsupported inttgt %d", __func__, inttgt);
return OPENPIC_OUTPUT_INT;
}
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)
{
clear_bit(n_IRQ, q->queue);
}
-static inline int IRQ_testbit(IRQQueue *q, int n_IRQ)
-{
- return test_bit(n_IRQ, q->queue);
-}
-
static void IRQ_check(OpenPICState *opp, IRQQueue *q)
{
int irq = -1;
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) {
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]);
*/
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]);
}
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;
}
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]);
}
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]);
}
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;
}
* 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;
}
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;
}
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 */
}
}
-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;
}
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;
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 */
}
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);
}
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;
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]);
}
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;
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;
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;
}
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;
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;
break;
}
- DPRINTF("%s: => 0x%08x\n", __func__, retval);
+ DPRINTF("%s: => 0x%08x", __func__, retval);
return retval;
}
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;
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;
}
{
uint64_t r = 0;
- DPRINTF("%s: addr %#" HWADDR_PRIx "\n", __func__, addr);
+ DPRINTF("%s: addr %#" HWADDR_PRIx, __func__, addr);
/* TODO: EISR/EIMR */
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 */
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;
}
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]);
}
/* 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;
}
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]);
}
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 */
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;
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 */
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;
}
default:
break;
}
- DPRINTF("%s: => 0x%08x\n", __func__, retval);
+ DPRINTF("%s: => 0x%08x", __func__, retval);
return retval;
}
},
};
-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;
- }
+ 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;
- qemu_get_sbe32s(f, &q->next);
- qemu_get_sbe32s(f, &q->priority);
-}
+ case IRQ_TYPE_FSLINT:
+ opp->src[i].ivpr |= IVPR_POLARITY_MASK;
+ break;
-static int openpic_load(QEMUFile* f, void *opaque, int version_id)
-{
- OpenPICState *opp = (OpenPICState *)opaque;
- unsigned int i;
+ case IRQ_TYPE_FSLSPECIAL:
+ break;
+ }
- if (version_id != 1) {
- return -EINVAL;
+ write_IRQreg_idr(opp, i, opp->idr_reset);
}
-
- 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);
-
- qemu_get_be32s(f, &opp->nb_cpus);
-
+ /* 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 {
opp->irq_msi = 224;
- msi_supported = true;
+ msi_nonbroken = true;
for (i = 0; i < opp->fsl->max_ext; i++) {
opp->src[i].level = false;
}
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)
}
}
+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);
{NULL}
};
+ if (opp->nb_cpus > 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;
+ }
+
switch (opp->model) {
case OPENPIC_MODEL_FSL_MPIC_20:
default:
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);
DeviceClass *dc = DEVICE_CLASS(oc);
dc->realize = openpic_realize;
- dc->props = openpic_properties;
+ device_class_set_props(dc, openpic_properties);
dc->reset = openpic_reset;
+ dc->vmsd = &vmstate_openpic;
+ set_bit(DEVICE_CATEGORY_MISC, dc->categories);
}
static const TypeInfo openpic_info = {