diff options
author | bellard <bellard@c046a42c-6fe2-441c-8c8c-71466251a162> | 2004-06-15 21:38:40 +0000 |
---|---|---|
committer | bellard <bellard@c046a42c-6fe2-441c-8c8c-71466251a162> | 2004-06-15 21:38:40 +0000 |
commit | dbda808a4ad5744fded19bf6a750e7aecbf993d8 (patch) | |
tree | 0a41d18d8d20042a8de3ed530d2ed425028f6b30 /hw | |
parent | a0497918559b49a2453eb0fb7289d06897ddf74d (diff) | |
download | hqemu-dbda808a4ad5744fded19bf6a750e7aecbf993d8.zip hqemu-dbda808a4ad5744fded19bf6a750e7aecbf993d8.tar.gz |
OpenPIC support (Jocelyn Mayer)
git-svn-id: svn://svn.savannah.nongnu.org/qemu/trunk@926 c046a42c-6fe2-441c-8c8c-71466251a162
Diffstat (limited to 'hw')
-rw-r--r-- | hw/openpic.c | 998 |
1 files changed, 998 insertions, 0 deletions
diff --git a/hw/openpic.c b/hw/openpic.c new file mode 100644 index 0000000..bd06618 --- /dev/null +++ b/hw/openpic.c @@ -0,0 +1,998 @@ +/* + * OpenPIC emulation + * + * Copyright (c) 2004 Jocelyn Mayer + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +/* + * + * Based on OpenPic implementations: + * - Intel GW80314 I/O compagnion chip developper's manual + * - Motorola MPC8245 & MPC8540 user manuals. + * - Motorola MCP750 (aka Raven) programmer manual. + * - Motorola Harrier programmer manuel + * + * Serial interrupts, as implemented in Raven chipset are not supported yet. + * + */ +#include "vl.h" + +#define DEBUG_OPENPIC + +#ifdef DEBUG_OPENPIC +#define DPRINTF(fmt, args...) do { printf(fmt , ##args); } while (0) +#else +#define DPRINTF(fmt, args...) do { } while (0) +#endif +#define ERROR(fmr, args...) do { printf("ERROR: " fmr , ##args); } while (0) + +#define USE_MPCxxx /* Intel model is broken, for now */ + +#if defined (USE_INTEL_GW80314) +/* Intel GW80314 I/O Companion chip */ + +#define MAX_CPU 4 +#define MAX_IRQ 32 +#define MAX_DBL 4 +#define MAX_MBX 4 +#define MAX_TMR 4 +#define VECTOR_BITS 8 +#define MAX_IPI 0 + +#define VID (0x00000000) + +#define OPENPIC_LITTLE_ENDIAN 1 +#define OPENPIC_BIG_ENDIAN 0 + +#elif defined(USE_MPCxxx) + +#define MAX_CPU 2 +#define MAX_IRQ 64 +#define EXT_IRQ 16 +#define MAX_DBL 0 +#define MAX_MBX 0 +#define MAX_TMR 4 +#define VECTOR_BITS 8 +#define MAX_IPI 4 +#define VID 0x03 /* MPIC version ID */ +#define VENI 0x00000000 /* Vendor ID */ + +enum { + IRQ_IPVP = 0, + IRQ_IDE, +}; + +#define OPENPIC_LITTLE_ENDIAN 1 +#define OPENPIC_BIG_ENDIAN 0 + +#else +#error "Please select which OpenPic implementation is to be emulated" +#endif + +#if (OPENPIC_BIG_ENDIAN && !TARGET_WORDS_BIGENDIAN) || \ + (OPENPIC_LITTLE_ENDIAN && TARGET_WORDS_BIGENDIAN) +#define OPENPIC_SWAP +#endif + +/* Interrupt definitions */ +#define IRQ_FE (EXT_IRQ) /* Internal functional IRQ */ +#define IRQ_ERR (EXT_IRQ + 1) /* Error IRQ */ +#define IRQ_TIM0 (EXT_IRQ + 2) /* First timer IRQ */ +#if MAX_IPI > 0 +#define IRQ_IPI0 (IRQ_TIM0 + MAX_TMR) /* First IPI IRQ */ +#define IRQ_DBL0 (IRQ_IPI0 + (MAX_CPU * MAX_IPI)) /* First doorbell IRQ */ +#else +#define IRQ_DBL0 (IRQ_TIM0 + MAX_TMR) /* First doorbell IRQ */ +#define IRQ_MBX0 (IRQ_DBL0 + MAX_DBL) /* First mailbox IRQ */ +#endif + +#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 ipvp; /* IRQ vector/priority register */ + uint32_t ide; /* IRQ destination register */ + int type; + int last_cpu; + int waited_acks; +} IRQ_src_t; + +enum IPVP_bits { + IPVP_MASK = 31, + IPVP_ACTIVITY = 30, + IPVP_MODE = 29, + IPVP_POLARITY = 23, + IPVP_SENSE = 22, +}; +#define IPVP_PRIORITY_MASK (0x1F << 16) +#define IPVP_PRIORITY(_ipvpr_) (((_ipvpr_) & IPVP_PRIORITY_MASK) >> 16) +#define IPVP_VECTOR_MASK ((1 << VECTOR_BITS) - 1) +#define IPVP_VECTOR(_ipvpr_) ((_ipvpr_) & IPVP_VECTOR_MASK) + +typedef struct IRQ_dst_t { + uint32_t pctp; /* CPU current task priority */ + uint32_t pcsr; /* CPU sensitivity register */ + IRQ_queue_t raised; + IRQ_queue_t servicing; + CPUState *env; /* Needed if we did SMP */ +} IRQ_dst_t; + +typedef struct openpic_t { + PCIDevice pci_dev; + /* Global registers */ + uint32_t frep; /* Feature reporting register */ + uint32_t glbc; /* Global configuration register */ + uint32_t micr; /* MPIC interrupt configuration register */ + uint32_t veni; /* Vendor identification register */ + uint32_t spve; /* Spurious vector register */ + uint32_t tifr; /* Timer frequency reporting 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 ticc; /* Global timer current count register */ + uint32_t tibc; /* Global timer base count register */ + } timers[MAX_TMR]; +#if MAX_DBL > 0 + /* Doorbell registers */ + uint32_t dar; /* Doorbell activate register */ + struct { + uint32_t dmr; /* Doorbell messaging register */ + } doorbells[MAX_DBL]; +#endif +#if MAX_MBX > 0 + /* Mailbox registers */ + struct { + uint32_t mbr; /* Mailbox register */ + } mailboxes[MAX_MAILBOXES]; +#endif +} openpic_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 (openpic_t *opp, 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)) { + if (IPVP_PRIORITY(opp->src[i].ipvp) > priority) { + next = i; + priority = IPVP_PRIORITY(opp->src[i].ipvp); + } + } + } + q->next = next; + q->priority = priority; +} + +static int IRQ_get_next (openpic_t *opp, IRQ_queue_t *q) +{ + if (q->next == -1) { + if (q->queue == 0) { + /* No more IRQ */ + return -1; + } + IRQ_check(opp, q); + } + + return q->next; +} + +static void IRQ_local_pipe (openpic_t *opp, int n_CPU, int n_IRQ) +{ + IRQ_dst_t *dst; + IRQ_src_t *src; + int priority; + + dst = &opp->dst[n_CPU]; + src = &opp->src[n_IRQ]; + priority = IPVP_PRIORITY(src->ipvp); + if (priority <= dst->pctp) { + /* Too low priority */ + return; + } + if (IRQ_testbit(&dst->raised, n_IRQ)) { + /* Interrupt miss */ + return; + } + set_bit(&src->ipvp, IPVP_ACTIVITY); + IRQ_setbit(&dst->raised, n_IRQ); + if (priority > dst->raised.priority) { + IRQ_get_next(opp, &dst->raised); + DPRINTF("Raise CPU IRQ\n"); + cpu_interrupt(cpu_single_env, CPU_INTERRUPT_HARD); + } +} + +void openpic_set_IRQ (openpic_t *opp, int n_IRQ, int level) +{ + IRQ_src_t *src; + int i; + + src = &opp->src[n_IRQ]; + if (!test_bit(&src->ipvp, IPVP_MASK)) { + /* Interrupt source is disabled */ + return; + } + if (IPVP_PRIORITY(src->ipvp) == 0) { + /* Priority set to zero */ + return; + } + if (src->ide == 0x00000000) { + /* No target */ + return; + } + if (level == 0) { + if (test_bit(&src->ipvp, IPVP_ACTIVITY) && + test_bit(&src->ipvp, IPVP_SENSE)) { + /* Inactivate a active level-sensitive IRQ */ + reset_bit(&src->ipvp, IPVP_ACTIVITY); + } + } else { + if (test_bit(&src->ipvp, IPVP_ACTIVITY)) { + /* Interrupt already pending */ + return; + } + if (!test_bit(&src->ipvp, IPVP_MODE) || + src->ide == (1 << src->last_cpu)) { + /* Directed delivery mode */ + for (i = 0; i < opp->nb_cpus; i++) { + if (test_bit(&src->ide, i)) + IRQ_local_pipe(opp, i, n_IRQ); + } + } else { + /* Distributed delivery mode */ + for (i = src->last_cpu; i < src->last_cpu; i++) { + if (i == MAX_IRQ) + i = 0; + if (test_bit(&src->ide, i)) { + IRQ_local_pipe(opp, i, n_IRQ); + src->last_cpu = i; + break; + } + } + } + } +} + +static void openpic_reset (openpic_t *opp) +{ + int i; + + opp->glbc = 0x80000000; + /* Initialise controler registers */ + opp->frep = ((EXT_IRQ - 1) << 16) | ((MAX_CPU - 1) << 8) | VID; + opp->veni = VENI; + opp->spve = 0x000000FF; + opp->tifr = 0x003F7A00; + /* ? */ + opp->micr = 0x00000000; + /* Initialise IRQ sources */ + for (i = 0; i < MAX_IRQ; i++) { + opp->src[i].ipvp = 0xA0000000; + opp->src[i].ide = 0x00000000; + } + /* Initialise IRQ destinations */ + for (i = 0; i < opp->nb_cpus; i++) { + opp->dst[i].pctp = 0x0000000F; + opp->dst[i].pcsr = 0x00000000; + memset(&opp->dst[i].raised, 0, sizeof(IRQ_queue_t)); + memset(&opp->dst[i].servicing, 0, sizeof(IRQ_queue_t)); + } + /* Initialise timers */ + for (i = 0; i < MAX_TMR; i++) { + opp->timers[i].ticc = 0x00000000; + opp->timers[i].tibc = 0x80000000; + } + /* Initialise doorbells */ +#if MAX_DBL > 0 + opp->dar = 0x00000000; + for (i = 0; i < MAX_DBL; i++) { + opp->doorbells[i].dmr = 0x00000000; + } +#endif + /* Initialise mailboxes */ +#if MAX_MBX > 0 + for (i = 0; i < MAX_MBX; i++) { /* ? */ + opp->mailboxes[i].mbr = 0x00000000; + } +#endif + /* Go out of RESET state */ + opp->glbc = 0x00000000; +} + +static inline uint32_t read_IRQreg (openpic_t *opp, int n_IRQ, uint32_t reg) +{ + uint32_t retval; + + switch (reg) { + case IRQ_IPVP: + retval = opp->src[n_IRQ].ipvp; + break; + case IRQ_IDE: + retval = opp->src[n_IRQ].ide; + break; + } + + return retval; +} + +static inline void write_IRQreg (openpic_t *opp, int n_IRQ, + uint32_t reg, uint32_t val) +{ + uint32_t tmp; + + switch (reg) { + case IRQ_IPVP: + tmp = opp->src[n_IRQ].ipvp & 0x40000000; + if (tmp == 0) { + tmp |= val & 0x80000000; + if ((opp->src[n_IRQ].type & IRQ_EXTERNAL) != 0) + tmp |= val & 0x40C00000; + else if ((opp->src[n_IRQ].type & IRQ_TIMER) != 0) + tmp |= val & 0x00F00000; + } else { + tmp |= val & 0x80000000; + } + opp->src[n_IRQ].ipvp = tmp | (val & 0x000F00FF); + DPRINTF("Set IPVP %d to 0x%08x\n", n_IRQ, opp->src[n_IRQ].ipvp); + break; + case IRQ_IDE: + tmp = val & 0xC0000000; + tmp |= val & ((1 << MAX_CPU) - 1); + opp->src[n_IRQ].ide = tmp; + DPRINTF("Set IDE %d to 0x%08x\n", n_IRQ, opp->src[n_IRQ].ide); + break; + } +} + +#if 0 // Code provision for Intel model +#if MAX_DBL > 0 +static uint32_t read_doorbell_register (openpic_t *opp, + int n_dbl, uint32_t offset) +{ + uint32_t retval; + + switch (offset) { + case DBL_IPVP_OFFSET: + retval = read_IRQreg(opp, IRQ_DBL0 + n_dbl, IRQ_IPVP); + break; + case DBL_IDE_OFFSET: + retval = read_IRQreg(opp, IRQ_DBL0 + n_dbl, IRQ_IDE); + break; + case DBL_DMR_OFFSET: + retval = opp->doorbells[n_dbl].dmr; + break; + } + + return retval; +} + +static void write_doorbell_register (penpic_t *opp, int n_dbl, + uint32_t offset, uint32_t value) +{ + switch (offset) { + case DBL_IVPR_OFFSET: + write_IRQreg(opp, IRQ_DBL0 + n_dbl, IRQ_IPVP, value); + break; + case DBL_IDE_OFFSET: + write_IRQreg(opp, IRQ_DBL0 + n_dbl, IRQ_IDE, value); + break; + case DBL_DMR_OFFSET: + opp->doorbells[n_dbl].dmr = value; + break; + } +} +#endif + +#if MAX_MBX > 0 +static uint32_t read_mailbox_register (openpic_t *opp, + int n_mbx, uint32_t offset) +{ + uint32_t retval; + + switch (offset) { + case MBX_MBR_OFFSET: + retval = opp->mailboxes[n_mbx].mbr; + break; + case MBX_IVPR_OFFSET: + retval = read_IRQreg(opp, IRQ_MBX0 + n_mbx, IRQ_IPVP); + break; + case MBX_DMR_OFFSET: + retval = read_IRQreg(opp, IRQ_MBX0 + n_mbx, IRQ_IDE); + break; + } + + return retval; +} + +static void write_mailbox_register (openpic_t *opp, int n_mbx, + uint32_t address, uint32_t value) +{ + switch (offset) { + case MBX_MBR_OFFSET: + opp->mailboxes[n_mbx].mbr = value; + break; + case MBX_IVPR_OFFSET: + write_IRQreg(opp, IRQ_MBX0 + n_mbx, IRQ_IPVP, value); + break; + case MBX_DMR_OFFSET: + write_IRQreg(opp, IRQ_MBX0 + n_mbx, IRQ_IDE, value); + break; + } +} +#endif +#endif /* 0 : Code provision for Intel model */ + +static void openpic_gbl_write (void *opaque, uint32_t addr, uint32_t val) +{ + openpic_t *opp = opaque; + + DPRINTF("%s: addr %08x <= %08x\n", __func__, addr, val); + if (addr & 0xF) + return; +#if defined OPENPIC_SWAP + val = bswap32(val); +#endif + addr &= 0xFF; + switch (addr) { + case 0x00: /* FREP */ + break; + case 0x20: /* GLBC */ + if (val & 0x80000000) + openpic_reset(opp); + opp->glbc = val & ~0x80000000; + break; + case 0x80: /* VENI */ + break; + case 0x90: /* PINT */ + /* XXX: Should be able to reset any CPU */ + if (val & 1) { + DPRINTF("Reset CPU IRQ\n"); + // cpu_interrupt(cpu_single_env, CPU_INTERRUPT_RESET); + } + break; +#if MAX_IPI > 0 + case 0xA0: /* IPI_IPVP */ + case 0xB0: + case 0xC0: + case 0xD0: + { + int idx; + idx = (addr - 0xA0) >> 4; + write_IRQreg(opp, IRQ_IPI0 + idx, IRQ_IPVP, val); + } + break; +#endif + case 0xE0: /* SPVE */ + opp->spve = val & 0x000000FF; + break; + case 0xF0: /* TIFR */ + opp->tifr = val; + break; + default: + break; + } +} + +static uint32_t openpic_gbl_read (void *opaque, uint32_t addr) +{ + openpic_t *opp = opaque; + uint32_t retval; + + DPRINTF("%s: addr %08x\n", __func__, addr); + retval = 0xFFFFFFFF; + if (addr & 0xF) + return retval; + addr &= 0xFF; + switch (addr) { + case 0x00: /* FREP */ + retval = opp->frep; + break; + case 0x20: /* GLBC */ + retval = opp->glbc; + break; + case 0x80: /* VENI */ + retval = opp->veni; + break; + case 0x90: /* PINT */ + retval = 0x00000000; + break; +#if MAX_IPI > 0 + case 0xA0: /* IPI_IPVP */ + case 0xB0: + case 0xC0: + case 0xD0: + { + int idx; + idx = (addr - 0xA0) >> 4; + retval = read_IRQreg(opp, IRQ_IPI0 + idx, IRQ_IPVP); + } + break; +#endif + case 0xE0: /* SPVE */ + retval = opp->spve; + break; + case 0xF0: /* TIFR */ + retval = opp->tifr; + break; + default: + break; + } + DPRINTF("%s: => %08x\n", __func__, retval); +#if defined OPENPIC_SWAP + retval = bswap32(retval); +#endif + + return retval; +} + +static void openpic_timer_write (void *opaque, uint32_t addr, uint32_t val) +{ + openpic_t *opp = opaque; + int idx; + + DPRINTF("%s: addr %08x <= %08x\n", __func__, addr, val); + if (addr & 0xF) + return; +#if defined OPENPIC_SWAP + val = bswap32(val); +#endif + addr -= 0x1100; + addr &= 0xFFFF; + idx = (addr & 0xFFF0) >> 6; + addr = addr & 0x30; + switch (addr) { + case 0x00: /* TICC */ + break; + case 0x10: /* TIBC */ + if ((opp->timers[idx].ticc & 0x80000000) != 0 && + (val & 0x800000000) == 0 && + (opp->timers[idx].tibc & 0x80000000) != 0) + opp->timers[idx].ticc &= ~0x80000000; + opp->timers[idx].tibc = val; + break; + case 0x20: /* TIVP */ + write_IRQreg(opp, IRQ_TIM0 + idx, IRQ_IPVP, val); + break; + case 0x30: /* TIDE */ + write_IRQreg(opp, IRQ_TIM0 + idx, IRQ_IDE, val); + break; + } +} + +static uint32_t openpic_timer_read (void *opaque, uint32_t addr) +{ + openpic_t *opp = opaque; + uint32_t retval; + int idx; + + DPRINTF("%s: addr %08x\n", __func__, addr); + retval = 0xFFFFFFFF; + if (addr & 0xF) + return retval; + addr -= 0x1100; + addr &= 0xFFFF; + idx = (addr & 0xFFF0) >> 6; + addr = addr & 0x30; + switch (addr) { + case 0x00: /* TICC */ + retval = opp->timers[idx].ticc; + break; + case 0x10: /* TIBC */ + retval = opp->timers[idx].tibc; + break; + case 0x20: /* TIPV */ + retval = read_IRQreg(opp, IRQ_TIM0 + idx, IRQ_IPVP); + break; + case 0x30: /* TIDE */ + retval = read_IRQreg(opp, IRQ_TIM0 + idx, IRQ_IDE); + break; + } + DPRINTF("%s: => %08x\n", __func__, retval); +#if defined OPENPIC_SWAP + retval = bswap32(retval); +#endif + + return retval; +} + +static void openpic_src_write (void *opaque, uint32_t addr, uint32_t val) +{ + openpic_t *opp = opaque; + int idx; + + DPRINTF("%s: addr %08x <= %08x\n", __func__, addr, val); + if (addr & 0xF) + return; +#if defined OPENPIC_SWAP + val = tswap32(val); +#endif + addr = addr & 0xFFF0; + idx = addr >> 5; + if (addr & 0x10) { + /* EXDE / IFEDE / IEEDE */ + write_IRQreg(opp, idx, IRQ_IDE, val); + } else { + /* EXVP / IFEVP / IEEVP */ + write_IRQreg(opp, idx, IRQ_IPVP, val); + } +} + +static uint32_t openpic_src_read (void *opaque, uint32_t addr) +{ + openpic_t *opp = opaque; + uint32_t retval; + int idx; + + DPRINTF("%s: addr %08x\n", __func__, addr); + retval = 0xFFFFFFFF; + if (addr & 0xF) + return retval; + addr = addr & 0xFFF0; + idx = addr >> 5; + if (addr & 0x10) { + /* EXDE / IFEDE / IEEDE */ + retval = read_IRQreg(opp, idx, IRQ_IDE); + } else { + /* EXVP / IFEVP / IEEVP */ + retval = read_IRQreg(opp, idx, IRQ_IPVP); + } + DPRINTF("%s: => %08x\n", __func__, retval); +#if defined OPENPIC_SWAP + retval = tswap32(retval); +#endif + + return retval; +} + +static void openpic_cpu_write (void *opaque, uint32_t addr, uint32_t val) +{ + openpic_t *opp = opaque; + IRQ_src_t *src; + IRQ_dst_t *dst; + int idx, n_IRQ; + + DPRINTF("%s: addr %08x <= %08x\n", __func__, addr, val); + if (addr & 0xF) + return; +#if defined OPENPIC_SWAP + val = bswap32(val); +#endif + addr &= 0x1FFF0; + idx = addr / 0x1000; + dst = &opp->dst[idx]; + addr &= 0xFF0; + switch (addr) { +#if MAX_IPI > 0 + case 0x40: /* PIPD */ + case 0x50: + case 0x60: + case 0x70: + idx = (addr - 0x40) >> 4; + write_IRQreg(opp, IRQ_IPI0 + idx, IRQ_IDE, val); + openpic_set_IRQ(opp, IRQ_IPI0 + idx, 1); + openpic_set_IRQ(opp, IRQ_IPI0 + idx, 0); + break; +#endif + case 0x80: /* PCTP */ + dst->pctp = val & 0x0000000F; + break; + case 0x90: /* WHOAMI */ + /* Read-only register */ + break; + case 0xA0: /* PIAC */ + /* Read-only register */ + break; + case 0xB0: /* PEOI */ + DPRINTF("PEOI\n"); + n_IRQ = IRQ_get_next(opp, &dst->servicing); + IRQ_resetbit(&dst->servicing, n_IRQ); + dst->servicing.next = -1; + src = &opp->src[n_IRQ]; + /* Set up next servicing IRQ */ + IRQ_get_next(opp, &dst->servicing); + /* Check queued interrupts. */ + n_IRQ = IRQ_get_next(opp, &dst->raised); + if (n_IRQ != -1) { + src = &opp->src[n_IRQ]; + if (IPVP_PRIORITY(src->ipvp) > dst->servicing.priority) { + DPRINTF("Raise CPU IRQ\n"); + cpu_interrupt(cpu_single_env, CPU_INTERRUPT_HARD); + } + } + break; + default: + break; + } +} + +static uint32_t openpic_cpu_read (void *opaque, uint32_t addr) +{ + openpic_t *opp = opaque; + IRQ_src_t *src; + IRQ_dst_t *dst; + uint32_t retval; + int idx, n_IRQ; + + DPRINTF("%s: addr %08x\n", __func__, addr); + retval = 0xFFFFFFFF; + if (addr & 0xF) + return retval; + addr &= 0x1FFF0; + idx = addr / 0x1000; + dst = &opp->dst[idx]; + addr &= 0xFF0; + switch (addr) { + case 0x80: /* PCTP */ + retval = dst->pctp; + break; + case 0x90: /* WHOAMI */ + retval = idx; + break; + case 0xA0: /* PIAC */ + n_IRQ = IRQ_get_next(opp, &dst->raised); + DPRINTF("PIAC: irq=%d\n", n_IRQ); + if (n_IRQ == -1) { + /* No more interrupt pending */ + retval = opp->spve; + } else { + src = &opp->src[n_IRQ]; + if (!test_bit(&src->ipvp, IPVP_ACTIVITY) || + !(IPVP_PRIORITY(src->ipvp) > dst->pctp)) { + /* - Spurious level-sensitive IRQ + * - Priorities has been changed + * and the pending IRQ isn't allowed anymore + */ + reset_bit(&src->ipvp, IPVP_ACTIVITY); + retval = IPVP_VECTOR(opp->spve); + } else { + /* IRQ enter servicing state */ + IRQ_setbit(&dst->servicing, n_IRQ); + retval = IPVP_VECTOR(src->ipvp); + } + IRQ_resetbit(&dst->raised, n_IRQ); + dst->raised.next = -1; + if (!test_bit(&src->ipvp, IPVP_SENSE)) + reset_bit(&src->ipvp, IPVP_ACTIVITY); + } + break; + case 0xB0: /* PEOI */ + retval = 0; + break; +#if MAX_IPI > 0 + case 0x40: /* IDE */ + case 0x50: + idx = (addr - 0x40) >> 4; + retval = read_IRQreg(opp, IRQ_IPI0 + idx, IRQ_IDE); + break; +#endif + default: + break; + } + DPRINTF("%s: => %08x\n", __func__, retval); +#if defined OPENPIC_SWAP + retval= bswap32(retval); +#endif + + return retval; +} + +static void openpic_buggy_write (void *opaque, + target_phys_addr_t addr, uint32_t val) +{ + printf("Invalid OPENPIC write access !\n"); +} + +static uint32_t openpic_buggy_read (void *opaque, target_phys_addr_t addr) +{ + printf("Invalid OPENPIC read access !\n"); + + return -1; +} + +static void openpic_writel (void *opaque, + target_phys_addr_t addr, uint32_t val) +{ + openpic_t *opp = opaque; + + addr &= 0x3FFFF; + DPRINTF("%s: offset %08lx val: %08x\n", __func__, addr, val); + if (addr < 0x1100) { + /* Global registers */ + openpic_gbl_write(opp, addr, val); + } else if (addr < 0x10000) { + /* Timers registers */ + openpic_timer_write(opp, addr, val); + } else if (addr < 0x20000) { + /* Source registers */ + openpic_src_write(opp, addr, val); + } else { + /* CPU registers */ + openpic_cpu_write(opp, addr, val); + } +} + +static uint32_t openpic_readl (void *opaque,target_phys_addr_t addr) +{ + openpic_t *opp = opaque; + uint32_t retval; + + addr &= 0x3FFFF; + DPRINTF("%s: offset %08lx\n", __func__, addr); + if (addr < 0x1100) { + /* Global registers */ + retval = openpic_gbl_read(opp, addr); + } else if (addr < 0x10000) { + /* Timers registers */ + retval = openpic_timer_read(opp, addr); + } else if (addr < 0x20000) { + /* Source registers */ + retval = openpic_src_read(opp, addr); + } else { + /* CPU registers */ + retval = openpic_cpu_read(opp, addr); + } + + return retval; +} + +static CPUWriteMemoryFunc *openpic_write[] = { + &openpic_buggy_write, + &openpic_buggy_write, + &openpic_writel, +}; + +static CPUReadMemoryFunc *openpic_read[] = { + &openpic_buggy_read, + &openpic_buggy_read, + &openpic_readl, +}; + +static void openpic_map(PCIDevice *pci_dev, int region_num, + uint32_t addr, uint32_t size, int type) +{ + openpic_t *opp; + int opp_io_memory; + + DPRINTF("Map OpenPIC\n"); + opp = (openpic_t *)pci_dev; + /* Global registers */ + DPRINTF("Register OPENPIC gbl %08x => %08x\n", + addr + 0x1000, addr + 0x1000 + 0x100); + /* Timer registers */ + DPRINTF("Register OPENPIC timer %08x => %08x\n", + addr + 0x1100, addr + 0x1100 + 0x40 * MAX_TMR); + /* Interrupt source registers */ + DPRINTF("Register OPENPIC src %08x => %08x\n", + addr + 0x10000, addr + 0x10000 + 0x20 * (EXT_IRQ + 2)); + /* Per CPU registers */ + DPRINTF("Register OPENPIC dst %08x => %08x\n", + addr + 0x20000, addr + 0x20000 + 0x1000 * MAX_CPU); + opp_io_memory = cpu_register_io_memory(0, openpic_read, + openpic_write, opp); + cpu_register_physical_memory(addr, 0x40000, opp_io_memory); +#if 0 // Don't implement ISU for now + opp_io_memory = cpu_register_io_memory(0, openpic_src_read, + openpic_src_write); + cpu_register_physical_memory(isu_base, 0x20 * (EXT_IRQ + 2), + opp_io_memory); +#endif +} + +openpic_t *openpic_init (uint32_t isu_base, uint32_t idu_base, int nb_cpus) +{ + openpic_t *opp; + uint8_t *pci_conf; + int i, m; + + /* XXX: for now, only one CPU is supported */ + if (nb_cpus != 1) + return NULL; + opp = (openpic_t *)pci_register_device("OpenPIC", sizeof(openpic_t), + 0, -1, NULL, NULL); + if (opp == NULL) + return NULL; + pci_conf = opp->pci_dev.config; + pci_conf[0x00] = 0x14; // IBM MPIC2 + pci_conf[0x01] = 0x10; + pci_conf[0x02] = 0xFF; + pci_conf[0x03] = 0xFF; + pci_conf[0x0a] = 0x80; // PIC + pci_conf[0x0b] = 0x08; + pci_conf[0x0e] = 0x00; // header_type + pci_conf[0x3d] = 0x00; // no interrupt pin + + /* Register I/O spaces */ + pci_register_io_region((PCIDevice *)opp, 0, 0x40000, + PCI_ADDRESS_SPACE_MEM, &openpic_map); + + isu_base &= 0xFFFC0000; + opp->nb_cpus = nb_cpus; + /* Set IRQ types */ + for (i = 0; i < EXT_IRQ; i++) { + opp->src[i].type = IRQ_EXTERNAL; + } + for (; i < IRQ_TIM0; i++) { + opp->src[i].type = IRQ_SPECIAL; + } +#if MAX_IPI > 0 + m = IRQ_IPI0; +#else + m = IRQ_DBL0; +#endif + for (; i < m; i++) { + opp->src[i].type = IRQ_TIMER; + } + for (; i < MAX_IRQ; i++) { + opp->src[i].type = IRQ_INTERNAL; + } + openpic_reset(opp); + + return opp; +} |