ARM Versatile Platform Baseboard emulation.
authorpbrook <pbrook@c046a42c-6fe2-441c-8c8c-71466251a162>
Sun, 9 Apr 2006 01:32:52 +0000 (01:32 +0000)
committerpbrook <pbrook@c046a42c-6fe2-441c-8c8c-71466251a162>
Sun, 9 Apr 2006 01:32:52 +0000 (01:32 +0000)
git-svn-id: svn://svn.savannah.nongnu.org/qemu/trunk@1804 c046a42c-6fe2-441c-8c8c-71466251a162

13 files changed:
Makefile.target
hw/arm_pic.c [new file with mode: 0644]
hw/arm_pic.h [new file with mode: 0644]
hw/arm_timer.c [new file with mode: 0644]
hw/integratorcp.c
hw/pl011.c [new file with mode: 0644]
hw/pl050.c [new file with mode: 0644]
hw/pl080.c [new file with mode: 0644]
hw/pl110.c
hw/pl190.c [new file with mode: 0644]
hw/versatilepb.c [new file with mode: 0644]
vl.c
vl.h

index a5c728b..da69393 100644 (file)
@@ -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 (file)
index 0000000..fbc2d67
--- /dev/null
@@ -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 (file)
index 0000000..b299149
--- /dev/null
@@ -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 (file)
index 0000000..a97d73e
--- /dev/null
@@ -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.  */
+}
+
index 1bcd734..bce9b59 100644 (file)
@@ -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 (file)
index 0000000..657f03b
--- /dev/null
@@ -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 (file)
index 0000000..a71ccf6
--- /dev/null
@@ -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 (file)
index 0000000..49996ca
--- /dev/null
@@ -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;
+}
+
index 839f103..09352e7 100644 (file)
@@ -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 (file)
index 0000000..55c7180
--- /dev/null
@@ -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 (file)
index 0000000..6d2e2dc
--- /dev/null
@@ -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 ed59988..e13ad3d 100644 (file)
--- 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 0c6f013..4f118fb 100644 (file)
--- 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 */