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