diff options
author | j_mayer <j_mayer@c046a42c-6fe2-441c-8c8c-71466251a162> | 2007-04-09 22:45:36 +0000 |
---|---|---|
committer | j_mayer <j_mayer@c046a42c-6fe2-441c-8c8c-71466251a162> | 2007-04-09 22:45:36 +0000 |
commit | e9df014c0b433ecd9785db4a423e472bc3db386a (patch) | |
tree | e16e40d4fd68aff979be0d8e57ffdc17ee108ca7 /hw | |
parent | 682c4f15598fa82eb00973b9b14be86d1e5a726c (diff) | |
download | qemu-e9df014c0b433ecd9785db4a423e472bc3db386a.zip |
Implement embedded IRQ controller for PowerPC 6xx/740 & 750.
Fix PowerPC external interrupt input handling and lowering.
Fix OpenPIC output pins management.
Fix multiples bugs in OpenPIC IRQ management.
Fix OpenPIC CPU(s) reset function.
Fix Mac99 machine to properly route OpenPIC outputs to the PowerPC input pins.
Fix PREP machine to properly route i8259 output to the PowerPC external
interrupt pin.
git-svn-id: svn://svn.savannah.nongnu.org/qemu/trunk@2647 c046a42c-6fe2-441c-8c8c-71466251a162
Diffstat (limited to 'hw')
-rw-r--r-- | hw/openpic.c | 106 | ||||
-rw-r--r-- | hw/ppc.c | 126 | ||||
-rw-r--r-- | hw/ppc_chrp.c | 42 | ||||
-rw-r--r-- | hw/ppc_prep.c | 3 |
4 files changed, 192 insertions, 85 deletions
diff --git a/hw/openpic.c b/hw/openpic.c index 3481f2d525..d52eb751e7 100644 --- a/hw/openpic.c +++ b/hw/openpic.c @@ -159,18 +159,18 @@ typedef struct IRQ_dst_t { uint32_t pcsr; /* CPU sensitivity register */ IRQ_queue_t raised; IRQ_queue_t servicing; - CPUState *env; + qemu_irq *irqs; } IRQ_dst_t; typedef struct openpic_t { PCIDevice pci_dev; - SetIRQFunc *set_irq; int mem_index; /* 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 pint; /* Processor initialization register */ uint32_t spve; /* Spurious vector register */ uint32_t tifr; /* Timer frequency reporting register */ /* Source registers */ @@ -196,6 +196,8 @@ typedef struct openpic_t { uint32_t mbr; /* Mailbox register */ } mailboxes[MAX_MAILBOXES]; #endif + /* IRQ out is used when in bypass mode (not implemented) */ + qemu_irq irq_out; } openpic_t; static inline void IRQ_setbit (IRQ_queue_t *q, int n_IRQ) @@ -255,19 +257,34 @@ static void IRQ_local_pipe (openpic_t *opp, int n_CPU, int n_IRQ) priority = IPVP_PRIORITY(src->ipvp); if (priority <= dst->pctp) { /* 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->ipvp, IPVP_ACTIVITY); IRQ_setbit(&dst->raised, n_IRQ); - if (priority > dst->raised.priority) { - IRQ_get_next(opp, &dst->raised); - DPRINTF("Raise CPU IRQ fn %p env %p\n", opp->set_irq, dst->env); - opp->set_irq(dst->env, OPENPIC_EVT_INT, 1); + 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_get_next(opp, &dst->raised); + if (IRQ_get_next(opp, &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); + /* Already servicing a higher priority IRQ */ + return; } + DPRINTF("Raise OpenPIC INT output cpu %d irq %d\n", n_CPU, n_IRQ); + qemu_irq_raise(dst->irqs[OPENPIC_OUTPUT_INT]); } /* update pic state because registers for n_IRQ have changed value */ @@ -280,27 +297,34 @@ static void openpic_update_irq(openpic_t *opp, int n_IRQ) if (!src->pending) { /* no irq pending */ + DPRINTF("%s: IRQ %d is not pending\n", __func__, n_IRQ); return; } if (test_bit(&src->ipvp, IPVP_MASK)) { /* Interrupt source is disabled */ + DPRINTF("%s: IRQ %d is disabled\n", __func__, n_IRQ); return; } if (IPVP_PRIORITY(src->ipvp) == 0) { /* Priority set to zero */ + DPRINTF("%s: IRQ %d has 0 priority\n", __func__, n_IRQ); return; } if (test_bit(&src->ipvp, IPVP_ACTIVITY)) { /* IRQ already active */ + DPRINTF("%s: IRQ %d is already active\n", __func__, n_IRQ); return; } if (src->ide == 0x00000000) { /* No target */ + DPRINTF("%s: IRQ %d has no target\n", __func__, n_IRQ); return; } - if (!test_bit(&src->ipvp, IPVP_MODE) || - src->ide == (1 << src->last_cpu)) { + if (src->ide == (1 << src->last_cpu)) { + /* Only one CPU is allowed to receive this IRQ */ + IRQ_local_pipe(opp, src->last_cpu, n_IRQ); + } else if (!test_bit(&src->ipvp, IPVP_MODE)) { /* Directed delivery mode */ for (i = 0; i < opp->nb_cpus; i++) { if (test_bit(&src->ide, i)) @@ -308,9 +332,8 @@ static void openpic_update_irq(openpic_t *opp, int n_IRQ) } } else { /* Distributed delivery mode */ - /* XXX: incorrect code */ - for (i = src->last_cpu; i < src->last_cpu; i++) { - if (i == MAX_IRQ) + for (i = src->last_cpu + 1; i != src->last_cpu; i++) { + if (i == opp->nb_cpus) i = 0; if (test_bit(&src->ide, i)) { IRQ_local_pipe(opp, i, n_IRQ); @@ -350,6 +373,7 @@ static void openpic_reset (openpic_t *opp) /* Initialise controller registers */ opp->frep = ((EXT_IRQ - 1) << 16) | ((MAX_CPU - 1) << 8) | VID; opp->veni = VENI; + opp->pint = 0x00000000; opp->spve = 0x000000FF; opp->tifr = 0x003F7A00; /* ? */ @@ -360,7 +384,7 @@ static void openpic_reset (openpic_t *opp) opp->src[i].ide = 0x00000000; } /* Initialise IRQ destinations */ - for (i = 0; i < opp->nb_cpus; i++) { + for (i = 0; i < MAX_CPU; i++) { opp->dst[i].pctp = 0x0000000F; opp->dst[i].pcsr = 0x00000000; memset(&opp->dst[i].raised, 0, sizeof(IRQ_queue_t)); @@ -511,6 +535,8 @@ static void write_mailbox_register (openpic_t *opp, int n_mbx, static void openpic_gbl_write (void *opaque, uint32_t addr, uint32_t val) { openpic_t *opp = opaque; + IRQ_dst_t *dst; + int idx; DPRINTF("%s: addr %08x <= %08x\n", __func__, addr, val); if (addr & 0xF) @@ -530,11 +556,18 @@ static void openpic_gbl_write (void *opaque, uint32_t addr, uint32_t val) case 0x80: /* VENI */ break; case 0x90: /* PINT */ - /* XXX: Should be able to reset any CPU */ - if (val & 1) { - DPRINTF("Reset CPU IRQ\n"); - // opp->set_irq(dst->env, OPENPIC_EVT_RESET, 1); + for (idx = 0; idx < opp->nb_cpus; idx++) { + if ((val & (1 << idx)) && !(opp->pint & (1 << idx))) { + DPRINTF("Raise OpenPIC RESET output for CPU %d\n", idx); + dst = &opp->dst[idx]; + qemu_irq_raise(dst->irqs[OPENPIC_OUTPUT_RESET]); + } else if (!(val & (1 << idx)) && (opp->pint & (1 << idx))) { + DPRINTF("Lower OpenPIC RESET output for CPU %d\n", idx); + dst = &opp->dst[idx]; + qemu_irq_lower(dst->irqs[OPENPIC_OUTPUT_RESET]); + } } + opp->pint = val; break; #if MAX_IPI > 0 case 0xA0: /* IPI_IPVP */ @@ -735,7 +768,7 @@ 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; + int idx, s_IRQ, n_IRQ; DPRINTF("%s: addr %08x <= %08x\n", __func__, addr, val); if (addr & 0xF) @@ -770,21 +803,21 @@ static void openpic_cpu_write (void *opaque, uint32_t addr, uint32_t val) break; case 0xB0: /* PEOI */ DPRINTF("PEOI\n"); - n_IRQ = IRQ_get_next(opp, &dst->servicing); - IRQ_resetbit(&dst->servicing, n_IRQ); + s_IRQ = IRQ_get_next(opp, &dst->servicing); + IRQ_resetbit(&dst->servicing, s_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"); - opp->set_irq(dst->env, OPENPIC_EVT_INT, 1); - } - } + s_IRQ = IRQ_get_next(opp, &dst->servicing); + /* Check queued interrupts. */ + n_IRQ = IRQ_get_next(opp, &dst->raised); + src = &opp->src[n_IRQ]; + if (n_IRQ != -1 && + (s_IRQ == -1 || + IPVP_PRIORITY(src->ipvp) > dst->servicing.priority)) { + DPRINTF("Raise OpenPIC INT output cpu %d irq %d\n", + idx, n_IRQ); + qemu_irq_raise(dst->irqs[OPENPIC_OUTPUT_INT]); + } break; default: break; @@ -815,11 +848,13 @@ static uint32_t openpic_cpu_read (void *opaque, uint32_t addr) retval = idx; break; case 0xA0: /* PIAC */ + DPRINTF("Lower OpenPIC INT output\n"); + qemu_irq_lower(dst->irqs[OPENPIC_OUTPUT_INT]); 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; + retval = IPVP_VECTOR(opp->spve); } else { src = &opp->src[n_IRQ]; if (!test_bit(&src->ipvp, IPVP_ACTIVITY) || @@ -964,8 +999,8 @@ static void openpic_map(PCIDevice *pci_dev, int region_num, #endif } -qemu_irq *openpic_init (PCIBus *bus, SetIRQFunc *set_irq, - int *pmem_index, int nb_cpus, CPUState **envp) +qemu_irq *openpic_init (PCIBus *bus, int *pmem_index, int nb_cpus, + qemu_irq **irqs, qemu_irq irq_out) { openpic_t *opp; uint8_t *pci_conf; @@ -995,7 +1030,6 @@ qemu_irq *openpic_init (PCIBus *bus, SetIRQFunc *set_irq, } else { opp = qemu_mallocz(sizeof(openpic_t)); } - opp->set_irq = set_irq; opp->mem_index = cpu_register_io_memory(0, openpic_read, openpic_write, opp); @@ -1020,9 +1054,11 @@ qemu_irq *openpic_init (PCIBus *bus, SetIRQFunc *set_irq, opp->src[i].type = IRQ_INTERNAL; } for (i = 0; i < nb_cpus; i++) - opp->dst[i].env = envp[i]; + opp->dst[i].irqs = irqs[i]; + opp->irq_out = irq_out; openpic_reset(opp); if (pmem_index) *pmem_index = opp->mem_index; + return qemu_allocate_irqs(openpic_set_irq, opp, MAX_IRQ); } @@ -1,5 +1,5 @@ /* - * QEMU generic PPC hardware System Emulator + * QEMU generic PowerPC hardware System Emulator * * Copyright (c) 2003-2007 Jocelyn Mayer * @@ -24,18 +24,13 @@ #include "vl.h" #include "m48t59.h" +//#define PPC_DEBUG_IRQ + extern FILE *logfile; extern int loglevel; -/*****************************************************************************/ -/* PowerPC internal fake IRQ controller - * used to manage multiple sources hardware events - */ -static void ppc_set_irq (void *opaque, int n_IRQ, int level) +void ppc_set_irq (CPUState *env, int n_IRQ, int level) { - CPUState *env; - - env = opaque; if (level) { env->pending_interrupts |= 1 << n_IRQ; cpu_interrupt(env, CPU_INTERRUPT_HARD); @@ -44,49 +39,104 @@ static void ppc_set_irq (void *opaque, int n_IRQ, int level) if (env->pending_interrupts == 0) cpu_reset_interrupt(env, CPU_INTERRUPT_HARD); } -#if 0 +#if defined(PPC_DEBUG_IRQ) printf("%s: %p n_IRQ %d level %d => pending %08x req %08x\n", __func__, env, n_IRQ, level, env->pending_interrupts, env->interrupt_request); #endif } -void cpu_ppc_irq_init_cpu(CPUState *env) +/* PowerPC 6xx / 7xx internal IRQ controller */ +static void ppc6xx_set_irq (void *opaque, int pin, int level) { - qemu_irq *qi; - int i; + CPUState *env = opaque; + int cur_level; - qi = qemu_allocate_irqs(ppc_set_irq, env, 32); - for (i = 0; i < 32; i++) { - env->irq[i] = qi[i]; +#if defined(PPC_DEBUG_IRQ) + printf("%s: env %p pin %d level %d\n", __func__, env, pin, level); +#endif + cur_level = (env->irq_input_state >> pin) & 1; + /* Don't generate spurious events */ + if ((cur_level == 1 && level == 0) || (cur_level == 0 && level != 0) || 0) { + switch (pin) { + case PPC_INPUT_INT: + /* Level sensitive - asserted high */ +#if defined(PPC_DEBUG_IRQ) + printf("%s: set the external IRQ state to %d\n", __func__, level); +#endif + ppc_set_irq(env, PPC_INTERRUPT_EXT, level); + break; + case PPC_INPUT_SMI: + /* Level sensitive - active high */ +#if defined(PPC_DEBUG_IRQ) + printf("%s: set the SMI IRQ state to %d\n", __func__, level); +#endif + ppc_set_irq(env, PPC_INTERRUPT_SMI, level); + break; + case PPC_INPUT_MCP: + /* Negative edge sensitive */ + /* XXX: TODO: actual reaction may depends on HID0 status + * 603/604/740/750: check HID0[EMCP] + */ + if (cur_level == 1 && level == 0) { +#if defined(PPC_DEBUG_IRQ) + printf("%s: raise machine check state\n", __func__); +#endif + ppc_set_irq(env, PPC_INTERRUPT_MCK, 1); + } + break; + case PPC_INPUT_CKSTP_IN: + /* Level sensitive - active low */ + /* XXX: TODO: relay the signal to CKSTP_OUT pin */ + if (level) { +#if defined(PPC_DEBUG_IRQ) + printf("%s: stop the CPU\n", __func__); +#endif + env->halted = 1; + } else { +#if defined(PPC_DEBUG_IRQ) + printf("%s: restart the CPU\n", __func__); +#endif + env->halted = 0; + } + break; + case PPC_INPUT_HRESET: + /* Level sensitive - active low */ + if (level) { +#if 0 // XXX: TOFIX +#if defined(PPC_DEBUG_IRQ) + printf("%s: reset the CPU\n", __func__); +#endif + cpu_reset(env); +#endif + } + break; + case PPC_INPUT_SRESET: +#if defined(PPC_DEBUG_IRQ) + printf("%s: set the RESET IRQ state to %d\n", __func__, level); +#endif + ppc_set_irq(env, PPC_INTERRUPT_RESET, level); + break; + default: + /* Unknown pin - do nothing */ +#if defined(PPC_DEBUG_IRQ) + printf("%s: unknown IRQ pin %d\n", __func__, pin); +#endif + return; + } + if (level) + env->irq_input_state |= 1 << pin; + else + env->irq_input_state &= ~(1 << pin); } } -/* External IRQ callback from OpenPIC IRQ controller */ -void ppc_openpic_irq (void *opaque, int n_IRQ, int level) +void ppc6xx_irq_init (CPUState *env) { - switch (n_IRQ) { - case OPENPIC_EVT_INT: - n_IRQ = PPC_INTERRUPT_EXT; - break; - case OPENPIC_EVT_CINT: - /* On PowerPC BookE, critical input use vector 0 */ - n_IRQ = PPC_INTERRUPT_RESET; - break; - case OPENPIC_EVT_MCK: - n_IRQ = PPC_INTERRUPT_MCK; - break; - case OPENPIC_EVT_DEBUG: - n_IRQ = PPC_INTERRUPT_DEBUG; - break; - case OPENPIC_EVT_RESET: - qemu_system_reset_request(); - return; - } - ppc_set_irq(opaque, n_IRQ, level); + env->irq_inputs = (void **)qemu_allocate_irqs(&ppc6xx_set_irq, env, 6); } /*****************************************************************************/ -/* PPC time base and decrementer emulation */ +/* PowerPC time base and decrementer emulation */ //#define DEBUG_TB struct ppc_tb_t { diff --git a/hw/ppc_chrp.c b/hw/ppc_chrp.c index 18a1aab2b5..c418cd1b4a 100644 --- a/hw/ppc_chrp.c +++ b/hw/ppc_chrp.c @@ -23,6 +23,9 @@ */ #include "vl.h" +/* SMP is not enabled, for now */ +#define MAX_CPUS 1 + #define BIOS_FILENAME "ppc_rom.bin" #define VGABIOS_FILENAME "video.x" #define NVRAM_SIZE 0x2000 @@ -296,9 +299,9 @@ static void ppc_chrp_init (int ram_size, int vga_ram_size, int boot_device, const char *cpu_model, int is_heathrow) { - CPUState *env; + CPUState *env, *envs[MAX_CPUS]; char buf[1024]; - qemu_irq *pic; + qemu_irq *pic, **openpic_irqs; m48t59_t *nvram; int unin_memory; int linux_boot, i; @@ -329,13 +332,13 @@ static void ppc_chrp_init (int ram_size, int vga_ram_size, int boot_device, if (def == NULL) { cpu_abort(env, "Unable to find PowerPC CPU definition\n"); } - cpu_ppc_register(env, def); - cpu_ppc_irq_init_cpu(env); - - /* Set time-base frequency to 100 Mhz */ - cpu_ppc_tb_init(env, 100UL * 1000UL * 1000UL); - - env->osi_call = vga_osi_call; + for (i = 0; i < smp_cpus; i++) { + cpu_ppc_register(env, def); + /* Set time-base frequency to 100 Mhz */ + cpu_ppc_tb_init(env, 100UL * 1000UL * 1000UL); + env->osi_call = vga_osi_call; + envs[i] = env; + } /* allocate RAM */ cpu_register_physical_memory(0, ram_size, IO_MEM_RAM); @@ -458,7 +461,26 @@ static void ppc_chrp_init (int ram_size, int vga_ram_size, int boot_device, unin_memory = cpu_register_io_memory(0, unin_read, unin_write, NULL); cpu_register_physical_memory(0xf8000000, 0x00001000, unin_memory); - pic = openpic_init(NULL, &ppc_openpic_irq, &openpic_mem_index, 1, &env); + openpic_irqs = qemu_mallocz(smp_cpus * sizeof(qemu_irq *)); + openpic_irqs[0] = + qemu_mallocz(smp_cpus * sizeof(qemu_irq) * OPENPIC_OUTPUT_NB); + for (i = 0; i < smp_cpus; i++) { + /* Mac99 IRQ connection between OpenPIC outputs pins + * and PowerPC input pins + */ + openpic_irqs[i] = openpic_irqs[0] + (i * OPENPIC_OUTPUT_NB); + openpic_irqs[i][OPENPIC_OUTPUT_INT] = + ((qemu_irq *)env->irq_inputs)[PPC_INPUT_INT]; + openpic_irqs[i][OPENPIC_OUTPUT_CINT] = + ((qemu_irq *)env->irq_inputs)[PPC_INPUT_INT]; + openpic_irqs[i][OPENPIC_OUTPUT_MCK] = + ((qemu_irq *)env->irq_inputs)[PPC_INPUT_MCP]; + openpic_irqs[i][OPENPIC_OUTPUT_DEBUG] = NULL; /* Not connected ? */ + openpic_irqs[i][OPENPIC_OUTPUT_RESET] = + ((qemu_irq *)env->irq_inputs)[PPC_INPUT_HRESET]; /* Check this */ + } + pic = openpic_init(NULL, &openpic_mem_index, smp_cpus, + openpic_irqs, NULL); pci_bus = pci_pmac_init(pic); /* init basic PC hardware */ pci_vga_init(pci_bus, ds, phys_ram_base + ram_size, diff --git a/hw/ppc_prep.c b/hw/ppc_prep.c index 225b531685..8b53ef30a8 100644 --- a/hw/ppc_prep.c +++ b/hw/ppc_prep.c @@ -548,7 +548,6 @@ static void ppc_prep_init (int ram_size, int vga_ram_size, int boot_device, cpu_abort(env, "Unable to find PowerPC CPU definition\n"); } cpu_ppc_register(env, def); - cpu_ppc_irq_init_cpu(env); /* Set time-base frequency to 100 Mhz */ cpu_ppc_tb_init(env, 100UL * 1000UL * 1000UL); @@ -599,7 +598,7 @@ static void ppc_prep_init (int ram_size, int vga_ram_size, int boot_device, } isa_mem_base = 0xc0000000; - i8259 = i8259_init(first_cpu->irq[PPC_INTERRUPT_EXT]); + i8259 = i8259_init(first_cpu->irq_inputs[PPC_INPUT_INT]); pci_bus = pci_prep_init(i8259); // pci_bus = i440fx_init(); /* Register 8 MB of ISA IO space (needed for non-contiguous map) */ |