aboutsummaryrefslogtreecommitdiff
path: root/hw
diff options
context:
space:
mode:
Diffstat (limited to 'hw')
-rw-r--r--hw/openpic.c106
-rw-r--r--hw/ppc.c126
-rw-r--r--hw/ppc_chrp.c42
-rw-r--r--hw/ppc_prep.c3
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);
}
diff --git a/hw/ppc.c b/hw/ppc.c
index 44555bd634..f5c45001bb 100644
--- a/hw/ppc.c
+++ b/hw/ppc.c
@@ -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) */