aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorpbrook <pbrook@c046a42c-6fe2-441c-8c8c-71466251a162>2006-04-09 01:32:52 +0000
committerpbrook <pbrook@c046a42c-6fe2-441c-8c8c-71466251a162>2006-04-09 01:32:52 +0000
commitcdbdb648b7c2867f0bb7dce27efb1986f770dedb (patch)
treef838b39e8f30e4872a792638e532d8ac8db6fbfc
parent95219897ff4e6d0502b920c521fccc612ad913dd (diff)
ARM Versatile Platform Baseboard emulation.
git-svn-id: svn://svn.savannah.nongnu.org/qemu/trunk@1804 c046a42c-6fe2-441c-8c8c-71466251a162
-rw-r--r--Makefile.target3
-rw-r--r--hw/arm_pic.c73
-rw-r--r--hw/arm_pic.h27
-rw-r--r--hw/arm_timer.c383
-rw-r--r--hw/integratorcp.c673
-rw-r--r--hw/pl011.c251
-rw-r--r--hw/pl050.c127
-rw-r--r--hw/pl080.c328
-rw-r--r--hw/pl110.c27
-rw-r--r--hw/pl190.c252
-rw-r--r--hw/versatilepb.c321
-rw-r--r--vl.c1
-rw-r--r--vl.h19
13 files changed, 1830 insertions, 655 deletions
diff --git a/Makefile.target b/Makefile.target
index a5c728baaf..da69393c5a 100644
--- a/Makefile.target
+++ b/Makefile.target
@@ -339,7 +339,8 @@ VL_OBJS+= slavio_timer.o slavio_serial.o slavio_misc.o fdc.o esp.o
endif
endif
ifeq ($(TARGET_BASE_ARCH), arm)
-VL_OBJS+= integratorcp.o ps2.o smc91c111.o pl110.o
+VL_OBJS+= integratorcp.o versatilepb.o ps2.o smc91c111.o arm_pic.o arm_timer.o
+VL_OBJS+= pl011.o pl050.o pl080.o pl110.o pl190.o
endif
ifdef CONFIG_GDBSTUB
VL_OBJS+=gdbstub.o
diff --git a/hw/arm_pic.c b/hw/arm_pic.c
new file mode 100644
index 0000000000..fbc2d67d0a
--- /dev/null
+++ b/hw/arm_pic.c
@@ -0,0 +1,73 @@
+/*
+ * Generic ARM Programmable Interrupt Controller support.
+ *
+ * Copyright (c) 2006 CodeSourcery.
+ * Written by Paul Brook
+ *
+ * This code is licenced under the LGPL
+ */
+
+#include "vl.h"
+#include "arm_pic.h"
+
+/* Stub functions for hardware that doesn't exist. */
+void pic_set_irq(int irq, int level)
+{
+ cpu_abort(cpu_single_env, "pic_set_irq");
+}
+
+void pic_info(void)
+{
+}
+
+void irq_info(void)
+{
+}
+
+
+void pic_set_irq_new(void *opaque, int irq, int level)
+{
+ arm_pic_handler *p = (arm_pic_handler *)opaque;
+ /* Call the real handler. */
+ (*p)(opaque, irq, level);
+}
+
+/* Model the IRQ/FIQ CPU interrupt lines as a two input interrupt controller.
+ Input 0 is IRQ and input 1 is FIQ. */
+typedef struct
+{
+ arm_pic_handler handler;
+ CPUState *cpu_env;
+} arm_pic_cpu_state;
+
+static void arm_pic_cpu_handler(void *opaque, int irq, int level)
+{
+ arm_pic_cpu_state *s = (arm_pic_cpu_state *)opaque;
+ switch (irq) {
+ case ARM_PIC_CPU_IRQ:
+ if (level)
+ cpu_interrupt(s->cpu_env, CPU_INTERRUPT_HARD);
+ else
+ cpu_reset_interrupt(s->cpu_env, CPU_INTERRUPT_HARD);
+ break;
+ case ARM_PIC_CPU_FIQ:
+ if (level)
+ cpu_interrupt(s->cpu_env, CPU_INTERRUPT_FIQ);
+ else
+ cpu_reset_interrupt(s->cpu_env, CPU_INTERRUPT_FIQ);
+ break;
+ default:
+ cpu_abort(s->cpu_env, "arm_pic_cpu_handler: Bad interrput line %d\n",
+ irq);
+ }
+}
+
+void *arm_pic_init_cpu(CPUState *env)
+{
+ arm_pic_cpu_state *s;
+
+ s = (arm_pic_cpu_state *)malloc(sizeof(arm_pic_cpu_state));
+ s->handler = arm_pic_cpu_handler;
+ s->cpu_env = env;
+ return s;
+}
diff --git a/hw/arm_pic.h b/hw/arm_pic.h
new file mode 100644
index 0000000000..b29914985a
--- /dev/null
+++ b/hw/arm_pic.h
@@ -0,0 +1,27 @@
+/*
+ * Generic ARM Programmable Interrupt Controller support.
+ *
+ * Copyright (c) 2006 CodeSourcery.
+ * Written by Paul Brook
+ *
+ * This code is licenced under the LGPL.
+ *
+ * Arm hardware uses a wide variety of interrupt handling hardware.
+ * This provides a generic framework for connecting interrupt sources and
+ * inputs.
+ */
+
+#ifndef ARM_INTERRUPT_H
+#define ARM_INTERRUPT_H 1
+
+/* The first element of an individual PIC state structures should
+ be a pointer to the handler routine. */
+typedef void (*arm_pic_handler)(void *opaque, int irq, int level);
+
+/* The CPU is also modeled as an interrupt controller. */
+#define ARM_PIC_CPU_IRQ 0
+#define ARM_PIC_CPU_FIQ 1
+void *arm_pic_init_cpu(CPUState *env);
+
+#endif /* !ARM_INTERRUPT_H */
+
diff --git a/hw/arm_timer.c b/hw/arm_timer.c
new file mode 100644
index 0000000000..a97d73e447
--- /dev/null
+++ b/hw/arm_timer.c
@@ -0,0 +1,383 @@
+/*
+ * ARM PrimeCell Timer modules.
+ *
+ * Copyright (c) 2005-2006 CodeSourcery.
+ * Written by Paul Brook
+ *
+ * This code is licenced under the GPL.
+ */
+
+#include "vl.h"
+#include "arm_pic.h"
+
+/* Common timer implementation. */
+
+#define TIMER_CTRL_ONESHOT (1 << 0)
+#define TIMER_CTRL_32BIT (1 << 1)
+#define TIMER_CTRL_DIV1 (0 << 2)
+#define TIMER_CTRL_DIV16 (1 << 2)
+#define TIMER_CTRL_DIV256 (2 << 2)
+#define TIMER_CTRL_IE (1 << 5)
+#define TIMER_CTRL_PERIODIC (1 << 6)
+#define TIMER_CTRL_ENABLE (1 << 7)
+
+typedef struct {
+ int64_t next_time;
+ int64_t expires;
+ int64_t loaded;
+ QEMUTimer *timer;
+ uint32_t control;
+ uint32_t count;
+ uint32_t limit;
+ int raw_freq;
+ int freq;
+ int int_level;
+ void *pic;
+ int irq;
+} arm_timer_state;
+
+/* Calculate the new expiry time of the given timer. */
+
+static void arm_timer_reload(arm_timer_state *s)
+{
+ int64_t delay;
+
+ s->loaded = s->expires;
+ delay = muldiv64(s->count, ticks_per_sec, s->freq);
+ if (delay == 0)
+ delay = 1;
+ s->expires += delay;
+}
+
+/* Check all active timers, and schedule the next timer interrupt. */
+
+static void arm_timer_update(arm_timer_state *s, int64_t now)
+{
+ int64_t next;
+
+ /* Ignore disabled timers. */
+ if ((s->control & TIMER_CTRL_ENABLE) == 0)
+ return;
+ /* Ignore expired one-shot timers. */
+ if (s->count == 0 && (s->control & TIMER_CTRL_ONESHOT))
+ return;
+ if (s->expires - now <= 0) {
+ /* Timer has expired. */
+ s->int_level = 1;
+ if (s->control & TIMER_CTRL_ONESHOT) {
+ /* One-shot. */
+ s->count = 0;
+ } else {
+ if ((s->control & TIMER_CTRL_PERIODIC) == 0) {
+ /* Free running. */
+ if (s->control & TIMER_CTRL_32BIT)
+ s->count = 0xffffffff;
+ else
+ s->count = 0xffff;
+ } else {
+ /* Periodic. */
+ s->count = s->limit;
+ }
+ }
+ }
+ while (s->expires - now <= 0) {
+ arm_timer_reload(s);
+ }
+ /* Update interrupts. */
+ if (s->int_level && (s->control & TIMER_CTRL_IE)) {
+ pic_set_irq_new(s->pic, s->irq, 1);
+ } else {
+ pic_set_irq_new(s->pic, s->irq, 0);
+ }
+
+ next = now;
+ if (next - s->expires < 0)
+ next = s->expires;
+
+ /* Schedule the next timer interrupt. */
+ if (next == now) {
+ qemu_del_timer(s->timer);
+ s->next_time = 0;
+ } else if (next != s->next_time) {
+ qemu_mod_timer(s->timer, next);
+ s->next_time = next;
+ }
+}
+
+/* Return the current value of the timer. */
+static uint32_t arm_timer_getcount(arm_timer_state *s, int64_t now)
+{
+ int64_t elapsed;
+ int64_t period;
+
+ if (s->count == 0)
+ return 0;
+ if ((s->control & TIMER_CTRL_ENABLE) == 0)
+ return s->count;
+ elapsed = now - s->loaded;
+ period = s->expires - s->loaded;
+ /* If the timer should have expired then return 0. This can happen
+ when the host timer signal doesnt occur immediately. It's better to
+ have a timer appear to sit at zero for a while than have it wrap
+ around before the guest interrupt is raised. */
+ /* ??? Could we trigger the interrupt here? */
+ if (elapsed > period)
+ return 0;
+ /* We need to calculate count * elapsed / period without overfowing.
+ Scale both elapsed and period so they fit in a 32-bit int. */
+ while (period != (int32_t)period) {
+ period >>= 1;
+ elapsed >>= 1;
+ }
+ return ((uint64_t)s->count * (uint64_t)(int32_t)elapsed)
+ / (int32_t)period;
+}
+
+uint32_t arm_timer_read(void *opaque, target_phys_addr_t offset)
+{
+ arm_timer_state *s = (arm_timer_state *)opaque;
+
+ switch (offset >> 2) {
+ case 0: /* TimerLoad */
+ case 6: /* TimerBGLoad */
+ return s->limit;
+ case 1: /* TimerValue */
+ return arm_timer_getcount(s, qemu_get_clock(vm_clock));
+ case 2: /* TimerControl */
+ return s->control;
+ case 4: /* TimerRIS */
+ return s->int_level;
+ case 5: /* TimerMIS */
+ if ((s->control & TIMER_CTRL_IE) == 0)
+ return 0;
+ return s->int_level;
+ default:
+ cpu_abort (cpu_single_env, "arm_timer_read: Bad offset %x\n", offset);
+ return 0;
+ }
+}
+
+static void arm_timer_write(void *opaque, target_phys_addr_t offset,
+ uint32_t value)
+{
+ arm_timer_state *s = (arm_timer_state *)opaque;
+ int64_t now;
+
+ now = qemu_get_clock(vm_clock);
+ switch (offset >> 2) {
+ case 0: /* TimerLoad */
+ s->limit = value;
+ s->count = value;
+ s->expires = now;
+ arm_timer_reload(s);
+ break;
+ case 1: /* TimerValue */
+ /* ??? Linux seems to want to write to this readonly register.
+ Ignore it. */
+ break;
+ case 2: /* TimerControl */
+ if (s->control & TIMER_CTRL_ENABLE) {
+ /* Pause the timer if it is running. This may cause some
+ inaccuracy dure to rounding, but avoids a whole lot of other
+ messyness. */
+ s->count = arm_timer_getcount(s, now);
+ }
+ s->control = value;
+ s->freq = s->raw_freq;
+ /* ??? Need to recalculate expiry time after changing divisor. */
+ switch ((value >> 2) & 3) {
+ case 1: s->freq >>= 4; break;
+ case 2: s->freq >>= 8; break;
+ }
+ if (s->control & TIMER_CTRL_ENABLE) {
+ /* Restart the timer if still enabled. */
+ s->expires = now;
+ arm_timer_reload(s);
+ }
+ break;
+ case 3: /* TimerIntClr */
+ s->int_level = 0;
+ break;
+ case 6: /* TimerBGLoad */
+ s->limit = value;
+ break;
+ default:
+ cpu_abort (cpu_single_env, "arm_timer_write: Bad offset %x\n", offset);
+ }
+ arm_timer_update(s, now);
+}
+
+static void arm_timer_tick(void *opaque)
+{
+ int64_t now;
+
+ now = qemu_get_clock(vm_clock);
+ arm_timer_update((arm_timer_state *)opaque, now);
+}
+
+static void *arm_timer_init(uint32_t freq, void *pic, int irq)
+{
+ arm_timer_state *s;
+
+ s = (arm_timer_state *)qemu_mallocz(sizeof(arm_timer_state));
+ s->pic = pic;
+ s->irq = irq;
+ s->raw_freq = s->freq = 1000000;
+ s->control = TIMER_CTRL_IE;
+ s->count = 0xffffffff;
+
+ s->timer = qemu_new_timer(vm_clock, arm_timer_tick, s);
+ /* ??? Save/restore. */
+ return s;
+}
+
+/* ARM PrimeCell SP804 dual timer module.
+ Docs for this device don't seem to be publicly available. This
+ implementation is based on gueswork, the linux kernel sources and the
+ Integrator/CP timer modules. */
+
+typedef struct {
+ /* Include a pseudo-PIC device to merge the two interrupt sources. */
+ arm_pic_handler handler;
+ void *timer[2];
+ int level[2];
+ uint32_t base;
+ /* The output PIC device. */
+ void *pic;
+ int irq;
+} sp804_state;
+
+static void sp804_set_irq(void *opaque, int irq, int level)
+{
+ sp804_state *s = (sp804_state *)opaque;
+
+ s->level[irq] = level;
+ pic_set_irq_new(s->pic, s->irq, s->level[0] || s->level[1]);
+}
+
+static uint32_t sp804_read(void *opaque, target_phys_addr_t offset)
+{
+ sp804_state *s = (sp804_state *)opaque;
+
+ /* ??? Don't know the PrimeCell ID for this device. */
+ offset -= s->base;
+ if (offset < 0x20) {
+ return arm_timer_read(s->timer[0], offset);
+ } else {
+ return arm_timer_read(s->timer[1], offset - 0x20);
+ }
+}
+
+static void sp804_write(void *opaque, target_phys_addr_t offset,
+ uint32_t value)
+{
+ sp804_state *s = (sp804_state *)opaque;
+
+ offset -= s->base;
+ if (offset < 0x20) {
+ arm_timer_write(s->timer[0], offset, value);
+ } else {
+ arm_timer_write(s->timer[1], offset - 0x20, value);
+ }
+}
+
+static CPUReadMemoryFunc *sp804_readfn[] = {
+ sp804_read,
+ sp804_read,
+ sp804_read
+};
+
+static CPUWriteMemoryFunc *sp804_writefn[] = {
+ sp804_write,
+ sp804_write,
+ sp804_write
+};
+
+void sp804_init(uint32_t base, void *pic, int irq)
+{
+ int iomemtype;
+ sp804_state *s;
+
+ s = (sp804_state *)qemu_mallocz(sizeof(sp804_state));
+ s->handler = sp804_set_irq;
+ s->base = base;
+ s->pic = pic;
+ s->irq = irq;
+ /* ??? The timers are actually configurable between 32kHz and 1MHz, but
+ we don't implement that. */
+ s->timer[0] = arm_timer_init(1000000, s, 0);
+ s->timer[1] = arm_timer_init(1000000, s, 1);
+ iomemtype = cpu_register_io_memory(0, sp804_readfn,
+ sp804_writefn, s);
+ cpu_register_physical_memory(base, 0x00000fff, iomemtype);
+ /* ??? Save/restore. */
+}
+
+
+/* Integrator/CP timer module. */
+
+typedef struct {
+ void *timer[3];
+ uint32_t base;
+} icp_pit_state;
+
+static uint32_t icp_pit_read(void *opaque, target_phys_addr_t offset)
+{
+ icp_pit_state *s = (icp_pit_state *)opaque;
+ int n;
+
+ /* ??? Don't know the PrimeCell ID for this device. */
+ offset -= s->base;
+ n = offset >> 8;
+ if (n > 3)
+ cpu_abort(cpu_single_env, "sp804_read: Bad timer %d\n", n);
+
+ return arm_timer_read(s->timer[n], offset & 0xff);
+}
+
+static void icp_pit_write(void *opaque, target_phys_addr_t offset,
+ uint32_t value)
+{
+ icp_pit_state *s = (icp_pit_state *)opaque;
+ int n;
+
+ offset -= s->base;
+ n = offset >> 8;
+ if (n > 3)
+ cpu_abort(cpu_single_env, "sp804_write: Bad timer %d\n", n);
+
+ arm_timer_write(s->timer[n], offset & 0xff, value);
+}
+
+
+static CPUReadMemoryFunc *icp_pit_readfn[] = {
+ icp_pit_read,
+ icp_pit_read,
+ icp_pit_read
+};
+
+static CPUWriteMemoryFunc *icp_pit_writefn[] = {
+ icp_pit_write,
+ icp_pit_write,
+ icp_pit_write
+};
+
+void icp_pit_init(uint32_t base, void *pic, int irq)
+{
+ int iomemtype;
+ icp_pit_state *s;
+
+ s = (icp_pit_state *)qemu_mallocz(sizeof(icp_pit_state));
+ s->base = base;
+ /* Timer 0 runs at the system clock speed (40MHz). */
+ s->timer[0] = arm_timer_init(40000000, pic, irq);
+ /* The other two timers run at 1MHz. */
+ s->timer[1] = arm_timer_init(1000000, pic, irq + 1);
+ s->timer[2] = arm_timer_init(1000000, pic, irq + 2);
+
+ iomemtype = cpu_register_io_memory(0, icp_pit_readfn,
+ icp_pit_writefn, s);
+ cpu_register_physical_memory(base, 0x00000fff, iomemtype);
+ /* ??? Save/restore. */
+}
+
diff --git a/hw/integratorcp.c b/hw/integratorcp.c
index 1bcd734b80..bce9b592c6 100644
--- a/hw/integratorcp.c
+++ b/hw/integratorcp.c
@@ -1,32 +1,19 @@
/*
* ARM Integrator CP System emulation.
*
- * Copyright (c) 2005 CodeSourcery, LLC.
+ * Copyright (c) 2005-2006 CodeSourcery.
* Written by Paul Brook
*
* This code is licenced under the GPL
*/
-#include <vl.h>
+#include "vl.h"
+#include "arm_pic.h"
#define KERNEL_ARGS_ADDR 0x100
#define KERNEL_LOAD_ADDR 0x00010000
#define INITRD_LOAD_ADDR 0x00800000
-/* Stub functions for hardware that doesn't exist. */
-void pic_set_irq(int irq, int level)
-{
- cpu_abort (cpu_single_env, "pic_set_irq");
-}
-
-void pic_info(void)
-{
-}
-
-void irq_info(void)
-{
-}
-
void DMA_run (void)
{
}
@@ -284,41 +271,31 @@ static void integratorcm_init(int memsz, uint32_t flash_offset)
typedef struct icp_pic_state
{
+ arm_pic_handler handler;
uint32_t base;
uint32_t level;
uint32_t irq_enabled;
uint32_t fiq_enabled;
void *parent;
- /* -1 if parent is a cpu, otherwise IRQ number on parent PIC. */
int parent_irq;
+ int parent_fiq;
} icp_pic_state;
static void icp_pic_update(icp_pic_state *s)
{
- CPUState *env;
- if (s->parent_irq != -1) {
- uint32_t flags;
+ uint32_t flags;
+ if (s->parent_irq != -1) {
flags = (s->level & s->irq_enabled);
- pic_set_irq_new(s->parent, s->parent_irq,
- flags != 0);
- return;
+ pic_set_irq_new(s->parent, s->parent_irq, flags != 0);
}
- /* Raise CPU interrupt. */
- env = (CPUState *)s->parent;
- if (s->level & s->fiq_enabled) {
- cpu_interrupt (env, CPU_INTERRUPT_FIQ);
- } else {
- cpu_reset_interrupt (env, CPU_INTERRUPT_FIQ);
- }
- if (s->level & s->irq_enabled) {
- cpu_interrupt (env, CPU_INTERRUPT_HARD);
- } else {
- cpu_reset_interrupt (env, CPU_INTERRUPT_HARD);
+ if (s->parent_fiq != -1) {
+ flags = (s->level & s->fiq_enabled);
+ pic_set_irq_new(s->parent, s->parent_fiq, flags != 0);
}
}
-void pic_set_irq_new(void *opaque, int irq, int level)
+static void icp_pic_set_irq(void *opaque, int irq, int level)
{
icp_pic_state *s = (icp_pic_state *)opaque;
if (level)
@@ -408,7 +385,7 @@ static CPUWriteMemoryFunc *icp_pic_writefn[] = {
};
static icp_pic_state *icp_pic_init(uint32_t base, void *parent,
- int parent_irq)
+ int parent_irq, int parent_fiq)
{
icp_pic_state *s;
int iomemtype;
@@ -416,10 +393,11 @@ static icp_pic_state *icp_pic_init(uint32_t base, void *parent,
s = (icp_pic_state *)qemu_mallocz(sizeof(icp_pic_state));
if (!s)
return NULL;
-
+ s->handler = icp_pic_set_irq;
s->base = base;
s->parent = parent;
s->parent_irq = parent_irq;
+ s->parent_fiq = parent_fiq;
iomemtype = cpu_register_io_memory(0, icp_pic_readfn,
icp_pic_writefn, s);
cpu_register_physical_memory(base, 0x007fffff, iomemtype);
@@ -427,499 +405,6 @@ static icp_pic_state *icp_pic_init(uint32_t base, void *parent,
return s;
}
-/* Timers. */
-
-/* System bus clock speed (40MHz) for timer 0. Not sure about this value. */
-#define ICP_BUS_FREQ 40000000
-
-typedef struct {
- int64_t next_time;
- int64_t expires[3];
- int64_t loaded[3];
- QEMUTimer *timer;
- icp_pic_state *pic;
- uint32_t base;
- uint32_t control[3];
- uint32_t count[3];
- uint32_t limit[3];
- int freq[3];
- int int_level[3];
-} icp_pit_state;
-
-/* Calculate the new expiry time of the given timer. */
-
-static void icp_pit_reload(icp_pit_state *s, int n)
-{
- int64_t delay;
-
- s->loaded[n] = s->expires[n];
- delay = muldiv64(s->count[n], ticks_per_sec, s->freq[n]);
- if (delay == 0)
- delay = 1;
- s->expires[n] += delay;
-}
-
-/* Check all active timers, and schedule the next timer interrupt. */
-
-static void icp_pit_update(icp_pit_state *s, int64_t now)
-{
- int n;
- int64_t next;
-
- next = now;
- for (n = 0; n < 3; n++) {
- /* Ignore disabled timers. */
- if ((s->control[n] & 0x80) == 0)
- continue;
- /* Ignore expired one-shot timers. */
- if (s->count[n] == 0 && s->control[n] & 1)
- continue;
- if (s->expires[n] - now <= 0) {
- /* Timer has expired. */
- s->int_level[n] = 1;
- if (s->control[n] & 1) {
- /* One-shot. */
- s->count[n] = 0;
- } else {
- if ((s->control[n] & 0x40) == 0) {
- /* Free running. */
- if (s->control[n] & 2)
- s->count[n] = 0xffffffff;
- else
- s->count[n] = 0xffff;
- } else {
- /* Periodic. */
- s->count[n] = s->limit[n];
- }
- }
- }
- while (s->expires[n] - now <= 0) {
- icp_pit_reload(s, n);
- }
- }
- /* Update interrupts. */
- for (n = 0; n < 3; n++) {
- if (s->int_level[n] && (s->control[n] & 0x20)) {
- pic_set_irq_new(s->pic, 5 + n, 1);
- } else {
- pic_set_irq_new(s->pic, 5 + n, 0);
- }
- if (next - s->expires[n] < 0)
- next = s->expires[n];
- }
- /* Schedule the next timer interrupt. */
- if (next == now) {
- qemu_del_timer(s->timer);
- s->next_time = 0;
- } else if (next != s->next_time) {
- qemu_mod_timer(s->timer, next);
- s->next_time = next;
- }
-}
-
-/* Return the current value of the timer. */
-static uint32_t icp_pit_getcount(icp_pit_state *s, int n, int64_t now)
-{
- int64_t elapsed;
- int64_t period;
-
- if (s->count[n] == 0)
- return 0;
- if ((s->control[n] & 0x80) == 0)
- return s->count[n];
- elapsed = now - s->loaded[n];
- period = s->expires[n] - s->loaded[n];
- /* If the timer should have expired then return 0. This can happen
- when the host timer signal doesnt occur immediately. It's better to
- have a timer appear to sit at zero for a while than have it wrap
- around before the guest interrupt is raised. */
- /* ??? Could we trigger the interrupt here? */
- if (elapsed > period)
- return 0;
- /* We need to calculate count * elapsed / period without overfowing.
- Scale both elapsed and period so they fit in a 32-bit int. */
- while (period != (int32_t)period) {
- period >>= 1;
- elapsed >>= 1;
- }
- return ((uint64_t)s->count[n] * (uint64_t)(int32_t)elapsed)
- / (int32_t)period;
-}
-
-static uint32_t icp_pit_read(void *opaque, target_phys_addr_t offset)
-{
- int n;
- icp_pit_state *s = (icp_pit_state *)opaque;
-
- offset -= s->base;
- n = offset >> 8;
- if (n > 2)
- cpu_abort (cpu_single_env, "icp_pit_read: Bad timer %x\n", offset);
- switch ((offset & 0xff) >> 2) {
- case 0: /* TimerLoad */
- case 6: /* TimerBGLoad */
- return s->limit[n];
- case 1: /* TimerValue */
- return icp_pit_getcount(s, n, qemu_get_clock(vm_clock));
- case 2: /* TimerControl */
- return s->control[n];
- case 4: /* TimerRIS */
- return s->int_level[n];
- case 5: /* TimerMIS */
- if ((s->control[n] & 0x20) == 0)
- return 0;
- return s->int_level[n];
- default:
- cpu_abort (cpu_single_env, "icp_pit_read: Bad offset %x\n", offset);
- return 0;
- }
-}
-
-static void icp_pit_write(void *opaque, target_phys_addr_t offset,
- uint32_t value)
-{
- icp_pit_state *s = (icp_pit_state *)opaque;
- int n;
- int64_t now;
-
- now = qemu_get_clock(vm_clock);
- offset -= s->base;
- n = offset >> 8;
- if (n > 2)
- cpu_abort (cpu_single_env, "icp_pit_write: Bad offset %x\n", offset);
-
- switch ((offset & 0xff) >> 2) {
- case 0: /* TimerLoad */
- s->limit[n] = value;
- s->count[n] = value;
- s->expires[n] = now;
- icp_pit_reload(s, n);
- break;
- case 1: /* TimerValue */
- /* ??? Linux seems to want to write to this readonly register.
- Ignore it. */
- break;
- case 2: /* TimerControl */
- if (s->control[n] & 0x80) {
- /* Pause the timer if it is running. This may cause some
- inaccuracy dure to rounding, but avoids a whole lot of other
- messyness. */
- s->count[n] = icp_pit_getcount(s, n, now);
- }
- s->control[n] = value;
- if (n == 0)
- s->freq[n] = ICP_BUS_FREQ;
- else
- s->freq[n] = 1000000;
- /* ??? Need to recalculate expiry time after changing divisor. */
- switch ((value >> 2) & 3) {
- case 1: s->freq[n] >>= 4; break;
- case 2: s->freq[n] >>= 8; break;
- }
- if (s->control[n] & 0x80) {
- /* Restart the timer if still enabled. */
- s->expires[n] = now;
- icp_pit_reload(s, n);
- }
- break;
- case 3: /* TimerIntClr */
- s->int_level[n] = 0;
- break;
- case 6: /* TimerBGLoad */
- s->limit[n] = value;
- break;
- default:
- cpu_abort (cpu_single_env, "icp_pit_write: Bad offset %x\n", offset);
- }
- icp_pit_update(s, now);
-}
-
-static void icp_pit_tick(void *opaque)
-{
- int64_t now;
-
- now = qemu_get_clock(vm_clock);
- icp_pit_update((icp_pit_state *)opaque, now);
-}
-
-static CPUReadMemoryFunc *icp_pit_readfn[] = {
- icp_pit_read,
- icp_pit_read,
- icp_pit_read
-};
-
-static CPUWriteMemoryFunc *icp_pit_writefn[] = {
- icp_pit_write,
- icp_pit_write,
- icp_pit_write
-};
-
-static void icp_pit_init(uint32_t base, icp_pic_state *pic)
-{
- int iomemtype;
- icp_pit_state *s;
- int n;
-
- s = (icp_pit_state *)qemu_mallocz(sizeof(icp_pit_state));
- s->base = base;
- s->pic = pic;
- s->freq[0] = ICP_BUS_FREQ;
- s->freq[1] = 1000000;
- s->freq[2] = 1000000;
- for (n = 0; n < 3; n++) {
- s->control[n] = 0x20;
- s->count[n] = 0xffffffff;
- }
-
- iomemtype = cpu_register_io_memory(0, icp_pit_readfn,
- icp_pit_writefn, s);
- cpu_register_physical_memory(base, 0x007fffff, iomemtype);
- s->timer = qemu_new_timer(vm_clock, icp_pit_tick, s);
- /* ??? Save/restore. */
-}
-
-/* ARM PrimeCell PL011 UART */
-
-typedef struct {
- uint32_t base;
- uint32_t readbuff;
- uint32_t flags;
- uint32_t lcr;
- uint32_t cr;
- uint32_t dmacr;
- uint32_t int_enabled;
- uint32_t int_level;
- uint32_t read_fifo[16];
- uint32_t ilpr;
- uint32_t ibrd;
- uint32_t fbrd;
- uint32_t ifl;
- int read_pos;
- int read_count;
- int read_trigger;
- CharDriverState *chr;
- icp_pic_state *pic;
- int irq;
-} pl011_state;
-
-#define PL011_INT_TX 0x20
-#define PL011_INT_RX 0x10
-
-#define PL011_FLAG_TXFE 0x80
-#define PL011_FLAG_RXFF 0x40
-#define PL011_FLAG_TXFF 0x20
-#define PL011_FLAG_RXFE 0x10
-
-static const unsigned char pl011_id[] =
-{ 0x11, 0x10, 0x14, 0x00, 0x0d, 0xf0, 0x05, 0xb1 };
-
-static void pl011_update(pl011_state *s)
-{
- uint32_t flags;
-
- flags = s->int_level & s->int_enabled;
- pic_set_irq_new(s->pic, s->irq, flags != 0);
-}
-
-static uint32_t pl011_read(void *opaque, target_phys_addr_t offset)
-{
- pl011_state *s = (pl011_state *)opaque;
- uint32_t c;
-
- offset -= s->base;
- if (offset >= 0xfe0 && offset < 0x1000) {
- return pl011_id[(offset - 0xfe0) >> 2];
- }
- switch (offset >> 2) {
- case 0: /* UARTDR */
- s->flags &= ~PL011_FLAG_RXFF;
- c = s->read_fifo[s->read_pos];
- if (s->read_count > 0) {
- s->read_count--;
- if (++s->read_pos == 16)
- s->read_pos = 0;
- }
- if (s->read_count == 0) {
- s->flags |= PL011_FLAG_RXFE;
- }
- if (s->read_count == s->read_trigger - 1)
- s->int_level &= ~ PL011_INT_RX;
- pl011_update(s);
- return c;
- case 1: /* UARTCR */
- return 0;
- case 6: /* UARTFR */
- return s->flags;
- case 8: /* UARTILPR */
- return s->ilpr;
- case 9: /* UARTIBRD */
- return s->ibrd;
- case 10: /* UARTFBRD */
- return s->fbrd;
- case 11: /* UARTLCR_H */
- return s->lcr;
- case 12: /* UARTCR */
- return s->cr;
- case 13: /* UARTIFLS */
- return s->ifl;
- case 14: /* UARTIMSC */
- return s->int_enabled;
- case 15: /* UARTRIS */
- return s->int_level;
- case 16: /* UARTMIS */
- return s->int_level & s->int_enabled;
- case 18: /* UARTDMACR */
- return s->dmacr;
- default:
- cpu_abort (cpu_single_env, "pl011_read: Bad offset %x\n", offset);
- return 0;
- }
-}
-
-static void pl011_set_read_trigger(pl011_state *s)
-{
-#if 0
- /* The docs say the RX interrupt is triggered when the FIFO exceeds
- the threshold. However linux only reads the FIFO in response to an
- interrupt. Triggering the interrupt when the FIFO is non-empty seems
- to make things work. */
- if (s->lcr & 0x10)
- s->read_trigger = (s->ifl >> 1) & 0x1c;
- else
-#endif
- s->read_trigger = 1;
-}
-
-static void pl011_write(void *opaque, target_phys_addr_t offset,
- uint32_t value)
-{
- pl011_state *s = (pl011_state *)opaque;
- unsigned char ch;
-
- offset -= s->base;
- switch (offset >> 2) {
- case 0: /* UARTDR */
- /* ??? Check if transmitter is enabled. */
- ch = value;
- if (s->chr)
- qemu_chr_write(s->chr, &ch, 1);
- s->int_level |= PL011_INT_TX;
- pl011_update(s);
- break;
- case 1: /* UARTCR */
- s->cr = value;
- break;
- case 8: /* UARTUARTILPR */
- s->ilpr = value;
- break;
- case 9: /* UARTIBRD */
- s->ibrd = value;
- break;
- case 10: /* UARTFBRD */
- s->fbrd = value;
- break;
- case 11: /* UARTLCR_H */
- s->lcr = value;
- pl011_set_read_trigger(s);
- break;
- case 12: /* UARTCR */
- /* ??? Need to implement the enable and loopback bits. */
- s->cr = value;
- break;
- case 13: /* UARTIFS */
- s->ifl = value;
- pl011_set_read_trigger(s);
- break;
- case 14: /* UARTIMSC */
- s->int_enabled = value;
- pl011_update(s);
- break;
- case 17: /* UARTICR */
- s->int_level &= ~value;
- pl011_update(s);
- break;
- case 18: /* UARTDMACR */
- s->dmacr = value;
- if (value & 3)
- cpu_abort(cpu_single_env, "PL011: DMA not implemented\n");
- break;
- default:
- cpu_abort (cpu_single_env, "pl011_write: Bad offset %x\n", offset);
- }
-}
-
-static int pl011_can_recieve(void *opaque)
-{
- pl011_state *s = (pl011_state *)opaque;
-
- if (s->lcr & 0x10)
- return s->read_count < 16;
- else
- return s->read_count < 1;
-}
-
-static void pl011_recieve(void *opaque, const uint8_t *buf, int size)
-{
- pl011_state *s = (pl011_state *)opaque;
- int slot;
-
- slot = s->read_pos + s->read_count;
- if (slot >= 16)
- slot -= 16;
- s->read_fifo[slot] = *buf;
- s->read_count++;
- s->flags &= ~PL011_FLAG_RXFE;
- if (s->cr & 0x10 || s->read_count == 16) {
- s->flags |= PL011_FLAG_RXFF;
- }
- if (s->read_count == s->read_trigger) {
- s->int_level |= PL011_INT_RX;
- pl011_update(s);
- }
-}
-
-static void pl011_event(void *opaque, int event)
-{
- /* ??? Should probably implement break. */
-}
-
-static CPUReadMemoryFunc *pl011_readfn[] = {
- pl011_read,
- pl011_read,
- pl011_read
-};
-
-static CPUWriteMemoryFunc *pl011_writefn[] = {
- pl011_write,
- pl011_write,
- pl011_write
-};
-
-static void pl011_init(uint32_t base, icp_pic_state *pic, int irq,
- CharDriverState *chr)
-{
- int iomemtype;
- pl011_state *s;
-
- s = (pl011_state *)qemu_mallocz(sizeof(pl011_state));
- iomemtype = cpu_register_io_memory(0, pl011_readfn,
- pl011_writefn, s);
- cpu_register_physical_memory(base, 0x007fffff, iomemtype);
- s->base = base;
- s->pic = pic;
- s->irq = irq;
- s->chr = chr;
- s->read_trigger = 1;
- s->ifl = 0x12;
- s->cr = 0x300;
- s->flags = 0x90;
- if (chr){
- qemu_chr_add_read_handler(chr, pl011_can_recieve, pl011_recieve, s);
- qemu_chr_add_event_handler(chr, pl011_event);
- }
- /* ??? Save/restore. */
-}
-
/* CP control registers. */
typedef struct {
uint32_t base;
@@ -985,122 +470,6 @@ static void icp_control_init(uint32_t base)
}
-/* Keyboard/Mouse Interface. */
-
-typedef struct {
- void *dev;
- uint32_t base;
- uint32_t cr;
- uint32_t clk;
- uint32_t last;
- icp_pic_state *pic;
- int pending;
- int irq;
- int is_mouse;
-} icp_kmi_state;
-
-static void icp_kmi_update(void *opaque, int level)
-{
- icp_kmi_state *s = (icp_kmi_state *)opaque;
- int raise;
-
- s->pending = level;
- raise = (s->pending && (s->cr & 0x10) != 0)
- || (s->cr & 0x08) != 0;
- pic_set_irq_new(s->pic, s->irq, raise);
-}
-
-static uint32_t icp_kmi_read(void *opaque, target_phys_addr_t offset)
-{
- icp_kmi_state *s = (icp_kmi_state *)opaque;
- offset -= s->base;
- if (offset >= 0xfe0 && offset < 0x1000)
- return 0;
-
- switch (offset >> 2) {
- case 0: /* KMICR */
- return s->cr;
- case 1: /* KMISTAT */
- /* KMIC and KMID bits not implemented. */
- if (s->pending) {
- return 0x10;
- } else {
- return 0;
- }
- case 2: /* KMIDATA */
- if (s->pending)
- s->last = ps2_read_data(s->dev);
- return s->last;
- case 3: /* KMICLKDIV */
- return s->clk;
- case 4: /* KMIIR */
- return s->pending | 2;
- default:
- cpu_abort (cpu_single_env, "icp_kmi_read: Bad offset %x\n", offset);
- return 0;
- }
-}
-
-static void icp_kmi_write(void *opaque, target_phys_addr_t offset,
- uint32_t value)
-{
- icp_kmi_state *s = (icp_kmi_state *)opaque;
- offset -= s->base;
- switch (offset >> 2) {
- case 0: /* KMICR */
- s->cr = value;
- icp_kmi_update(s, s->pending);
- /* ??? Need to implement the enable/disable bit. */
- break;
- case 2: /* KMIDATA */
- /* ??? This should toggle the TX interrupt line. */
- /* ??? This means kbd/mouse can block each other. */
- if (s->is_mouse) {
- ps2_write_mouse(s->dev, value);
- } else {
- ps2_write_keyboard(s->dev, value);
- }
- break;
- case 3: /* KMICLKDIV */
- s->clk = value;
- return;
- default:
- cpu_abort (cpu_single_env, "icp_kmi_write: Bad offset %x\n", offset);
- }
-}
-static CPUReadMemoryFunc *icp_kmi_readfn[] = {
- icp_kmi_read,
- icp_kmi_read,
- icp_kmi_read
-};
-
-static CPUWriteMemoryFunc *icp_kmi_writefn[] = {
- icp_kmi_write,
- icp_kmi_write,
- icp_kmi_write
-};
-
-static void icp_kmi_init(uint32_t base, icp_pic_state * pic, int irq,
- int is_mouse)
-{
- int iomemtype;
- icp_kmi_state *s;
-
- s = (icp_kmi_state *)qemu_mallocz(sizeof(icp_kmi_state));
- iomemtype = cpu_register_io_memory(0, icp_kmi_readfn,
- icp_kmi_writefn, s);
- cpu_register_physical_memory(base, 0x007fffff, iomemtype);
- s->base = base;
- s->pic = pic;
- s->irq = irq;
- s->is_mouse = is_mouse;
- if (is_mouse)
- s->dev = ps2_mouse_init(icp_kmi_update, s);
- else
- s->dev = ps2_kbd_init(icp_kmi_update, s);
- /* ??? Save/restore. */
-}
-
/* The worlds second smallest bootloader. Set r0-r2, then jump to kernel. */
static uint32_t bootloader[] = {
0xe3a00000, /* mov r0, #0 */
@@ -1162,6 +531,7 @@ static void integratorcp_init(int ram_size, int vga_ram_size, int boot_device,
CPUState *env;
uint32_t bios_offset;
icp_pic_state *pic;
+ void *cpu_pic;
int kernel_size;
int initrd_size;
int n;
@@ -1177,14 +547,15 @@ static void integratorcp_init(int ram_size, int vga_ram_size, int boot_device,
cpu_register_physical_memory(0x80000000, ram_size, IO_MEM_RAM);
integratorcm_init(ram_size >> 20, bios_offset);
- pic = icp_pic_init(0x14000000, env, -1);
- icp_pic_init(0xca000000, pic, 26);
- icp_pit_init(0x13000000, pic);
+ cpu_pic = arm_pic_init_cpu(env);
+ pic = icp_pic_init(0x14000000, cpu_pic, ARM_PIC_CPU_IRQ, ARM_PIC_CPU_FIQ);
+ icp_pic_init(0xca000000, pic, 26, -1);
+ icp_pit_init(0x13000000, pic, 5);
pl011_init(0x16000000, pic, 1, serial_hds[0]);
pl011_init(0x17000000, pic, 2, serial_hds[1]);
icp_control_init(0xcb000000);
- icp_kmi_init(0x18000000, pic, 3, 0);
- icp_kmi_init(0x19000000, pic, 4, 1);
+ pl050_init(0x18000000, pic, 3, 0);
+ pl050_init(0x19000000, pic, 4, 1);
if (nd_table[0].vlan) {
if (nd_table[0].model == NULL
|| strcmp(nd_table[0].model, "smc91c111") == 0) {
diff --git a/hw/pl011.c b/hw/pl011.c
new file mode 100644
index 0000000000..657f03bbe8
--- /dev/null
+++ b/hw/pl011.c
@@ -0,0 +1,251 @@
+/*
+ * Arm PrimeCell PL011 UART
+ *
+ * Copyright (c) 2006 CodeSourcery.
+ * Written by Paul Brook
+ *
+ * This code is licenced under the GPL.
+ */
+
+#include "vl.h"
+
+typedef struct {
+ uint32_t base;
+ uint32_t readbuff;
+ uint32_t flags;
+ uint32_t lcr;
+ uint32_t cr;
+ uint32_t dmacr;
+ uint32_t int_enabled;
+ uint32_t int_level;
+ uint32_t read_fifo[16];
+ uint32_t ilpr;
+ uint32_t ibrd;
+ uint32_t fbrd;
+ uint32_t ifl;
+ int read_pos;
+ int read_count;
+ int read_trigger;
+ CharDriverState *chr;
+ void *pic;
+ int irq;
+} pl011_state;
+
+#define PL011_INT_TX 0x20
+#define PL011_INT_RX 0x10
+
+#define PL011_FLAG_TXFE 0x80
+#define PL011_FLAG_RXFF 0x40
+#define PL011_FLAG_TXFF 0x20
+#define PL011_FLAG_RXFE 0x10
+
+static const unsigned char pl011_id[] =
+{ 0x11, 0x10, 0x14, 0x00, 0x0d, 0xf0, 0x05, 0xb1 };
+
+static void pl011_update(pl011_state *s)
+{
+ uint32_t flags;
+
+ flags = s->int_level & s->int_enabled;
+ pic_set_irq_new(s->pic, s->irq, flags != 0);
+}
+
+static uint32_t pl011_read(void *opaque, target_phys_addr_t offset)
+{
+ pl011_state *s = (pl011_state *)opaque;
+ uint32_t c;
+
+ offset -= s->base;
+ if (offset >= 0xfe0 && offset < 0x1000) {
+ return pl011_id[(offset - 0xfe0) >> 2];
+ }
+ switch (offset >> 2) {
+ case 0: /* UARTDR */
+ s->flags &= ~PL011_FLAG_RXFF;
+ c = s->read_fifo[s->read_pos];
+ if (s->read_count > 0) {
+ s->read_count--;
+ if (++s->read_pos == 16)
+ s->read_pos = 0;
+ }
+ if (s->read_count == 0) {
+ s->flags |= PL011_FLAG_RXFE;
+ }
+ if (s->read_count == s->read_trigger - 1)
+ s->int_level &= ~ PL011_INT_RX;
+ pl011_update(s);
+ return c;
+ case 1: /* UARTCR */
+ return 0;
+ case 6: /* UARTFR */
+ return s->flags;
+ case 8: /* UARTILPR */
+ return s->ilpr;
+ case 9: /* UARTIBRD */
+ return s->ibrd;
+ case 10: /* UARTFBRD */
+ return s->fbrd;
+ case 11: /* UARTLCR_H */
+ return s->lcr;
+ case 12: /* UARTCR */
+ return s->cr;
+ case 13: /* UARTIFLS */
+ return s->ifl;
+ case 14: /* UARTIMSC */
+ return s->int_enabled;
+ case 15: /* UARTRIS */
+ return s->int_level;
+ case 16: /* UARTMIS */
+ return s->int_level & s->int_enabled;
+ case 18: /* UARTDMACR */
+ return s->dmacr;
+ default:
+ cpu_abort (cpu_single_env, "pl011_read: Bad offset %x\n", offset);
+ return 0;
+ }
+}
+
+static void pl011_set_read_trigger(pl011_state *s)
+{
+#if 0
+ /* The docs say the RX interrupt is triggered when the FIFO exceeds
+ the threshold. However linux only reads the FIFO in response to an
+ interrupt. Triggering the interrupt when the FIFO is non-empty seems
+ to make things work. */
+ if (s->lcr & 0x10)
+ s->read_trigger = (s->ifl >> 1) & 0x1c;
+ else
+#endif
+ s->read_trigger = 1;
+}
+
+static void pl011_write(void *opaque, target_phys_addr_t offset,
+ uint32_t value)
+{
+ pl011_state *s = (pl011_state *)opaque;
+ unsigned char ch;
+
+ offset -= s->base;
+ switch (offset >> 2) {
+ case 0: /* UARTDR */
+ /* ??? Check if transmitter is enabled. */
+ ch = value;
+ if (s->chr)
+ qemu_chr_write(s->chr, &ch, 1);
+ s->int_level |= PL011_INT_TX;
+ pl011_update(s);
+ break;
+ case 1: /* UARTCR */
+ s->cr = value;
+ break;
+ case 8: /* UARTUARTILPR */
+ s->ilpr = value;
+ break;
+ case 9: /* UARTIBRD */
+ s->ibrd = value;
+ break;
+ case 10: /* UARTFBRD */
+ s->fbrd = value;
+ break;
+ case 11: /* UARTLCR_H */
+ s->lcr = value;
+ pl011_set_read_trigger(s);
+ break;
+ case 12: /* UARTCR */
+ /* ??? Need to implement the enable and loopback bits. */
+ s->cr = value;
+ break;
+ case 13: /* UARTIFS */
+ s->ifl = value;
+ pl011_set_read_trigger(s);
+ break;
+ case 14: /* UARTIMSC */
+ s->int_enabled = value;
+ pl011_update(s);
+ break;
+ case 17: /* UARTICR */
+ s->int_level &= ~value;
+ pl011_update(s);
+ break;
+ case 18: /* UARTDMACR */
+ s->dmacr = value;
+ if (value & 3)
+ cpu_abort(cpu_single_env, "PL011: DMA not implemented\n");
+ break;
+ default:
+ cpu_abort (cpu_single_env, "pl011_write: Bad offset %x\n", offset);
+ }
+}
+
+static int pl011_can_recieve(void *opaque)
+{
+ pl011_state *s = (pl011_state *)opaque;
+
+ if (s->lcr & 0x10)
+ return s->read_count < 16;
+ else
+ return s->read_count < 1;
+}
+
+static void pl011_recieve(void *opaque, const uint8_t *buf, int size)
+{
+ pl011_state *s = (pl011_state *)opaque;
+ int slot;
+
+ slot = s->read_pos + s->read_count;
+ if (slot >= 16)
+ slot -= 16;
+ s->read_fifo[slot] = *buf;
+ s->read_count++;
+ s->flags &= ~PL011_FLAG_RXFE;
+ if (s->cr & 0x10 || s->read_count == 16) {
+ s->flags |= PL011_FLAG_RXFF;
+ }
+ if (s->read_count == s->read_trigger) {
+ s->int_level |= PL011_INT_RX;
+ pl011_update(s);
+ }
+}
+
+static void pl011_event(void *opaque, int event)
+{
+ /* ??? Should probably implement break. */
+}
+
+static CPUReadMemoryFunc *pl011_readfn[] = {
+ pl011_read,
+ pl011_read,
+ pl011_read
+};
+
+static CPUWriteMemoryFunc *pl011_writefn[] = {
+ pl011_write,
+ pl011_write,
+ pl011_write
+};
+
+void pl011_init(uint32_t base, void *pic, int irq,
+ CharDriverState *chr)
+{
+ int iomemtype;
+ pl011_state *s;
+
+ s = (pl011_state *)qemu_mallocz(sizeof(pl011_state));
+ iomemtype = cpu_register_io_memory(0, pl011_readfn,
+ pl011_writefn, s);
+ cpu_register_physical_memory(base, 0x00000fff, iomemtype);
+ s->base = base;
+ s->pic = pic;
+ s->irq = irq;
+ s->chr = chr;
+ s->read_trigger = 1;
+ s->ifl = 0x12;
+ s->cr = 0x300;
+ s->flags = 0x90;
+ if (chr){
+ qemu_chr_add_read_handler(chr, pl011_can_recieve, pl011_recieve, s);
+ qemu_chr_add_event_handler(chr, pl011_event);
+ }
+ /* ??? Save/restore. */
+}
+
diff --git a/hw/pl050.c b/hw/pl050.c
new file mode 100644
index 0000000000..a71ccf6149
--- /dev/null
+++ b/hw/pl050.c
@@ -0,0 +1,127 @@
+/*
+ * Arm PrimeCell PL050 Kyeboard / Mouse Interface
+ *
+ * Copyright (c) 2006 CodeSourcery.
+ * Written by Paul Brook
+ *
+ * This code is licenced under the GPL.
+ */
+
+#include "vl.h"
+
+typedef struct {
+ void *dev;
+ uint32_t base;
+ uint32_t cr;
+ uint32_t clk;
+ uint32_t last;
+ void *pic;
+ int pending;
+ int irq;
+ int is_mouse;
+} pl050_state;
+
+static const unsigned char pl050_id[] =
+{ 0x50, 0x10, 0x04, 0x00, 0x0d, 0xf0, 0x05, 0xb1 };
+
+static void pl050_update(void *opaque, int level)
+{
+ pl050_state *s = (pl050_state *)opaque;
+ int raise;
+
+ s->pending = level;
+ raise = (s->pending && (s->cr & 0x10) != 0)
+ || (s->cr & 0x08) != 0;
+ pic_set_irq_new(s->pic, s->irq, raise);
+}
+
+static uint32_t pl050_read(void *opaque, target_phys_addr_t offset)
+{
+ pl050_state *s = (pl050_state *)opaque;
+ offset -= s->base;
+ if (offset >= 0xfe0 && offset < 0x1000)
+ return pl050_id[(offset - 0xfe0) >> 2];
+
+ switch (offset >> 2) {
+ case 0: /* KMICR */
+ return s->cr;
+ case 1: /* KMISTAT */
+ /* KMIC and KMID bits not implemented. */
+ if (s->pending) {
+ return 0x10;
+ } else {
+ return 0;
+ }
+ case 2: /* KMIDATA */
+ if (s->pending)
+ s->last = ps2_read_data(s->dev);
+ return s->last;
+ case 3: /* KMICLKDIV */
+ return s->clk;
+ case 4: /* KMIIR */
+ return s->pending | 2;
+ default:
+ cpu_abort (cpu_single_env, "pl050_read: Bad offset %x\n", offset);
+ return 0;
+ }
+}
+
+static void pl050_write(void *opaque, target_phys_addr_t offset,
+ uint32_t value)
+{
+ pl050_state *s = (pl050_state *)opaque;
+ offset -= s->base;
+ switch (offset >> 2) {
+ case 0: /* KMICR */
+ s->cr = value;
+ pl050_update(s, s->pending);
+ /* ??? Need to implement the enable/disable bit. */
+ break;
+ case 2: /* KMIDATA */
+ /* ??? This should toggle the TX interrupt line. */
+ /* ??? This means kbd/mouse can block each other. */
+ if (s->is_mouse) {
+ ps2_write_mouse(s->dev, value);
+ } else {
+ ps2_write_keyboard(s->dev, value);
+ }
+ break;
+ case 3: /* KMICLKDIV */
+ s->clk = value;
+ return;
+ default:
+ cpu_abort (cpu_single_env, "pl050_write: Bad offset %x\n", offset);
+ }
+}
+static CPUReadMemoryFunc *pl050_readfn[] = {
+ pl050_read,
+ pl050_read,
+ pl050_read
+};
+
+static CPUWriteMemoryFunc *pl050_writefn[] = {
+ pl050_write,
+ pl050_write,
+ pl050_write
+};
+
+void pl050_init(uint32_t base, void *pic, int irq, int is_mouse)
+{
+ int iomemtype;
+ pl050_state *s;
+
+ s = (pl050_state *)qemu_mallocz(sizeof(pl050_state));
+ iomemtype = cpu_register_io_memory(0, pl050_readfn,
+ pl050_writefn, s);
+ cpu_register_physical_memory(base, 0x00000fff, iomemtype);
+ s->base = base;
+ s->pic = pic;
+ s->irq = irq;
+ s->is_mouse = is_mouse;
+ if (is_mouse)
+ s->dev = ps2_mouse_init(pl050_update, s);
+ else
+ s->dev = ps2_kbd_init(pl050_update, s);
+ /* ??? Save/restore. */
+}
+
diff --git a/hw/pl080.c b/hw/pl080.c
new file mode 100644
index 0000000000..49996ca91d
--- /dev/null
+++ b/hw/pl080.c
@@ -0,0 +1,328 @@
+/*
+ * Arm PrimeCell PL080 DMA controller
+ *
+ * Copyright (c) 2006 CodeSourcery.
+ * Written by Paul Brook
+ *
+ * This code is licenced under the GPL.
+ */
+
+#include "vl.h"
+
+#define PL080_NUM_CHANNELS 8
+#define PL080_CONF_E 0x1
+#define PL080_CONF_M1 0x2
+#define PL080_CONF_M2 0x4
+
+#define PL080_CCONF_H 0x40000
+#define PL080_CCONF_A 0x20000
+#define PL080_CCONF_L 0x10000
+#define PL080_CCONF_ITC 0x08000
+#define PL080_CCONF_IE 0x04000
+#define PL080_CCONF_E 0x00001
+
+#define PL080_CCTRL_I 0x80000000
+#define PL080_CCTRL_DI 0x08000000
+#define PL080_CCTRL_SI 0x04000000
+#define PL080_CCTRL_D 0x02000000
+#define PL080_CCTRL_S 0x01000000
+
+typedef struct {
+ uint32_t src;
+ uint32_t dest;
+ uint32_t lli;
+ uint32_t ctrl;
+ uint32_t conf;
+} pl080_channel;
+
+typedef struct {
+ uint32_t base;
+ uint8_t tc_int;
+ uint8_t tc_mask;
+ uint8_t err_int;
+ uint8_t err_mask;
+ uint32_t conf;
+ uint32_t sync;
+ uint32_t req_single;
+ uint32_t req_burst;
+ pl080_channel chan[PL080_NUM_CHANNELS];
+ /* Flag to avoid recursive DMA invocations. */
+ int running;
+ void *pic;
+ int irq;
+} pl080_state;
+
+static const unsigned char pl080_id[] =
+{ 0x80, 0x10, 0x04, 0x0a, 0x0d, 0xf0, 0x05, 0xb1 };
+
+static void pl080_update(pl080_state *s)
+{
+ if ((s->tc_int & s->tc_mask)
+ || (s->err_int & s->err_mask))
+ pic_set_irq_new(s->pic, s->irq, 1);
+ else
+ pic_set_irq_new(s->pic, s->irq, 1);
+}
+
+static void pl080_run(pl080_state *s)
+{
+ int c;
+ int flow;
+ pl080_channel *ch;
+ int swidth;
+ int dwidth;
+ int xsize;
+ int n;
+ int src_id;
+ int dest_id;
+ int size;
+ char buff[4];
+ uint32_t req;
+
+ s->tc_mask = 0;
+ for (c = 0; c < PL080_NUM_CHANNELS; c++) {
+ if (s->chan[c].conf & PL080_CCONF_ITC)
+ s->tc_mask |= 1 << c;
+ if (s->chan[c].conf & PL080_CCONF_IE)
+ s->err_mask |= 1 << c;
+ }
+
+ if ((s->conf & PL080_CONF_E) == 0)
+ return;
+
+cpu_abort(cpu_single_env, "DMA active\n");
+ /* If we are already in the middle of a DMA operation then indicate that
+ there may be new DMA requests and return immediately. */
+ if (s->running) {
+ s->running++;
+ return;
+ }
+ s->running = 1;
+ while (s->running) {
+ for (c = 0; c < PL080_NUM_CHANNELS; c++) {
+ ch = &s->chan[c];
+again:
+ /* Test if thiws channel has any pending DMA requests. */
+ if ((ch->conf & (PL080_CCONF_H | PL080_CCONF_E))
+ != PL080_CCONF_E)
+ continue;
+ flow = (ch->conf >> 11) & 7;
+ if (flow >= 4) {
+ cpu_abort(cpu_single_env,
+ "pl080_run: Peripheral flow control not implemented\n");
+ }
+ src_id = (ch->conf >> 1) & 0x1f;
+ dest_id = (ch->conf >> 6) & 0x1f;
+ size = ch->ctrl & 0xfff;
+ req = s->req_single | s->req_burst;
+ switch (flow) {
+ case 0:
+ break;
+ case 1:
+ if ((req & (1u << dest_id)) == 0)
+ size = 0;
+ break;
+ case 2:
+ if ((req & (1u << src_id)) == 0)
+ size = 0;
+ break;
+ case 3:
+ if ((req & (1u << src_id)) == 0
+ || (req & (1u << dest_id)) == 0)
+ size = 0;
+ break;
+ }
+ if (!size)
+ continue;
+
+ /* Transfer one element. */
+ /* ??? Should transfer multiple elements for a burst request. */
+ /* ??? Unclear what the proper behavior is when source and
+ destination widths are different. */
+ swidth = 1 << ((ch->ctrl >> 18) & 7);
+ dwidth = 1 << ((ch->ctrl >> 21) & 7);
+ for (n = 0; n < dwidth; n+= swidth) {
+ cpu_physical_memory_read(ch->src, buff + n, swidth);
+ if (ch->ctrl & PL080_CCTRL_SI)
+ ch->src += swidth;
+ }
+ xsize = (dwidth < swidth) ? swidth : dwidth;
+ /* ??? This may pad the value incorrectly for dwidth < 32. */
+ for (n = 0; n < xsize; n += dwidth) {
+ cpu_physical_memory_write(ch->dest + n, buff + n, dwidth);
+ if (ch->ctrl & PL080_CCTRL_DI)
+ ch->dest += swidth;
+ }
+
+ size--;
+ ch->ctrl = (ch->ctrl & 0xfffff000) | size;
+ if (size == 0) {
+ /* Transfer complete. */
+ if (ch->lli) {
+ ch->src = ldl_phys(ch->lli);
+ ch->dest = ldl_phys(ch->lli + 4);
+ ch->ctrl = ldl_phys(ch->lli + 12);
+ ch->lli = ldl_phys(ch->lli + 8);
+ } else {
+ ch->conf &= ~PL080_CCONF_E;
+ }
+ if (ch->ctrl & PL080_CCTRL_I) {
+ s->tc_int |= 1 << c;
+ }
+ }
+ goto again;
+ }
+ if (--s->running)
+ s->running = 1;
+ }
+}
+
+static uint32_t pl080_read(void *opaque, target_phys_addr_t offset)
+{
+ pl080_state *s = (pl080_state *)opaque;
+ uint32_t i;
+ uint32_t mask;
+
+ offset -= s->base;
+ if (offset >= 0xfe0 && offset < 0x1000) {
+ return pl080_id[(offset - 0xfe0) >> 2];
+ }
+ if (offset >= 0x100 && offset < 0x200) {
+ i = (offset & 0xe0) >> 5;
+ switch (offset >> 2) {
+ case 0: /* SrcAddr */
+ return s->chan[i].src;
+ case 1: /* DestAddr */
+ return s->chan[i].dest;
+ case 2: /* LLI */
+ return s->chan[i].lli;
+ case 3: /* Control */
+ return s->chan[i].ctrl;
+ case 4: /* Configuration */
+ return s->chan[i].conf;
+ default:
+ goto bad_offset;
+ }
+ }
+ switch (offset >> 2) {
+ case 0: /* IntStatus */
+ return (s->tc_int & s->tc_mask) | (s->err_int & s->err_mask);
+ case 1: /* IntTCStatus */
+ return (s->tc_int & s->tc_mask);
+ case 3: /* IntErrorStatus */
+ return (s->err_int & s->err_mask);
+ case 5: /* RawIntTCStatus */
+ return s->tc_int;
+ case 6: /* RawIntErrorStatus */
+ return s->err_int;
+ case 7: /* EnbldChns */
+ mask = 0;
+ for (i = 0; i < PL080_NUM_CHANNELS; i++) {
+ if (s->chan[i].conf & PL080_CCONF_E)
+ mask |= 1 << i;
+ }
+ return mask;
+ case 8: /* SoftBReq */
+ case 9: /* SoftSReq */
+ case 10: /* SoftLBReq */
+ case 11: /* SoftLSReq */
+ /* ??? Implement these. */
+ return 0;
+ case 12: /* Configuration */
+ return s->conf;
+ case 13: /* Sync */
+ return s->sync;
+ default:
+ bad_offset:
+ cpu_abort(cpu_single_env, "pl080_read: Bad offset %x\n", offset);
+ return 0;
+ }
+}
+
+static void pl080_write(void *opaque, target_phys_addr_t offset,
+ uint32_t value)
+{
+ pl080_state *s = (pl080_state *)opaque;
+ int i;
+
+ offset -= s->base;
+ if (offset >= 0x100 && offset < 0x200) {
+ i = (offset & 0xe0) >> 5;
+ switch (offset >> 2) {
+ case 0: /* SrcAddr */
+ s->chan[i].src = value;
+ break;
+ case 1: /* DestAddr */
+ s->chan[i].dest = value;
+ break;
+ case 2: /* LLI */
+ s->chan[i].lli = value;
+ break;
+ case 3: /* Control */
+ s->chan[i].ctrl = value;
+ break;
+ case 4: /* Configuration */
+ s->chan[i].conf = value;
+ pl080_run(s);
+ break;
+ }
+ }
+ switch (offset >> 2) {
+ case 2: /* IntTCClear */
+ s->tc_int &= ~value;
+ break;
+ case 4: /* IntErrorClear */
+ s->err_int &= ~value;
+ break;
+ case 8: /* SoftBReq */
+ case 9: /* SoftSReq */
+ case 10: /* SoftLBReq */
+ case 11: /* SoftLSReq */
+ /* ??? Implement these. */
+ cpu_abort(cpu_single_env, "pl080_write: Soft DMA not implemented\n");
+ break;
+ case 12: /* Configuration */
+ s->conf = value;
+ if (s->conf & (PL080_CONF_M1 | PL080_CONF_M1)) {
+ cpu_abort(cpu_single_env,
+ "pl080_write: Big-endian DMA not implemented\n");
+ }
+ pl080_run(s);
+ break;
+ case 13: /* Sync */
+ s->sync = value;
+ break;
+ default:
+ cpu_abort(cpu_single_env, "pl080_write: Bad offset %x\n", offset);
+ }
+ pl080_update(s);
+}
+
+static CPUReadMemoryFunc *pl080_readfn[] = {
+ pl080_read,
+ pl080_read,
+ pl080_read
+};
+
+static CPUWriteMemoryFunc *pl080_writefn[] = {
+ pl080_write,
+ pl080_write,
+ pl080_write
+};
+
+void *pl080_init(uint32_t base, void *pic, int irq)
+{
+ int iomemtype;
+ pl080_state *s;
+
+ s = (pl080_state *)qemu_mallocz(sizeof(pl080_state));
+ iomemtype = cpu_register_io_memory(0, pl080_readfn,
+ pl080_writefn, s);
+ cpu_register_physical_memory(base, 0x00000fff, iomemtype);
+ s->base = base;
+ s->pic = pic;
+ s->irq = irq;
+ /* ??? Save/restore. */
+ return s;
+}
+
diff --git a/hw/pl110.c b/hw/pl110.c
index 839f103b15..09352e742f 100644
--- a/hw/pl110.c
+++ b/hw/pl110.c
@@ -1,7 +1,7 @@
/*
* Arm PrimeCell PL110 Color LCD Controller
*
- * Copyright (c) 2005 CodeSourcery, LLC.
+ * Copyright (c) 2005-2006 CodeSourcery.
* Written by Paul Brook
*
* This code is licenced under the GNU LGPL
@@ -27,6 +27,8 @@ enum pl110_bppmode
typedef struct {
uint32_t base;
DisplayState *ds;
+ /* The Versatile/PB uses a slightly modified PL110 controller. */
+ int versatile;
void *pic;
uint32_t timing[4];
uint32_t cr;
@@ -46,6 +48,15 @@ typedef struct {
static const unsigned char pl110_id[] =
{ 0x10, 0x11, 0x04, 0x00, 0x0d, 0xf0, 0x05, 0xb1 };
+/* The Arm documentation (DDI0224C) says the CLDC on the Versatile board
+ has a different ID. However Linux only looks for the normal ID. */
+#if 0
+static const unsigned char pl110_versatile_id[] =
+{ 0x93, 0x10, 0x04, 0x00, 0x0d, 0xf0, 0x05, 0xb1 };
+#else
+#define pl110_versatile_id pl110_id
+#endif
+
static inline uint32_t rgb_to_pixel8(unsigned int r, unsigned int g, unsigned b)
{
return ((r >> 5) << 5) | ((g >> 5) << 2) | (b >> 6);
@@ -101,7 +112,7 @@ static void pl110_update_display(void *opaque)
int src_width;
uint8_t *dest;
uint8_t *src;
- int first, last;
+ int first, last = 0;
int dirty, new_dirty;
int i;
@@ -269,7 +280,10 @@ static uint32_t pl110_read(void *opaque, target_phys_addr_t offset)
offset -= s->base;
if (offset >= 0xfe0 && offset < 0x1000) {
- return pl110_id[(offset - 0xfe0) >> 2];
+ if (s->versatile)
+ return pl110_versatile_id[(offset - 0xfe0) >> 2];
+ else
+ return pl110_id[(offset - 0xfe0) >> 2];
}
if (offset >= 0x200 && offset < 0x400) {
return s->raw_pallette[(offset - 0x200) >> 2];
@@ -347,10 +361,16 @@ static void pl110_write(void *opaque, target_phys_addr_t offset,
s->lpbase = val;
break;
case 6: /* LCDIMSC */
+ if (s->versatile)
+ goto control;
+ imsc:
s->int_mask = val;
pl110_update(s);
break;
case 7: /* LCDControl */
+ if (s->versatile)
+ goto imsc;
+ control:
s->cr = val;
s->bpp = (val >> 1) & 7;
if (pl110_enabled(s)) {
@@ -390,6 +410,7 @@ void *pl110_init(DisplayState *ds, uint32_t base, void *pic, int irq,
cpu_register_physical_memory(base, 0x00000fff, iomemtype);
s->base = base;
s->ds = ds;
+ s->versatile = versatile;
s->pic = pic;
s->irq = irq;
graphic_console_init(ds, pl110_update_display, pl110_invalidate_display,
diff --git a/hw/pl190.c b/hw/pl190.c
new file mode 100644
index 0000000000..55c7180f5a
--- /dev/null
+++ b/hw/pl190.c
@@ -0,0 +1,252 @@
+/*
+ * Arm PrimeCell PL190 Vector Interrupt Controller
+ *
+ * Copyright (c) 2006 CodeSourcery.
+ * Written by Paul Brook
+ *
+ * This code is licenced under the GPL.
+ */
+
+#include "vl.h"
+#include "arm_pic.h"
+
+/* The number of virtual priority levels. 16 user vectors plus the
+ unvectored IRQ. Chained interrupts would require an additional level
+ if implemented. */
+
+#define PL190_NUM_PRIO 17
+
+typedef struct {
+ arm_pic_handler handler;
+ uint32_t base;
+ DisplayState *ds;
+ uint32_t level;
+ uint32_t soft_level;
+ uint32_t irq_enable;
+ uint32_t fiq_select;
+ uint32_t default_addr;
+ uint8_t vect_control[16];
+ uint32_t vect_addr[PL190_NUM_PRIO];
+ /* Mask containing interrupts with higher priority than this one. */
+ uint32_t prio_mask[PL190_NUM_PRIO + 1];
+ int protected;
+ /* Current priority level. */
+ int priority;
+ int prev_prio[PL190_NUM_PRIO];
+ void *parent;
+ int irq;
+ int fiq;
+} pl190_state;
+
+static const unsigned char pl190_id[] =
+{ 0x90, 0x11, 0x04, 0x00, 0x0D, 0xf0, 0x05, 0xb1 };
+
+static inline uint32_t pl190_irq_level(pl190_state *s)
+{
+ return (s->level | s->soft_level) & s->irq_enable & ~s->fiq_select;
+}
+
+/* Update interrupts. */
+static void pl190_update(pl190_state *s)
+{
+ uint32_t level = pl190_irq_level(s);
+ int set;
+
+ set = (level & s->prio_mask[s->priority]) != 0;
+ pic_set_irq_new(s->parent, s->irq, set);
+ set = ((s->level | s->soft_level) & s->fiq_select) != 0;
+ pic_set_irq_new(s->parent, s->fiq, set);
+}
+
+static void pl190_set_irq(void *opaque, int irq, int level)
+{
+ pl190_state *s = (pl190_state *)opaque;
+
+ if (level)
+ s->level |= 1u << irq;
+ else
+ s->level &= ~(1u << irq);
+ pl190_update(s);
+}
+
+static void pl190_update_vectors(pl190_state *s)
+{
+ uint32_t mask;
+ int i;
+ int n;
+
+ mask = 0;
+ for (i = 0; i < 16; i++)
+ {
+ s->prio_mask[i] = mask;
+ if (s->vect_control[i] & 0x20)
+ {
+ n = s->vect_control[i] & 0x1f;
+ mask |= 1 << n;
+ }
+ }
+ s->prio_mask[16] = mask;
+ pl190_update(s);
+}
+
+static uint32_t pl190_read(void *opaque, target_phys_addr_t offset)
+{
+ pl190_state *s = (pl190_state *)opaque;
+ int i;
+
+ offset -= s->base;
+ if (offset >= 0xfe0 && offset < 0x1000) {
+ return pl190_id[(offset - 0xfe0) >> 2];
+ }
+ if (offset >= 0x100 && offset < 0x140) {
+ return s->vect_addr[(offset - 0x100) >> 2];
+ }
+ if (offset >= 0x200 && offset < 0x240) {
+ return s->vect_control[(offset - 0x200) >> 2];
+ }
+ switch (offset >> 2) {
+ case 0: /* IRQSTATUS */
+ return pl190_irq_level(s);
+ case 1: /* FIQSATUS */
+ return (s->level | s->soft_level) & s->fiq_select;
+ case 2: /* RAWINTR */
+ return s->level | s->soft_level;
+ case 3: /* INTSELECT */
+ return s->fiq_select;
+ case 4: /* INTENABLE */
+ return s->irq_enable;
+ case 6: /* SOFTINT */
+ return s->soft_level;
+ case 8: /* PROTECTION */
+ return s->protected;
+ case 12: /* VECTADDR */
+ /* Read vector address at the start of an ISR. Increases the
+ current priority level to that of the current interrupt. */
+ for (i = 0; i < s->priority; i++)
+ {
+ if ((s->level | s->soft_level) & s->prio_mask[i])
+ break;
+ }
+ /* Reading this value with no pending interrupts is undefined.
+ We return the default address. */
+ if (i == PL190_NUM_PRIO)
+ return s->vect_addr[16];
+ if (i < s->priority)
+ {
+ s->prev_prio[i] = s->priority;
+ s->priority = i;
+ pl190_update(s);
+ }
+ return s->vect_addr[s->priority];
+ case 13: /* DEFVECTADDR */
+ return s->vect_addr[16];
+ default:
+ cpu_abort (cpu_single_env, "pl190_read: Bad offset %x\n", offset);
+ return 0;
+ }
+}
+
+static void pl190_write(void *opaque, target_phys_addr_t offset, uint32_t val)
+{
+ pl190_state *s = (pl190_state *)opaque;
+
+ offset -= s->base;
+ if (offset >= 0x100 && offset < 0x140) {
+ s->vect_addr[(offset - 0x100) >> 2] = val;
+ pl190_update_vectors(s);
+ return;
+ }
+ if (offset >= 0x200 && offset < 0x240) {
+ s->vect_control[(offset - 0x200) >> 2] = val;
+ pl190_update_vectors(s);
+ return;
+ }
+ switch (offset >> 2) {
+ case 0: /* SELECT */
+ /* This is a readonly register, but linux tries to write to it
+ anyway. Ignore the write. */
+ break;
+ case 3: /* INTSELECT */
+ s->fiq_select = val;
+ break;
+ case 4: /* INTENABLE */
+ s->irq_enable |= val;
+ break;
+ case 5: /* INTENCLEAR */
+ s->irq_enable &= ~val;
+ break;
+ case 6: /* SOFTINT */
+ s->soft_level |= val;
+ break;
+ case 7: /* SOFTINTCLEAR */
+ s->soft_level &= ~val;
+ break;
+ case 8: /* PROTECTION */
+ /* TODO: Protection (supervisor only access) is not implemented. */
+ s->protected = val & 1;
+ break;
+ case 12: /* VECTADDR */
+ /* Restore the previous priority level. The value written is
+ ignored. */
+ if (s->priority < PL190_NUM_PRIO)
+ s->priority = s->prev_prio[s->priority];
+ break;
+ case 13: /* DEFVECTADDR */
+ s->default_addr = val;
+ break;
+ case 0xc0: /* ITCR */
+ if (val)
+ cpu_abort(cpu_single_env, "pl190: Test mode not implemented\n");
+ break;
+ default:
+ cpu_abort(cpu_single_env, "pl190_write: Bad offset %x\n", offset);
+ return;
+ }
+ pl190_update(s);
+}
+
+static CPUReadMemoryFunc *pl190_readfn[] = {
+ pl190_read,
+ pl190_read,
+ pl190_read
+};
+
+static CPUWriteMemoryFunc *pl190_writefn[] = {
+ pl190_write,
+ pl190_write,
+ pl190_write
+};
+
+void pl190_reset(pl190_state *s)
+{
+ int i;
+
+ for (i = 0; i < 16; i++)
+ {
+ s->vect_addr[i] = 0;
+ s->vect_control[i] = 0;
+ }
+ s->vect_addr[16] = 0;
+ s->prio_mask[17] = 0xffffffff;
+ s->priority = PL190_NUM_PRIO;
+ pl190_update_vectors(s);
+}
+
+void *pl190_init(uint32_t base, void *parent, int irq, int fiq)
+{
+ pl190_state *s;
+ int iomemtype;
+
+ s = (pl190_state *)qemu_mallocz(sizeof(pl190_state));
+ iomemtype = cpu_register_io_memory(0, pl190_readfn,
+ pl190_writefn, s);
+ cpu_register_physical_memory(base, 0x00000fff, iomemtype);
+ s->handler = pl190_set_irq;
+ s->base = base;
+ s->parent = parent;
+ s->irq = irq;
+ s->fiq = fiq;
+ pl190_reset(s);
+ /* ??? Save/restore. */
+ return s;
+}
diff --git a/hw/versatilepb.c b/hw/versatilepb.c
new file mode 100644
index 0000000000..6d2e2dcfea
--- /dev/null
+++ b/hw/versatilepb.c
@@ -0,0 +1,321 @@
+/*
+ * ARM Versatile Platform Baseboard System emulation.
+ *
+ * Copyright (c) 2005-2006 CodeSourcery.
+ * Written by Paul Brook
+ *
+ * This code is licenced under the GPL.
+ */
+
+#include "vl.h"
+#include "arm_pic.h"
+
+#define KERNEL_ARGS_ADDR 0x100
+#define KERNEL_LOAD_ADDR 0x00010000
+#define INITRD_LOAD_ADDR 0x00800000
+
+/* Primary interrupt controller. */
+
+typedef struct vpb_sic_state
+{
+ arm_pic_handler handler;
+ uint32_t base;
+ uint32_t level;
+ uint32_t mask;
+ uint32_t pic_enable;
+ void *parent;
+ int irq;
+} vpb_sic_state;
+
+static void vpb_sic_update(vpb_sic_state *s)
+{
+ uint32_t flags;
+
+ flags = s->level & s->mask;
+ pic_set_irq_new(s->parent, s->irq, flags != 0);
+}
+
+static void vpb_sic_update_pic(vpb_sic_state *s)
+{
+ int i;
+ uint32_t mask;
+
+ for (i = 21; i <= 30; i++) {
+ mask = 1u << i;
+ if (!(s->pic_enable & mask))
+ continue;
+ pic_set_irq_new(s->parent, i, (s->level & mask) != 0);
+ }
+}
+
+static void vpb_sic_set_irq(void *opaque, int irq, int level)
+{
+ vpb_sic_state *s = (vpb_sic_state *)opaque;
+ if (level)
+ s->level |= 1u << irq;
+ else
+ s->level &= ~(1u << irq);
+ if (s->pic_enable & (1u << irq))
+ pic_set_irq_new(s->parent, irq, level);
+ vpb_sic_update(s);
+}
+
+static uint32_t vpb_sic_read(void *opaque, target_phys_addr_t offset)
+{
+ vpb_sic_state *s = (vpb_sic_state *)opaque;
+
+ offset -= s->base;
+ switch (offset >> 2) {
+ case 0: /* STATUS */
+ return s->level & s->mask;
+ case 1: /* RAWSTAT */
+ return s->level;
+ case 2: /* ENABLE */
+ return s->mask;
+ case 4: /* SOFTINT */
+ return s->level & 1;
+ case 8: /* PICENABLE */
+ return s->pic_enable;
+ default:
+ printf ("vpb_sic_read: Bad register offset 0x%x\n", offset);
+ return 0;
+ }
+}
+
+static void vpb_sic_write(void *opaque, target_phys_addr_t offset,
+ uint32_t value)
+{
+ vpb_sic_state *s = (vpb_sic_state *)opaque;
+ offset -= s->base;
+
+ switch (offset >> 2) {
+ case 2: /* ENSET */
+ s->mask |= value;
+ break;
+ case 3: /* ENCLR */
+ s->mask &= ~value;
+ break;
+ case 4: /* SOFTINTSET */
+ if (value)
+ s->mask |= 1;
+ break;
+ case 5: /* SOFTINTCLR */
+ if (value)
+ s->mask &= ~1u;
+ break;
+ case 8: /* PICENSET */
+ s->pic_enable |= (value & 0x7fe00000);
+ vpb_sic_update_pic(s);
+ break;
+ case 9: /* PICENCLR */
+ s->pic_enable &= ~value;
+ vpb_sic_update_pic(s);
+ break;
+ default:
+ printf ("vpb_sic_write: Bad register offset 0x%x\n", offset);
+ return;
+ }
+ vpb_sic_update(s);
+}
+
+static CPUReadMemoryFunc *vpb_sic_readfn[] = {
+ vpb_sic_read,
+ vpb_sic_read,
+ vpb_sic_read
+};
+
+static CPUWriteMemoryFunc *vpb_sic_writefn[] = {
+ vpb_sic_write,
+ vpb_sic_write,
+ vpb_sic_write
+};
+
+static vpb_sic_state *vpb_sic_init(uint32_t base, void *parent, int irq)
+{
+ vpb_sic_state *s;
+ int iomemtype;
+
+ s = (vpb_sic_state *)qemu_mallocz(sizeof(vpb_sic_state));
+ if (!s)
+ return NULL;
+ s->handler = vpb_sic_set_irq;
+ s->base = base;
+ s->parent = parent;
+ s->irq = irq;
+ iomemtype = cpu_register_io_memory(0, vpb_sic_readfn,
+ vpb_sic_writefn, s);
+ cpu_register_physical_memory(base, 0x00000fff, iomemtype);
+ /* ??? Save/restore. */
+ return s;
+}
+
+/* Board init. */
+
+/* The worlds second smallest bootloader. Set r0-r2, then jump to kernel. */
+static uint32_t bootloader[] = {
+ 0xe3a00000, /* mov r0, #0 */
+ 0xe3a01083, /* mov r1, #0x83 */
+ 0xe3811c01, /* orr r1, r1, #0x100 */
+ 0xe59f2000, /* ldr r2, [pc, #0] */
+ 0xe59ff000, /* ldr pc, [pc, #0] */
+ 0, /* Address of kernel args. Set by integratorcp_init. */
+ 0 /* Kernel entry point. Set by integratorcp_init. */
+};
+
+static void set_kernel_args(uint32_t ram_size, int initrd_size,
+ const char *kernel_cmdline)
+{
+ uint32_t *p;
+
+ p = (uint32_t *)(phys_ram_base + KERNEL_ARGS_ADDR);
+ /* ATAG_CORE */
+ stl_raw(p++, 5);
+ stl_raw(p++, 0x54410001);
+ stl_raw(p++, 1);
+ stl_raw(p++, 0x1000);
+ stl_raw(p++, 0);
+ /* ATAG_MEM */
+ stl_raw(p++, 4);
+ stl_raw(p++, 0x54410002);
+ stl_raw(p++, ram_size);
+ stl_raw(p++, 0);
+ if (initrd_size) {
+ /* ATAG_INITRD2 */
+ stl_raw(p++, 4);
+ stl_raw(p++, 0x54420005);
+ stl_raw(p++, INITRD_LOAD_ADDR);
+ stl_raw(p++, initrd_size);
+ }
+ if (kernel_cmdline && *kernel_cmdline) {
+ /* ATAG_CMDLINE */
+ int cmdline_size;
+
+ cmdline_size = strlen(kernel_cmdline);
+ memcpy (p + 2, kernel_cmdline, cmdline_size + 1);
+ cmdline_size = (cmdline_size >> 2) + 1;
+ stl_raw(p++, cmdline_size + 2);
+ stl_raw(p++, 0x54410009);
+ p += cmdline_size;
+ }
+ /* ATAG_END */
+ stl_raw(p++, 0);
+ stl_raw(p++, 0);
+}
+
+static void vpb_init(int ram_size, int vga_ram_size, int boot_device,
+ DisplayState *ds, const char **fd_filename, int snapshot,
+ const char *kernel_filename, const char *kernel_cmdline,
+ const char *initrd_filename)
+{
+ CPUState *env;
+ int kernel_size;
+ int initrd_size;
+ int n;
+ void *pic;
+ void *sic;
+
+ env = cpu_init();
+ cpu_arm_set_model(env, ARM_CPUID_ARM926);
+ /* ??? RAM shoud repeat to fill physical memory space. */
+ /* SDRAM at address zero. */
+ cpu_register_physical_memory(0, ram_size, IO_MEM_RAM);
+
+ pic = arm_pic_init_cpu(env);
+ pic = pl190_init(0x10140000, pic, ARM_PIC_CPU_IRQ, ARM_PIC_CPU_FIQ);
+ sic = vpb_sic_init(0x10003000, pic, 31);
+ pl050_init(0x10006000, sic, 3, 0);
+ pl050_init(0x10007000, sic, 4, 1);
+
+ /* TODO: Init PCI NICs. */
+ if (nd_table[0].vlan) {
+ if (nd_table[0].model == NULL
+ || strcmp(nd_table[0].model, "smc91c111") == 0) {
+ smc91c111_init(&nd_table[0], 0x10010000, sic, 25);
+ } else {
+ fprintf(stderr, "qemu: Unsupported NIC: %s\n", nd_table[0].model);
+ exit (1);
+ }
+ }
+
+ pl011_init(0x101f1000, pic, 12, serial_hds[0]);
+ pl011_init(0x101f2000, pic, 13, serial_hds[1]);
+ pl011_init(0x101f3000, pic, 14, serial_hds[2]);
+ pl011_init(0x10009000, sic, 6, serial_hds[3]);
+
+ pl080_init(0x10130000, pic, 17);
+ sp804_init(0x101e2000, pic, 4);
+ sp804_init(0x101e3000, pic, 5);
+
+ /* The versatile/PB actually has a modified Color LCD controller
+ that includes hardware cursor support from the PL111. */
+ pl110_init(ds, 0x10120000, pic, 16, 1);
+
+ /* 0x10000000 System registers. */
+ /* 0x10001000 PCI controller config registers. */
+ /* 0x10002000 Serial bus interface. */
+ /* 0x10003000 Secondary interrupt controller. */
+ /* 0x10004000 AACI (audio). */
+ /* 0x10005000 MMCI0. */
+ /* 0x10006000 KMI0 (keyboard). */
+ /* 0x10007000 KMI1 (mouse). */
+ /* 0x10008000 Character LCD Interface. */
+ /* 0x10009000 UART3. */
+ /* 0x1000a000 Smart card 1. */
+ /* 0x1000b000 MMCI1. */
+ /* 0x10010000 Ethernet. */
+ /* 0x10020000 USB. */
+ /* 0x10100000 SSMC. */
+ /* 0x10110000 MPMC. */
+ /* 0x10120000 CLCD Controller. */
+ /* 0x10130000 DMA Controller. */
+ /* 0x10140000 Vectored interrupt controller. */
+ /* 0x101d0000 AHB Monitor Interface. */
+ /* 0x101e0000 System Controller. */
+ /* 0x101e1000 Watchdog Interface. */
+ /* 0x101e2000 Timer 0/1. */
+ /* 0x101e3000 Timer 2/3. */
+ /* 0x101e4000 GPIO port 0. */
+ /* 0x101e5000 GPIO port 1. */
+ /* 0x101e6000 GPIO port 2. */
+ /* 0x101e7000 GPIO port 3. */
+ /* 0x101e8000 RTC. */
+ /* 0x101f0000 Smart card 0. */
+ /* 0x101f1000 UART0. */
+ /* 0x101f2000 UART1. */
+ /* 0x101f3000 UART2. */
+ /* 0x101f4000 SSPI. */
+
+ /* Load the kernel. */
+ if (!kernel_filename) {
+ fprintf(stderr, "Kernel image must be specified\n");
+ exit(1);
+ }
+ kernel_size = load_image(kernel_filename,
+ phys_ram_base + KERNEL_LOAD_ADDR);
+ if (kernel_size < 0) {
+ fprintf(stderr, "qemu: could not load kernel '%s'\n", kernel_filename);
+ exit(1);
+ }
+ if (initrd_filename) {
+ initrd_size = load_image(initrd_filename,
+ phys_ram_base + INITRD_LOAD_ADDR);
+ if (initrd_size < 0) {
+ fprintf(stderr, "qemu: could not load initrd '%s'\n",
+ initrd_filename);
+ exit(1);
+ }
+ } else {
+ initrd_size = 0;
+ }
+ bootloader[5] = KERNEL_ARGS_ADDR;
+ bootloader[6] = KERNEL_LOAD_ADDR;
+ for (n = 0; n < sizeof(bootloader) / 4; n++)
+ stl_raw(phys_ram_base + (n * 4), bootloader[n]);
+ set_kernel_args(ram_size, initrd_size, kernel_cmdline);
+}
+
+QEMUMachine versatilepb_machine = {
+ "versatilepb",
+ "ARM Versatile/PB (ARM926EJ-S)",
+ vpb_init,
+};
diff --git a/vl.c b/vl.c
index ed59988d3c..e13ad3d3d3 100644
--- a/vl.c
+++ b/vl.c
@@ -4436,6 +4436,7 @@ void register_machines(void)
#elif defined(TARGET_ARM)
qemu_register_machine(&integratorcp926_machine);
qemu_register_machine(&integratorcp1026_machine);
+ qemu_register_machine(&versatilepb_machine);
#else
#error unsupported CPU
#endif
diff --git a/vl.h b/vl.h
index 0c6f0132f7..4f118fbfa6 100644
--- a/vl.h
+++ b/vl.h
@@ -966,6 +966,9 @@ void usb_info(void);
extern QEMUMachine integratorcp926_machine;
extern QEMUMachine integratorcp1026_machine;
+/* versatilepb.c */
+extern QEMUMachine versatilepb_machine;
+
/* ps2.c */
void *ps2_kbd_init(void (*update_irq)(void *, int), void *update_arg);
void *ps2_mouse_init(void (*update_irq)(void *, int), void *update_arg);
@@ -981,6 +984,22 @@ void smc91c111_init(NICInfo *, uint32_t, void *, int);
/* pl110.c */
void *pl110_init(DisplayState *ds, uint32_t base, void *pic, int irq, int);
+/* pl011.c */
+void pl011_init(uint32_t base, void *pic, int irq, CharDriverState *chr);
+
+/* pl050.c */
+void pl050_init(uint32_t base, void *pic, int irq, int is_mouse);
+
+/* pl080.c */
+void *pl080_init(uint32_t base, void *pic, int irq);
+
+/* pl190.c */
+void *pl190_init(uint32_t base, void *parent, int irq, int fiq);
+
+/* arm-timer.c */
+void sp804_init(uint32_t base, void *pic, int irq);
+void icp_pit_init(uint32_t base, void *pic, int irq);
+
#endif /* defined(QEMU_TOOL) */
/* monitor.c */