summaryrefslogtreecommitdiff
path: root/hw
diff options
context:
space:
mode:
Diffstat (limited to 'hw')
-rw-r--r--hw/arm_boot.c22
-rw-r--r--hw/arm_gic.c460
-rw-r--r--hw/arm_sysctl.c5
-rw-r--r--hw/armv7m.c204
-rw-r--r--hw/armv7m_nvic.c381
-rw-r--r--hw/integratorcp.c4
-rw-r--r--hw/mpcore.c323
-rw-r--r--hw/pl011.c15
-rw-r--r--hw/pl022.c264
-rw-r--r--hw/pl061.c256
-rw-r--r--hw/pxa2xx.c8
-rw-r--r--hw/realview.c66
-rw-r--r--hw/realview_gic.c64
-rw-r--r--hw/ssd0303.c273
-rw-r--r--hw/ssd0323.c267
-rw-r--r--hw/stellaris.c1101
-rw-r--r--hw/versatilepb.c8
17 files changed, 3522 insertions, 199 deletions
diff --git a/hw/arm_boot.c b/hw/arm_boot.c
index 7a99b41751..8ef14ab4e4 100644
--- a/hw/arm_boot.c
+++ b/hw/arm_boot.c
@@ -1,7 +1,7 @@
/*
* ARM kernel loader.
*
- * Copyright (c) 2006 CodeSourcery.
+ * Copyright (c) 2006-2007 CodeSourcery.
* Written by Paul Brook
*
* This code is licenced under the GPL.
@@ -24,6 +24,22 @@ static uint32_t bootloader[] = {
0 /* Kernel entry point. Set by integratorcp_init. */
};
+/* Entry point for secondary CPUs. Enable interrupt controller and
+ Issue WFI until start address is written to system controller. */
+static uint32_t smpboot[] = {
+ 0xe3a00201, /* mov r0, #0x10000000 */
+ 0xe3800601, /* orr r0, r0, #0x001000000 */
+ 0xe3a01001, /* mov r1, #1 */
+ 0xe5801100, /* str r1, [r0, #0x100] */
+ 0xe3a00201, /* mov r0, #0x10000000 */
+ 0xe3800030, /* orr r0, #0x30 */
+ 0xe320f003, /* wfi */
+ 0xe5901000, /* ldr r1, [r0] */
+ 0xe3110003, /* tst r1, #3 */
+ 0x1afffffb, /* bne <wfi> */
+ 0xe12fff11 /* bx r1 */
+};
+
static void main_cpu_reset(void *opaque)
{
CPUState *env = opaque;
@@ -33,6 +49,8 @@ static void main_cpu_reset(void *opaque)
arm_load_kernel(env, env->ram_size, env->kernel_filename,
env->kernel_cmdline, env->initrd_filename,
env->board_id, env->loader_start);
+
+ /* TODO: Reset secondary CPUs. */
}
static void set_kernel_args(uint32_t ram_size, int initrd_size,
@@ -211,6 +229,8 @@ void arm_load_kernel(CPUState *env, int ram_size, const char *kernel_filename,
bootloader[6] = entry;
for (n = 0; n < sizeof(bootloader) / 4; n++)
stl_raw(phys_ram_base + (n * 4), bootloader[n]);
+ for (n = 0; n < sizeof(smpboot) / 4; n++)
+ stl_raw(phys_ram_base + ram_size + (n * 4), smpboot[n]);
if (old_param)
set_kernel_args_old(ram_size, initrd_size,
kernel_cmdline, loader_start);
diff --git a/hw/arm_gic.c b/hw/arm_gic.c
index 8cd7182cff..774b79bf86 100644
--- a/hw/arm_gic.c
+++ b/hw/arm_gic.c
@@ -1,17 +1,15 @@
/*
- * ARM AMBA Generic/Distributed Interrupt Controller
+ * ARM Generic/Distributed Interrupt Controller
*
- * Copyright (c) 2006 CodeSourcery.
+ * Copyright (c) 2006-2007 CodeSourcery.
* Written by Paul Brook
*
* This code is licenced under the GPL.
*/
-/* TODO: Some variants of this controller can handle multiple CPUs.
- Currently only single CPU operation is implemented. */
-
-#include "vl.h"
-#include "arm_pic.h"
+/* This file contains implementation code for the RealView EB interrupt
+ controller, MPCore distributed interrupt controller and ARMv7-M
+ Nested Vectored Interrupt Controller. */
//#define DEBUG_GIC
@@ -22,58 +20,84 @@ do { printf("arm_gic: " fmt , ##args); } while (0)
#define DPRINTF(fmt, args...) do {} while(0)
#endif
-/* Distributed interrupt controller. */
-
+#ifdef NVIC
+static const uint8_t gic_id[] =
+{ 0x00, 0xb0, 0x1b, 0x00, 0x0d, 0xe0, 0x05, 0xb1 };
+#define GIC_DIST_OFFSET 0
+/* The NVIC has 16 internal vectors. However these are not exposed
+ through the normal GIC interface. */
+#define GIC_BASE_IRQ 32
+#else
static const uint8_t gic_id[] =
{ 0x90, 0x13, 0x04, 0x00, 0x0d, 0xf0, 0x05, 0xb1 };
-
-#define GIC_NIRQ 96
+#define GIC_DIST_OFFSET 0x1000
+#define GIC_BASE_IRQ 0
+#endif
typedef struct gic_irq_state
{
+ /* ??? The documentation seems to imply the enable bits are global, even
+ for per-cpu interrupts. This seems strange. */
unsigned enabled:1;
- unsigned pending:1;
- unsigned active:1;
+ unsigned pending:NCPU;
+ unsigned active:NCPU;
unsigned level:1;
- unsigned model:1; /* 0 = 1:N, 1 = N:N */
+ unsigned model:1; /* 0 = N:N, 1 = 1:N */
unsigned trigger:1; /* nonzero = edge triggered. */
} gic_irq_state;
+#define ALL_CPU_MASK ((1 << NCPU) - 1)
+
#define GIC_SET_ENABLED(irq) s->irq_state[irq].enabled = 1
#define GIC_CLEAR_ENABLED(irq) s->irq_state[irq].enabled = 0
#define GIC_TEST_ENABLED(irq) s->irq_state[irq].enabled
-#define GIC_SET_PENDING(irq) s->irq_state[irq].pending = 1
-#define GIC_CLEAR_PENDING(irq) s->irq_state[irq].pending = 0
-#define GIC_TEST_PENDING(irq) s->irq_state[irq].pending
-#define GIC_SET_ACTIVE(irq) s->irq_state[irq].active = 1
-#define GIC_CLEAR_ACTIVE(irq) s->irq_state[irq].active = 0
-#define GIC_TEST_ACTIVE(irq) s->irq_state[irq].active
+#define GIC_SET_PENDING(irq, cm) s->irq_state[irq].pending |= (cm)
+#define GIC_CLEAR_PENDING(irq, cm) s->irq_state[irq].pending &= ~(cm)
+#define GIC_TEST_PENDING(irq, cm) ((s->irq_state[irq].pending & (cm)) != 0)
+#define GIC_SET_ACTIVE(irq, cm) s->irq_state[irq].active |= (cm)
+#define GIC_CLEAR_ACTIVE(irq, cm) s->irq_state[irq].active &= ~(cm)
+#define GIC_TEST_ACTIVE(irq, cm) ((s->irq_state[irq].active & (cm)) != 0)
#define GIC_SET_MODEL(irq) s->irq_state[irq].model = 1
#define GIC_CLEAR_MODEL(irq) s->irq_state[irq].model = 0
#define GIC_TEST_MODEL(irq) s->irq_state[irq].model
-#define GIC_SET_LEVEL(irq) s->irq_state[irq].level = 1
-#define GIC_CLEAR_LEVEL(irq) s->irq_state[irq].level = 0
-#define GIC_TEST_LEVEL(irq) s->irq_state[irq].level
+#define GIC_SET_LEVEL(irq, cm) s->irq_state[irq].level = (cm)
+#define GIC_CLEAR_LEVEL(irq, cm) s->irq_state[irq].level &= ~(cm)
+#define GIC_TEST_LEVEL(irq, cm) (s->irq_state[irq].level & (cm)) != 0
#define GIC_SET_TRIGGER(irq) s->irq_state[irq].trigger = 1
#define GIC_CLEAR_TRIGGER(irq) s->irq_state[irq].trigger = 0
#define GIC_TEST_TRIGGER(irq) s->irq_state[irq].trigger
+#define GIC_GET_PRIORITY(irq, cpu) \
+ (((irq) < 32) ? s->priority1[irq][cpu] : s->priority2[(irq) - 32])
+#ifdef NVIC
+#define GIC_TARGET(irq) 1
+#else
+#define GIC_TARGET(irq) s->irq_target[irq]
+#endif
typedef struct gic_state
{
uint32_t base;
- qemu_irq parent_irq;
+ qemu_irq parent_irq[NCPU];
int enabled;
- int cpu_enabled;
+ int cpu_enabled[NCPU];
gic_irq_state irq_state[GIC_NIRQ];
+#ifndef NVIC
int irq_target[GIC_NIRQ];
- int priority[GIC_NIRQ];
- int last_active[GIC_NIRQ];
-
- int priority_mask;
- int running_irq;
- int running_priority;
- int current_pending;
+#endif
+ int priority1[32][NCPU];
+ int priority2[GIC_NIRQ - 32];
+ int last_active[GIC_NIRQ][NCPU];
+
+ int priority_mask[NCPU];
+ int running_irq[NCPU];
+ int running_priority[NCPU];
+ int current_pending[NCPU];
+
+ qemu_irq *in;
+#ifdef NVIC
+ void *nvic;
+#endif
} gic_state;
/* TODO: Many places that call this routine could be optimized. */
@@ -83,112 +107,136 @@ static void gic_update(gic_state *s)
int best_irq;
int best_prio;
int irq;
-
- s->current_pending = 1023;
- if (!s->enabled || !s->cpu_enabled) {
- qemu_irq_lower(s->parent_irq);
- return;
- }
- best_prio = 0x100;
- best_irq = 1023;
- for (irq = 0; irq < 96; irq++) {
- if (GIC_TEST_ENABLED(irq) && GIC_TEST_PENDING(irq)) {
- if (s->priority[irq] < best_prio) {
- best_prio = s->priority[irq];
- best_irq = irq;
+ int level;
+ int cpu;
+ int cm;
+
+ for (cpu = 0; cpu < NCPU; cpu++) {
+ cm = 1 << cpu;
+ s->current_pending[cpu] = 1023;
+ if (!s->enabled || !s->cpu_enabled[cpu]) {
+ qemu_irq_lower(s->parent_irq[cpu]);
+ return;
+ }
+ best_prio = 0x100;
+ best_irq = 1023;
+ for (irq = 0; irq < GIC_NIRQ; irq++) {
+ if (GIC_TEST_ENABLED(irq) && GIC_TEST_PENDING(irq, cm)) {
+ if (GIC_GET_PRIORITY(irq, cpu) < best_prio) {
+ best_prio = GIC_GET_PRIORITY(irq, cpu);
+ best_irq = irq;
+ }
}
}
- }
- if (best_prio > s->priority_mask) {
- qemu_irq_lower(s->parent_irq);
- } else {
- s->current_pending = best_irq;
- if (best_prio < s->running_priority) {
- DPRINTF("Raised pending IRQ %d\n", best_irq);
- qemu_irq_raise(s->parent_irq);
+ level = 0;
+ if (best_prio <= s->priority_mask[cpu]) {
+ s->current_pending[cpu] = best_irq;
+ if (best_prio < s->running_priority[cpu]) {
+ DPRINTF("Raised pending IRQ %d\n", best_irq);
+ level = 1;
+ }
}
+ qemu_set_irq(s->parent_irq[cpu], level);
}
}
+static void __attribute__((unused))
+gic_set_pending_private(gic_state *s, int cpu, int irq)
+{
+ int cm = 1 << cpu;
+
+ if (GIC_TEST_PENDING(irq, cm))
+ return;
+
+ DPRINTF("Set %d pending cpu %d\n", irq, cpu);
+ GIC_SET_PENDING(irq, cm);
+ gic_update(s);
+}
+
+/* Process a change in an external IRQ input. */
static void gic_set_irq(void *opaque, int irq, int level)
{
gic_state *s = (gic_state *)opaque;
/* The first external input line is internal interrupt 32. */
irq += 32;
- if (level == GIC_TEST_LEVEL(irq))
+ if (level == GIC_TEST_LEVEL(irq, ALL_CPU_MASK))
return;
if (level) {
- GIC_SET_LEVEL(irq);
+ GIC_SET_LEVEL(irq, ALL_CPU_MASK);
if (GIC_TEST_TRIGGER(irq) || GIC_TEST_ENABLED(irq)) {
- DPRINTF("Set %d pending\n", irq);
- GIC_SET_PENDING(irq);
+ DPRINTF("Set %d pending mask %x\n", irq, GIC_TARGET(irq));
+ GIC_SET_PENDING(irq, GIC_TARGET(irq));
}
} else {
- GIC_CLEAR_LEVEL(irq);
+ GIC_CLEAR_LEVEL(irq, ALL_CPU_MASK);
}
gic_update(s);
}
-static void gic_set_running_irq(gic_state *s, int irq)
+static void gic_set_running_irq(gic_state *s, int cpu, int irq)
{
- s->running_irq = irq;
- if (irq == 1023)
- s->running_priority = 0x100;
- else
- s->running_priority = s->priority[irq];
+ s->running_irq[cpu] = irq;
+ if (irq == 1023) {
+ s->running_priority[cpu] = 0x100;
+ } else {
+ s->running_priority[cpu] = GIC_GET_PRIORITY(irq, cpu);
+ }
gic_update(s);
}
-static uint32_t gic_acknowledge_irq(gic_state *s)
+static uint32_t gic_acknowledge_irq(gic_state *s, int cpu)
{
int new_irq;
- new_irq = s->current_pending;
- if (new_irq == 1023 || s->priority[new_irq] >= s->running_priority) {
+ int cm = 1 << cpu;
+ new_irq = s->current_pending[cpu];
+ if (new_irq == 1023
+ || GIC_GET_PRIORITY(new_irq, cpu) >= s->running_priority[cpu]) {
DPRINTF("ACK no pending IRQ\n");
return 1023;
}
- qemu_irq_lower(s->parent_irq);
- s->last_active[new_irq] = s->running_irq;
- /* For level triggered interrupts we clear the pending bit while
- the interrupt is active. */
- GIC_CLEAR_PENDING(new_irq);
- gic_set_running_irq(s, new_irq);
+ s->last_active[new_irq][cpu] = s->running_irq[cpu];
+ /* Clear pending flags for both level and edge triggered interrupts.
+ Level triggered IRQs will be reasserted once they become inactive. */
+ GIC_CLEAR_PENDING(new_irq, GIC_TEST_MODEL(new_irq) ? ALL_CPU_MASK : cm);
+ gic_set_running_irq(s, cpu, new_irq);
DPRINTF("ACK %d\n", new_irq);
return new_irq;
}
-static void gic_complete_irq(gic_state * s, int irq)
+static void gic_complete_irq(gic_state * s, int cpu, int irq)
{
int update = 0;
+ int cm = 1 << cpu;
DPRINTF("EOI %d\n", irq);
- if (s->running_irq == 1023)
+ if (s->running_irq[cpu] == 1023)
return; /* No active IRQ. */
if (irq != 1023) {
/* Mark level triggered interrupts as pending if they are still
raised. */
if (!GIC_TEST_TRIGGER(irq) && GIC_TEST_ENABLED(irq)
- && GIC_TEST_LEVEL(irq)) {
- GIC_SET_PENDING(irq);
+ && GIC_TEST_LEVEL(irq, cm) && (GIC_TARGET(irq) & cm) != 0) {
+ DPRINTF("Set %d pending mask %x\n", irq, cm);
+ GIC_SET_PENDING(irq, cm);
update = 1;
}
}
- if (irq != s->running_irq) {
+ if (irq != s->running_irq[cpu]) {
/* Complete an IRQ that is not currently running. */
- int tmp = s->running_irq;
- while (s->last_active[tmp] != 1023) {
- if (s->last_active[tmp] == irq) {
- s->last_active[tmp] = s->last_active[irq];
+ int tmp = s->running_irq[cpu];
+ while (s->last_active[tmp][cpu] != 1023) {
+ if (s->last_active[tmp][cpu] == irq) {
+ s->last_active[tmp][cpu] = s->last_active[irq][cpu];
break;
}
- tmp = s->last_active[tmp];
+ tmp = s->last_active[tmp][cpu];
}
if (update) {
gic_update(s);
}
} else {
/* Complete the current running IRQ. */
- gic_set_running_irq(s, s->last_active[s->running_irq]);
+ gic_set_running_irq(s, cpu, s->last_active[s->running_irq[cpu]][cpu]);
}
}
@@ -198,15 +246,22 @@ static uint32_t gic_dist_readb(void *opaque, target_phys_addr_t offset)
uint32_t res;
int irq;
int i;
+ int cpu;
+ int cm;
+ int mask;
- offset -= s->base + 0x1000;
+ cpu = gic_get_current_cpu();
+ cm = 1 << cpu;
+ offset -= s->base + GIC_DIST_OFFSET;
if (offset < 0x100) {
+#ifndef NVIC
if (offset == 0)
return s->enabled;
if (offset == 4)
- return (GIC_NIRQ / 32) - 1;
+ return ((GIC_NIRQ / 32) - 1) | ((NCPU - 1) << 5);
if (offset < 0x08)
return 0;
+#endif
goto bad_reg;
} else if (offset < 0x200) {
/* Interrupt Set/Clear Enable. */
@@ -214,6 +269,7 @@ static uint32_t gic_dist_readb(void *opaque, target_phys_addr_t offset)
irq = (offset - 0x100) * 8;
else
irq = (offset - 0x180) * 8;
+ irq += GIC_BASE_IRQ;
if (irq >= GIC_NIRQ)
goto bad_reg;
res = 0;
@@ -228,40 +284,48 @@ static uint32_t gic_dist_readb(void *opaque, target_phys_addr_t offset)
irq = (offset - 0x200) * 8;
else
irq = (offset - 0x280) * 8;
+ irq += GIC_BASE_IRQ;
if (irq >= GIC_NIRQ)
goto bad_reg;
res = 0;
+ mask = (irq < 32) ? cm : ALL_CPU_MASK;
for (i = 0; i < 8; i++) {
- if (GIC_TEST_PENDING(irq + i)) {
+ if (GIC_TEST_PENDING(irq + i, mask)) {
res |= (1 << i);
}
}
} else if (offset < 0x400) {
/* Interrupt Active. */
- irq = (offset - 0x300) * 8;
+ irq = (offset - 0x300) * 8 + GIC_BASE_IRQ;
if (irq >= GIC_NIRQ)
goto bad_reg;
res = 0;
+ mask = (irq < 32) ? cm : ALL_CPU_MASK;
for (i = 0; i < 8; i++) {
- if (GIC_TEST_ACTIVE(irq + i)) {
+ if (GIC_TEST_ACTIVE(irq + i, mask)) {
res |= (1 << i);
}
}
} else if (offset < 0x800) {
/* Interrupt Priority. */
- irq = offset - 0x400;
+ irq = (offset - 0x400) + GIC_BASE_IRQ;
if (irq >= GIC_NIRQ)
goto bad_reg;
- res = s->priority[irq];
+ res = GIC_GET_PRIORITY(irq, cpu);
+#ifndef NVIC
} else if (offset < 0xc00) {
/* Interrupt CPU Target. */
- irq = offset - 0x800;
+ irq = (offset - 0x800) + GIC_BASE_IRQ;
if (irq >= GIC_NIRQ)
goto bad_reg;
- res = s->irq_target[irq];
+ if (irq >= 29 && irq <= 31) {
+ res = cm;
+ } else {
+ res = GIC_TARGET(irq);
+ }
} else if (offset < 0xf00) {
/* Interrupt Configuration. */
- irq = (offset - 0xc00) * 2;
+ irq = (offset - 0xc00) * 2 + GIC_BASE_IRQ;
if (irq >= GIC_NIRQ)
goto bad_reg;
res = 0;
@@ -271,6 +335,7 @@ static uint32_t gic_dist_readb(void *opaque, target_phys_addr_t offset)
if (GIC_TEST_TRIGGER(irq + i))
res |= (2 << (i * 2));
}
+#endif
} else if (offset < 0xfe0) {
goto bad_reg;
} else /* offset >= 0xfe0 */ {
@@ -282,7 +347,7 @@ static uint32_t gic_dist_readb(void *opaque, target_phys_addr_t offset)
}
return res;
bad_reg:
- cpu_abort (cpu_single_env, "gic_dist_readb: Bad offset %x\n", offset);
+ cpu_abort(cpu_single_env, "gic_dist_readb: Bad offset %x\n", (int)offset);
return 0;
}
@@ -297,6 +362,13 @@ static uint32_t gic_dist_readw(void *opaque, target_phys_addr_t offset)
static uint32_t gic_dist_readl(void *opaque, target_phys_addr_t offset)
{
uint32_t val;
+#ifdef NVIC
+ gic_state *s = (gic_state *)opaque;
+ uint32_t addr;
+ addr = offset - s->base;
+ if (addr < 0x100 || addr > 0xd00)
+ return nvic_readl(s->nvic, addr);
+#endif
val = gic_dist_readw(opaque, offset);
val |= gic_dist_readw(opaque, offset + 2) << 16;
return val;
@@ -308,9 +380,14 @@ static void gic_dist_writeb(void *opaque, target_phys_addr_t offset,
gic_state *s = (gic_state *)opaque;
int irq;
int i;
+ int cpu;
- offset -= s->base + 0x1000;
+ cpu = gic_get_current_cpu();
+ offset -= s->base + GIC_DIST_OFFSET;
if (offset < 0x100) {
+#ifdef NVIC
+ goto bad_reg;
+#else
if (offset == 0) {
s->enabled = (value & 1);
DPRINTF("Distribution %sabled\n", s->enabled ? "En" : "Dis");
@@ -319,27 +396,36 @@ static void gic_dist_writeb(void *opaque, target_phys_addr_t offset,
} else {
goto bad_reg;
}
+#endif
} else if (offset < 0x180) {
/* Interrupt Set Enable. */
- irq = (offset - 0x100) * 8;
+ irq = (offset - 0x100) * 8 + GIC_BASE_IRQ;
if (irq >= GIC_NIRQ)
goto bad_reg;
+ if (irq < 16)
+ value = 0xff;
for (i = 0; i < 8; i++) {
if (value & (1 << i)) {
+ int mask = (irq < 32) ? (1 << cpu) : GIC_TARGET(irq);
if (!GIC_TEST_ENABLED(irq + i))
DPRINTF("Enabled IRQ %d\n", irq + i);
GIC_SET_ENABLED(irq + i);
/* If a raised level triggered IRQ enabled then mark
is as pending. */
- if (GIC_TEST_LEVEL(irq + i) && !GIC_TEST_TRIGGER(irq + i))
- GIC_SET_PENDING(irq + i);
+ if (GIC_TEST_LEVEL(irq + i, mask)
+ && !GIC_TEST_TRIGGER(irq + i)) {
+ DPRINTF("Set %d pending mask %x\n", irq + i, mask);
+ GIC_SET_PENDING(irq + i, mask);
+ }
}
}
} else if (offset < 0x200) {
/* Interrupt Clear Enable. */
- irq = (offset - 0x180) * 8;
+ irq = (offset - 0x180) * 8 + GIC_BASE_IRQ;
if (irq >= GIC_NIRQ)
goto bad_reg;
+ if (irq < 16)
+ value = 0;
for (i = 0; i < 8; i++) {
if (value & (1 << i)) {
if (GIC_TEST_ENABLED(irq + i))
@@ -349,22 +435,28 @@ static void gic_dist_writeb(void *opaque, target_phys_addr_t offset,
}
} else if (offset < 0x280) {
/* Interrupt Set Pending. */
- irq = (offset - 0x200) * 8;
+ irq = (offset - 0x200) * 8 + GIC_BASE_IRQ;
if (irq >= GIC_NIRQ)
goto bad_reg;
+ if (irq < 16)
+ irq = 0;
+
for (i = 0; i < 8; i++) {
if (value & (1 << i)) {
- GIC_SET_PENDING(irq + i);
+ GIC_SET_PENDING(irq + i, GIC_TARGET(irq));
}
}
} else if (offset < 0x300) {
/* Interrupt Clear Pending. */
- irq = (offset - 0x280) * 8;
+ irq = (offset - 0x280) * 8 + GIC_BASE_IRQ;
if (irq >= GIC_NIRQ)
goto bad_reg;
for (i = 0; i < 8; i++) {
+ /* ??? This currently clears the pending bit for all CPUs, even
+ for per-CPU interrupts. It's unclear whether this is the
+ corect behavior. */
if (value & (1 << i)) {
- GIC_CLEAR_PENDING(irq + i);
+ GIC_CLEAR_PENDING(irq + i, ALL_CPU_MASK);
}
}
} else if (offset < 0x400) {
@@ -372,21 +464,32 @@ static void gic_dist_writeb(void *opaque, target_phys_addr_t offset,
goto bad_reg;
} else if (offset < 0x800) {
/* Interrupt Priority. */
- irq = offset - 0x400;
+ irq = (offset - 0x400) + GIC_BASE_IRQ;
if (irq >= GIC_NIRQ)
goto bad_reg;
- s->priority[irq] = value;
+ if (irq < 32) {
+ s->priority1[irq][cpu] = value;
+ } else {
+ s->priority2[irq - 32] = value;
+ }
+#ifndef NVIC
} else if (offset < 0xc00) {
/* Interrupt CPU Target. */
- irq = offset - 0x800;
+ irq = (offset - 0x800) + GIC_BASE_IRQ;
if (irq >= GIC_NIRQ)
goto bad_reg;
- s->irq_target[irq] = value;
+ if (irq < 29)
+ value = 0;
+ else if (irq < 32)
+ value = ALL_CPU_MASK;
+ s->irq_target[irq] = value & ALL_CPU_MASK;
} else if (offset < 0xf00) {
/* Interrupt Configuration. */
- irq = (offset - 0xc00) * 4;
+ irq = (offset - 0xc00) * 4 + GIC_BASE_IRQ;
if (irq >= GIC_NIRQ)
goto bad_reg;
+ if (irq < 32)
+ value |= 0xaa;
for (i = 0; i < 4; i++) {
if (value & (1 << (i * 2))) {
GIC_SET_MODEL(irq + i);
@@ -399,25 +502,20 @@ static void gic_dist_writeb(void *opaque, target_phys_addr_t offset,
GIC_CLEAR_TRIGGER(irq + i);
}
}
+#endif
} else {
- /* 0xf00 is only handled for word writes. */
+ /* 0xf00 is only handled for 32-bit writes. */
goto bad_reg;
}
gic_update(s);
return;
bad_reg:
- cpu_abort (cpu_single_env, "gic_dist_writeb: Bad offset %x\n", offset);
+ cpu_abort(cpu_single_env, "gic_dist_writeb: Bad offset %x\n", (int)offset);
}
static void gic_dist_writew(void *opaque, target_phys_addr_t offset,
uint32_t value)
{
- gic_state *s = (gic_state *)opaque;
- if (offset - s->base == 0xf00) {
- GIC_SET_PENDING(value & 0x3ff);
- gic_update(s);
- return;
- }
gic_dist_writeb(opaque, offset, value & 0xff);
gic_dist_writeb(opaque, offset + 1, value >> 8);
}
@@ -425,6 +523,41 @@ static void gic_dist_writew(void *opaque, target_phys_addr_t offset,
static void gic_dist_writel(void *opaque, target_phys_addr_t offset,
uint32_t value)
{
+ gic_state *s = (gic_state *)opaque;
+#ifdef NVIC
+ uint32_t addr;
+ addr = offset - s->base;
+ if (addr < 0x100 || (addr > 0xd00 && addr != 0xf00)) {
+ nvic_writel(s->nvic, addr, value);
+ return;
+ }
+#endif
+ if (offset - s->base == GIC_DIST_OFFSET + 0xf00) {
+ int cpu;
+ int irq;
+ int mask;
+
+ cpu = gic_get_current_cpu();
+ irq = value & 0x3ff;
+ switch ((value >> 24) & 3) {
+ case 0:
+ mask = (value >> 16) & ALL_CPU_MASK;
+ break;
+ case 1:
+ mask = 1 << cpu;
+ break;
+ case 2:
+ mask = ALL_CPU_MASK ^ (1 << cpu);
+ break;
+ default:
+ DPRINTF("Bad Soft Int target filter\n");
+ mask = ALL_CPU_MASK;
+ break;
+ }
+ GIC_SET_PENDING(irq, mask);
+ gic_update(s);
+ return;
+ }
gic_dist_writew(opaque, offset, value & 0xffff);
gic_dist_writew(opaque, offset + 2, value >> 16);
}
@@ -441,105 +574,100 @@ static CPUWriteMemoryFunc *gic_dist_writefn[] = {
gic_dist_writel
};
-static uint32_t gic_cpu_read(void *opaque, target_phys_addr_t offset)
+#ifndef NVIC
+static uint32_t gic_cpu_read(gic_state *s, int cpu, int offset)
{
- gic_state *s = (gic_state *)opaque;
- offset -= s->base;
switch (offset) {
case 0x00: /* Control */
- return s->cpu_enabled;
+ return s->cpu_enabled[cpu];
case 0x04: /* Priority mask */
- return s->priority_mask;
+ return s->priority_mask[cpu];
case 0x08: /* Binary Point */
/* ??? Not implemented. */
return 0;
case 0x0c: /* Acknowledge */
- return gic_acknowledge_irq(s);
+ return gic_acknowledge_irq(s, cpu);
case 0x14: /* Runing Priority */
- return s->running_priority;
+ return s->running_priority[cpu];
case 0x18: /* Highest Pending Interrupt */
- return s->current_pending;
+ return s->current_pending[cpu];
default:
- cpu_abort (cpu_single_env, "gic_cpu_read: Bad offset %x\n", offset);
+ cpu_abort(cpu_single_env, "gic_cpu_read: Bad offset %x\n",
+ (int)offset);
return 0;
}
}
-static void gic_cpu_write(void *opaque, target_phys_addr_t offset,
- uint32_t value)
+static void gic_cpu_write(gic_state *s, int cpu, int offset, uint32_t value)
{
- gic_state *s = (gic_state *)opaque;
- offset -= s->base;
switch (offset) {
case 0x00: /* Control */
- s->cpu_enabled = (value & 1);
+ s->cpu_enabled[cpu] = (value & 1);
DPRINTF("CPU %sabled\n", s->cpu_enabled ? "En" : "Dis");
break;
case 0x04: /* Priority mask */
- s->priority_mask = (value & 0x3ff);
+ s->priority_mask[cpu] = (value & 0xff);
break;
case 0x08: /* Binary Point */
/* ??? Not implemented. */
break;
case 0x10: /* End Of Interrupt */
- return gic_complete_irq(s, value & 0x3ff);
+ return gic_complete_irq(s, cpu, value & 0x3ff);
default:
- cpu_abort (cpu_single_env, "gic_cpu_write: Bad offset %x\n", offset);
+ cpu_abort(cpu_single_env, "gic_cpu_write: Bad offset %x\n",
+ (int)offset);
return;
}
gic_update(s);
}
-
-static CPUReadMemoryFunc *gic_cpu_readfn[] = {
- gic_cpu_read,
- gic_cpu_read,
- gic_cpu_read
-};
-
-static CPUWriteMemoryFunc *gic_cpu_writefn[] = {
- gic_cpu_write,
- gic_cpu_write,
- gic_cpu_write
-};
+#endif
static void gic_reset(gic_state *s)
{
int i;
memset(s->irq_state, 0, GIC_NIRQ * sizeof(gic_irq_state));
- s->priority_mask = 0xf0;
- s->current_pending = 1023;
- s->running_irq = 1023;
- s->running_priority = 0x100;
+ for (i = 0 ; i < NCPU; i++) {
+ s->priority_mask[i] = 0xf0;
+ s->current_pending[i] = 1023;
+ s->running_irq[i] = 1023;
+ s->running_priority[i] = 0x100;
+#ifdef NVIC
+ /* The NVIC doesn't have per-cpu interfaces, so enable by default. */
+ s->cpu_enabled[i] = 1;
+#else
+ s->cpu_enabled[i] = 0;
+#endif
+ }
for (i = 0; i < 15; i++) {
GIC_SET_ENABLED(i);
GIC_SET_TRIGGER(i);
}
+#ifdef NVIC
+ /* The NVIC is always enabled. */
+ s->enabled = 1;
+#else
s->enabled = 0;
- s->cpu_enabled = 0;
+#endif
}
-qemu_irq *arm_gic_init(uint32_t base, qemu_irq parent_irq)
+static gic_state *gic_init(uint32_t base, qemu_irq *parent_irq)
{
gic_state *s;
- qemu_irq *qi;
int iomemtype;
+ int i;
s = (gic_state *)qemu_mallocz(sizeof(gic_state));
if (!s)
return NULL;
- qi = qemu_allocate_irqs(gic_set_irq, s, GIC_NIRQ);
- s->parent_irq = parent_irq;
- if (base != 0xffffffff) {
- iomemtype = cpu_register_io_memory(0, gic_cpu_readfn,
- gic_cpu_writefn, s);
- cpu_register_physical_memory(base, 0x00001000, iomemtype);
- iomemtype = cpu_register_io_memory(0, gic_dist_readfn,
- gic_dist_writefn, s);
- cpu_register_physical_memory(base + 0x1000, 0x00001000, iomemtype);
- s->base = base;
- } else {
- s->base = 0;
+ s->in = qemu_allocate_irqs(gic_set_irq, s, GIC_NIRQ);
+ for (i = 0; i < NCPU; i++) {
+ s->parent_irq[i] = parent_irq[i];
}
+ iomemtype = cpu_register_io_memory(0, gic_dist_readfn,
+ gic_dist_writefn, s);
+ cpu_register_physical_memory(base + GIC_DIST_OFFSET, 0x00001000,
+ iomemtype);
+ s->base = base;
gic_reset(s);
- return qi;
+ return s;
}
diff --git a/hw/arm_sysctl.c b/hw/arm_sysctl.c
index 468a494dbf..e3179e2a91 100644
--- a/hw/arm_sysctl.c
+++ b/hw/arm_sysctl.c
@@ -1,7 +1,7 @@
/*
* Status and system control registers for ARM RealView/Versatile boards.
*
- * Copyright (c) 2006 CodeSourcery.
+ * Copyright (c) 2006-2007 CodeSourcery.
* Written by Paul Brook
*
* This code is licenced under the GPL.
@@ -200,6 +200,9 @@ void arm_sysctl_init(uint32_t base, uint32_t sys_id)
return;
s->base = base;
s->sys_id = sys_id;
+ /* The MPcore bootloader uses these flags to start secondary CPUs.
+ We don't use a bootloader, so do this here. */
+ s->flags = 3;
iomemtype = cpu_register_io_memory(0, arm_sysctl_readfn,
arm_sysctl_writefn, s);
cpu_register_physical_memory(base, 0x00001000, iomemtype);
diff --git a/hw/armv7m.c b/hw/armv7m.c
new file mode 100644
index 0000000000..f0a90e12d1
--- /dev/null
+++ b/hw/armv7m.c
@@ -0,0 +1,204 @@
+/*
+ * ARMV7M System emulation.
+ *
+ * Copyright (c) 2006-2007 CodeSourcery.
+ * Written by Paul Brook
+ *
+ * This code is licenced under the GPL.
+ */
+
+#include "vl.h"
+
+/* Bitbanded IO. Each word corresponds to a single bit. */
+
+/* Get the byte address of the real memory for a bitband acess. */
+static inline uint32_t bitband_addr(uint32_t addr)
+{
+ uint32_t res;
+
+ res = addr & 0xe0000000;
+ res |= (addr & 0x1ffffff) >> 5;
+ return res;
+
+}
+
+static uint32_t bitband_readb(void *opaque, target_phys_addr_t offset)
+{
+ uint8_t v;
+ cpu_physical_memory_read(bitband_addr(offset), &v, 1);
+ return (v & (1 << ((offset >> 2) & 7))) != 0;
+}
+
+static void bitband_writeb(void *opaque, target_phys_addr_t offset,
+ uint32_t value)
+{
+ uint32_t addr;
+ uint8_t mask;
+ uint8_t v;
+ addr = bitband_addr(offset);
+ mask = (1 << ((offset >> 2) & 7));
+ cpu_physical_memory_read(addr, &v, 1);
+ if (value & 1)
+ v |= mask;
+ else
+ v &= ~mask;
+ cpu_physical_memory_write(addr, &v, 1);
+}
+
+static uint32_t bitband_readw(void *opaque, target_phys_addr_t offset)
+{
+ uint32_t addr;
+ uint16_t mask;
+ uint16_t v;
+ addr = bitband_addr(offset) & ~1;
+ mask = (1 << ((offset >> 2) & 15));
+ mask = tswap16(mask);
+ cpu_physical_memory_read(addr, (uint8_t *)&v, 2);
+ return (v & mask) != 0;
+}
+
+static void bitband_writew(void *opaque, target_phys_addr_t offset,
+ uint32_t value)
+{
+ uint32_t addr;
+ uint16_t mask;
+ uint16_t v;
+ addr = bitband_addr(offset) & ~1;
+ mask = (1 << ((offset >> 2) & 15));
+ mask = tswap16(mask);
+ cpu_physical_memory_read(addr, (uint8_t *)&v, 2);
+ if (value & 1)
+ v |= mask;
+ else
+ v &= ~mask;
+ cpu_physical_memory_write(addr, (uint8_t *)&v, 2);
+}
+
+static uint32_t bitband_readl(void *opaque, target_phys_addr_t offset)
+{
+ uint32_t addr;
+ uint32_t mask;
+ uint32_t v;
+ addr = bitband_addr(offset) & ~3;
+ mask = (1 << ((offset >> 2) & 31));
+ mask = tswap32(mask);
+ cpu_physical_memory_read(addr, (uint8_t *)&v, 4);
+ return (v & mask) != 0;
+}
+
+static void bitband_writel(void *opaque, target_phys_addr_t offset,
+ uint32_t value)
+{
+ uint32_t addr;
+ uint32_t mask;
+ uint32_t v;
+ addr = bitband_addr(offset) & ~3;
+ mask = (1 << ((offset >> 2) & 31));
+ mask = tswap32(mask);
+ cpu_physical_memory_read(addr, (uint8_t *)&v, 4);
+ if (value & 1)
+ v |= mask;
+ else
+ v &= ~mask;
+ cpu_physical_memory_write(addr, (uint8_t *)&v, 4);
+}
+
+static CPUReadMemoryFunc *bitband_readfn[] = {
+ bitband_readb,
+ bitband_readw,
+ bitband_readl
+};
+
+static CPUWriteMemoryFunc *bitband_writefn[] = {
+ bitband_writeb,
+ bitband_writew,
+ bitband_writel
+};
+
+static void armv7m_bitband_init(void)
+{
+ int iomemtype;
+
+ iomemtype = cpu_register_io_memory(0, bitband_readfn, bitband_writefn,
+ NULL);
+ cpu_register_physical_memory(0x22000000, 0x02000000, iomemtype);
+ cpu_register_physical_memory(0x42000000, 0x02000000, iomemtype);
+}
+
+/* Board init. */
+/* Init CPU and memory for a v7-M based board.
+ flash_size and sram_size are in kb.
+ Returns the NVIC array. */
+
+qemu_irq *armv7m_init(int flash_size, int sram_size,
+ const char *kernel_filename, const char *cpu_model)
+{
+ CPUState *env;
+ qemu_irq *pic;
+ uint32_t pc;
+ int image_size;
+ uint64_t entry;
+ uint64_t lowaddr;
+
+ flash_size *= 1024;
+ sram_size *= 1024;
+
+ if (!cpu_model)
+ cpu_model = "cortex-m3";
+ env = cpu_init(cpu_model);
+ if (!env) {
+ fprintf(stderr, "Unable to find CPU definition\n");
+ exit(1);
+ }
+
+#if 0
+ /* > 32Mb SRAM gets complicated because it overlaps the bitband area.
+ We don't have proper commandline options, so allocate half of memory
+ as SRAM, up to a maximum of 32Mb, and the rest as code. */
+ if (ram_size > (512 + 32) * 1024 * 1024)
+ ram_size = (512 + 32) * 1024 * 1024;
+ sram_size = (ram_size / 2) & TARGET_PAGE_MASK;
+ if (sram_size > 32 * 1024 * 1024)
+ sram_size = 32 * 1024 * 1024;
+ code_size = ram_size - sram_size;
+#endif
+
+ /* Flash programming is done via the SCU, so pretend it is ROM. */
+ cpu_register_physical_memory(0, flash_size, IO_MEM_ROM);
+ cpu_register_physical_memory(0x20000000, sram_size,
+ flash_size + IO_MEM_RAM);
+ armv7m_bitband_init();
+
+ pic = armv7m_nvic_init(env);
+
+ image_size = load_elf(kernel_filename, 0, &entry, &lowaddr, NULL);
+ if (image_size < 0) {
+ image_size = load_image(kernel_filename, phys_ram_base);
+ lowaddr = 0;
+ }
+ if (image_size < 0) {
+ fprintf(stderr, "qemu: could not load kernel '%s'\n",
+ kernel_filename);
+ exit(1);
+ }
+
+ /* If the image was loaded at address zero then assume it is a
+ regular ROM image and perform the normal CPU reset sequence.
+ Otherwise jump directly to the entry point. */
+ if (lowaddr == 0) {
+ env->regs[13] = tswap32(*(uint32_t *)phys_ram_base);
+ pc = tswap32(*(uint32_t *)(phys_ram_base + 4));
+ } else {
+ pc = entry;
+ }
+ env->thumb = pc & 1;
+ env->regs[15] = pc & ~1;
+
+ /* Hack to map an additional page of ram at the top of the address
+ space. This stops qemu complaining about executing code outside RAM
+ when returning from an exception. */
+ cpu_register_physical_memory(0xfffff000, 0x1000, IO_MEM_RAM + ram_size);
+
+ return pic;
+}
+
diff --git a/hw/armv7m_nvic.c b/hw/armv7m_nvic.c
new file mode 100644
index 0000000000..d304082886
--- /dev/null
+++ b/hw/armv7m_nvic.c
@@ -0,0 +1,381 @@
+/*
+ * ARM Nested Vectored Interrupt Controller
+ *
+ * Copyright (c) 2006-2007 CodeSourcery.
+ * Written by Paul Brook
+ *
+ * This code is licenced under the GPL.
+ *
+ * The ARMv7M System controller is fairly tightly tied in with the
+ * NVIC. Much of that is also implemented here.
+ */
+
+#include "vl.h"
+#include "arm_pic.h"
+
+#define GIC_NIRQ 64
+#define NCPU 1
+#define NVIC 1
+
+/* Only a single "CPU" interface is present. */
+static inline int
+gic_get_current_cpu(void)
+{
+ return 0;
+}
+
+static uint32_t nvic_readl(void *opaque, uint32_t offset);
+static void nvic_writel(void *opaque, uint32_t offset, uint32_t value);
+
+#include "arm_gic.c"
+
+typedef struct {
+ struct {
+ uint32_t control;
+ uint32_t reload;
+ int64_t tick;
+ QEMUTimer *timer;
+ } systick;
+ gic_state *gic;
+} nvic_state;
+
+/* qemu timers run at 1GHz. We want something closer to 1MHz. */
+#define SYSTICK_SCALE 1000ULL
+
+#define SYSTICK_ENABLE (1 << 0)
+#define SYSTICK_TICKINT (1 << 1)
+#define SYSTICK_CLKSOURCE (1 << 2)
+#define SYSTICK_COUNTFLAG (1 << 16)
+
+/* Conversion factor from qemu timer to SysTick frequencies.
+ QEMU uses a base of 1GHz, so these give 20MHz and 1MHz for core and
+ reference frequencies. */
+
+static inline int64_t systick_scale(nvic_state *s)
+{
+ if (s->systick.control & SYSTICK_CLKSOURCE)
+ return 50;
+ else
+ return 1000;
+}
+
+static void systick_reload(nvic_state *s, int reset)
+{
+ if (reset)
+ s->systick.tick = qemu_get_clock(vm_clock);
+ s->systick.tick += (s->systick.reload + 1) * systick_scale(s);
+ qemu_mod_timer(s->systick.timer, s->systick.tick);
+}
+
+static void systick_timer_tick(void * opaque)
+{
+ nvic_state *s = (nvic_state *)opaque;
+ s->systick.control |= SYSTICK_COUNTFLAG;
+ if (s->systick.control & SYSTICK_TICKINT) {
+ /* Trigger the interrupt. */
+ armv7m_nvic_set_pending(s, ARMV7M_EXCP_SYSTICK);
+ }
+ if (s->systick.reload == 0) {
+ s->systick.control &= ~SYSTICK_ENABLE;
+ } else {
+ systick_reload(s, 0);
+ }
+}
+
+/* The external routines use the hardware vector numbering, ie. the first
+ IRQ is #16. The internal GIC routines use #32 as the first IRQ. */
+void armv7m_nvic_set_pending(void *opaque, int irq)
+{
+ nvic_state *s = (nvic_state *)opaque;
+ if (irq >= 16)
+ irq += 16;
+ gic_set_pending_private(s->gic, 0, irq);
+}
+
+/* Make pending IRQ active. */
+int armv7m_nvic_acknowledge_irq(void *opaque)
+{
+ nvic_state *s = (nvic_state *)opaque;
+ uint32_t irq;
+
+ irq = gic_acknowledge_irq(s->gic, 0);
+ if (irq == 1023)
+ cpu_abort(cpu_single_env, "Interrupt but no vector\n");
+ if (irq >= 32)
+ irq -= 16;
+ return irq;
+}
+
+void armv7m_nvic_complete_irq(void *opaque, int irq)
+{
+ nvic_state *s = (nvic_state *)opaque;
+ if (irq >= 16)
+ irq += 16;
+ gic_complete_irq(s->gic, 0, irq);
+}
+
+static uint32_t nvic_readl(void *opaque, uint32_t offset)
+{
+ nvic_state *s = (nvic_state *)opaque;
+ uint32_t val;
+ int irq;
+
+ switch (offset) {
+ case 4: /* Interrupt Control Type. */
+ return (GIC_NIRQ / 32) - 1;
+ case 0x10: /* SysTick Control and Status. */
+ val = s->systick.control;
+ s->systick.control &= ~SYSTICK_COUNTFLAG;
+ return val;
+ case 0x14: /* SysTick Reload Value. */
+ return s->systick.reload;
+ case 0x18: /* SysTick Current Value. */
+ {
+ int64_t t;
+ if ((s->systick.control & SYSTICK_ENABLE) == 0)
+ return 0;
+ t = qemu_get_clock(vm_clock);
+ if (t >= s->systick.tick)
+ return 0;
+ val = ((s->systick.tick - (t + 1)) / systick_scale(s)) + 1;
+ /* The interrupt in triggered when the timer reaches zero.
+ However the counter is not reloaded until the next clock
+ tick. This is a hack to return zero during the first tick. */
+ if (val > s->systick.reload)
+ val = 0;
+ return val;
+ }
+ case 0x1c: /* SysTick Calibration Value. */
+ return 10000;
+ case 0xd00: /* CPUID Base. */
+ return cpu_single_env->cp15.c0_cpuid;
+ case 0xd04: /* Interrypt Control State. */
+ /* VECTACTIVE */
+ val = s->gic->running_irq[0];
+ if (val == 1023) {
+ val = 0;
+ } else if (val >= 32) {
+ val -= 16;
+ }
+ /* RETTOBASE */
+ if (s->gic->running_irq[0] == 1023
+ || s->gic->last_active[s->gic->running_irq[0]][0] == 1023) {
+ val |= (1 << 11);
+ }
+ /* VECTPENDING */
+ if (s->gic->current_pending[0] != 1023)
+ val |= (s->gic->current_pending[0] << 12);
+ /* ISRPENDING */
+ for (irq = 32; irq < GIC_NIRQ; irq++) {
+ if (s->gic->irq_state[irq].pending) {
+ val |= (1 << 22);
+ break;
+ }
+ }
+ /* PENDSTSET */
+ if (s->gic->irq_state[ARMV7M_EXCP_SYSTICK].pending)
+ val |= (1 << 26);
+ /* PENDSVSET */
+ if (s->gic->irq_state[ARMV7M_EXCP_PENDSV].pending)
+ val |= (1 << 28);
+ /* NMIPENDSET */
+ if (s->gic->irq_state[ARMV7M_EXCP_NMI].pending)
+ val |= (1 << 31);
+ return val;
+ case 0xd08: /* Vector Table Offset. */
+ return cpu_single_env->v7m.vecbase;
+ case 0xd0c: /* Application Interrupt/Reset Control. */
+ return 0xfa05000;
+ case 0xd10: /* System Control. */
+ /* TODO: Implement SLEEPONEXIT. */
+ return 0;
+ case 0xd14: /* Configuration Control. */
+ /* TODO: Implement Configuration Control bits. */
+ return 0;
+ case 0xd18: case 0xd1c: case 0xd20: /* System Handler Priority. */
+ irq = offset - 0xd14;
+ val = 0;
+ val = s->gic->priority1[irq++][0];
+ val = s->gic->priority1[irq++][0] << 8;
+ val = s->gic->priority1[irq++][0] << 16;
+ val = s->gic->priority1[irq][0] << 24;
+ return val;
+ case 0xd24: /* System Handler Status. */
+ val = 0;
+ if (s->gic->irq_state[ARMV7M_EXCP_MEM].active) val |= (1 << 0);
+ if (s->gic->irq_state[ARMV7M_EXCP_BUS].active) val |= (1 << 1);
+ if (s->gic->irq_state[ARMV7M_EXCP_USAGE].active) val |= (1 << 3);
+ if (s->gic->irq_state[ARMV7M_EXCP_SVC].active) val |= (1 << 7);
+ if (s->gic->irq_state[ARMV7M_EXCP_DEBUG].active) val |= (1 << 8);
+ if (s->gic->irq_state[ARMV7M_EXCP_PENDSV].active) val |= (1 << 10);
+ if (s->gic->irq_state[ARMV7M_EXCP_SYSTICK].active) val |= (1 << 11);
+ if (s->gic->irq_state[ARMV7M_EXCP_USAGE].pending) val |= (1 << 12);
+ if (s->gic->irq_state[ARMV7M_EXCP_MEM].pending) val |= (1 << 13);
+ if (s->gic->irq_state[ARMV7M_EXCP_BUS].pending) val |= (1 << 14);
+ if (s->gic->irq_state[ARMV7M_EXCP_SVC].pending) val |= (1 << 15);
+ if (s->gic->irq_state[ARMV7M_EXCP_MEM].enabled) val |= (1 << 16);
+ if (s->gic->irq_state[ARMV7M_EXCP_BUS].enabled) val |= (1 << 17);
+ if (s->gic->irq_state[ARMV7M_EXCP_USAGE].enabled) val |= (1 << 18);
+ return val;
+ case 0xd28: /* Configurable Fault Status. */
+ /* TODO: Implement Fault Status. */
+ cpu_abort(cpu_single_env,
+ "Not implemented: Configurable Fault Status.");
+ return 0;
+ case 0xd2c: /* Hard Fault Status. */
+ case 0xd30: /* Debug Fault Status. */
+ case 0xd34: /* Mem Manage Address. */
+ case 0xd38: /* Bus Fault Address. */
+ case 0xd3c: /* Aux Fault Status. */
+ /* TODO: Implement fault status registers. */
+ goto bad_reg;
+ case 0xd40: /* PFR0. */
+ return 0x00000030;
+ case 0xd44: /* PRF1. */
+ return 0x00000200;
+ case 0xd48: /* DFR0. */
+ return 0x00100000;
+ case 0xd4c: /* AFR0. */
+ return 0x00000000;
+ case 0xd50: /* MMFR0. */
+ return 0x00000030;
+ case 0xd54: /* MMFR1. */
+ return 0x00000000;
+ case 0xd58: /* MMFR2. */
+ return 0x00000000;
+ case 0xd5c: /* MMFR3. */
+ return 0x00000000;
+ case 0xd60: /* ISAR0. */
+ return 0x01141110;
+ case 0xd64: /* ISAR1. */
+ return 0x02111000;
+ case 0xd68: /* ISAR2. */
+ return 0x21112231;
+ case 0xd6c: /* ISAR3. */
+ return 0x01111110;
+ case 0xd70: /* ISAR4. */
+ return 0x01310102;
+ /* TODO: Implement debug registers. */
+ default:
+ bad_reg:
+ cpu_abort(cpu_single_env, "NVIC: Bad read offset 0x%x\n", offset);
+ }
+}
+
+static void nvic_writel(void *opaque, uint32_t offset, uint32_t value)
+{
+ nvic_state *s = (nvic_state *)opaque;
+ uint32_t oldval;
+ switch (offset) {
+ case 0x10: /* SysTick Control and Status. */
+ oldval = s->systick.control;
+ s->systick.control &= 0xfffffff8;
+ s->systick.control |= value & 7;
+ if ((oldval ^ value) & SYSTICK_ENABLE) {
+ int64_t now = qemu_get_clock(vm_clock);
+ if (value & SYSTICK_ENABLE) {
+ if (s->systick.tick) {
+ s->systick.tick += now;
+ qemu_mod_timer(s->systick.timer, s->systick.tick);
+ } else {
+ systick_reload(s, 1);
+ }
+ } else {
+ qemu_del_timer(s->systick.timer);
+ s->systick.tick -= now;
+ if (s->systick.tick < 0)
+ s->systick.tick = 0;
+ }
+ } else if ((oldval ^ value) & SYSTICK_CLKSOURCE) {
+ /* This is a hack. Force the timer to be reloaded
+ when the reference clock is changed. */
+ systick_reload(s, 1);
+ }
+ break;
+ case 0x14: /* SysTick Reload Value. */
+ s->systick.reload = value;
+ break;
+ case 0x18: /* SysTick Current Value. Writes reload the timer. */
+ systick_reload(s, 1);
+ s->systick.control &= ~SYSTICK_COUNTFLAG;
+ break;
+ case 0xd04: /* Interrupt Control State. */
+ if (value & (1 << 31)) {
+ armv7m_nvic_set_pending(s, ARMV7M_EXCP_NMI);
+ }
+ if (value & (1 << 28)) {
+ armv7m_nvic_set_pending(s, ARMV7M_EXCP_PENDSV);
+ } else if (value & (1 << 27)) {
+ s->gic->irq_state[ARMV7M_EXCP_PENDSV].pending = 0;
+ gic_update(s->gic);
+ }
+ if (value & (1 << 26)) {
+ armv7m_nvic_set_pending(s, ARMV7M_EXCP_SYSTICK);
+ } else if (value & (1 << 25)) {
+ s->gic->irq_state[ARMV7M_EXCP_SYSTICK].pending = 0;
+ gic_update(s->gic);
+ }
+ break;
+ case 0xd08: /* Vector Table Offset. */
+ cpu_single_env->v7m.vecbase = value & 0xffffff80;
+ break;
+ case 0xd0c: /* Application Interrupt/Reset Control. */
+ if ((value >> 16) == 0x05fa) {
+ if (value & 2) {
+ cpu_abort(cpu_single_env, "VECTCLRACTIVE not implemented");
+ }
+ if (value & 5) {
+ cpu_abort(cpu_single_env, "System reset");
+ }
+ }
+ break;
+ case 0xd10: /* System Control. */
+ case 0xd14: /* Configuration Control. */
+ /* TODO: Implement control registers. */
+ goto bad_reg;
+ case 0xd18: case 0xd1c: case 0xd20: /* System Handler Priority. */
+ {
+ int irq;
+ irq = offset - 0xd14;
+ s->gic->priority1[irq++][0] = value & 0xff;
+ s->gic->priority1[irq++][0] = (value >> 8) & 0xff;
+ s->gic->priority1[irq++][0] = (value >> 16) & 0xff;
+ s->gic->priority1[irq][0] = (value >> 24) & 0xff;
+ gic_update(s->gic);
+ }
+ break;
+ case 0xd24: /* System Handler Control. */
+ /* TODO: Real hardware allows you to set/clear the active bits
+ under some circumstances. We don't implement this. */
+ s->gic->irq_state[ARMV7M_EXCP_MEM].enabled = (value & (1 << 16)) != 0;
+ s->gic->irq_state[ARMV7M_EXCP_BUS].enabled = (value & (1 << 17)) != 0;
+ s->gic->irq_state[ARMV7M_EXCP_USAGE].enabled = (value & (1 << 18)) != 0;
+ break;
+ case 0xd28: /* Configurable Fault Status. */
+ case 0xd2c: /* Hard Fault Status. */
+ case 0xd30: /* Debug Fault Status. */
+ case 0xd34: /* Mem Manage Address. */
+ case 0xd38: /* Bus Fault Address. */
+ case 0xd3c: /* Aux Fault Status. */
+ goto bad_reg;
+ default:
+ bad_reg:
+ cpu_abort(cpu_single_env, "NVIC: Bad write offset 0x%x\n", offset);
+ }
+}
+
+qemu_irq *armv7m_nvic_init(CPUState *env)
+{
+ nvic_state *s;
+ qemu_irq *parent;
+
+ parent = arm_pic_init_cpu(env);
+ s = (nvic_state *)qemu_mallocz(sizeof(nvic_state));
+ s->gic = gic_init(0xe000e000, &parent[ARM_PIC_CPU_IRQ]);
+ s->gic->nvic = s;
+ s->systick.timer = qemu_new_timer(vm_clock, systick_timer_tick, s);
+ if (env->v7m.nvic)
+ cpu_abort(env, "CPU can only have one NVIC\n");
+ env->v7m.nvic = s;
+ return s->gic->in;
+}
diff --git a/hw/integratorcp.c b/hw/integratorcp.c
index 75315a8b99..31e7d7d0e6 100644
--- a/hw/integratorcp.c
+++ b/hw/integratorcp.c
@@ -497,8 +497,8 @@ static void integratorcp_init(int ram_size, int vga_ram_size,
icp_pic_init(0xca000000, pic[26], NULL);
icp_pit_init(0x13000000, pic, 5);
pl031_init(0x15000000, pic[8]);
- pl011_init(0x16000000, pic[1], serial_hds[0]);
- pl011_init(0x17000000, pic[2], serial_hds[1]);
+ pl011_init(0x16000000, pic[1], serial_hds[0], PL011_ARM);
+ pl011_init(0x17000000, pic[2], serial_hds[1], PL011_ARM);
icp_control_init(0xcb000000);
pl050_init(0x18000000, pic[3], 0);
pl050_init(0x19000000, pic[4], 1);
diff --git a/hw/mpcore.c b/hw/mpcore.c
new file mode 100644
index 0000000000..cc33208e04
--- /dev/null
+++ b/hw/mpcore.c
@@ -0,0 +1,323 @@
+/*
+ * ARM MPCore internal peripheral emulation.
+ *
+ * Copyright (c) 2006-2007 CodeSourcery.
+ * Written by Paul Brook
+ *
+ * This code is licenced under the GPL.
+ */
+
+#include "vl.h"
+
+#define MPCORE_PRIV_BASE 0x10100000
+#define NCPU 4
+/* ??? The MPCore TRM says the on-chip controller has 224 external IRQ lines
+ (+ 32 internal). However my test chip only exposes/reports 32.
+ More importantly Linux falls over if more than 32 are present! */
+#define GIC_NIRQ 64
+
+static inline int
+gic_get_current_cpu(void)
+{
+ return cpu_single_env->cpu_index;
+}
+
+#include "arm_gic.c"
+
+/* MPCore private memory region. */
+
+typedef struct {
+ uint32_t count;
+ uint32_t load;
+ uint32_t control;
+ uint32_t status;
+ uint32_t old_status;
+ int64_t tick;
+ QEMUTimer *timer;
+ struct mpcore_priv_state *mpcore;
+ int id; /* Encodes both timer/watchdog and CPU. */
+} mpcore_timer_state;
+
+typedef struct mpcore_priv_state {
+ gic_state *gic;
+ uint32_t scu_control;
+ mpcore_timer_state timer[8];
+} mpcore_priv_state;
+
+/* Per-CPU Timers. */
+
+static inline void mpcore_timer_update_irq(mpcore_timer_state *s)
+{
+ if (s->status & ~s->old_status) {
+ gic_set_pending_private(s->mpcore->gic, s->id >> 1, 29 + (s->id & 1));
+ }
+ s->old_status = s->status;
+}
+
+/* Return conversion factor from mpcore timer ticks to qemu timer ticks. */
+static inline uint32_t mpcore_timer_scale(mpcore_timer_state *s)
+{
+ return (((s->control >> 8) & 0xff) + 1) * 10;
+}
+
+static void mpcore_timer_reload(mpcore_timer_state *s, int restart)
+{
+ if (s->count == 0)
+ return;
+ if (restart)
+ s->tick = qemu_get_clock(vm_clock);
+ s->tick += (int64_t)s->count * mpcore_timer_scale(s);
+ qemu_mod_timer(s->timer, s->tick);
+}
+
+static void mpcore_timer_tick(void *opaque)
+{
+ mpcore_timer_state *s = (mpcore_timer_state *)opaque;
+ s->status = 1;
+ if (s->control & 2) {
+ s->count = s->load;
+ mpcore_timer_reload(s, 0);
+ } else {
+ s->count = 0;
+ }
+ mpcore_timer_update_irq(s);
+}
+
+static uint32_t mpcore_timer_read(mpcore_timer_state *s, int offset)
+{
+ int64_t val;
+ switch (offset) {
+ case 0: /* Load */
+ return s->load;
+ /* Fall through. */
+ case 4: /* Counter. */
+ if (((s->control & 1) == 0) || (s->count == 0))
+ return 0;
+ /* Slow and ugly, but hopefully won't happen too often. */
+ val = s->tick - qemu_get_clock(vm_clock);
+ val /= mpcore_timer_scale(s);
+ if (val < 0)
+ val = 0;
+ return val;
+ case 8: /* Control. */
+ return s->control;
+ case 12: /* Interrupt status. */
+ return s->status;
+ }
+}
+
+static void mpcore_timer_write(mpcore_timer_state *s, int offset,
+ uint32_t value)
+{
+ int64_t old;
+ switch (offset) {
+ case 0: /* Load */
+ s->load = value;
+ /* Fall through. */
+ case 4: /* Counter. */
+ if ((s->control & 1) && s->count) {
+ /* Cancel the previous timer. */
+ qemu_del_timer(s->timer);
+ }
+ s->count = value;
+ if (s->control & 1) {
+ mpcore_timer_reload(s, 1);
+ }
+ break;
+ case 8: /* Control. */
+ old = s->control;
+ s->control = value;
+ if (((old & 1) == 0) && (value & 1)) {
+ if (s->count == 0 && (s->control & 2))
+ s->count = s->load;
+ mpcore_timer_reload(s, 1);
+ }
+ break;
+ case 12: /* Interrupt status. */
+ s->status &= ~value;
+ mpcore_timer_update_irq(s);
+ break;
+ }
+}
+
+static void mpcore_timer_init(mpcore_priv_state *mpcore,
+ mpcore_timer_state *s, int id)
+{
+ s->id = id;
+ s->mpcore = mpcore;
+ s->timer = qemu_new_timer(vm_clock, mpcore_timer_tick, s);
+}
+
+
+/* Per-CPU private memory mapped IO. */
+
+static uint32_t mpcore_priv_read(void *opaque, target_phys_addr_t offset)
+{
+ mpcore_priv_state *s = (mpcore_priv_state *)opaque;
+ int id;
+ offset &= 0xfff;
+ if (offset < 0x100) {
+ /* SCU */
+ switch (offset) {
+ case 0x00: /* Control. */
+ return s->scu_control;
+ case 0x04: /* Configuration. */
+ return 0xf3;
+ case 0x08: /* CPU status. */
+ return 0;
+ case 0x0c: /* Invalidate all. */
+ return 0;
+ default:
+ goto bad_reg;
+ }
+ } else if (offset < 0x600) {
+ /* Interrupt controller. */
+ if (offset < 0x200) {
+ id = gic_get_current_cpu();
+ } else {
+ id = (offset - 0x200) >> 8;
+ }
+ return gic_cpu_read(s->gic, id, offset & 0xff);
+ } else if (offset < 0xb00) {
+ /* Timers. */
+ if (offset < 0x700) {
+ id = gic_get_current_cpu();
+ } else {
+ id = (offset - 0x700) >> 8;
+ }
+ id <<= 1;
+ if (offset & 0x20)
+ id++;
+ return mpcore_timer_read(&s->timer[id], offset & 0xf);
+ }
+bad_reg:
+ cpu_abort(cpu_single_env, "mpcore_priv_read: Bad offset %x\n",
+ (int)offset);
+ return 0;
+}
+
+static void mpcore_priv_write(void *opaque, target_phys_addr_t offset,
+ uint32_t value)
+{
+ mpcore_priv_state *s = (mpcore_priv_state *)opaque;
+ int id;
+ offset &= 0xfff;
+ if (offset < 0x100) {
+ /* SCU */
+ switch (offset) {
+ case 0: /* Control register. */
+ s->scu_control = value & 1;
+ break;
+ case 0x0c: /* Invalidate all. */
+ /* This is a no-op as cache is not emulated. */
+ break;
+ default:
+ goto bad_reg;
+ }
+ } else if (offset < 0x600) {
+ /* Interrupt controller. */
+ if (offset < 0x200) {
+ id = gic_get_current_cpu();
+ } else {
+ id = (offset - 0x200) >> 8;
+ }
+ gic_cpu_write(s->gic, id, offset & 0xff, value);
+ } else if (offset < 0xb00) {
+ /* Timers. */
+ if (offset < 0x700) {
+ id = gic_get_current_cpu();
+ } else {
+ id = (offset - 0x700) >> 8;
+ }
+ id <<= 1;
+ if (offset & 0x20)
+ id++;
+ mpcore_timer_write(&s->timer[id], offset & 0xf, value);
+ return;
+ }
+ return;
+bad_reg:
+ cpu_abort(cpu_single_env, "mpcore_priv_read: Bad offset %x\n",
+ (int)offset);
+}
+
+static CPUReadMemoryFunc *mpcore_priv_readfn[] = {
+ mpcore_priv_read,
+ mpcore_priv_read,
+ mpcore_priv_read
+};
+
+static CPUWriteMemoryFunc *mpcore_priv_writefn[] = {
+ mpcore_priv_write,
+ mpcore_priv_write,
+ mpcore_priv_write
+};
+
+
+static qemu_irq *mpcore_priv_init(uint32_t base, qemu_irq *pic_irq)
+{
+ mpcore_priv_state *s;
+ int iomemtype;
+ int i;
+
+ s = (mpcore_priv_state *)qemu_mallocz(sizeof(mpcore_priv_state));
+ if (!s)
+ return NULL;
+ s->gic = gic_init(base, pic_irq);
+ if (!s->gic)
+ return NULL;
+ iomemtype = cpu_register_io_memory(0, mpcore_priv_readfn,
+ mpcore_priv_writefn, s);
+ cpu_register_physical_memory(base, 0x00001000, iomemtype);
+ for (i = 0; i < 8; i++) {
+ mpcore_timer_init(s, &s->timer[i], i);
+ }
+ return s->gic->in;
+}
+
+/* Dummy PIC to route IRQ lines. The baseboard has 4 independent IRQ
+ controllers. The output of these, plus some of the raw input lines
+ are fed into a single SMP-aware interrupt controller on the CPU. */
+typedef struct {
+ qemu_irq *cpuic;
+ qemu_irq *rvic[4];
+} mpcore_rirq_state;
+
+/* Map baseboard IRQs onto CPU IRQ lines. */
+static const int mpcore_irq_map[32] = {
+ -1, -1, -1, -1, 1, 2, -1, -1,
+ -1, -1, 6, -1, 4, 5, -1, -1,
+ -1, 14, 15, 0, 7, 8, -1, -1,
+ -1, -1, -1, -1, 9, 3, -1, -1,
+};
+
+static void mpcore_rirq_set_irq(void *opaque, int irq, int level)
+{
+ mpcore_rirq_state *s = (mpcore_rirq_state *)opaque;
+ int i;
+
+ for (i = 0; i < 4; i++) {
+ qemu_set_irq(s->rvic[i][irq], level);
+ }
+ if (irq < 32) {
+ irq = mpcore_irq_map[irq];
+ if (irq >= 0) {
+ qemu_set_irq(s->cpuic[irq], level);
+ }
+ }
+}
+
+qemu_irq *mpcore_irq_init(qemu_irq *cpu_irq)
+{
+ mpcore_rirq_state *s;
+ int n;
+
+ /* ??? IRQ routing is hardcoded to "normal" mode. */
+ s = qemu_mallocz(sizeof(mpcore_rirq_state));
+ s->cpuic = mpcore_priv_init(MPCORE_PRIV_BASE, cpu_irq);
+ for (n = 0; n < 4; n++) {
+ s->rvic[n] = realview_gic_init(0x10040000 + n * 0x10000,
+ s->cpuic[10 + n]);
+ }
+ return qemu_allocate_irqs(mpcore_rirq_set_irq, s, 64);
+}
diff --git a/hw/pl011.c b/hw/pl011.c
index df3349188e..9037554148 100644
--- a/hw/pl011.c
+++ b/hw/pl011.c
@@ -28,6 +28,7 @@ typedef struct {
int read_trigger;
CharDriverState *chr;
qemu_irq irq;
+ enum pl011_type type;
} pl011_state;
#define PL011_INT_TX 0x20
@@ -38,8 +39,10 @@ typedef struct {
#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 const unsigned char pl011_id[2][8] = {
+ { 0x11, 0x10, 0x14, 0x00, 0x0d, 0xf0, 0x05, 0xb1 }, /* PL011_ARM */
+ { 0x11, 0x00, 0x18, 0x01, 0x0d, 0xf0, 0x05, 0xb1 }, /* PL011_LUMINARY */
+};
static void pl011_update(pl011_state *s)
{
@@ -56,7 +59,7 @@ static uint32_t pl011_read(void *opaque, target_phys_addr_t offset)
offset -= s->base;
if (offset >= 0xfe0 && offset < 0x1000) {
- return pl011_id[(offset - 0xfe0) >> 2];
+ return pl011_id[s->type][(offset - 0xfe0) >> 2];
}
switch (offset >> 2) {
case 0: /* UARTDR */
@@ -137,6 +140,9 @@ static void pl011_write(void *opaque, target_phys_addr_t offset,
case 1: /* UARTCR */
s->cr = value;
break;
+ case 6: /* UARTFR */
+ /* Writes to Flag register are ignored. */
+ break;
case 8: /* UARTUARTILPR */
s->ilpr = value;
break;
@@ -224,7 +230,7 @@ static CPUWriteMemoryFunc *pl011_writefn[] = {
};
void pl011_init(uint32_t base, qemu_irq irq,
- CharDriverState *chr)
+ CharDriverState *chr, enum pl011_type type)
{
int iomemtype;
pl011_state *s;
@@ -235,6 +241,7 @@ void pl011_init(uint32_t base, qemu_irq irq,
cpu_register_physical_memory(base, 0x00001000, iomemtype);
s->base = base;
s->irq = irq;
+ s->type = type;
s->chr = chr;
s->read_trigger = 1;
s->ifl = 0x12;
diff --git a/hw/pl022.c b/hw/pl022.c
new file mode 100644
index 0000000000..d7c735b7ce
--- /dev/null
+++ b/hw/pl022.c
@@ -0,0 +1,264 @@
+/*
+ * Arm PrimeCell PL022 Synchronous Serial Port
+ *
+ * Copyright (c) 2007 CodeSourcery.
+ * Written by Paul Brook
+ *
+ * This code is licenced under the GPL.
+ */
+
+#include "vl.h"
+
+//#define DEBUG_PL022 1
+
+#ifdef DEBUG_PL022
+#define DPRINTF(fmt, args...) \
+do { printf("pl022: " fmt , ##args); } while (0)
+#define BADF(fmt, args...) \
+do { fprintf(stderr, "pl022: error: " fmt , ##args); exit(1);} while (0)
+#else
+#define DPRINTF(fmt, args...) do {} while(0)
+#define BADF(fmt, args...) \
+do { fprintf(stderr, "pl022: error: " fmt , ##args);} while (0)
+#endif
+
+#define PL022_CR1_LBM 0x01
+#define PL022_CR1_SSE 0x02
+#define PL022_CR1_MS 0x04
+#define PL022_CR1_SDO 0x08
+
+#define PL022_SR_TFE 0x01
+#define PL022_SR_TNF 0x02
+#define PL022_SR_RNE 0x04
+#define PL022_SR_RFF 0x08
+#define PL022_SR_BSY 0x10
+
+#define PL022_INT_ROR 0x01
+#define PL022_INT_RT 0x04
+#define PL022_INT_RX 0x04
+#define PL022_INT_TX 0x08
+
+typedef struct {
+ uint32_t base;
+ uint32_t cr0;
+ uint32_t cr1;
+ uint32_t bitmask;
+ uint32_t sr;
+ uint32_t cpsr;
+ uint32_t is;
+ uint32_t im;
+ /* The FIFO head points to the next empty entry. */
+ int tx_fifo_head;
+ int rx_fifo_head;
+ int tx_fifo_len;
+ int rx_fifo_len;
+ uint16_t tx_fifo[8];
+ uint16_t rx_fifo[8];
+ qemu_irq irq;
+ int (*xfer_cb)(void *, int);
+ void *opaque;
+} pl022_state;
+
+static const unsigned char pl022_id[8] =
+ { 0x22, 0x10, 0x04, 0x00, 0x0d, 0xf0, 0x05, 0xb1 };
+
+static void pl022_update(pl022_state *s)
+{
+ s->sr = 0;
+ if (s->tx_fifo_len == 0)
+ s->sr |= PL022_SR_TFE;
+ if (s->tx_fifo_len != 8)
+ s->sr |= PL022_SR_TNF;
+ if (s->rx_fifo_len != 0)
+ s->sr |= PL022_SR_RNE;
+ if (s->rx_fifo_len == 8)
+ s->sr |= PL022_SR_RFF;
+ if (s->tx_fifo_len)
+ s->sr |= PL022_SR_BSY;
+ s->is = 0;
+ if (s->rx_fifo_len >= 4)
+ s->is |= PL022_INT_RX;
+ if (s->tx_fifo_len <= 4)
+ s->is |= PL022_INT_TX;
+
+ qemu_set_irq(s->irq, (s->is & s->im) != 0);
+}
+
+static void pl022_xfer(pl022_state *s)
+{
+ int i;
+ int o;
+ int val;
+
+ if ((s->cr1 & PL022_CR1_SSE) == 0) {
+ pl022_update(s);
+ DPRINTF("Disabled\n");
+ return;
+ }
+
+ DPRINTF("Maybe xfer %d/%d\n", s->tx_fifo_len, s->rx_fifo_len);
+ i = (s->tx_fifo_head - s->tx_fifo_len) & 7;
+ o = s->rx_fifo_head;
+ /* ??? We do not emulate the line speed.
+ This may break some applications. The are two problematic cases:
+ (a) A driver feeds data into the TX FIFO until it is full,
+ and only then drains the RX FIFO. On real hardware the CPU can
+ feed data fast enough that the RX fifo never gets chance to overflow.
+ (b) A driver transmits data, deliberately allowing the RX FIFO to
+ overflow because it ignores the RX data anyway.
+
+ We choose to support (a) by stalling the transmit engine if it would
+ cause the RX FIFO to overflow. In practice much transmit-only code
+ falls into (a) because it flushes the RX FIFO to determine when
+ the transfer has completed. */
+ while (s->tx_fifo_len && s->rx_fifo_len < 8) {
+ DPRINTF("xfer\n");
+ val = s->tx_fifo[i];
+ if (s->cr1 & PL022_CR1_LBM) {
+ /* Loopback mode. */
+ } else if (s->xfer_cb) {
+ val = s->xfer_cb(s->opaque, val);
+ } else {
+ val = 0;
+ }
+ s->rx_fifo[o] = val & s->bitmask;
+ i = (i + 1) & 7;
+ o = (o + 1) & 7;
+ s->tx_fifo_len--;
+ s->rx_fifo_len++;
+ }
+ s->rx_fifo_head = o;
+ pl022_update(s);
+}
+
+static uint32_t pl022_read(void *opaque, target_phys_addr_t offset)
+{
+ pl022_state *s = (pl022_state *)opaque;
+ int val;
+
+ offset -= s->base;
+ if (offset >= 0xfe0 && offset < 0x1000) {
+ return pl022_id[(offset - 0xfe0) >> 2];
+ }
+ switch (offset) {
+ case 0x00: /* CR0 */
+ return s->cr0;
+ case 0x04: /* CR1 */
+ return s->cr1;
+ case 0x08: /* DR */
+ if (s->rx_fifo_len) {
+ val = s->rx_fifo[(s->rx_fifo_head - s->rx_fifo_len) & 7];
+ DPRINTF("RX %02x\n", val);
+ s->rx_fifo_len--;
+ pl022_xfer(s);
+ } else {
+ val = 0;
+ }
+ return val;
+ case 0x0c: /* SR */
+ return s->sr;
+ case 0x10: /* CPSR */
+ return s->cpsr;
+ case 0x14: /* IMSC */
+ return s->im;
+ case 0x18: /* RIS */
+ return s->is;
+ case 0x1c: /* MIS */
+ return s->im & s->is;
+ case 0x20: /* DMACR */
+ /* Not implemented. */
+ return 0;
+ default:
+ cpu_abort (cpu_single_env, "pl022_read: Bad offset %x\n",
+ (int)offset);
+ return 0;
+ }
+}
+
+static void pl022_write(void *opaque, target_phys_addr_t offset,
+ uint32_t value)
+{
+ pl022_state *s = (pl022_state *)opaque;
+
+ offset -= s->base;
+ switch (offset) {
+ case 0x00: /* CR0 */
+ s->cr0 = value;
+ /* Clock rate and format are ignored. */
+ s->bitmask = (1 << ((value & 15) + 1)) - 1;
+ break;
+ case 0x04: /* CR1 */
+ s->cr1 = value;
+ if ((s->cr1 & (PL022_CR1_MS | PL022_CR1_SSE))
+ == (PL022_CR1_MS | PL022_CR1_SSE)) {
+ BADF("SPI slave mode not implemented\n");
+ }
+ pl022_xfer(s);
+ break;
+ case 0x08: /* DR */
+ if (s->tx_fifo_len < 8) {
+ DPRINTF("TX %02x\n", value);
+ s->tx_fifo[s->tx_fifo_head] = value & s->bitmask;
+ s->tx_fifo_head = (s->tx_fifo_head + 1) & 7;
+ s->tx_fifo_len++;
+ pl022_xfer(s);
+ }
+ break;
+ case 0x10: /* CPSR */
+ /* Prescaler. Ignored. */
+ s->cpsr = value & 0xff;
+ break;
+ case 0x14: /* IMSC */
+ s->im = value;
+ pl022_update(s);
+ break;
+ case 0x20: /* DMACR */
+ if (value)
+ cpu_abort (cpu_single_env, "pl022: DMA not implemented\n");
+ break;
+ default:
+ cpu_abort (cpu_single_env, "pl022_write: Bad offset %x\n",
+ (int)offset);
+ }
+}
+
+static void pl022_reset(pl022_state *s)
+{
+ s->rx_fifo_len = 0;
+ s->tx_fifo_len = 0;
+ s->im = 0;
+ s->is = PL022_INT_TX;
+ s->sr = PL022_SR_TFE | PL022_SR_TNF;
+}
+
+static CPUReadMemoryFunc *pl022_readfn[] = {
+ pl022_read,
+ pl022_read,
+ pl022_read
+};
+
+static CPUWriteMemoryFunc *pl022_writefn[] = {
+ pl022_write,
+ pl022_write,
+ pl022_write
+};
+
+void pl022_init(uint32_t base, qemu_irq irq, int (*xfer_cb)(void *, int),
+ void * opaque)
+{
+ int iomemtype;
+ pl022_state *s;
+
+ s = (pl022_state *)qemu_mallocz(sizeof(pl022_state));
+ iomemtype = cpu_register_io_memory(0, pl022_readfn,
+ pl022_writefn, s);
+ cpu_register_physical_memory(base, 0x00001000, iomemtype);
+ s->base = base;
+ s->irq = irq;
+ s->xfer_cb = xfer_cb;
+ s->opaque = opaque;
+ pl022_reset(s);
+ /* ??? Save/restore. */
+}
+
+
diff --git a/hw/pl061.c b/hw/pl061.c
new file mode 100644
index 0000000000..fa5004a96c
--- /dev/null
+++ b/hw/pl061.c
@@ -0,0 +1,256 @@
+/*
+ * Arm PrimeCell PL061 General Purpose IO with additional
+ * Luminary Micro Stellaris bits.
+ *
+ * Copyright (c) 2007 CodeSourcery.
+ * Written by Paul Brook
+ *
+ * This code is licenced under the GPL.
+ */
+
+#include "vl.h"
+
+//#define DEBUG_PL061 1
+
+#ifdef DEBUG_PL061
+#define DPRINTF(fmt, args...) \
+do { printf("pl061: " fmt , ##args); } while (0)
+#define BADF(fmt, args...) \
+do { fprintf(stderr, "pl061: error: " fmt , ##args); exit(1);} while (0)
+#else
+#define DPRINTF(fmt, args...) do {} while(0)
+#define BADF(fmt, args...) \
+do { fprintf(stderr, "pl061: error: " fmt , ##args);} while (0)
+#endif
+
+static const uint8_t pl061_id[12] =
+ { 0x00, 0x00, 0x00, 0x00, 0x61, 0x00, 0x18, 0x01, 0x0d, 0xf0, 0x05, 0xb1 };
+
+typedef struct {
+ uint32_t base;
+ int locked;
+ uint8_t data;
+ uint8_t old_data;
+ uint8_t dir;
+ uint8_t isense;
+ uint8_t ibe;
+ uint8_t iev;
+ uint8_t im;
+ uint8_t istate;
+ uint8_t afsel;
+ uint8_t dr2r;
+ uint8_t dr4r;
+ uint8_t dr8r;
+ uint8_t odr;
+ uint8_t pur;
+ uint8_t pdr;
+ uint8_t slr;
+ uint8_t den;
+ uint8_t cr;
+ qemu_irq irq;
+ qemu_irq out[8];
+} pl061_state;
+
+static void pl061_update(pl061_state *s)
+{
+ uint8_t changed;
+ uint8_t mask;
+ int i;
+
+ changed = s->old_data ^ s->data;
+ if (!changed)
+ return;
+
+ s->old_data = s->data;
+ for (i = 0; i < 8; i++) {
+ mask = 1 << i;
+ if ((changed & mask & s->dir) && s->out) {
+ DPRINTF("Set output %d = %d\n", i, (s->data & mask) != 0);
+ qemu_set_irq(s->out[i], (s->data & mask) != 0);
+ }
+ }
+
+ /* FIXME: Implement input interrupts. */
+}
+
+static uint32_t pl061_read(void *opaque, target_phys_addr_t offset)
+{
+ pl061_state *s = (pl061_state *)opaque;
+
+ offset -= s->base;
+ if (offset >= 0xfd0 && offset < 0x1000) {
+ return pl061_id[(offset - 0xfd0) >> 2];
+ }
+ if (offset < 0x400) {
+ return s->data & (offset >> 2);
+ }
+ switch (offset) {
+ case 0x400: /* Direction */
+ return s->dir;
+ case 0x404: /* Interrupt sense */
+ return s->isense;
+ case 0x408: /* Interrupt both edges */
+ return s->ibe;
+ case 0x40c: /* Interupt event */
+ return s->iev;
+ case 0x410: /* Interrupt mask */
+ return s->im;
+ case 0x414: /* Raw interrupt status */
+ return s->istate;
+ case 0x418: /* Masked interrupt status */
+ return s->istate | s->im;
+ case 0x420: /* Alternate function select */
+ return s->afsel;
+ case 0x500: /* 2mA drive */
+ return s->dr2r;
+ case 0x504: /* 4mA drive */
+ return s->dr4r;
+ case 0x508: /* 8mA drive */
+ return s->dr8r;
+ case 0x50c: /* Open drain */
+ return s->odr;
+ case 0x510: /* Pull-up */
+ return s->pur;
+ case 0x514: /* Pull-down */
+ return s->pdr;
+ case 0x518: /* Slew rate control */
+ return s->slr;
+ case 0x51c: /* Digital enable */
+ return s->den;
+ case 0x520: /* Lock */
+ return s->locked;
+ case 0x524: /* Commit */
+ return s->cr;
+ default:
+ cpu_abort (cpu_single_env, "pl061_read: Bad offset %x\n",
+ (int)offset);
+ return 0;
+ }
+}
+
+static void pl061_write(void *opaque, target_phys_addr_t offset,
+ uint32_t value)
+{
+ pl061_state *s = (pl061_state *)opaque;
+ uint8_t mask;
+
+ offset -= s->base;
+ if (offset < 0x400) {
+ mask = (offset >> 2) & s->dir;
+ s->data = (s->data & ~mask) | (value & mask);
+ pl061_update(s);
+ return;
+ }
+ switch (offset) {
+ case 0x400: /* Direction */
+ s->dir = value;
+ break;
+ case 0x404: /* Interrupt sense */
+ s->isense = value;
+ break;
+ case 0x408: /* Interrupt both edges */
+ s->ibe = value;
+ break;
+ case 0x40c: /* Interupt event */
+ s->iev = value;
+ break;
+ case 0x410: /* Interrupt mask */
+ s->im = value;
+ break;
+ case 0x41c: /* Interrupt clear */
+ s->istate &= ~value;
+ break;
+ case 0x420: /* Alternate function select */
+ mask = s->cr;
+ s->afsel = (s->afsel & ~mask) | (value & mask);
+ break;
+ case 0x500: /* 2mA drive */
+ s->dr2r = value;
+ break;
+ case 0x504: /* 4mA drive */
+ s->dr4r = value;
+ break;
+ case 0x508: /* 8mA drive */
+ s->dr8r = value;
+ break;
+ case 0x50c: /* Open drain */
+ s->odr = value;
+ break;
+ case 0x510: /* Pull-up */
+ s->pur = value;
+ break;
+ case 0x514: /* Pull-down */
+ s->pdr = value;
+ break;
+ case 0x518: /* Slew rate control */
+ s->slr = value;
+ break;
+ case 0x51c: /* Digital enable */
+ s->den = value;
+ break;
+ case 0x520: /* Lock */
+ s->locked = (value != 0xacce551);
+ break;
+ case 0x524: /* Commit */
+ if (!s->locked)
+ s->cr = value;
+ break;
+ default:
+ cpu_abort (cpu_single_env, "pl061_write: Bad offset %x\n",
+ (int)offset);
+ }
+ pl061_update(s);
+}
+
+static void pl061_reset(pl061_state *s)
+{
+ s->locked = 1;
+ s->cr = 0xff;
+}
+
+void pl061_set_irq(void * opaque, int irq, int level)
+{
+ pl061_state *s = (pl061_state *)opaque;
+ uint8_t mask;
+
+ mask = 1 << irq;
+ if ((s->dir & mask) == 0) {
+ s->data &= ~mask;
+ if (level)
+ s->data |= mask;
+ pl061_update(s);
+ }
+}
+
+static CPUReadMemoryFunc *pl061_readfn[] = {
+ pl061_read,
+ pl061_read,
+ pl061_read
+};
+
+static CPUWriteMemoryFunc *pl061_writefn[] = {
+ pl061_write,
+ pl061_write,
+ pl061_write
+};
+
+/* Returns an array of inputs. */
+qemu_irq *pl061_init(uint32_t base, qemu_irq irq, qemu_irq **out)
+{
+ int iomemtype;
+ pl061_state *s;
+
+ s = (pl061_state *)qemu_mallocz(sizeof(pl061_state));
+ iomemtype = cpu_register_io_memory(0, pl061_readfn,
+ pl061_writefn, s);
+ cpu_register_physical_memory(base, 0x00001000, iomemtype);
+ s->base = base;
+ s->irq = irq;
+ pl061_reset(s);
+ if (out)
+ *out = s->out;
+
+ /* ??? Save/restore. */
+ return qemu_allocate_irqs(pl061_set_irq, s, 8);
+}
+
diff --git a/hw/pxa2xx.c b/hw/pxa2xx.c
index ebaff1320a..3c10839022 100644
--- a/hw/pxa2xx.c
+++ b/hw/pxa2xx.c
@@ -297,7 +297,7 @@ static void pxa2xx_clkpwr_write(void *opaque, int op2, int reg, int crm,
ARM_CPU_MODE_SVC | CPSR_A | CPSR_F | CPSR_I;
s->env->cp15.c1_sys = 0;
s->env->cp15.c1_coproc = 0;
- s->env->cp15.c2_base = 0;
+ s->env->cp15.c2_base0 = 0;
s->env->cp15.c3 = 0;
s->pm_regs[PSSR >> 2] |= 0x8; /* Set STS */
s->pm_regs[RCSR >> 2] |= 0x8; /* Set GPR */
@@ -2031,7 +2031,8 @@ struct pxa2xx_state_s *pxa270_init(unsigned int sdram_size,
fprintf(stderr, "Unable to find CPU definition\n");
exit(1);
}
- register_savevm("cpu", 0, 0, cpu_save, cpu_load, s->env);
+ register_savevm("cpu", 0, ARM_CPU_SAVE_VERSION, cpu_save, cpu_load,
+ s->env);
/* SDRAM & Internal Memory Storage */
cpu_register_physical_memory(PXA2XX_SDRAM_BASE,
@@ -2145,7 +2146,8 @@ struct pxa2xx_state_s *pxa255_init(unsigned int sdram_size,
fprintf(stderr, "Unable to find CPU definition\n");
exit(1);
}
- register_savevm("cpu", 0, 0, cpu_save, cpu_load, s->env);
+ register_savevm("cpu", 0, ARM_CPU_SAVE_VERSION, cpu_save, cpu_load,
+ s->env);
/* SDRAM & Internal Memory Storage */
cpu_register_physical_memory(PXA2XX_SDRAM_BASE, sdram_size,
diff --git a/hw/realview.c b/hw/realview.c
index f97d6e625a..e02deeed74 100644
--- a/hw/realview.c
+++ b/hw/realview.c
@@ -25,13 +25,32 @@ static void realview_init(int ram_size, int vga_ram_size,
NICInfo *nd;
int n;
int done_smc = 0;
+ qemu_irq cpu_irq[4];
+ int ncpu;
if (!cpu_model)
cpu_model = "arm926";
- env = cpu_init(cpu_model);
- if (!env) {
- fprintf(stderr, "Unable to find CPU definition\n");
- exit(1);
+ /* FIXME: obey smp_cpus. */
+ if (strcmp(cpu_model, "arm11mpcore") == 0) {
+ ncpu = 4;
+ } else {
+ ncpu = 1;
+ }
+
+ for (n = 0; n < ncpu; n++) {
+ env = cpu_init(cpu_model);
+ if (!env) {
+ fprintf(stderr, "Unable to find CPU definition\n");
+ exit(1);
+ }
+ pic = arm_pic_init_cpu(env);
+ cpu_irq[n] = pic[ARM_PIC_CPU_IRQ];
+ if (n > 0) {
+ /* Set entry point for secondary CPUs. This assumes we're using
+ the init code from arm_boot.c. Real hardware resets all CPUs
+ the same. */
+ env->regs[15] = 0x80000000;
+ }
}
/* ??? RAM shoud repeat to fill physical memory space. */
@@ -39,18 +58,23 @@ static void realview_init(int ram_size, int vga_ram_size,
cpu_register_physical_memory(0, ram_size, IO_MEM_RAM);
arm_sysctl_init(0x10000000, 0xc1400400);
- pic = arm_pic_init_cpu(env);
- /* ??? The documentation says GIC1 is nFIQ and either GIC2 or GIC3
- is nIRQ (there are inconsistencies). However Linux 2.6.17 expects
- GIC1 to be nIRQ and ignores all the others, so do that for now. */
- pic = arm_gic_init(0x10040000, pic[ARM_PIC_CPU_IRQ]);
+
+ if (ncpu == 1) {
+ /* ??? The documentation says GIC1 is nFIQ and either GIC2 or GIC3
+ is nIRQ (there are inconsistencies). However Linux 2.6.17 expects
+ GIC1 to be nIRQ and ignores all the others, so do that for now. */
+ pic = realview_gic_init(0x10040000, cpu_irq[0]);
+ } else {
+ pic = mpcore_irq_init(cpu_irq);
+ }
+
pl050_init(0x10006000, pic[20], 0);
pl050_init(0x10007000, pic[21], 1);
- pl011_init(0x10009000, pic[12], serial_hds[0]);
- pl011_init(0x1000a000, pic[13], serial_hds[1]);
- pl011_init(0x1000b000, pic[14], serial_hds[2]);
- pl011_init(0x1000c000, pic[15], serial_hds[3]);
+ pl011_init(0x10009000, pic[12], serial_hds[0], PL011_ARM);
+ pl011_init(0x1000a000, pic[13], serial_hds[1], PL011_ARM);
+ pl011_init(0x1000b000, pic[14], serial_hds[2], PL011_ARM);
+ pl011_init(0x1000c000, pic[15], serial_hds[3], PL011_ARM);
/* DMA controller is optional, apparently. */
pl080_init(0x10030000, pic[24], 2);
@@ -114,10 +138,10 @@ static void realview_init(int ram_size, int vga_ram_size,
/* 0x10019000 PCI controller config. */
/* 0x10020000 CLCD. */
/* 0x10030000 DMA Controller. */
- /* 0x10040000 GIC1 (FIQ1). */
- /* 0x10050000 GIC2 (IRQ1). */
- /* 0x10060000 GIC3 (FIQ2). */
- /* 0x10070000 GIC4 (IRQ2). */
+ /* 0x10040000 GIC1. */
+ /* 0x10050000 GIC2. */
+ /* 0x10060000 GIC3. */
+ /* 0x10070000 GIC4. */
/* 0x10080000 SMC. */
/* 0x40000000 NOR flash. */
/* 0x44000000 DoC flash. */
@@ -137,8 +161,14 @@ static void realview_init(int ram_size, int vga_ram_size,
/* 0x68000000 PCI mem 1. */
/* 0x6c000000 PCI mem 2. */
- arm_load_kernel(env, ram_size, kernel_filename, kernel_cmdline,
+ arm_load_kernel(first_cpu, ram_size, kernel_filename, kernel_cmdline,
initrd_filename, 0x33b, 0x0);
+
+ /* ??? Hack to map an additional page of ram for the secondary CPU
+ startup code. I guess this works on real hardware because the
+ BootROM happens to be in ROM/flash or in memory that isn't clobbered
+ until after Linux boots the secondary CPUs. */
+ cpu_register_physical_memory(0x80000000, 0x1000, IO_MEM_RAM + ram_size);
}
QEMUMachine realview_machine = {
diff --git a/hw/realview_gic.c b/hw/realview_gic.c
new file mode 100644
index 0000000000..cbc961491c
--- /dev/null
+++ b/hw/realview_gic.c
@@ -0,0 +1,64 @@
+/*
+ * ARM RealView Emulation Baseboard Interrupt Controller
+ *
+ * Copyright (c) 2006-2007 CodeSourcery.
+ * Written by Paul Brook
+ *
+ * This code is licenced under the GPL.
+ */
+
+#include "vl.h"
+#include "arm_pic.h"
+
+#define GIC_NIRQ 96
+#define NCPU 1
+
+/* Only a single "CPU" interface is present. */
+static inline int
+gic_get_current_cpu(void)
+{
+ return 0;
+}
+
+#include "arm_gic.c"
+
+static uint32_t realview_gic_cpu_read(void *opaque, target_phys_addr_t offset)
+{
+ gic_state *s = (gic_state *)opaque;
+ offset -= s->base;
+ return gic_cpu_read(s, gic_get_current_cpu(), offset);
+}
+
+static void realview_gic_cpu_write(void *opaque, target_phys_addr_t offset,
+ uint32_t value)
+{
+ gic_state *s = (gic_state *)opaque;
+ offset -= s->base;
+ gic_cpu_write(s, gic_get_current_cpu(), offset, value);
+}
+
+static CPUReadMemoryFunc *realview_gic_cpu_readfn[] = {
+ realview_gic_cpu_read,
+ realview_gic_cpu_read,
+ realview_gic_cpu_read
+};
+
+static CPUWriteMemoryFunc *realview_gic_cpu_writefn[] = {
+ realview_gic_cpu_write,
+ realview_gic_cpu_write,
+ realview_gic_cpu_write
+};
+
+qemu_irq *realview_gic_init(uint32_t base, qemu_irq parent_irq)
+{
+ gic_state *s;
+ int iomemtype;
+
+ s = gic_init(base, &parent_irq);
+ if (!s)
+ return NULL;
+ iomemtype = cpu_register_io_memory(0, realview_gic_cpu_readfn,
+ realview_gic_cpu_writefn, s);
+ cpu_register_physical_memory(base, 0x00001000, iomemtype);
+ return s->in;
+}
diff --git a/hw/ssd0303.c b/hw/ssd0303.c
new file mode 100644
index 0000000000..138cfc7fa7
--- /dev/null
+++ b/hw/ssd0303.c
@@ -0,0 +1,273 @@
+/*
+ * SSD0303 OLED controller with OSRAM Pictiva 96x16 display.
+ *
+ * Copyright (c) 2006-2007 CodeSourcery.
+ * Written by Paul Brook
+ *
+ * This code is licenced under the GPL.
+ */
+
+/* The controller can support a variety of different displays, but we only
+ implement one. Most of the commends relating to brightness and geometry
+ setup are ignored. */
+#include "vl.h"
+
+//#define DEBUG_SSD0303 1
+
+#ifdef DEBUG_SSD0303
+#define DPRINTF(fmt, args...) \
+do { printf("ssd0303: " fmt , ##args); } while (0)
+#define BADF(fmt, args...) \
+do { fprintf(stderr, "ssd0303: error: " fmt , ##args); exit(1);} while (0)
+#else
+#define DPRINTF(fmt, args...) do {} while(0)
+#define BADF(fmt, args...) \
+do { fprintf(stderr, "ssd0303: error: " fmt , ##args);} while (0)
+#endif
+
+/* Scaling factor for pixels. */
+#define MAGNIFY 4
+
+enum ssd0303_mode
+{
+ SSD0303_IDLE,
+ SSD0303_DATA,
+ SSD0303_CMD
+};
+
+enum ssd0303_cmd {
+ SSD0303_CMD_NONE,
+ SSD0303_CMD_SKIP1
+};
+
+typedef struct {
+ i2c_slave i2c;
+ DisplayState *ds;
+ int row;
+ int col;
+ int start_line;
+ int mirror;
+ int flash;
+ int enabled;
+ int inverse;
+ int redraw;
+ enum ssd0303_mode mode;
+ enum ssd0303_cmd cmd_state;
+ uint8_t framebuffer[132*8];
+} ssd0303_state;
+
+static int ssd0303_recv(i2c_slave *i2c)
+{
+ BADF("Reads not implemented\n");
+ return -1;
+}
+
+static int ssd0303_send(i2c_slave *i2c, uint8_t data)
+{
+ ssd0303_state *s = (ssd0303_state *)i2c;
+ enum ssd0303_cmd old_cmd_state;
+ switch (s->mode) {
+ case SSD0303_IDLE:
+ DPRINTF("byte 0x%02x\n", data);
+ if (data == 0x80)
+ s->mode = SSD0303_CMD;
+ else if (data == 0x40)
+ s->mode = SSD0303_DATA;
+ else
+ BADF("Unexpected byte 0x%x\n", data);
+ break;
+ case SSD0303_DATA:
+ DPRINTF("data 0x%02x\n", data);
+ if (s->col < 132) {
+ s->framebuffer[s->col + s->row * 132] = data;
+ s->col++;
+ s->redraw = 1;
+ }
+ break;
+ case SSD0303_CMD:
+ old_cmd_state = s->cmd_state;
+ s->cmd_state = SSD0303_CMD_NONE;
+ switch (old_cmd_state) {
+ case SSD0303_CMD_NONE:
+ DPRINTF("cmd 0x%02x\n", data);
+ s->mode = SSD0303_IDLE;
+ switch (data) {
+ case 0x00 ... 0x0f: /* Set lower colum address. */
+ s->col = (s->col & 0xf0) | (data & 0xf);
+ break;
+ case 0x10 ... 0x20: /* Set higher column address. */
+ s->col = (s->col & 0x0f) | ((data & 0xf) << 4);
+ break;
+ case 0x40 ... 0x7f: /* Set start line. */
+ s->start_line = 0;
+ break;
+ case 0x81: /* Set contrast (Ignored). */
+ s->cmd_state = SSD0303_CMD_SKIP1;
+ break;
+ case 0xa0: /* Mirror off. */
+ s->mirror = 0;
+ break;
+ case 0xa1: /* Mirror off. */
+ s->mirror = 1;
+ break;
+ case 0xa4: /* Entire display off. */
+ s->flash = 0;
+ break;
+ case 0xa5: /* Entire display on. */
+ s->flash = 1;
+ break;
+ case 0xa6: /* Inverse off. */
+ s->inverse = 0;
+ break;
+ case 0xa7: /* Inverse on. */
+ s->inverse = 1;
+ break;
+ case 0xa8: /* Set multipled ratio (Ignored). */
+ s->cmd_state = SSD0303_CMD_SKIP1;
+ break;
+ case 0xad: /* DC-DC power control. */
+ s->cmd_state = SSD0303_CMD_SKIP1;
+ break;
+ case 0xae: /* Display off. */
+ s->enabled = 0;
+ break;
+ case 0xaf: /* Display on. */
+ s->enabled = 1;
+ break;
+ case 0xb0 ... 0xbf: /* Set Page address. */
+ s->row = data & 7;
+ break;
+ case 0xc0 ... 0xc8: /* Set COM output direction (Ignored). */
+ break;
+ case 0xd3: /* Set display offset (Ignored). */
+ s->cmd_state = SSD0303_CMD_SKIP1;
+ break;
+ case 0xd5: /* Set display clock (Ignored). */
+ s->cmd_state = SSD0303_CMD_SKIP1;
+ break;
+ case 0xd8: /* Set color and power mode (Ignored). */
+ s->cmd_state = SSD0303_CMD_SKIP1;
+ break;
+ case 0xd9: /* Set pre-charge period (Ignored). */
+ s->cmd_state = SSD0303_CMD_SKIP1;
+ break;
+ case 0xda: /* Set COM pin configuration (Ignored). */
+ s->cmd_state = SSD0303_CMD_SKIP1;
+ break;
+ case 0xdb: /* Set VCOM dselect level (Ignored). */
+ s->cmd_state = SSD0303_CMD_SKIP1;
+ break;
+ case 0xe3: /* no-op. */
+ break;
+ default:
+ BADF("Unknown command: 0x%x\n", data);
+ }
+ break;
+ case SSD0303_CMD_SKIP1:
+ DPRINTF("skip 0x%02x\n", data);
+ break;
+ }
+ break;
+ }
+ return 0;
+}
+
+static void ssd0303_event(i2c_slave *i2c, enum i2c_event event)
+{
+ ssd0303_state *s = (ssd0303_state *)i2c;
+ switch (event) {
+ case I2C_FINISH:
+ s->mode = SSD0303_IDLE;
+ break;
+ case I2C_START_RECV:
+ case I2C_START_SEND:
+ case I2C_NACK:
+ /* Nothing to do. */
+ break;
+ }
+}
+
+static void ssd0303_update_display(void *opaque)
+{
+ ssd0303_state *s = (ssd0303_state *)opaque;
+ uint8_t *dest;
+ uint8_t *src;
+ int x;
+ int y;
+ int line;
+ char *colors[2];
+ char colortab[MAGNIFY * 8];
+ int dest_width;
+ uint8_t mask;
+
+ if (s->redraw) {
+ switch (s->ds->depth) {
+ case 0:
+ return;
+ case 15:
+ dest_width = 2;
+ break;
+ case 16:
+ dest_width = 2;
+ break;
+ case 24:
+ dest_width = 3;
+ break;
+ case 32:
+ dest_width = 4;
+ break;
+ default:
+ BADF("Bad color depth\n");
+ return;
+ }
+ dest_width *= MAGNIFY;
+ memset(colortab, 0xff, dest_width);
+ memset(colortab + dest_width, 0, dest_width);
+ if (s->flash) {
+ colors[0] = colortab;
+ colors[1] = colortab;
+ } else if (s->inverse) {
+ colors[0] = colortab;
+ colors[1] = colortab + dest_width;
+ } else {
+ colors[0] = colortab + dest_width;
+ colors[1] = colortab;
+ }
+ dest = s->ds->data;
+ for (y = 0; y < 16; y++) {
+ line = (y + s->start_line) & 63;
+ src = s->framebuffer + 132 * (line >> 3) + 36;
+ mask = 1 << (line & 7);
+ for (x = 0; x < 96; x++) {
+ memcpy(dest, colors[(*src & mask) != 0], dest_width);
+ dest += dest_width;
+ src++;
+ }
+ for (x = 1; x < MAGNIFY; x++) {
+ memcpy(dest, dest - dest_width * 96, dest_width * 96);
+ dest += dest_width * 96;
+ }
+ }
+ }
+ dpy_update(s->ds, 0, 0, 96 * MAGNIFY, 16 * MAGNIFY);
+}
+
+static void ssd0303_invalidate_display(void * opaque)
+{
+ ssd0303_state *s = (ssd0303_state *)opaque;
+ s->redraw = 1;
+}
+
+void ssd0303_init(DisplayState *ds, i2c_bus *bus, int address)
+{
+ ssd0303_state *s;
+
+ s = (ssd0303_state *)i2c_slave_init(bus, address, sizeof(ssd0303_state));
+ s->ds = ds;
+ s->i2c.event = ssd0303_event;
+ s->i2c.recv = ssd0303_recv;
+ s->i2c.send = ssd0303_send;
+ graphic_console_init(ds, ssd0303_update_display, ssd0303_invalidate_display,
+ NULL, s);
+ dpy_resize(s->ds, 96 * MAGNIFY, 16 * MAGNIFY);
+}
diff --git a/hw/ssd0323.c b/hw/ssd0323.c
new file mode 100644
index 0000000000..67361bce2e
--- /dev/null
+++ b/hw/ssd0323.c
@@ -0,0 +1,267 @@
+/*
+ * SSD0323 OLED controller with OSRAM Pictiva 128x64 display.
+ *
+ * Copyright (c) 2006-2007 CodeSourcery.
+ * Written by Paul Brook
+ *
+ * This code is licenced under the GPL.
+ */
+
+/* The controller can support a variety of different displays, but we only
+ implement one. Most of the commends relating to brightness and geometry
+ setup are ignored. */
+#include "vl.h"
+
+//#define DEBUG_SSD0323 1
+
+#ifdef DEBUG_SSD0323
+#define DPRINTF(fmt, args...) \
+do { printf("ssd0323: " fmt , ##args); } while (0)
+#define BADF(fmt, args...) \
+do { fprintf(stderr, "ssd0323: error: " fmt , ##args); exit(1);} while (0)
+#else
+#define DPRINTF(fmt, args...) do {} while(0)
+#define BADF(fmt, args...) \
+do { fprintf(stderr, "ssd0323: error: " fmt , ##args);} while (0)
+#endif
+
+/* Scaling factor for pixels. */
+#define MAGNIFY 4
+
+enum ssd0323_mode
+{
+ SSD0323_CMD,
+ SSD0323_DATA
+};
+
+typedef struct {
+ DisplayState *ds;
+
+ int cmd_len;
+ int cmd;
+ int cmd_data[8];
+ int row;
+ int row_start;
+ int row_end;
+ int col;
+ int col_start;
+ int col_end;
+ int redraw;
+ enum ssd0323_mode mode;
+ uint8_t framebuffer[128 * 80 / 2];
+} ssd0323_state;
+
+int ssd0323_xfer_ssi(void *opaque, int data)
+{
+ ssd0323_state *s = (ssd0323_state *)opaque;
+ switch (s->mode) {
+ case SSD0323_DATA:
+ DPRINTF("data 0x%02x\n", data);
+ s->framebuffer[s->col + s->row * 64] = data;
+ s->col++;
+ if (s->col > s->col_end) {
+ s->row++;
+ s->col = s->col_start;
+ }
+ if (s->row > s->row_end) {
+ s->row = s->row_start;
+ }
+ s->redraw = 1;
+ break;
+ case SSD0323_CMD:
+ DPRINTF("cmd 0x%02x\n", data);
+ if (s->cmd_len == 0) {
+ s->cmd = data;
+ } else {
+ s->cmd_data[s->cmd_len - 1] = data;
+ }
+ s->cmd_len++;
+ switch (s->cmd) {
+#define DATA(x) if (s->cmd_len <= (x)) return 0
+ case 0x15: /* Set column. */
+ DATA(2);
+ s->col_start = s->cmd_data[0] % 64;
+ s->col_end = s->cmd_data[1] % 64;
+ break;
+ case 0x75: /* Set row. */
+ DATA(2);
+ s->row_start = s->cmd_data[0] % 80;
+ s->row_end = s->cmd_data[1] % 80;
+ break;
+ case 0x81: /* Set contrast */
+ DATA(1);
+ break;
+ case 0x84: case 0x85: case 0x86: /* Max current. */
+ DATA(0);
+ break;
+ case 0xa0: /* Set remapping. */
+ /* FIXME: Implement this. */
+ DATA(1);
+ break;
+ case 0xa1: /* Set display start line. */
+ case 0xa2: /* Set display offset. */
+ /* FIXME: Implement these. */
+ DATA(1);
+ break;
+ case 0xa4: /* Normal mode. */
+ case 0xa5: /* All on. */
+ case 0xa6: /* All off. */
+ case 0xa7: /* Inverse. */
+ /* FIXME: Implement these. */
+ DATA(0);
+ break;
+ case 0xa8: /* Set multiplex ratio. */
+ case 0xad: /* Set DC-DC converter. */
+ DATA(1);
+ /* Ignored. Don't care. */
+ break;
+ case 0xae: /* Display off. */
+ case 0xaf: /* Display on. */
+ DATA(0);
+ /* TODO: Implement power control. */
+ break;
+ case 0xb1: /* Set phase length. */
+ case 0xb2: /* Set row period. */
+ case 0xb3: /* Set clock rate. */
+ case 0xbc: /* Set precharge. */
+ case 0xbe: /* Set VCOMH. */
+ case 0xbf: /* Set segment low. */
+ DATA(1);
+ /* Ignored. Don't care. */
+ break;
+ case 0xb8: /* Set grey scale table. */
+ /* FIXME: Implement this. */
+ DATA(8);
+ break;
+ case 0xe3: /* NOP. */
+ DATA(0);
+ break;
+ default:
+ BADF("Unknown command: 0x%x\n", data);
+ }
+ s->cmd_len = 0;
+ return 0;
+ }
+ return 0;
+}
+
+static void ssd0323_update_display(void *opaque)
+{
+ ssd0323_state *s = (ssd0323_state *)opaque;
+ uint8_t *dest;
+ uint8_t *src;
+ int x;
+ int y;
+ int i;
+ int line;
+ char *colors[16];
+ char colortab[MAGNIFY * 64];
+ char *p;
+ int dest_width;
+
+ if (s->redraw) {
+ switch (s->ds->depth) {
+ case 0:
+ return;
+ case 15:
+ dest_width = 2;
+ break;
+ case 16:
+ dest_width = 2;
+ break;
+ case 24:
+ dest_width = 3;
+ break;
+ case 32:
+ dest_width = 4;
+ break;
+ default:
+ BADF("Bad color depth\n");
+ return;
+ }
+ p = colortab;
+ for (i = 0; i < 16; i++) {
+ int n;
+ colors[i] = p;
+ switch (s->ds->depth) {
+ case 15:
+ n = i * 2 + (i >> 3);
+ p[0] = n | (n << 5);
+ p[1] = (n << 2) | (n >> 3);
+ break;
+ case 16:
+ n = i * 2 + (i >> 3);
+ p[0] = n | (n << 6) | ((n << 1) & 0x20);
+ p[1] = (n << 3) | (n >> 2);
+ break;
+ case 24:
+ case 32:
+ n = (i << 4) | i;
+ p[0] = p[1] = p[2] = n;
+ break;
+ default:
+ BADF("Bad color depth\n");
+ return;
+ }
+ p += dest_width;
+ }
+ dest = s->ds->data;
+ for (y = 0; y < 64; y++) {
+ line = y;
+ src = s->framebuffer + 64 * line;
+ for (x = 0; x < 64; x++) {
+ int val;
+ val = *src >> 4;
+ for (i = 0; i < MAGNIFY; i++) {
+ memcpy(dest, colors[val], dest_width);
+ dest += dest_width;
+ }
+ val = *src & 0xf;
+ for (i = 0; i < MAGNIFY; i++) {
+ memcpy(dest, colors[val], dest_width);
+ dest += dest_width;
+ }
+ src++;
+ }
+ for (i = 1; i < MAGNIFY; i++) {
+ memcpy(dest, dest - dest_width * MAGNIFY * 128,
+ dest_width * 128 * MAGNIFY);
+ dest += dest_width * 128 * MAGNIFY;
+ }
+ }
+ }
+ dpy_update(s->ds, 0, 0, 128 * MAGNIFY, 64 * MAGNIFY);
+}
+
+static void ssd0323_invalidate_display(void * opaque)
+{
+ ssd0323_state *s = (ssd0323_state *)opaque;
+ s->redraw = 1;
+}
+
+/* Command/data input. */
+static void ssd0323_cd(void *opaque, int n, int level)
+{
+ ssd0323_state *s = (ssd0323_state *)opaque;
+ DPRINTF("%s mode\n", level ? "Data" : "Command");
+ s->mode = level ? SSD0323_DATA : SSD0323_CMD;
+}
+
+void *ssd0323_init(DisplayState *ds, qemu_irq *cmd_p)
+{
+ ssd0323_state *s;
+ qemu_irq *cmd;
+
+ s = (ssd0323_state *)qemu_mallocz(sizeof(ssd0323_state));
+ s->ds = ds;
+ graphic_console_init(ds, ssd0323_update_display, ssd0323_invalidate_display,
+ NULL, s);
+ dpy_resize(s->ds, 128 * MAGNIFY, 64 * MAGNIFY);
+ s->col_end = 63;
+ s->row_end = 79;
+
+ cmd = qemu_allocate_irqs(ssd0323_cd, s, 1);
+ *cmd_p = *cmd;
+
+ return s;
+}
diff --git a/hw/stellaris.c b/hw/stellaris.c
new file mode 100644
index 0000000000..62f2c03445
--- /dev/null
+++ b/hw/stellaris.c
@@ -0,0 +1,1101 @@
+/*
+ * Luminary Micro Stellaris preipherals
+ *
+ * Copyright (c) 2006 CodeSourcery.
+ * Written by Paul Brook
+ *
+ * This code is licenced under the GPL.
+ */
+
+#include "vl.h"
+#include "arm_pic.h"
+
+typedef const struct {
+ const char *name;
+ uint32_t did0;
+ uint32_t did1;
+ uint32_t dc0;
+ uint32_t dc1;
+ uint32_t dc2;
+ uint32_t dc3;
+ uint32_t dc4;
+ enum {OLED_I2C, OLED_SSI} oled;
+} stellaris_board_info;
+
+/* General purpose timer module. */
+
+/* Multiplication factor to convert from GPTM timer ticks to qemu timer
+ ticks. */
+static int stellaris_clock_scale;
+
+typedef struct gptm_state {
+ uint32_t config;
+ uint32_t mode[2];
+ uint32_t control;
+ uint32_t state;
+ uint32_t mask;
+ uint32_t load[2];
+ uint32_t match[2];
+ uint32_t prescale[2];
+ uint32_t match_prescale[2];
+ uint32_t rtc;
+ int64_t tick[2];
+ struct gptm_state *opaque[2];
+ uint32_t base;
+ QEMUTimer *timer[2];
+ /* The timers have an alternate output used to trigger the ADC. */
+ qemu_irq trigger;
+ qemu_irq irq;
+} gptm_state;
+
+static void gptm_update_irq(gptm_state *s)
+{
+ int level;
+ level = (s->state & s->mask) != 0;
+ qemu_set_irq(s->irq, level);
+}
+
+static void gptm_stop(gptm_state *s, int n)
+{
+ qemu_del_timer(s->timer[n]);
+}
+
+static void gptm_reload(gptm_state *s, int n, int reset)
+{
+ int64_t tick;
+ if (reset)
+ tick = qemu_get_clock(vm_clock);
+ else
+ tick = s->tick[n];
+
+ if (s->config == 0) {
+ /* 32-bit CountDown. */
+ uint32_t count;
+ count = s->load[0] | (s->load[1] << 16);
+ tick += (int64_t)count * stellaris_clock_scale;
+ } else if (s->config == 1) {
+ /* 32-bit RTC. 1Hz tick. */
+ tick += ticks_per_sec;
+ } else if (s->mode[n] == 0xa) {
+ /* PWM mode. Not implemented. */
+ } else {
+ cpu_abort(cpu_single_env, "TODO: 16-bit timer mode 0x%x\n",
+ s->mode[n]);
+ }
+ s->tick[n] = tick;
+ qemu_mod_timer(s->timer[n], tick);
+}
+
+static void gptm_tick(void *opaque)
+{
+ gptm_state **p = (gptm_state **)opaque;
+ gptm_state *s;
+ int n;
+
+ s = *p;
+ n = p - s->opaque;
+ if (s->config == 0) {
+ s->state |= 1;
+ if ((s->control & 0x20)) {
+ /* Output trigger. */
+ qemu_irq_raise(s->trigger);
+ qemu_irq_lower(s->trigger);
+ }
+ if (s->mode[0] & 1) {
+ /* One-shot. */
+ s->control &= ~1;
+ } else {
+ /* Periodic. */
+ gptm_reload(s, 0, 0);
+ }
+ } else if (s->config == 1) {
+ /* RTC. */
+ uint32_t match;
+ s->rtc++;
+ match = s->match[0] | (s->match[1] << 16);
+ if (s->rtc > match)
+ s->rtc = 0;
+ if (s->rtc == 0) {
+ s->state |= 8;
+ }
+ gptm_reload(s, 0, 0);
+ } else if (s->mode[n] == 0xa) {
+ /* PWM mode. Not implemented. */
+ } else {
+ cpu_abort(cpu_single_env, "TODO: 16-bit timer mode 0x%x\n",
+ s->mode[n]);
+ }
+ gptm_update_irq(s);
+}
+
+static uint32_t gptm_read(void *opaque, target_phys_addr_t offset)
+{
+ gptm_state *s = (gptm_state *)opaque;
+
+ offset -= s->base;
+ switch (offset) {
+ case 0x00: /* CFG */
+ return s->config;
+ case 0x04: /* TAMR */
+ return s->mode[0];
+ case 0x08: /* TBMR */
+ return s->mode[1];
+ case 0x0c: /* CTL */
+ return s->control;
+ case 0x18: /* IMR */
+ return s->mask;
+ case 0x1c: /* RIS */
+ return s->state;
+ case 0x20: /* MIS */
+ return s->state & s->mask;
+ case 0x24: /* CR */
+ return 0;
+ case 0x28: /* TAILR */
+ return s->load[0] | ((s->config < 4) ? (s->load[1] << 16) : 0);
+ case 0x2c: /* TBILR */
+ return s->load[1];
+ case 0x30: /* TAMARCHR */
+ return s->match[0] | ((s->config < 4) ? (s->match[1] << 16) : 0);
+ case 0x34: /* TBMATCHR */
+ return s->match[1];
+ case 0x38: /* TAPR */
+ return s->prescale[0];
+ case 0x3c: /* TBPR */
+ return s->prescale[1];
+ case 0x40: /* TAPMR */
+ return s->match_prescale[0];
+ case 0x44: /* TBPMR */
+ return s->match_prescale[1];
+ case 0x48: /* TAR */
+ if (s->control == 1)
+ return s->rtc;
+ case 0x4c: /* TBR */
+ cpu_abort(cpu_single_env, "TODO: Timer value read\n");
+ default:
+ cpu_abort(cpu_single_env, "gptm_read: Bad offset 0x%x\n", (int)offset);
+ return 0;
+ }
+}
+
+static void gptm_write(void *opaque, target_phys_addr_t offset, uint32_t value)
+{
+ gptm_state *s = (gptm_state *)opaque;
+ uint32_t oldval;
+
+ offset -= s->base;
+ /* The timers should be disabled before changing the configuration.
+ We take advantage of this and defer everything until the timer
+ is enabled. */
+ switch (offset) {
+ case 0x00: /* CFG */
+ s->config = value;
+ break;
+ case 0x04: /* TAMR */
+ s->mode[0] = value;
+ break;
+ case 0x08: /* TBMR */
+ s->mode[1] = value;
+ break;
+ case 0x0c: /* CTL */
+ oldval = s->control;
+ s->control = value;
+ /* TODO: Implement pause. */
+ if ((oldval ^ value) & 1) {
+ if (value & 1) {
+ gptm_reload(s, 0, 1);
+ } else {
+ gptm_stop(s, 0);
+ }
+ }
+ if (((oldval ^ value) & 0x100) && s->config >= 4) {
+ if (value & 0x100) {
+ gptm_reload(s, 1, 1);
+ } else {
+ gptm_stop(s, 1);
+ }
+ }
+ break;
+ case 0x18: /* IMR */
+ s->mask = value & 0x77;
+ gptm_update_irq(s);
+ break;
+ case 0x24: /* CR */
+ s->state &= ~value;
+ break;
+ case 0x28: /* TAILR */
+ s->load[0] = value & 0xffff;
+ if (s->config < 4) {
+ s->load[1] = value >> 16;
+ }
+ break;
+ case 0x2c: /* TBILR */
+ s->load[1] = value & 0xffff;
+ break;
+ case 0x30: /* TAMARCHR */
+ s->match[0] = value & 0xffff;
+ if (s->config < 4) {
+ s->match[1] = value >> 16;
+ }
+ break;
+ case 0x34: /* TBMATCHR */
+ s->match[1] = value >> 16;
+ break;
+ case 0x38: /* TAPR */
+ s->prescale[0] = value;
+ break;
+ case 0x3c: /* TBPR */
+ s->prescale[1] = value;
+ break;
+ case 0x40: /* TAPMR */
+ s->match_prescale[0] = value;
+ break;
+ case 0x44: /* TBPMR */
+ s->match_prescale[0] = value;
+ break;
+ default:
+ cpu_abort(cpu_single_env, "gptm_write: Bad offset 0x%x\n", (int)offset);
+ }
+ gptm_update_irq(s);
+}
+
+static CPUReadMemoryFunc *gptm_readfn[] = {
+ gptm_read,
+ gptm_read,
+ gptm_read
+};
+
+static CPUWriteMemoryFunc *gptm_writefn[] = {
+ gptm_write,
+ gptm_write,
+ gptm_write
+};
+
+static void stellaris_gptm_init(uint32_t base, qemu_irq irq, qemu_irq trigger)
+{
+ int iomemtype;
+ gptm_state *s;
+
+ s = (gptm_state *)qemu_mallocz(sizeof(gptm_state));
+ s->base = base;
+ s->irq = irq;
+ s->trigger = trigger;
+ s->opaque[0] = s->opaque[1] = s;
+
+ iomemtype = cpu_register_io_memory(0, gptm_readfn,
+ gptm_writefn, s);
+ cpu_register_physical_memory(base, 0x00001000, iomemtype);
+ s->timer[0] = qemu_new_timer(vm_clock, gptm_tick, &s->opaque[0]);
+ s->timer[1] = qemu_new_timer(vm_clock, gptm_tick, &s->opaque[1]);
+ /* ??? Save/restore. */
+}
+
+
+/* System controller. */
+
+typedef struct {
+ uint32_t base;
+ uint32_t pborctl;
+ uint32_t ldopctl;
+ uint32_t int_status;
+ uint32_t int_mask;
+ uint32_t resc;
+ uint32_t rcc;
+ uint32_t rcgc[3];
+ uint32_t scgc[3];
+ uint32_t dcgc[3];
+ uint32_t clkvclr;
+ uint32_t ldoarst;
+ qemu_irq irq;
+ stellaris_board_info *board;
+} ssys_state;
+
+static void ssys_update(ssys_state *s)
+{
+ qemu_set_irq(s->irq, (s->int_status & s->int_mask) != 0);
+}
+
+static uint32_t pllcfg_sandstorm[16] = {
+ 0x31c0, /* 1 Mhz */
+ 0x1ae0, /* 1.8432 Mhz */
+ 0x18c0, /* 2 Mhz */
+ 0xd573, /* 2.4576 Mhz */
+ 0x37a6, /* 3.57954 Mhz */
+ 0x1ae2, /* 3.6864 Mhz */
+ 0x0c40, /* 4 Mhz */
+ 0x98bc, /* 4.906 Mhz */
+ 0x935b, /* 4.9152 Mhz */
+ 0x09c0, /* 5 Mhz */
+ 0x4dee, /* 5.12 Mhz */
+ 0x0c41, /* 6 Mhz */
+ 0x75db, /* 6.144 Mhz */
+ 0x1ae6, /* 7.3728 Mhz */
+ 0x0600, /* 8 Mhz */
+ 0x585b /* 8.192 Mhz */
+};
+
+static uint32_t pllcfg_fury[16] = {
+ 0x3200, /* 1 Mhz */
+ 0x1b20, /* 1.8432 Mhz */
+ 0x1900, /* 2 Mhz */
+ 0xf42b, /* 2.4576 Mhz */
+ 0x37e3, /* 3.57954 Mhz */
+ 0x1b21, /* 3.6864 Mhz */
+ 0x0c80, /* 4 Mhz */
+ 0x98ee, /* 4.906 Mhz */
+ 0xd5b4, /* 4.9152 Mhz */
+ 0x0a00, /* 5 Mhz */
+ 0x4e27, /* 5.12 Mhz */
+ 0x1902, /* 6 Mhz */
+ 0xec1c, /* 6.144 Mhz */
+ 0x1b23, /* 7.3728 Mhz */
+ 0x0640, /* 8 Mhz */
+ 0xb11c /* 8.192 Mhz */
+};
+
+static uint32_t ssys_read(void *opaque, target_phys_addr_t offset)
+{
+ ssys_state *s = (ssys_state *)opaque;
+
+ offset -= s->base;
+ switch (offset) {
+ case 0x000: /* DID0 */
+ return s->board->did0;
+ case 0x004: /* DID1 */
+ return s->board->did1;
+ case 0x008: /* DC0 */
+ return s->board->dc0;
+ case 0x010: /* DC1 */
+ return s->board->dc1;
+ case 0x014: /* DC2 */
+ return s->board->dc2;
+ case 0x018: /* DC3 */
+ return s->board->dc3;
+ case 0x01c: /* DC4 */
+ return s->board->dc4;
+ case 0x030: /* PBORCTL */
+ return s->pborctl;
+ case 0x034: /* LDOPCTL */
+ return s->ldopctl;
+ case 0x040: /* SRCR0 */
+ return 0;
+ case 0x044: /* SRCR1 */
+ return 0;
+ case 0x048: /* SRCR2 */
+ return 0;
+ case 0x050: /* RIS */
+ return s->int_status;
+ case 0x054: /* IMC */
+ return s->int_mask;
+ case 0x058: /* MISC */
+ return s->int_status & s->int_mask;
+ case 0x05c: /* RESC */
+ return s->resc;
+ case 0x060: /* RCC */
+ return s->rcc;
+ case 0x064: /* PLLCFG */
+ {
+ int xtal;
+ xtal = (s->rcc >> 6) & 0xf;
+ if (s->board->did0 & (1 << 16)) {
+ return pllcfg_fury[xtal];
+ } else {
+ return pllcfg_sandstorm[xtal];
+ }
+ }
+ case 0x100: /* RCGC0 */
+ return s->rcgc[0];
+ case 0x104: /* RCGC1 */
+ return s->rcgc[1];
+ case 0x108: /* RCGC2 */
+ return s->rcgc[2];
+ case 0x110: /* SCGC0 */
+ return s->scgc[0];
+ case 0x114: /* SCGC1 */
+ return s->scgc[1];
+ case 0x118: /* SCGC2 */
+ return s->scgc[2];
+ case 0x120: /* DCGC0 */
+ return s->dcgc[0];
+ case 0x124: /* DCGC1 */
+ return s->dcgc[1];
+ case 0x128: /* DCGC2 */
+ return s->dcgc[2];
+ case 0x150: /* CLKVCLR */
+ return s->clkvclr;
+ case 0x160: /* LDOARST */
+ return s->ldoarst;
+ default:
+ cpu_abort(cpu_single_env, "gptm_read: Bad offset 0x%x\n", (int)offset);
+ return 0;
+ }
+}
+
+static void ssys_write(void *opaque, target_phys_addr_t offset, uint32_t value)
+{
+ ssys_state *s = (ssys_state *)opaque;
+
+ offset -= s->base;
+ switch (offset) {
+ case 0x030: /* PBORCTL */
+ s->pborctl = value & 0xffff;
+ break;
+ case 0x034: /* LDOPCTL */
+ s->ldopctl = value & 0x1f;
+ break;
+ case 0x040: /* SRCR0 */
+ case 0x044: /* SRCR1 */
+ case 0x048: /* SRCR2 */
+ fprintf(stderr, "Peripheral reset not implemented\n");
+ break;
+ case 0x054: /* IMC */
+ s->int_mask = value & 0x7f;
+ break;
+ case 0x058: /* MISC */
+ s->int_status &= ~value;
+ break;
+ case 0x05c: /* RESC */
+ s->resc = value & 0x3f;
+ break;
+ case 0x060: /* RCC */
+ if ((s->rcc & (1 << 13)) != 0 && (value & (1 << 13)) == 0) {
+ /* PLL enable. */
+ s->int_status |= (1 << 6);
+ }
+ s->rcc = value;
+ stellaris_clock_scale = 5 * (((s->rcc >> 23) & 0xf) + 1);
+ break;
+ case 0x100: /* RCGC0 */
+ s->rcgc[0] = value;
+ break;
+ case 0x104: /* RCGC1 */
+ s->rcgc[1] = value;
+ break;
+ case 0x108: /* RCGC2 */
+ s->rcgc[2] = value;
+ break;
+ case 0x110: /* SCGC0 */
+ s->scgc[0] = value;
+ break;
+ case 0x114: /* SCGC1 */
+ s->scgc[1] = value;
+ break;
+ case 0x118: /* SCGC2 */
+ s->scgc[2] = value;
+ break;
+ case 0x120: /* DCGC0 */
+ s->dcgc[0] = value;
+ break;
+ case 0x124: /* DCGC1 */
+ s->dcgc[1] = value;
+ break;
+ case 0x128: /* DCGC2 */
+ s->dcgc[2] = value;
+ break;
+ case 0x150: /* CLKVCLR */
+ s->clkvclr = value;
+ break;
+ case 0x160: /* LDOARST */
+ s->ldoarst = value;
+ break;
+ default:
+ cpu_abort(cpu_single_env, "gptm_write: Bad offset 0x%x\n", (int)offset);
+ }
+ ssys_update(s);
+}
+
+static CPUReadMemoryFunc *ssys_readfn[] = {
+ ssys_read,
+ ssys_read,
+ ssys_read
+};
+
+static CPUWriteMemoryFunc *ssys_writefn[] = {
+ ssys_write,
+ ssys_write,
+ ssys_write
+};
+
+void ssys_reset(void *opaque)
+{
+ ssys_state *s = (ssys_state *)opaque;
+
+ s->pborctl = 0x7ffd;
+ s->rcc = 0x078e3ac0;
+ s->rcgc[0] = 1;
+ s->scgc[0] = 1;
+ s->dcgc[0] = 1;
+}
+
+static void stellaris_sys_init(uint32_t base, qemu_irq irq,
+ stellaris_board_info * board)
+{
+ int iomemtype;
+ ssys_state *s;
+
+ s = (ssys_state *)qemu_mallocz(sizeof(ssys_state));
+ s->base = base;
+ s->irq = irq;
+ s->board = board;
+
+ iomemtype = cpu_register_io_memory(0, ssys_readfn,
+ ssys_writefn, s);
+ cpu_register_physical_memory(base, 0x00001000, iomemtype);
+ ssys_reset(s);
+ /* ??? Save/restore. */
+}
+
+
+/* I2C controller. */
+
+typedef struct {
+ i2c_bus *bus;
+ qemu_irq irq;
+ uint32_t base;
+ uint32_t msa;
+ uint32_t mcs;
+ uint32_t mdr;
+ uint32_t mtpr;
+ uint32_t mimr;
+ uint32_t mris;
+ uint32_t mcr;
+} stellaris_i2c_state;
+
+#define STELLARIS_I2C_MCS_BUSY 0x01
+#define STELLARIS_I2C_MCS_ERROR 0x02
+#define STELLARIS_I2C_MCS_ADRACK 0x04
+#define STELLARIS_I2C_MCS_DATACK 0x08
+#define STELLARIS_I2C_MCS_ARBLST 0x10
+#define STELLARIS_I2C_MCS_IDLE 0x20
+#define STELLARIS_I2C_MCS_BUSBSY 0x40
+
+static uint32_t stellaris_i2c_read(void *opaque, target_phys_addr_t offset)
+{
+ stellaris_i2c_state *s = (stellaris_i2c_state *)opaque;
+
+ offset -= s->base;
+ switch (offset) {
+ case 0x00: /* MSA */
+ return s->msa;
+ case 0x04: /* MCS */
+ /* We don't emulate timing, so the controller is never busy. */
+ return s->mcs | STELLARIS_I2C_MCS_IDLE;
+ case 0x08: /* MDR */
+ return s->mdr;
+ case 0x0c: /* MTPR */
+ return s->mtpr;
+ case 0x10: /* MIMR */
+ return s->mimr;
+ case 0x14: /* MRIS */
+ return s->mris;
+ case 0x18: /* MMIS */
+ return s->mris & s->mimr;
+ case 0x20: /* MCR */
+ return s->mcr;
+ default:
+ cpu_abort(cpu_single_env, "strllaris_i2c_read: Bad offset 0x%x\n",
+ (int)offset);
+ return 0;
+ }
+}
+
+static void stellaris_i2c_update(stellaris_i2c_state *s)
+{
+ int level;
+
+ level = (s->mris & s->mimr) != 0;
+ qemu_set_irq(s->irq, level);
+}
+
+static void stellaris_i2c_write(void *opaque, target_phys_addr_t offset,
+ uint32_t value)
+{
+ stellaris_i2c_state *s = (stellaris_i2c_state *)opaque;
+
+ offset -= s->base;
+ switch (offset) {
+ case 0x00: /* MSA */
+ s->msa = value & 0xff;
+ break;
+ case 0x04: /* MCS */
+ if ((s->mcr & 0x10) == 0) {
+ /* Disabled. Do nothing. */
+ break;
+ }
+ /* Grab the bus if this is starting a transfer. */
+ if ((value & 2) && (s->mcs & STELLARIS_I2C_MCS_BUSBSY) == 0) {
+ if (i2c_start_transfer(s->bus, s->msa >> 1, s->msa & 1)) {
+ s->mcs |= STELLARIS_I2C_MCS_ARBLST;
+ } else {
+ s->mcs &= ~STELLARIS_I2C_MCS_ARBLST;
+ s->mcs |= STELLARIS_I2C_MCS_BUSBSY;
+ }
+ }
+ /* If we don't have the bus then indicate an error. */
+ if (!i2c_bus_busy(s->bus)
+ || (s->mcs & STELLARIS_I2C_MCS_BUSBSY) == 0) {
+ s->mcs |= STELLARIS_I2C_MCS_ERROR;
+ break;
+ }
+ s->mcs &= ~STELLARIS_I2C_MCS_ERROR;
+ if (value & 1) {
+ /* Transfer a byte. */
+ /* TODO: Handle errors. */
+ if (s->msa & 1) {
+ /* Recv */
+ s->mdr = i2c_recv(s->bus) & 0xff;
+ } else {
+ /* Send */
+ i2c_send(s->bus, s->mdr);
+ }
+ /* Raise an interrupt. */
+ s->mris |= 1;
+ }
+ if (value & 4) {
+ /* Finish transfer. */
+ i2c_end_transfer(s->bus);
+ s->mcs &= ~STELLARIS_I2C_MCS_BUSBSY;
+ }
+ break;
+ case 0x08: /* MDR */
+ s->mdr = value & 0xff;
+ break;
+ case 0x0c: /* MTPR */
+ s->mtpr = value & 0xff;
+ break;
+ case 0x10: /* MIMR */
+ s->mimr = 1;
+ break;
+ case 0x1c: /* MICR */
+ s->mris &= ~value;
+ break;
+ case 0x20: /* MCR */
+ if (value & 1)
+ cpu_abort(cpu_single_env,
+ "stellaris_i2c_write: Loopback not implemented\n");
+ if (value & 0x20)
+ cpu_abort(cpu_single_env,
+ "stellaris_i2c_write: Slave mode not implemented\n");
+ s->mcr = value & 0x31;
+ break;
+ default:
+ cpu_abort(cpu_single_env, "stellaris_i2c_write: Bad offset 0x%x\n",
+ (int)offset);
+ }
+ stellaris_i2c_update(s);
+}
+
+static void stellaris_i2c_reset(stellaris_i2c_state *s)
+{
+ if (s->mcs & STELLARIS_I2C_MCS_BUSBSY)
+ i2c_end_transfer(s->bus);
+
+ s->msa = 0;
+ s->mcs = 0;
+ s->mdr = 0;
+ s->mtpr = 1;
+ s->mimr = 0;
+ s->mris = 0;
+ s->mcr = 0;
+ stellaris_i2c_update(s);
+}
+
+static CPUReadMemoryFunc *stellaris_i2c_readfn[] = {
+ stellaris_i2c_read,
+ stellaris_i2c_read,
+ stellaris_i2c_read
+};
+
+static CPUWriteMemoryFunc *stellaris_i2c_writefn[] = {
+ stellaris_i2c_write,
+ stellaris_i2c_write,
+ stellaris_i2c_write
+};
+
+static void stellaris_i2c_init(uint32_t base, qemu_irq irq, i2c_bus *bus)
+{
+ stellaris_i2c_state *s;
+ int iomemtype;
+
+ s = (stellaris_i2c_state *)qemu_mallocz(sizeof(stellaris_i2c_state));
+ s->base = base;
+ s->irq = irq;
+ s->bus = bus;
+
+ iomemtype = cpu_register_io_memory(0, stellaris_i2c_readfn,
+ stellaris_i2c_writefn, s);
+ cpu_register_physical_memory(base, 0x00001000, iomemtype);
+ /* ??? For now we only implement the master interface. */
+ stellaris_i2c_reset(s);
+}
+
+/* Analogue to Digital Converter. This is only partially implemented,
+ enough for applications that use a combined ADC and timer tick. */
+
+#define STELLARIS_ADC_EM_CONTROLLER 0
+#define STELLARIS_ADC_EM_COMP 1
+#define STELLARIS_ADC_EM_EXTERNAL 4
+#define STELLARIS_ADC_EM_TIMER 5
+#define STELLARIS_ADC_EM_PWM0 6
+#define STELLARIS_ADC_EM_PWM1 7
+#define STELLARIS_ADC_EM_PWM2 8
+
+#define STELLARIS_ADC_FIFO_EMPTY 0x0100
+#define STELLARIS_ADC_FIFO_FULL 0x1000
+
+typedef struct
+{
+ uint32_t base;
+ uint32_t actss;
+ uint32_t ris;
+ uint32_t im;
+ uint32_t emux;
+ uint32_t ostat;
+ uint32_t ustat;
+ uint32_t sspri;
+ uint32_t sac;
+ struct {
+ uint32_t state;
+ uint32_t data[16];
+ } fifo[4];
+ uint32_t ssmux[4];
+ uint32_t ssctl[4];
+ qemu_irq irq;
+} stellaris_adc_state;
+
+static uint32_t stellaris_adc_fifo_read(stellaris_adc_state *s, int n)
+{
+ int tail;
+
+ tail = s->fifo[n].state & 0xf;
+ if (s->fifo[n].state & STELLARIS_ADC_FIFO_EMPTY) {
+ s->ustat |= 1 << n;
+ } else {
+ s->fifo[n].state = (s->fifo[n].state & ~0xf) | ((tail + 1) & 0xf);
+ s->fifo[n].state &= ~STELLARIS_ADC_FIFO_FULL;
+ if (tail + 1 == ((s->fifo[n].state >> 4) & 0xf))
+ s->fifo[n].state |= STELLARIS_ADC_FIFO_EMPTY;
+ }
+ return s->fifo[n].data[tail];
+}
+
+static void stellaris_adc_fifo_write(stellaris_adc_state *s, int n,
+ uint32_t value)
+{
+ int head;
+
+ head = (s->fifo[n].state >> 4) & 0xf;
+ if (s->fifo[n].state & STELLARIS_ADC_FIFO_FULL) {
+ s->ostat |= 1 << n;
+ return;
+ }
+ s->fifo[n].data[head] = value;
+ head = (head + 1) & 0xf;
+ s->fifo[n].state &= ~STELLARIS_ADC_FIFO_EMPTY;
+ s->fifo[n].state = (s->fifo[n].state & ~0xf0) | (head << 4);
+ if ((s->fifo[n].state & 0xf) == head)
+ s->fifo[n].state |= STELLARIS_ADC_FIFO_FULL;
+}
+
+static void stellaris_adc_update(stellaris_adc_state *s)
+{
+ int level;
+
+ level = (s->ris & s->im) != 0;
+ qemu_set_irq(s->irq, level);
+}
+
+static void stellaris_adc_trigger(void *opaque, int irq, int level)
+{
+ stellaris_adc_state *s = (stellaris_adc_state *)opaque;
+ /* Some applications use the ADC as a random number source, so introduce
+ some variation into the signal. */
+ static uint32_t noise = 0;
+
+ if ((s->actss & 1) == 0) {
+ return;
+ }
+
+ noise = noise * 314159 + 1;
+ /* ??? actual inputs not implemented. Return an arbitrary value. */
+ stellaris_adc_fifo_write(s, 0, 0x200 + ((noise >> 16) & 7));
+ s->ris |= 1;
+ stellaris_adc_update(s);
+}
+
+static void stellaris_adc_reset(stellaris_adc_state *s)
+{
+ int n;
+
+ for (n = 0; n < 4; n++) {
+ s->ssmux[n] = 0;
+ s->ssctl[n] = 0;
+ s->fifo[n].state = STELLARIS_ADC_FIFO_EMPTY;
+ }
+}
+
+static uint32_t stellaris_adc_read(void *opaque, target_phys_addr_t offset)
+{
+ stellaris_adc_state *s = (stellaris_adc_state *)opaque;
+
+ /* TODO: Implement this. */
+ offset -= s->base;
+ if (offset >= 0x40 && offset < 0xc0) {
+ int n;
+ n = (offset - 0x40) >> 5;
+ switch (offset & 0x1f) {
+ case 0x00: /* SSMUX */
+ return s->ssmux[n];
+ case 0x04: /* SSCTL */
+ return s->ssctl[n];
+ case 0x08: /* SSFIFO */
+ return stellaris_adc_fifo_read(s, n);
+ case 0x0c: /* SSFSTAT */
+ return s->fifo[n].state;
+ default:
+ break;
+ }
+ }
+ switch (offset) {
+ case 0x00: /* ACTSS */
+ return s->actss;
+ case 0x04: /* RIS */
+ return s->ris;
+ case 0x08: /* IM */
+ return s->im;
+ case 0x0c: /* ISC */
+ return s->ris & s->im;
+ case 0x10: /* OSTAT */
+ return s->ostat;
+ case 0x14: /* EMUX */
+ return s->emux;
+ case 0x18: /* USTAT */
+ return s->ustat;
+ case 0x20: /* SSPRI */
+ return s->sspri;
+ case 0x30: /* SAC */
+ return s->sac;
+ default:
+ cpu_abort(cpu_single_env, "strllaris_adc_read: Bad offset 0x%x\n",
+ (int)offset);
+ return 0;
+ }
+}
+
+static void stellaris_adc_write(void *opaque, target_phys_addr_t offset,
+ uint32_t value)
+{
+ stellaris_adc_state *s = (stellaris_adc_state *)opaque;
+
+ /* TODO: Implement this. */
+ offset -= s->base;
+ if (offset >= 0x40 && offset < 0xc0) {
+ int n;
+ n = (offset - 0x40) >> 5;
+ switch (offset & 0x1f) {
+ case 0x00: /* SSMUX */
+ s->ssmux[n] = value & 0x33333333;
+ return;
+ case 0x04: /* SSCTL */
+ if (value != 6) {
+ cpu_abort(cpu_single_env, "ADC: Unimplemented sequence %x\n",
+ value);
+ }
+ s->ssctl[n] = value;
+ return;
+ default:
+ break;
+ }
+ }
+ switch (offset) {
+ case 0x00: /* ACTSS */
+ s->actss = value & 0xf;
+ if (value & 0xe) {
+ cpu_abort(cpu_single_env,
+ "Not implemented: ADC sequencers 1-3\n");
+ }
+ break;
+ case 0x08: /* IM */
+ s->im = value;
+ break;
+ case 0x0c: /* ISC */
+ s->ris &= ~value;
+ break;
+ case 0x10: /* OSTAT */
+ s->ostat &= ~value;
+ break;
+ case 0x14: /* EMUX */
+ s->emux = value;
+ break;
+ case 0x18: /* USTAT */
+ s->ustat &= ~value;
+ break;
+ case 0x20: /* SSPRI */
+ s->sspri = value;
+ break;
+ case 0x28: /* PSSI */
+ cpu_abort(cpu_single_env, "Not implemented: ADC sample initiate\n");
+ break;
+ case 0x30: /* SAC */
+ s->sac = value;
+ break;
+ default:
+ cpu_abort(cpu_single_env, "stellaris_adc_write: Bad offset 0x%x\n",
+ (int)offset);
+ }
+ stellaris_adc_update(s);
+}
+
+static CPUReadMemoryFunc *stellaris_adc_readfn[] = {
+ stellaris_adc_read,
+ stellaris_adc_read,
+ stellaris_adc_read
+};
+
+static CPUWriteMemoryFunc *stellaris_adc_writefn[] = {
+ stellaris_adc_write,
+ stellaris_adc_write,
+ stellaris_adc_write
+};
+
+static qemu_irq stellaris_adc_init(uint32_t base, qemu_irq irq)
+{
+ stellaris_adc_state *s;
+ int iomemtype;
+ qemu_irq *qi;
+
+ s = (stellaris_adc_state *)qemu_mallocz(sizeof(stellaris_adc_state));
+ s->base = base;
+ s->irq = irq;
+
+ iomemtype = cpu_register_io_memory(0, stellaris_adc_readfn,
+ stellaris_adc_writefn, s);
+ cpu_register_physical_memory(base, 0x00001000, iomemtype);
+ stellaris_adc_reset(s);
+ qi = qemu_allocate_irqs(stellaris_adc_trigger, s, 1);
+ return qi[0];
+}
+
+/* Board init. */
+static stellaris_board_info stellaris_boards[] = {
+ { "LM3S811EVB",
+ 0,
+ 0x0032000e,
+ 0x001f001f, /* dc0 */
+ 0x001132bf,
+ 0x01071013,
+ 0x3f0f01ff,
+ 0x0000001f,
+ OLED_I2C
+ },
+ { "LM3S6965EVB",
+ 0x10010002,
+ 0x1073402e,
+ 0x00ff007f, /* dc0 */
+ 0x001133ff,
+ 0x030f5317,
+ 0x0f0f87ff,
+ 0x5000007f,
+ OLED_SSI
+ }
+};
+
+static void stellaris_init(const char *kernel_filename, const char *cpu_model,
+ DisplayState *ds, stellaris_board_info *board)
+{
+ static const int uart_irq[] = {5, 6, 33, 34};
+ static const int timer_irq[] = {19, 21, 23, 35};
+ static const uint32_t gpio_addr[7] =
+ { 0x40004000, 0x40005000, 0x40006000, 0x40007000,
+ 0x40024000, 0x40025000, 0x40026000};
+ static const int gpio_irq[7] = {0, 1, 2, 3, 4, 30, 31};
+
+ qemu_irq *pic;
+ qemu_irq *gpio_in[5];
+ qemu_irq *gpio_out[5];
+ qemu_irq adc;
+ int sram_size;
+ int flash_size;
+ i2c_bus *i2c;
+ int i;
+
+ flash_size = ((board->dc0 & 0xffff) + 1) << 1;
+ sram_size = (board->dc0 >> 18) + 1;
+ pic = armv7m_init(flash_size, sram_size, kernel_filename, cpu_model);
+
+ if (board->dc1 & (1 << 16)) {
+ adc = stellaris_adc_init(0x40038000, pic[14]);
+ } else {
+ adc = NULL;
+ }
+ for (i = 0; i < 4; i++) {
+ if (board->dc2 & (0x10000 << i)) {
+ stellaris_gptm_init(0x40030000 + i * 0x1000,
+ pic[timer_irq[i]], adc);
+ }
+ }
+
+ stellaris_sys_init(0x400fe000, pic[28], board);
+
+ for (i = 0; i < 7; i++) {
+ if (board->dc4 & (1 << i)) {
+ gpio_in[i] = pl061_init(gpio_addr[i], pic[gpio_irq[i]],
+ &gpio_out[i]);
+ }
+ }
+
+ if (board->dc2 & (1 << 12)) {
+ i2c = i2c_init_bus();
+ stellaris_i2c_init(0x40020000, pic[8], i2c);
+ if (board->oled == OLED_I2C) {
+ ssd0303_init(ds, i2c, 0x3d);
+ }
+ }
+
+ for (i = 0; i < 4; i++) {
+ if (board->dc2 & (1 << i)) {
+ pl011_init(0x4000c000 + i * 0x1000, pic[uart_irq[i]],
+ serial_hds[i], PL011_LUMINARY);
+ }
+ }
+ if (board->dc2 & (1 << 4)) {
+ if (board->oled == OLED_SSI) {
+ void * oled;
+ /* FIXME: Implement chip select for OLED/MMC. */
+ oled = ssd0323_init(ds, &gpio_out[2][7]);
+ pl022_init(0x40008000, pic[7], ssd0323_xfer_ssi, oled);
+ } else {
+ pl022_init(0x40008000, pic[7], NULL, NULL);
+ }
+ }
+}
+
+/* FIXME: Figure out how to generate these from stellaris_boards. */
+static void lm3s811evb_init(int ram_size, int vga_ram_size,
+ const char *boot_device, DisplayState *ds,
+ const char **fd_filename, int snapshot,
+ const char *kernel_filename, const char *kernel_cmdline,
+ const char *initrd_filename, const char *cpu_model)
+{
+ stellaris_init(kernel_filename, cpu_model, ds, &stellaris_boards[0]);
+}
+
+static void lm3s6965evb_init(int ram_size, int vga_ram_size,
+ const char *boot_device, DisplayState *ds,
+ const char **fd_filename, int snapshot,
+ const char *kernel_filename, const char *kernel_cmdline,
+ const char *initrd_filename, const char *cpu_model)
+{
+ stellaris_init(kernel_filename, cpu_model, ds, &stellaris_boards[1]);
+}
+
+QEMUMachine lm3s811evb_machine = {
+ "lm3s811evb",
+ "Stellaris LM3S811EVB",
+ lm3s811evb_init,
+};
+
+QEMUMachine lm3s6965evb_machine = {
+ "lm3s6965evb",
+ "Stellaris LM3S6965EVB",
+ lm3s6965evb_init,
+};
diff --git a/hw/versatilepb.c b/hw/versatilepb.c
index fc27c4688d..4e8e76e26e 100644
--- a/hw/versatilepb.c
+++ b/hw/versatilepb.c
@@ -208,10 +208,10 @@ static void versatile_init(int ram_size, int vga_ram_size,
}
}
- 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]);
+ pl011_init(0x101f1000, pic[12], serial_hds[0], PL011_ARM);
+ pl011_init(0x101f2000, pic[13], serial_hds[1], PL011_ARM);
+ pl011_init(0x101f3000, pic[14], serial_hds[2], PL011_ARM);
+ pl011_init(0x10009000, sic[6], serial_hds[3], PL011_ARM);
pl080_init(0x10130000, pic[17], 8);
sp804_init(0x101e2000, pic[4]);