[PATCH 3/5] qemu/kvm: MPIC and E500 PCI controller emualtion

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



Signed-off-by: Liu Yu <yu.liu@xxxxxxxxxxxxx>
---
 hw/mpic.c        |  903 ++++++++++++++++++++++++++++++++++++++++++++++++++++++
 hw/ppce500_pci.c |  322 +++++++++++++++++++
 2 files changed, 1225 insertions(+), 0 deletions(-)
 create mode 100644 hw/mpic.c
 create mode 100644 hw/ppce500_pci.c

diff --git a/hw/mpic.c b/hw/mpic.c
new file mode 100644
index 0000000..a68948a
--- /dev/null
+++ b/hw/mpic.c
@@ -0,0 +1,903 @@
+/*
+ * MPIC emulation
+ *
+ * Copyright (C) 2009 Freescale Semiconductor, Inc. All rights reserved.
+ *
+ * Author: Yu Liu,     <yu.liu@xxxxxxxxxxxxx>
+ *
+ * This file is derived from hw/openpic.c,
+ * the copyright for that material belongs to the original owners.
+ *
+ * This is free software; you can redistribute it and/or modify
+ * it under the terms of  the GNU General  Public License as published by
+ * the Free Software Foundation;  either version 2 of the  License, or
+ * (at your option) any later version.
+ */
+
+#include "hw.h"
+#include "ppce500.h"
+
+#ifdef DEBUG_MPIC
+#define DPRINTF(fmt, args...) do { fprintf(stderr, fmt , ##args); fflush(stderr);} while (0)
+#else
+#define DPRINTF(fmt, args...)
+#endif
+
+#define MPIC_MAP_SIZE	0x40000
+
+#define MAX_CPU     1
+#define MAX_EXT    12
+#define MAX_INT    64
+#define MAX_TMR     4
+#define MAX_MSG     4
+#define MAX_MSI     8
+#define MAX_IPI     4
+#define MAX_IRQ    (MAX_EXT + MAX_INT + MAX_TMR + MAX_MSG + MAX_MSI + (MAX_IPI * MAX_CPU))
+
+#define VECTOR_BITS 8
+#define VID         0x0 /* MPIC version ID */
+#define VIR         0x00000000 /* Vendor ID */
+
+enum {
+    IRQ_IPVR = 0,
+    IRQ_IDR,
+};
+
+#define MPIC_LITTLE_ENDIAN 0
+#define MPIC_BIG_ENDIAN    1
+
+#if (MPIC_BIG_ENDIAN && !TARGET_WORDS_BIGENDIAN) || \
+    (MPIC_LITTLE_ENDIAN && TARGET_WORDS_BIGENDIAN)
+#define MPIC_SWAP
+#endif
+
+/* Interrupt definitions */
+#define EXT_IRQ		0
+#define INT_IRQ		(EXT_IRQ + MAX_EXT)
+#define TMR_IRQ		(INT_IRQ + MAX_INT)
+#define MSG_IRQ		(TMR_IRQ + MAX_TMR)
+#define MSI_IRQ		(MSG_IRQ + MAX_MSG)
+#define IPI_IRQ		(MSI_IRQ + MAX_MSI)
+
+#define BF_WIDTH(_bits_) \
+(((_bits_) + (sizeof(uint32_t) * 8) - 1) / (sizeof(uint32_t) * 8))
+
+static inline void set_bit (uint32_t *field, int bit)
+{
+    field[bit >> 5] |= 1 << (bit & 0x1F);
+}
+
+static inline void reset_bit (uint32_t *field, int bit)
+{
+    field[bit >> 5] &= ~(1 << (bit & 0x1F));
+}
+
+static inline int test_bit (uint32_t *field, int bit)
+{
+    return (field[bit >> 5] & 1 << (bit & 0x1F)) != 0;
+}
+
+enum {
+    IRQ_EXTERNAL = 0x01,
+    IRQ_INTERNAL = 0x02,
+    IRQ_TIMER    = 0x04,
+    IRQ_SPECIAL  = 0x08,
+} IRQ_src_type;
+
+typedef struct IRQ_queue_t {
+    uint32_t queue[BF_WIDTH(MAX_IRQ)];
+    int next;
+    int priority;
+} IRQ_queue_t;
+
+typedef struct IRQ_src_t {
+    uint32_t ipvr;  /* IRQ vector/priority register */
+    uint32_t idr;   /* IRQ destination register */
+    int last_cpu;
+    int pending;    /* TRUE if IRQ is pending */
+} IRQ_src_t;
+
+enum ipvr_bits {
+    IPVR_MASK     = 31,
+    IPVR_ACTIVITY = 30,
+    IPVR_POLARITY = 23,
+    IPVR_SENSE    = 22,
+};
+#define IPVR_PRIORITY_MASK     (0x1F << 16)
+#define IPVR_PRIORITY(_ipvrr_) ((int)(((_ipvrr_) & IPVR_PRIORITY_MASK) >> 16))
+#define IPVR_VECTOR_MASK       ((1 << VECTOR_BITS) - 1)
+#define IPVR_VECTOR(_ipvrr_)   ((_ipvrr_) & IPVR_VECTOR_MASK)
+
+enum idr_bits {
+    IDR_EP     = 0,
+    IDR_CI0     = 1,
+    IDR_CI1     = 2,
+    IDR_P1     = 30,
+    IDR_P0     = 31,
+};
+typedef struct IRQ_dst_t {
+    uint32_t tfrr; /* Timer frequency reporting register */
+    uint32_t ctpr; /* CPU current task priority */
+    uint32_t whoami;
+    uint32_t iack;
+    uint32_t eoi;
+    IRQ_queue_t raised;
+    IRQ_queue_t servicing;
+    qemu_irq *irqs;
+} IRQ_dst_t;
+
+typedef struct mpic {
+    int mem_index;
+    /* Global registers */
+    uint32_t frr; /* Feature reporting register */
+    uint32_t gcr; /* Global configuration register  */
+    uint32_t vir; /* Vendor identification register */
+    uint32_t pir; /* Processor initialization register */
+    uint32_t svr; /* Spurious vector register */
+    /* Source registers */
+    IRQ_src_t src[MAX_IRQ];
+    /* Local registers per output pin */
+    IRQ_dst_t dst[MAX_CPU];
+    int nb_cpus;
+    /* Timer registers */
+    struct {
+	uint32_t gtccr;  /* Global timer current count register */
+	uint32_t gtbcr;  /* Global timer base count register */
+    } timers[MAX_TMR];
+    /* IRQ out is used when in bypass mode (not implemented) */
+    qemu_irq irq_out;
+} mpic_t;
+
+static inline void IRQ_setbit (IRQ_queue_t *q, int n_IRQ)
+{
+    set_bit(q->queue, n_IRQ);
+}
+
+static inline void IRQ_resetbit (IRQ_queue_t *q, int n_IRQ)
+{
+    reset_bit(q->queue, n_IRQ);
+}
+
+static inline int IRQ_testbit (IRQ_queue_t *q, int n_IRQ)
+{
+    return test_bit(q->queue, n_IRQ);
+}
+
+static void IRQ_check (mpic_t *mpp, IRQ_queue_t *q)
+{
+    int next, i;
+    int priority;
+
+    next = -1;
+    priority = -1;
+    for (i = 0; i < MAX_IRQ; i++) {
+	if (IRQ_testbit(q, i)) {
+            DPRINTF("IRQ_check: irq %d set ipvr_pr=%d pr=%d\n",
+                    i, IPVR_PRIORITY(mpp->src[i].ipvr), priority);
+	    if (IPVR_PRIORITY(mpp->src[i].ipvr) > priority) {
+		next = i;
+		priority = IPVR_PRIORITY(mpp->src[i].ipvr);
+	    }
+	}
+    }
+    q->next = next;
+    q->priority = priority;
+}
+
+static int IRQ_get_next (mpic_t *mpp, IRQ_queue_t *q)
+{
+    if (q->next == -1) {
+        /* XXX: optimize */
+	IRQ_check(mpp, q);
+    }
+
+    return q->next;
+}
+
+static void mpic_irq_raise(mpic_t *mpp, int n_CPU, int n_IRQ)
+{
+    int n_ci = IDR_CI0 - n_CPU;
+    DPRINTF("%s: cpu:%d irq:%d (testbit idr:%x ci:%d)\n", __func__,
+		    n_CPU, n_IRQ, mpp->src[n_IRQ].idr, n_ci);
+    if(test_bit(&mpp->src[n_IRQ].idr, n_ci)) {
+        qemu_irq_raise(mpp->dst[n_CPU].irqs[MPIC_OUTPUT_CINT]);
+    }
+    else {
+        qemu_irq_raise(mpp->dst[n_CPU].irqs[MPIC_OUTPUT_INT]);
+    }
+}
+
+static void IRQ_local_pipe (mpic_t *mpp, int n_CPU, int n_IRQ)
+{
+    IRQ_dst_t *dst;
+    IRQ_src_t *src;
+    int priority;
+
+    dst = &mpp->dst[n_CPU];
+    src = &mpp->src[n_IRQ];
+    priority = IPVR_PRIORITY(src->ipvr);
+    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;
+    }
+    set_bit(&src->ipvr, IPVR_ACTIVITY);
+    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;
+    }
+    IRQ_check(mpp, &dst->raised);
+    if (IRQ_get_next(mpp, &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);
+        /* 
+	 * Only allow a higher priority IRQ 
+	 * Stop the same priority IRQ to avoid reentrance
+	 */
+        return;
+    }
+    DPRINTF("Raise mpic INT output cpu %d irq %d\n", n_CPU, n_IRQ);
+    mpic_irq_raise(mpp, n_CPU, n_IRQ);
+}
+
+/* update pic state because registers for n_IRQ have changed value */
+static void mpic_update_irq(mpic_t *mpp, int n_IRQ)
+{
+    IRQ_src_t *src;
+    int i;
+
+    src = &mpp->src[n_IRQ];
+
+#ifdef DEBUG_MPIC
+    if (n_IRQ >= MAX_IRQ) {
+	DPRINTF("%s: IRQ %d exceed max\n", __func__, n_IRQ);
+	return;
+    }
+#endif
+
+    if (!src->pending) {
+        /* no irq pending */
+        DPRINTF("%s: IRQ %d is not pending\n", __func__, n_IRQ);
+        return;
+    }
+    if (test_bit(&src->ipvr, IPVR_MASK)) {
+	/* Interrupt source is disabled */
+        DPRINTF("%s: IRQ %d is disabled\n", __func__, n_IRQ);
+	return;
+    }
+    if (IPVR_PRIORITY(src->ipvr) == 0) {
+	/* Priority set to zero */
+        DPRINTF("%s: IRQ %d has 0 priority\n", __func__, n_IRQ);
+	return;
+    }
+    if (test_bit(&src->ipvr, IPVR_ACTIVITY)) {
+        /* IRQ already active */
+        DPRINTF("%s: IRQ %d is already active\n", __func__, n_IRQ);
+        return;
+    }
+    if (src->idr == 0x00000000) {
+	/* No target */
+	DPRINTF("%s: IRQ %d has no target\n", __func__, n_IRQ);
+	return;
+    }
+
+    /* Distributed delivery mode now only support 1 cpu */
+    for (i = src->last_cpu + 1; i != src->last_cpu; i++) {
+        if (i >= mpp->nb_cpus)
+            i = 0;
+        if (test_bit(&src->idr, i)) {
+            IRQ_local_pipe(mpp, i, n_IRQ);
+            src->last_cpu = i;
+            break;
+        }
+    }
+}
+
+static void mpic_set_irq(void *opaque, int n_IRQ, int level)
+{
+    mpic_t *mpp = opaque;
+    IRQ_src_t *src;
+
+    src = &mpp->src[n_IRQ];
+    DPRINTF("mpic: set irq %d = %d ipvr=%08x\n",
+            n_IRQ, level, src->ipvr);
+    if (n_IRQ < MAX_INT || test_bit(&src->ipvr, IPVR_SENSE)) {
+        /* level-sensitive irq */
+        src->pending = level;
+        if (!level)
+            reset_bit(&src->ipvr, IPVR_ACTIVITY);
+    } else {
+        /* edge-sensitive irq */
+        if (level)
+            src->pending = 1;
+    }
+    mpic_update_irq(mpp, n_IRQ);
+}
+
+static void mpic_reset (mpic_t *mpp)
+{
+    int i;
+
+    mpp->gcr = 0x80000000;
+    /* Initialise controller registers */
+    mpp->frr = 0x004f0002;
+    mpp->vir = VIR;
+    mpp->pir = 0x00000000;
+    mpp->svr = 0x0000FFFF;
+    /* Initialise IRQ sources */
+    for (i = 0; i < MAX_IRQ; i++) {
+	mpp->src[i].ipvr = 0x80800000;
+	mpp->src[i].idr  = 0x00000001;
+    }
+    /* Initialise IRQ destinations */
+    for (i = 0; i < MAX_CPU; i++) {
+	mpp->dst[i].ctpr      = 0x0000000F;
+        mpp->dst[i].tfrr      = 0x00000000;
+	memset(&mpp->dst[i].raised, 0, sizeof(IRQ_queue_t));
+	mpp->dst[i].raised.next = -1;
+	memset(&mpp->dst[i].servicing, 0, sizeof(IRQ_queue_t));
+	mpp->dst[i].servicing.next = -1;
+    }
+    /* Initialise timers */
+    for (i = 0; i < MAX_TMR; i++) {
+	mpp->timers[i].gtccr = 0x00000000;
+	mpp->timers[i].gtbcr = 0x80000000;
+    }
+    /* Go out of RESET state */
+    mpp->gcr = 0x00000000;
+}
+
+static inline uint32_t read_IRQreg (mpic_t *mpp, int n_IRQ, uint32_t reg)
+{
+    uint32_t retval;
+
+    switch (reg) {
+    case IRQ_IPVR:
+	retval = mpp->src[n_IRQ].ipvr;
+	break;
+    case IRQ_IDR:
+	retval = mpp->src[n_IRQ].idr;
+	break;
+    }
+
+    return retval;
+}
+
+static inline void write_IRQreg (mpic_t *mpp, int n_IRQ,
+                                 uint32_t reg, uint32_t val)
+{
+    switch (reg) {
+    case IRQ_IPVR:
+        /* NOTE: not fully accurate for special IRQs, but simple and
+           sufficient */
+        /* ACTIVITY bit is read-only */
+	mpp->src[n_IRQ].ipvr =
+            (mpp->src[n_IRQ].ipvr & 0x40000000) |
+            (val & ~0x40000000);
+        mpic_update_irq(mpp, n_IRQ);
+        DPRINTF("Set ipvr %d to 0x%08x -> 0x%08x\n",
+                n_IRQ, val, mpp->src[n_IRQ].ipvr);
+	break;
+    case IRQ_IDR:
+	mpp->src[n_IRQ].idr = val;
+        DPRINTF("Set IDR %d to 0x%08x\n", n_IRQ, mpp->src[n_IRQ].idr);
+	break;
+    }
+}
+
+static void mpic_gbl_write (void *opaque, uint32_t addr, uint32_t val)
+{
+    mpic_t *mpp = opaque;
+    IRQ_dst_t *dst;
+    int idx;
+
+    DPRINTF("%s: addr %08x <= %08x\n", __func__, addr, val);
+    if (addr & 0xF)
+        return;
+#if defined MPIC_SWAP
+    val = bswap32(val);
+#endif
+    addr &= 0xFF;
+    switch (addr) {
+    case 0x00: /* frr */
+        break;
+    case 0x20: /* gcr */
+        if (val & 0x80000000)
+            mpic_reset(mpp);
+        mpp->gcr = val & ~0x80000000;
+	break;
+    case 0x80: /* vir */
+	break;
+    case 0x90: /* pir */
+        for (idx = 0; idx < mpp->nb_cpus; idx++) {
+            if ((val & (1 << idx)) && !(mpp->pir & (1 << idx))) {
+                DPRINTF("Raise mpic RESET output for CPU %d\n", idx);
+                dst = &mpp->dst[idx];
+		qemu_irq_raise(dst->irqs[MPIC_OUTPUT_RESET]);
+            } else if (!(val & (1 << idx)) && (mpp->pir & (1 << idx))) {
+                DPRINTF("Lower mpic RESET output for CPU %d\n", idx);
+                dst = &mpp->dst[idx];
+                qemu_irq_lower(dst->irqs[MPIC_OUTPUT_RESET]);
+            }
+        }
+        mpp->pir = val;
+	break;
+#if MAX_IPI > 0
+    case 0xA0: /* IPI_ipvr */
+    case 0xB0:
+    case 0xC0:
+    case 0xD0:
+        {
+            int idx;
+            idx = (addr - 0xA0) >> 4;
+            write_IRQreg(mpp, IPI_IRQ + idx, IRQ_IPVR, val);
+        }
+        break;
+#endif
+    case 0xE0: /* svr */
+        mpp->svr = val & 0x000000FF;
+        break;
+    default:
+	DPRINTF("unknow addr %p for glb write\n",addr);
+        break;
+    }
+}
+
+static uint32_t mpic_gbl_read (void *opaque, uint32_t addr)
+{
+    mpic_t *mpp = opaque;
+    uint32_t retval;
+
+    DPRINTF("%s: addr %08x\n", __func__, addr);
+    retval = 0xFFFFFFFF;
+    if (addr & 0xF)
+        return retval;
+    addr &= 0xFF;
+    switch (addr) {
+    case 0x00: /* frr */
+        retval = mpp->frr;
+        break;
+    case 0x20: /* gcr */
+        retval = mpp->gcr;
+	break;
+    case 0x80: /* vir */
+	retval = mpp->vir;
+	break;
+    case 0x90: /* pir */
+        retval = 0x00000000;
+	break;
+#if MAX_IPI > 0
+    case 0xA0: /* IPI_ipvr */
+    case 0xB0:
+    case 0xC0:
+    case 0xD0:
+        {
+            int idx;
+            idx = (addr - 0xA0) >> 4;
+            retval = read_IRQreg(mpp, IPI_IRQ + idx, IRQ_IPVR);
+        }
+	break;
+#endif
+    case 0xE0: /* svr */
+        retval = mpp->svr;
+        break;
+    default:
+        break;
+    }
+    DPRINTF("%s: => %08x\n", __func__, retval);
+#if defined MPIC_SWAP
+    retval = bswap32(retval);
+#endif
+
+    return retval;
+}
+
+static void mpic_timer_write (void *opaque, uint32_t addr, uint32_t val)
+{
+    mpic_t *mpp = opaque;
+    int idx, cpu;
+
+    DPRINTF("%s: addr %08x <= %08x\n", __func__, addr, val);
+    if (addr & 0xF)
+        return;
+#if defined MPIC_SWAP
+    val = bswap32(val);
+#endif
+    addr &= 0xFFFF;
+    cpu = addr >> 12;
+    idx = (addr >> 6) & 0x3;
+    switch (addr & 0x30) {
+    case 0x00: /* gtccr */
+        break;
+    case 0x10: /* gtbcr */
+	if ((mpp->timers[idx].gtccr & 0x80000000) != 0 &&
+	    (val & 0x80000000) == 0 &&
+            (mpp->timers[idx].gtbcr & 0x80000000) != 0)
+	    mpp->timers[idx].gtccr &= ~0x80000000;
+	mpp->timers[idx].gtbcr = val;
+	break;
+    case 0x20: /* GTIVPR */
+	write_IRQreg(mpp, TMR_IRQ + idx, IRQ_IPVR, val);
+	break;
+    case 0x30: /* GTIDR & TFRR */
+	if ((addr & 0xF0) == 0xF0)
+            mpp->dst[cpu].tfrr = val;
+	else
+	    write_IRQreg(mpp, TMR_IRQ + idx, IRQ_IDR, val);
+	break;
+    }
+}
+
+static uint32_t mpic_timer_read (void *opaque, uint32_t addr)
+{
+    mpic_t *mpp = opaque;
+    uint32_t retval;
+    int idx, cpu;
+
+    DPRINTF("%s: addr %08x\n", __func__, addr);
+    retval = 0xFFFFFFFF;
+    if (addr & 0xF)
+        return retval;
+    addr &= 0xFFFF;
+    cpu = addr >> 12;
+    idx = (addr >> 6) & 0x3;
+    switch (addr & 0x30) {
+    case 0x00: /* gtccr */
+	retval = mpp->timers[idx].gtccr;
+        break;
+    case 0x10: /* gtbcr */
+	retval = mpp->timers[idx].gtbcr;
+	break;
+    case 0x20: /* TIPV */
+	retval = read_IRQreg(mpp, TMR_IRQ + idx, IRQ_IPVR);
+	break;
+    case 0x30: /* TIDR */
+        if ((addr &0xF0) == 0XF0)
+	    retval = mpp->dst[cpu].tfrr;
+	else
+	    retval = read_IRQreg(mpp, TMR_IRQ + idx, IRQ_IDR);
+	break;
+    }
+    DPRINTF("%s: => %08x\n", __func__, retval);
+#if defined MPIC_SWAP
+    retval = bswap32(retval);
+#endif
+
+    return retval;
+}
+
+static void mpic_src_write (void *opaque, uint32_t addr, uint32_t val)
+{
+    mpic_t *mpp = opaque;
+    int idx;
+
+    DPRINTF("%s: addr %08x <= %08x\n", __func__, addr, val);
+    if (addr & 0xF)
+        return;
+#if defined MPIC_SWAP
+    val = tswap32(val);
+#endif
+    addr = addr & 0xFFF0;
+    if (addr < 0x180) {
+	idx = EXT_IRQ;
+    } else if (addr >= 0x200 && addr < 0xa00) {
+	idx = INT_IRQ;
+	addr -= 0x200;
+    } else if (addr >= 0x1600 && addr < 0x1700) {
+	idx = MSG_IRQ;
+	addr -= 0x1600;
+    } else if (addr >= 0x1C00 && addr < 0x1D00) {
+	idx = MSI_IRQ;
+	addr -= 0x1C00;
+    } else {
+	return;
+    }
+    idx += addr >> 5;
+    if (addr & 0x10) {
+        /* EXDE / IFEDE / IEEDE */
+        write_IRQreg(mpp, idx, IRQ_IDR, val);
+    } else {
+        /* EXVP / IFEVP / IEEVP */
+        write_IRQreg(mpp, idx, IRQ_IPVR, val);
+    }
+}
+
+static uint32_t mpic_src_read (void *opaque, uint32_t addr)
+{
+    mpic_t *mpp = opaque;
+    uint32_t retval;
+    int idx;
+
+    DPRINTF("%s: addr %08x\n", __func__, addr);
+    retval = 0xFFFFFFFF;
+    if (addr & 0xF)
+        return retval;
+    addr = addr & 0xFFF0;
+    if (addr < 0x180) {
+	idx = EXT_IRQ;
+    } else if (addr >= 0x200 && addr < 0xa00) {
+	idx = INT_IRQ;
+	addr -= 0x200;
+    } else if (addr >= 0x1600 && addr < 0x1700) {
+	idx = MSG_IRQ;
+	addr -= 0x1600;
+    } else if (addr >= 0x1C00 && addr < 0x1D00) {
+	idx = MSI_IRQ;
+	addr -= 0x1C00;
+    } else {
+	return retval;
+    }
+    idx += addr >> 5;
+    if (addr & 0x10) {
+        /* EXDE / IFEDE / IEEDE */
+        retval = read_IRQreg(mpp, idx, IRQ_IDR);
+    } else {
+        /* EXVP / IFEVP / IEEVP */
+        retval = read_IRQreg(mpp, idx, IRQ_IPVR);
+    }
+    DPRINTF("%s: => %08x\n", __func__, retval);
+#if defined MPIC_SWAP
+    retval = tswap32(retval);
+#endif
+
+    return retval;
+}
+
+static void mpic_cpu_write (void *opaque, uint32_t addr, uint32_t val)
+{
+    mpic_t *mpp = opaque;
+    IRQ_src_t *src;
+    IRQ_dst_t *dst;
+    int idx, cpu, s_IRQ, n_IRQ;
+
+    DPRINTF("%s: addr %08x <= %08x\n", __func__, addr, val);
+    if (addr & 0xF)
+        return;
+#if defined MPIC_SWAP
+    val = bswap32(val);
+#endif
+    addr &= 0x1FFF0;
+    cpu = addr >> 12;
+    idx = MAX_IPI * cpu;
+    dst = &mpp->dst[cpu];
+    addr &= 0xFF0;
+    switch (addr) {
+#if MAX_IPI > 0
+    case 0x40: /* IPI */
+    case 0x50:
+    case 0x60:
+    case 0x70:
+        idx += addr >> 4;
+        write_IRQreg(mpp, IPI_IRQ + idx, IRQ_IDR, val);
+        mpic_set_irq(mpp, IPI_IRQ + idx, 1);
+        mpic_set_irq(mpp, IPI_IRQ + idx, 0);
+        break;
+#endif
+    case 0x80: /* CTPR */
+	dst->ctpr = val & 0x0000000F;
+	break;
+    case 0x90: /* WHOAMI */
+	/* Read-only register */
+	break;
+    case 0xA0: /* IACK */
+	/* Read-only register */
+	break;
+    case 0xB0: /* EOI */
+        DPRINTF("EOI\n");
+	s_IRQ = IRQ_get_next(mpp, &dst->servicing);
+	IRQ_resetbit(&dst->servicing, s_IRQ);
+	dst->servicing.next = -1;
+	/* Set up next servicing IRQ */
+	s_IRQ = IRQ_get_next(mpp, &dst->servicing);
+        /* Check queued interrupts. */
+        n_IRQ = IRQ_get_next(mpp, &dst->raised);
+        src = &mpp->src[n_IRQ];
+        if (n_IRQ != -1 &&
+            (s_IRQ == -1 ||
+             IPVR_PRIORITY(src->ipvr) > dst->servicing.priority)) {
+            DPRINTF("Raise mpic INT output cpu %d irq %d\n",
+                    cpu, n_IRQ);
+	    mpic_irq_raise(mpp, cpu, n_IRQ);
+        }
+	break;
+    default:
+        break;
+    }
+}
+
+static uint32_t mpic_cpu_read (void *opaque, uint32_t addr)
+{
+    mpic_t *mpp = opaque;
+    IRQ_src_t *src;
+    IRQ_dst_t *dst;
+    uint32_t retval;
+    int idx, cpu, n_IRQ;
+
+    DPRINTF("%s: addr %08x\n", __func__, addr);
+    retval = 0xFFFFFFFF;
+    if (addr & 0xF)
+        return retval;
+    addr &= 0x1FFF0;
+    cpu = addr >> 12;
+    idx = MAX_IPI * cpu;
+    dst = &mpp->dst[cpu];
+    addr &= 0xFF0;
+    switch (addr) {
+    case 0x80: /* CTPR */
+	retval = dst->ctpr;
+	break;
+    case 0x90: /* WHOAMI */
+	retval = cpu;
+	break;
+    case 0xA0: /* IACK */
+        DPRINTF("Lower mpic INT output\n");
+        qemu_irq_lower(dst->irqs[MPIC_OUTPUT_INT]);
+	n_IRQ = IRQ_get_next(mpp, &dst->raised);
+        DPRINTF("IACK: irq=%d\n", n_IRQ);
+	if (n_IRQ == -1) {
+	    /* No more interrupt pending */
+            retval = IPVR_VECTOR(mpp->svr);
+	} else {
+	    src = &mpp->src[n_IRQ];
+	    if (!test_bit(&src->ipvr, IPVR_ACTIVITY) ||
+		!(IPVR_PRIORITY(src->ipvr) > dst->ctpr)) {
+		/* - Spurious level-sensitive IRQ
+		 * - Priorities has been changed
+		 *   and the pending IRQ isn't allowed anymore
+		 */
+        	DPRINTF("\tspurious\n");
+		reset_bit(&src->ipvr, IPVR_ACTIVITY);
+		retval = IPVR_VECTOR(mpp->svr);
+	    } else {
+		/* IRQ enter servicing state */
+		IRQ_setbit(&dst->servicing, n_IRQ);
+		retval = IPVR_VECTOR(src->ipvr);
+	    }
+	    IRQ_resetbit(&dst->raised, n_IRQ);
+	    dst->raised.next = -1;
+	    if (!test_bit(&src->ipvr, IPVR_SENSE)) {
+                /* edge-sensitive IRQ */
+		reset_bit(&src->ipvr, IPVR_ACTIVITY);
+                src->pending = 0;
+            }
+	}
+	break;
+    case 0xB0: /* EOI */
+	retval = 0;
+	break;
+#if MAX_IPI > 0
+    case 0x40: /* IDR */
+    case 0x50:
+    case 0x60:
+    case 0x70:
+        idx += addr >> 4;
+        retval = read_IRQreg(mpp, IPI_IRQ + idx, IRQ_IDR);
+        break;
+#endif
+    default:
+        break;
+    }
+    DPRINTF("%s: => %08x\n", __func__, retval);
+#if defined MPIC_SWAP
+    retval= bswap32(retval);
+#endif
+
+    return retval;
+}
+
+static void mpic_buggy_write (void *opaque,
+                                 target_phys_addr_t addr, uint32_t val)
+{
+    printf("Invalid mpic write access !\n");
+}
+
+static uint32_t mpic_buggy_read (void *opaque, target_phys_addr_t addr)
+{
+    printf("Invalid mpic read access !\n");
+
+    return -1;
+}
+
+static void mpic_writel (void *opaque,
+                            target_phys_addr_t addr, uint32_t val)
+{
+    mpic_t *mpp = opaque;
+
+    addr &= 0x3FFFF;
+    DPRINTF("%s: offset %08x val: %08x\n", __func__, (int)addr, val);
+    if (addr < 0x10F0) {
+        /* Global registers */
+        mpic_gbl_write(mpp, addr, val);
+    } else if (addr < 0x10000) {
+        /* Timers registers */
+        mpic_timer_write(mpp, addr, val);
+    } else if (addr < 0x20000) {
+        /* Source registers */
+        mpic_src_write(mpp, addr, val);
+    } else if (addr < 0x30000){
+        /* CPU registers */
+        mpic_cpu_write(mpp, addr, val);
+    } else {
+    	DPRINTF("wrong mpic write addr %p\n",addr);
+    }
+}
+
+static uint32_t mpic_readl (void *opaque,target_phys_addr_t addr)
+{
+    mpic_t *mpp = opaque;
+    uint32_t retval = 0;
+
+    addr &= 0x3FFFF;
+    DPRINTF("%s: offset %08x\n", __func__, (int)addr);
+    if (addr < 0x10F0) {
+        /* Global registers */
+        retval = mpic_gbl_read(mpp, addr);
+    } else if (addr < 0x10000) {
+        /* Timers registers */
+        retval = mpic_timer_read(mpp, addr);
+    } else if (addr < 0x20000) {
+        /* Source registers */
+        retval = mpic_src_read(mpp, addr);
+    } else if (addr < 0x30000){
+        /* CPU registers */
+        retval = mpic_cpu_read(mpp, addr);
+    } else {
+    	DPRINTF("wrong mpic read addr %p\n",addr);
+    }
+
+    return retval;
+}
+
+static CPUWriteMemoryFunc *mpic_write[] = {
+    &mpic_buggy_write,
+    &mpic_buggy_write,
+    &mpic_writel,
+};
+
+static CPUReadMemoryFunc *mpic_read[] = {
+    &mpic_buggy_read,
+    &mpic_buggy_read,
+    &mpic_readl,
+};
+
+qemu_irq *mpic_init (target_phys_addr_t base, int nb_cpus,
+                        qemu_irq **irqs, qemu_irq irq_out)
+{
+    mpic_t *mpp;
+    int i;
+
+    /* XXX: for now, only one CPU is supported */
+    if (nb_cpus != 1)
+        return NULL;
+
+    mpp = qemu_mallocz(sizeof(mpic_t));
+
+    mpp->mem_index = cpu_register_io_memory(0, mpic_read, mpic_write, mpp);
+    if (mpp->mem_index < 0)
+	goto free;
+    cpu_register_physical_memory(base, MPIC_MAP_SIZE, mpp->mem_index);
+
+    mpp->nb_cpus = nb_cpus;
+
+    for (i = 0; i < nb_cpus; i++)
+        mpp->dst[i].irqs = irqs[i];
+    mpp->irq_out = irq_out;
+    mpic_reset(mpp);
+
+    return qemu_allocate_irqs(mpic_set_irq, mpp, MAX_IRQ);
+
+free:
+    qemu_free(mpp);
+    return NULL;
+}
diff --git a/hw/ppce500_pci.c b/hw/ppce500_pci.c
new file mode 100644
index 0000000..9242639
--- /dev/null
+++ b/hw/ppce500_pci.c
@@ -0,0 +1,322 @@
+/*
+ * QEMU PowerPC E500 embedded processors pci controller emulation
+ *
+ * Copyright (C) 2009 Freescale Semiconductor, Inc. All rights reserved.
+ *
+ * Author: Yu Liu,     <yu.liu@xxxxxxxxxxxxx>
+ *
+ * This file is derived from hw/ppc4xx_pci.c,
+ * the copyright for that material belongs to the original owners.
+ *
+ * This is free software; you can redistribute it and/or modify
+ * it under the terms of  the GNU General  Public License as published by
+ * the Free Software Foundation;  either version 2 of the  License, or
+ * (at your option) any later version.
+ */
+
+#include "hw.h"
+#include "ppc.h"
+#include "ppce500.h"
+#include "sysemu.h"
+#include "pci.h"
+#include "bswap.h"
+#include "qemu-log.h"
+
+#ifdef DEBUG_PCI
+#define pci_debug(fmt, arg...) fprintf(stderr, fmt, ##arg)
+#else
+#define pci_debug(fmt, arg...)
+#endif
+
+#define PPCE500_PCI_REG_SIZE		0x1000
+
+#define PPCE500_PCI_CONFIG_ADDR		0x0
+#define PPCE500_PCI_CONFIG_DATA		0x4
+#define PPCE500_PCI_INTACK		0x8
+
+#define PPCE500_PCI_OW1			0xC20
+#define PPCE500_PCI_OW2			0xC40
+#define PPCE500_PCI_OW3			0xC60
+#define PPCE500_PCI_OW4			0xC80
+#define PPCE500_PCI_IW3			0xDA0
+#define PPCE500_PCI_IW2			0xDC0
+#define PPCE500_PCI_IW1			0xDE0
+
+#define PPCE500_PCI_GAS_TIMR		0xE20
+
+#define PCI_POTAR		0x0
+#define PCI_POTEAR		0x4
+#define PCI_POWBAR		0x8
+#define PCI_POWAR		0x10
+
+#define PCI_PITAR		0x0
+#define PCI_PIWBAR		0x8
+#define PCI_PIWBEAR		0xC
+#define PCI_PIWAR		0x10
+
+#define PPCE500_PCI_NR_POBS 5
+#define PPCE500_PCI_NR_PIBS 3
+
+struct ppce500_pci_t {
+    target_phys_addr_t registers;
+    struct pci_outbound pob[PPCE500_PCI_NR_POBS];
+    struct pci_inbound pib[PPCE500_PCI_NR_PIBS];
+    uint32_t gas_time;
+
+    qemu_irq *pic;
+
+    uint32_t pcic0_cfgaddr;
+    PCIBus *bus;
+};
+
+typedef struct ppce500_pci_t ppce500_pci_t;
+
+static uint32_t pci_reg_read(void *opaque, target_phys_addr_t addr, int size)
+{
+    ppce500_pci_t *pci = opaque;
+    unsigned long offset = addr - pci->registers, win;
+    uint32_t value = 0;
+
+    win = offset & 0xfe0;
+
+    switch (win) {
+    case 0:
+	switch(offset & 0xC) {
+	case PPCE500_PCI_CONFIG_ADDR:
+	    value = pci->pcic0_cfgaddr;
+	    break;
+
+	case PPCE500_PCI_CONFIG_DATA: {
+	    uint32_t cfgaddr = pci->pcic0_cfgaddr;
+
+	    if (!(cfgaddr & (1<<31)))
+		return 0xffffffff;
+	
+	    value = pci_data_read(pci->bus, cfgaddr | (offset & 0x3), size);
+
+	    if (size == 2)
+		value = cpu_to_le16(value);
+	    else if (size == 4)
+		value = cpu_to_le32(value);
+
+	    break;
+	}
+	default:;
+	}
+	break;
+    
+    case PPCE500_PCI_OW1:
+    case PPCE500_PCI_OW2:
+    case PPCE500_PCI_OW3:
+    case PPCE500_PCI_OW4:
+	switch (offset & 0xC) {
+	case PCI_POTAR: value = pci->pob[(offset >> 5) & 0x7].potar; break;
+	case PCI_POTEAR: value = pci->pob[(offset >> 5) & 0x7].potear; break;
+	case PCI_POWBAR: value = pci->pob[(offset >> 5) & 0x7].powbar; break;
+	case PCI_POWAR: value = pci->pob[(offset >> 5) & 0x7].powar; break;
+	default: break;
+	}
+	break;
+
+    case PPCE500_PCI_IW3:
+    case PPCE500_PCI_IW2:
+    case PPCE500_PCI_IW1:
+	switch (offset & 0xC) {
+	case PCI_PITAR: value = pci->pib[(offset >> 5) & 0x3].pitar; break;
+	case PCI_PIWBAR: value = pci->pib[(offset >> 5) & 0x3].piwbar; break;
+	case PCI_PIWBEAR: value = pci->pib[(offset >> 5) & 0x3].piwbear; break;
+	case PCI_PIWAR: value = pci->pib[(offset >> 5) & 0x3].piwar; break;
+	default: break;
+	};
+	break;
+
+    case PPCE500_PCI_GAS_TIMR:
+	value = pci->gas_time;
+	break;
+
+    default:
+	break;
+    }
+
+    pci_debug("%s: offset:%p -> value:%x\n", __func__, offset, value);
+    return value;
+}
+
+static uint32_t pci_reg_read1(void *opaque, target_phys_addr_t addr)
+{
+    return pci_reg_read(opaque, addr, 1);
+}
+
+static uint32_t pci_reg_read2(void *opaque, target_phys_addr_t addr)
+{
+    return pci_reg_read(opaque, addr, 2);
+}
+
+static uint32_t pci_reg_read4(void *opaque, target_phys_addr_t addr)
+{
+    return pci_reg_read(opaque, addr, 4);
+}
+
+static CPUReadMemoryFunc *e500_pci_reg_read[] = {
+    &pci_reg_read1,
+    &pci_reg_read2,
+    &pci_reg_read4,
+};
+
+static void pci_reg_write(void *opaque, target_phys_addr_t addr,
+                               uint32_t value, int size)
+{
+    ppce500_pci_t *pci = opaque;
+    unsigned long offset = addr - pci->registers, win;
+
+    pci_debug("%s: value:%x -> offset:%p(addr:%Lx - base:%Lx)\n", __func__, value, offset, addr, pci->registers);
+
+    win = offset & 0xfe0;
+
+    switch (win) {
+    case 0:
+	switch(offset & 0xC) {
+	case PPCE500_PCI_CONFIG_ADDR:
+	    pci->pcic0_cfgaddr = value & ~0x3;
+	    break;
+
+	case PPCE500_PCI_CONFIG_DATA:
+	    if (size == 2)
+		value = le16_to_cpu(value);
+	    else if (size == 4)
+		value = le32_to_cpu(value);
+
+	    pci_data_write(pci->bus, pci->pcic0_cfgaddr | (offset & 0x3),
+			    value, size);
+	    break;
+	default:
+	    break;
+	};
+	break;
+    
+    case PPCE500_PCI_OW1:
+    case PPCE500_PCI_OW2:
+    case PPCE500_PCI_OW3:
+    case PPCE500_PCI_OW4:
+	switch (offset & 0xC) {
+	case PCI_POTAR: pci->pob[(offset >> 5) & 0x7].potar = value; break;
+	case PCI_POTEAR: pci->pob[(offset >> 5) & 0x7].potear = value; break;
+	case PCI_POWBAR: pci->pob[(offset >> 5) & 0x7].powbar = value; break;
+	case PCI_POWAR: pci->pob[(offset >> 5) & 0x7].powar = value; break;
+	default: break;
+	};
+	break;
+
+    case PPCE500_PCI_IW3:
+    case PPCE500_PCI_IW2:
+    case PPCE500_PCI_IW1:
+	switch (offset & 0xC) {
+	case PCI_PITAR: pci->pib[(offset >> 5) & 0x3].pitar = value; break;
+	case PCI_PIWBAR: pci->pib[(offset >> 5) & 0x3].piwbar = value; break;
+	case PCI_PIWBEAR: pci->pib[(offset >> 5) & 0x3].piwbear = value; break;
+	case PCI_PIWAR: pci->pib[(offset >> 5) & 0x3].piwar = value; break;
+	default: break;
+	};
+	break;
+
+    case PPCE500_PCI_GAS_TIMR:
+	pci->gas_time = value;
+	break;
+
+    default:
+	break;
+    };
+}
+
+static void pci_reg_write1(void *opaque, target_phys_addr_t addr,
+                               uint32_t value)
+{
+    pci_reg_write(opaque, addr, value, 1);
+}
+
+static void pci_reg_write2(void *opaque, target_phys_addr_t addr,
+                               uint32_t value)
+{
+    pci_reg_write(opaque, addr, value, 2);
+}
+
+static void pci_reg_write4(void *opaque, target_phys_addr_t addr,
+                               uint32_t value)
+{
+    pci_reg_write(opaque, addr, value, 4);
+}
+
+static CPUWriteMemoryFunc *e500_pci_reg_write[] = {
+    &pci_reg_write1,
+    &pci_reg_write2,
+    &pci_reg_write4,
+};
+
+static int mpc85xx_pci_map_irq(PCIDevice *pci_dev, int irq_num)
+{
+    int devno = pci_dev->devfn >> 3, ret = 0;
+
+    switch (devno) {
+	/* Two PCI slot */
+	case 0x11:
+	case 0x12:
+	    ret = (irq_num + devno - 0x10) % 4;
+	    break;
+	default:
+	    printf("Error:%s:unknow dev number\n", __func__);
+    }
+
+    pci_debug("%s: devfn %x irq %d -> %d  devno:%x\n", __func__,
+           pci_dev->devfn, irq_num, ret, devno);
+
+    return ret;
+}
+
+static void mpc85xx_pci_set_irq(qemu_irq *pic, int irq_num, int level)
+{
+    pci_debug("%s: PCI irq %d, level:%d\n", __func__, irq_num, level);
+
+    qemu_set_irq(pic[irq_num], level);
+}
+
+PCIBus *ppce500_pci_init(qemu_irq pci_irqs[4], target_phys_addr_t registers)
+{
+    ppce500_pci_t *pci;
+    PCIDevice *d;
+    int index;
+
+    pci = qemu_mallocz(sizeof(ppce500_pci_t));
+    if (!pci)
+	return NULL;
+
+    pci->registers = registers;
+    pci->pic = pci_irqs;
+
+    pci->bus = pci_register_bus(mpc85xx_pci_set_irq, mpc85xx_pci_map_irq,
+				pci_irqs, 0x88, 4);
+    d = pci_register_device(pci->bus, "host bridge", sizeof(PCIDevice),
+		0, NULL, NULL);
+
+    d->config[0x00] = 0x57; // vendor_id
+    d->config[0x01] = 0x19;
+    d->config[0x02] = 0x30; // device_id
+    d->config[0x03] = 0x00;
+    d->config[0x0a] = 0x20; // class_sub = other bridge type
+    d->config[0x0b] = 0x0B; // class_base = PCI_bridge
+
+    index = cpu_register_io_memory(0, e500_pci_reg_read,
+		    e500_pci_reg_write, pci);
+    if (index < 0)
+	goto free;
+    cpu_register_physical_memory(registers, PPCE500_PCI_REG_SIZE, index);
+
+    /* XXX register_savevm() */
+
+    return pci->bus;
+
+free:
+    printf("%s error\n", __func__);
+    qemu_free(pci);
+    return NULL;
+}
+
-- 
1.5.4

--
To unsubscribe from this list: send the line "unsubscribe kvm-ppc" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html

[Index of Archives]     [KVM Development]     [KVM ARM]     [KVM ia64]     [Linux Virtualization]     [Linux USB Devel]     [Linux Video]     [Linux Audio Users]     [Linux Kernel]     [Linux SCSI]     [Big List of Linux Books]

  Powered by Linux