diff --git a/Makefile.target b/Makefile.target index d61b8e5d09..3d6fdc1c25 100644 --- a/Makefile.target +++ b/Makefile.target @@ -368,6 +368,7 @@ ifeq ($(TARGET_BASE_ARCH), arm) VL_OBJS+= integratorcp.o versatilepb.o ps2.o smc91c111.o arm_pic.o arm_timer.o VL_OBJS+= arm_boot.o pl011.o pl050.o pl080.o pl110.o pl190.o VL_OBJS+= versatile_pci.o +VL_OBJS+= arm_gic.o realview.o arm_sysctl.o endif ifeq ($(TARGET_BASE_ARCH), sh4) VL_OBJS+= shix.o sh7750.o sh7750_regnames.o tc58128.o diff --git a/hw/arm_gic.c b/hw/arm_gic.c new file mode 100644 index 0000000000..c7cc380881 --- /dev/null +++ b/hw/arm_gic.c @@ -0,0 +1,544 @@ +/* + * ARM AMBA Generic/Distributed Interrupt Controller + * + * Copyright (c) 2006 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" + +//#define DEBUG_GIC + +#ifdef DEBUG_GIC +#define DPRINTF(fmt, args...) \ +do { printf("arm_gic: " fmt , (int)s->base, ##args); } while (0) +#else +#define DPRINTF(fmt, args...) do {} while(0) +#endif + +/* Distributed interrupt controller. */ + +static const uint8_t gic_id[] = +{ 0x90, 0x13, 0x04, 0x00, 0x0d, 0xf0, 0x05, 0xb1 }; + +#define GIC_NIRQ 96 + +typedef struct gic_irq_state +{ + unsigned enabled:1; + unsigned pending:1; + unsigned active:1; + unsigned level:1; + unsigned model:1; /* 0 = 1:N, 1 = N:N */ + unsigned trigger:1; /* nonzero = edge triggered. */ +} gic_irq_state; + +#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_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_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 + +typedef struct gic_state +{ + arm_pic_handler handler; + uint32_t base; + void *parent; + int parent_irq; + int enabled; + int cpu_enabled; + + gic_irq_state irq_state[GIC_NIRQ]; + 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; +} gic_state; + +/* TODO: Many places that call this routine could be optimized. */ +/* Update interrupt status after enabled or pending bits have been changed. */ +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) { + pic_set_irq_new(s->parent, s->parent_irq, 0); + 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; + } + } + } + if (best_prio > s->priority_mask) { + pic_set_irq_new(s->parent, s->parent_irq, 0); + } else { + s->current_pending = best_irq; + if (best_prio < s->running_priority) { + DPRINTF("Raised pending IRQ %d\n", best_irq); + pic_set_irq_new(s->parent, s->parent_irq, 1); + } + } +} + +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)) + return; + + if (level) { + GIC_SET_LEVEL(irq); + if (GIC_TEST_TRIGGER(irq) || GIC_TEST_ENABLED(irq)) { + DPRINTF("Set %d pending\n", irq); + GIC_SET_PENDING(irq); + } + } else { + GIC_CLEAR_LEVEL(irq); + } + gic_update(s); +} + +static void gic_set_running_irq(gic_state *s, int irq) +{ + s->running_irq = irq; + s->running_priority = s->priority[irq]; + gic_update(s); +} + +static uint32_t gic_acknowledge_irq(gic_state *s) +{ + int new_irq; + new_irq = s->current_pending; + if (new_irq == 1023 || s->priority[new_irq] >= s->running_priority) { + DPRINTF("ACK no pending IRQ\n"); + return 1023; + } + pic_set_irq_new(s->parent, s->parent_irq, 0); + 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); + DPRINTF("ACK %d\n", new_irq); + return new_irq; +} + +static void gic_complete_irq(gic_state * s, int irq) +{ + int update = 0; + DPRINTF("EIO %d\n", irq); + if (s->running_irq == 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); + update = 1; + } + } + if (irq != s->running_irq) { + /* 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]; + break; + } + tmp = s->last_active[tmp]; + } + if (update) { + gic_update(s); + } + } else { + /* Complete the current running IRQ. */ + gic_set_running_irq(s, s->last_active[s->running_irq]); + } +} + +static uint32_t gic_dist_readb(void *opaque, target_phys_addr_t offset) +{ + gic_state *s = (gic_state *)opaque; + uint32_t res; + int irq; + int i; + + offset -= s->base + 0x1000; + if (offset < 0x100) { + if (offset == 0) + return s->enabled; + if (offset == 4) + return (GIC_NIRQ / 32) - 1; + if (offset < 0x08) + return 0; + goto bad_reg; + } else if (offset < 0x200) { + /* Interrupt Set/Clear Enable. */ + if (offset < 0x180) + irq = (offset - 0x100) * 8; + else + irq = (offset - 0x180) * 8; + if (irq >= GIC_NIRQ) + goto bad_reg; + res = 0; + for (i = 0; i < 8; i++) { + if (GIC_TEST_ENABLED(irq + i)) { + res |= (1 << i); + } + } + } else if (offset < 0x300) { + /* Interrupt Set/Clear Pending. */ + if (offset < 0x280) + irq = (offset - 0x200) * 8; + else + irq = (offset - 0x280) * 8; + if (irq >= GIC_NIRQ) + goto bad_reg; + res = 0; + for (i = 0; i < 8; i++) { + if (GIC_TEST_PENDING(irq + i)) { + res |= (1 << i); + } + } + } else if (offset < 0x400) { + /* Interrupt Active. */ + irq = (offset - 0x300) * 8; + if (irq >= GIC_NIRQ) + goto bad_reg; + res = 0; + for (i = 0; i < 8; i++) { + if (GIC_TEST_ACTIVE(irq + i)) { + res |= (1 << i); + } + } + } else if (offset < 0x800) { + /* Interrupt Priority. */ + irq = offset - 0x400; + if (irq >= GIC_NIRQ) + goto bad_reg; + res = s->priority[irq]; + } else if (offset < 0xc00) { + /* Interrupt CPU Target. */ + irq = offset - 0x800; + if (irq >= GIC_NIRQ) + goto bad_reg; + res = s->irq_target[irq]; + } else if (offset < 0xf00) { + /* Interrupt Configuration. */ + irq = (offset - 0xc00) * 2; + if (irq >= GIC_NIRQ) + goto bad_reg; + res = 0; + for (i = 0; i < 4; i++) { + if (GIC_TEST_MODEL(irq + i)) + res |= (1 << (i * 2)); + if (GIC_TEST_TRIGGER(irq + i)) + res |= (2 << (i * 2)); + } + } else if (offset < 0xfe0) { + goto bad_reg; + } else /* offset >= 0xfe0 */ { + if (offset & 3) { + res = 0; + } else { + res = gic_id[(offset - 0xfe0) >> 2]; + } + } + return res; +bad_reg: + cpu_abort (cpu_single_env, "gic_dist_readb: Bad offset %x\n", offset); + return 0; +} + +static uint32_t gic_dist_readw(void *opaque, target_phys_addr_t offset) +{ + uint32_t val; + val = gic_dist_readb(opaque, offset); + val |= gic_dist_readb(opaque, offset + 1) << 8; + return val; +} + +static uint32_t gic_dist_readl(void *opaque, target_phys_addr_t offset) +{ + uint32_t val; + val = gic_dist_readw(opaque, offset); + val |= gic_dist_readw(opaque, offset + 2) << 16; + return val; +} + +static void gic_dist_writeb(void *opaque, target_phys_addr_t offset, + uint32_t value) +{ + gic_state *s = (gic_state *)opaque; + int irq; + int i; + + offset -= s->base + 0x1000; + if (offset < 0x100) { + if (offset == 0) { + s->enabled = (value & 1); + DPRINTF("Distribution %sabled\n", s->enabled ? "En" : "Dis"); + } else if (offset < 4) { + /* ignored. */ + } else { + goto bad_reg; + } + } else if (offset < 0x180) { + /* Interrupt Set Enable. */ + irq = (offset - 0x100) * 8; + if (irq >= GIC_NIRQ) + goto bad_reg; + for (i = 0; i < 8; i++) { + if (value & (1 << i)) { + 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); + } + } + } else if (offset < 0x200) { + /* Interrupt Clear Enable. */ + irq = (offset - 0x180) * 8; + if (irq >= GIC_NIRQ) + goto bad_reg; + for (i = 0; i < 8; i++) { + if (value & (1 << i)) { + if (GIC_TEST_ENABLED(irq + i)) + DPRINTF("Disabled IRQ %d\n", irq + i); + GIC_CLEAR_ENABLED(irq + i); + } + } + } else if (offset < 0x280) { + /* Interrupt Set Pending. */ + irq = (offset - 0x200) * 8; + if (irq >= GIC_NIRQ) + goto bad_reg; + for (i = 0; i < 8; i++) { + if (value & (1 << i)) { + GIC_SET_PENDING(irq + i); + } + } + } else if (offset < 0x300) { + /* Interrupt Clear Pending. */ + irq = (offset - 0x280) * 8; + if (irq >= GIC_NIRQ) + goto bad_reg; + for (i = 0; i < 8; i++) { + if (value & (1 << i)) { + GIC_CLEAR_PENDING(irq + i); + } + } + } else if (offset < 0x400) { + /* Interrupt Active. */ + goto bad_reg; + } else if (offset < 0x800) { + /* Interrupt Priority. */ + irq = offset - 0x400; + if (irq >= GIC_NIRQ) + goto bad_reg; + s->priority[irq] = value; + } else if (offset < 0xc00) { + /* Interrupt CPU Target. */ + irq = offset - 0x800; + if (irq >= GIC_NIRQ) + goto bad_reg; + s->irq_target[irq] = value; + } else if (offset < 0xf00) { + /* Interrupt Configuration. */ + irq = (offset - 0xc00) * 2; + if (irq >= GIC_NIRQ) + goto bad_reg; + for (i = 0; i < 4; i++) { + if (value & (1 << (i * 2))) { + GIC_SET_MODEL(irq + i); + } else { + GIC_CLEAR_MODEL(irq + i); + } + if (value & (2 << (i * 2))) { + GIC_SET_TRIGGER(irq + i); + } else { + GIC_CLEAR_TRIGGER(irq + i); + } + } + } else { + /* 0xf00 is only handled for word writes. */ + goto bad_reg; + } + gic_update(s); + return; +bad_reg: + cpu_abort (cpu_single_env, "gic_dist_writeb: Bad offset %x\n", 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); +} + +static void gic_dist_writel(void *opaque, target_phys_addr_t offset, + uint32_t value) +{ + gic_dist_writew(opaque, offset, value & 0xffff); + gic_dist_writew(opaque, offset + 2, value >> 16); +} + +static CPUReadMemoryFunc *gic_dist_readfn[] = { + gic_dist_readb, + gic_dist_readw, + gic_dist_readl +}; + +static CPUWriteMemoryFunc *gic_dist_writefn[] = { + gic_dist_writeb, + gic_dist_writew, + gic_dist_writel +}; + +static uint32_t gic_cpu_read(void *opaque, target_phys_addr_t offset) +{ + gic_state *s = (gic_state *)opaque; + offset -= s->base; + switch (offset) { + case 0x00: /* Control */ + return s->cpu_enabled; + case 0x04: /* Priority mask */ + return s->priority_mask; + case 0x08: /* Binary Point */ + /* ??? Not implemented. */ + return 0; + case 0x0c: /* Acknowledge */ + return gic_acknowledge_irq(s); + case 0x14: /* Runing Priority */ + return s->running_priority; + case 0x18: /* Highest Pending Interrupt */ + return s->current_pending; + default: + cpu_abort (cpu_single_env, "gic_cpu_writeb: Bad offset %x\n", offset); + return 0; + } +} + +static void gic_cpu_write(void *opaque, target_phys_addr_t offset, + uint32_t value) +{ + gic_state *s = (gic_state *)opaque; + offset -= s->base; + switch (offset) { + case 0x00: /* Control */ + s->cpu_enabled = (value & 1); + DPRINTF("CPU %sabled\n", s->cpu_enabled ? "En" : "Dis"); + break; + case 0x04: /* Priority mask */ + s->priority_mask = (value & 0x3ff); + break; + case 0x08: /* Binary Point */ + /* ??? Not implemented. */ + break; + case 0x10: /* End Of Interrupt */ + return gic_complete_irq(s, value & 0x3ff); + default: + cpu_abort (cpu_single_env, "gic_cpu_writeb: Bad offset %x\n", 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 +}; + +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 < 15; i++) { + GIC_SET_ENABLED(i); + GIC_SET_TRIGGER(i); + } + s->enabled = 0; + s->cpu_enabled = 0; +} + +void *arm_gic_init(uint32_t base, void *parent, int parent_irq) +{ + gic_state *s; + int iomemtype; + + s = (gic_state *)qemu_mallocz(sizeof(gic_state)); + if (!s) + return NULL; + s->handler = gic_set_irq; + s->parent = parent; + 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, 0x00000fff, iomemtype); + iomemtype = cpu_register_io_memory(0, gic_dist_readfn, + gic_dist_writefn, s); + cpu_register_physical_memory(base + 0x1000, 0x00000fff, iomemtype); + s->base = base; + } else { + s->base = 0; + } + gic_reset(s); + return s; +} diff --git a/hw/arm_sysctl.c b/hw/arm_sysctl.c new file mode 100644 index 0000000000..e9de998a2a --- /dev/null +++ b/hw/arm_sysctl.c @@ -0,0 +1,208 @@ +/* + * Status and system control registers for ARM RealView/Versatile boards. + * + * Copyright (c) 2006 CodeSourcery. + * Written by Paul Brook + * + * This code is licenced under the GPL. + */ + +#include "vl.h" +#include "arm_pic.h" + +#define LOCK_VALUE 0xa05f + +typedef struct { + uint32_t base; + uint32_t sys_id; + uint32_t leds; + uint16_t lockval; + uint32_t cfgdata1; + uint32_t cfgdata2; + uint32_t flags; + uint32_t nvflags; + uint32_t resetlevel; +} arm_sysctl_state; + +static uint32_t arm_sysctl_read(void *opaque, target_phys_addr_t offset) +{ + arm_sysctl_state *s = (arm_sysctl_state *)opaque; + + offset -= s->base; + switch (offset) { + case 0x00: /* ID */ + return s->sys_id; + case 0x04: /* SW */ + /* General purpose hardware switches. + We don't have a useful way of exposing these to the user. */ + return 0; + case 0x08: /* LED */ + return s->leds; + case 0x20: /* LOCK */ + return s->lockval; + case 0x0c: /* OSC0 */ + case 0x10: /* OSC1 */ + case 0x14: /* OSC2 */ + case 0x18: /* OSC3 */ + case 0x1c: /* OSC4 */ + case 0x24: /* 100HZ */ + /* ??? Implement these. */ + return 0; + case 0x28: /* CFGDATA1 */ + return s->cfgdata1; + case 0x2c: /* CFGDATA2 */ + return s->cfgdata2; + case 0x30: /* FLAGS */ + return s->flags; + case 0x38: /* NVFLAGS */ + return s->nvflags; + case 0x40: /* RESETCTL */ + return s->resetlevel; + case 0x44: /* PCICTL */ + return 1; + case 0x48: /* MCI */ + return 0; + case 0x4c: /* FLASH */ + return 0; + case 0x50: /* CLCD */ + return 0x1000; + case 0x54: /* CLCDSER */ + return 0; + case 0x58: /* BOOTCS */ + return 0; + case 0x5c: /* 24MHz */ + /* ??? not implemented. */ + return 0; + case 0x60: /* MISC */ + return 0; + case 0x84: /* PROCID0 */ + /* ??? Don't know what the proper value for the core tile ID is. */ + return 0x02000000; + case 0x88: /* PROCID1 */ + return 0xff000000; + case 0x64: /* DMAPSR0 */ + case 0x68: /* DMAPSR1 */ + case 0x6c: /* DMAPSR2 */ + case 0x70: /* IOSEL */ + case 0x74: /* PLDCTL */ + case 0x80: /* BUSID */ + case 0x8c: /* OSCRESET0 */ + case 0x90: /* OSCRESET1 */ + case 0x94: /* OSCRESET2 */ + case 0x98: /* OSCRESET3 */ + case 0x9c: /* OSCRESET4 */ + case 0xc0: /* SYS_TEST_OSC0 */ + case 0xc4: /* SYS_TEST_OSC1 */ + case 0xc8: /* SYS_TEST_OSC2 */ + case 0xcc: /* SYS_TEST_OSC3 */ + case 0xd0: /* SYS_TEST_OSC4 */ + return 0; + default: + printf ("arm_sysctl_read: Bad register offset 0x%x\n", (int)offset); + return 0; + } +} + +static void arm_sysctl_write(void *opaque, target_phys_addr_t offset, + uint32_t val) +{ + arm_sysctl_state *s = (arm_sysctl_state *)opaque; + offset -= s->base; + + switch (offset) { + case 0x08: /* LED */ + s->leds = val; + case 0x0c: /* OSC0 */ + case 0x10: /* OSC1 */ + case 0x14: /* OSC2 */ + case 0x18: /* OSC3 */ + case 0x1c: /* OSC4 */ + /* ??? */ + break; + case 0x20: /* LOCK */ + if (val == LOCK_VALUE) + s->lockval = val; + else + s->lockval = val & 0x7fff; + break; + case 0x28: /* CFGDATA1 */ + /* ??? Need to implement this. */ + s->cfgdata1 = val; + break; + case 0x2c: /* CFGDATA2 */ + /* ??? Need to implement this. */ + s->cfgdata2 = val; + break; + case 0x30: /* FLAGSSET */ + s->flags |= val; + break; + case 0x34: /* FLAGSCLR */ + s->flags &= ~val; + break; + case 0x38: /* NVFLAGSSET */ + s->nvflags |= val; + break; + case 0x3c: /* NVFLAGSCLR */ + s->nvflags &= ~val; + break; + case 0x40: /* RESETCTL */ + if (s->lockval == LOCK_VALUE) { + s->resetlevel = val; + if (val & 0x100) + cpu_abort(cpu_single_env, "Board reset\n"); + } + break; + case 0x44: /* PCICTL */ + /* nothing to do. */ + break; + case 0x4c: /* FLASH */ + case 0x50: /* CLCD */ + case 0x54: /* CLCDSER */ + case 0x64: /* DMAPSR0 */ + case 0x68: /* DMAPSR1 */ + case 0x6c: /* DMAPSR2 */ + case 0x70: /* IOSEL */ + case 0x74: /* PLDCTL */ + case 0x80: /* BUSID */ + case 0x84: /* PROCID0 */ + case 0x88: /* PROCID1 */ + case 0x8c: /* OSCRESET0 */ + case 0x90: /* OSCRESET1 */ + case 0x94: /* OSCRESET2 */ + case 0x98: /* OSCRESET3 */ + case 0x9c: /* OSCRESET4 */ + break; + default: + printf ("arm_sysctl_write: Bad register offset 0x%x\n", (int)offset); + return; + } +} + +static CPUReadMemoryFunc *arm_sysctl_readfn[] = { + arm_sysctl_read, + arm_sysctl_read, + arm_sysctl_read +}; + +static CPUWriteMemoryFunc *arm_sysctl_writefn[] = { + arm_sysctl_write, + arm_sysctl_write, + arm_sysctl_write +}; + +void arm_sysctl_init(uint32_t base, uint32_t sys_id) +{ + arm_sysctl_state *s; + int iomemtype; + + s = (arm_sysctl_state *)qemu_mallocz(sizeof(arm_sysctl_state)); + if (!s) + return; + s->base = base; + s->sys_id = sys_id; + iomemtype = cpu_register_io_memory(0, arm_sysctl_readfn, + arm_sysctl_writefn, s); + cpu_register_physical_memory(base, 0x00000fff, iomemtype); + /* ??? Save/restore. */ +} + diff --git a/hw/pl080.c b/hw/pl080.c index 49996ca91d..549b3bfd10 100644 --- a/hw/pl080.c +++ b/hw/pl080.c @@ -1,5 +1,5 @@ /* - * Arm PrimeCell PL080 DMA controller + * Arm PrimeCell PL080/PL081 DMA controller * * Copyright (c) 2006 CodeSourcery. * Written by Paul Brook @@ -9,7 +9,7 @@ #include "vl.h" -#define PL080_NUM_CHANNELS 8 +#define PL080_MAX_CHANNELS 8 #define PL080_CONF_E 0x1 #define PL080_CONF_M1 0x2 #define PL080_CONF_M2 0x4 @@ -45,7 +45,8 @@ typedef struct { uint32_t sync; uint32_t req_single; uint32_t req_burst; - pl080_channel chan[PL080_NUM_CHANNELS]; + pl080_channel chan[PL080_MAX_CHANNELS]; + int nchannels; /* Flag to avoid recursive DMA invocations. */ int running; void *pic; @@ -55,6 +56,9 @@ typedef struct { static const unsigned char pl080_id[] = { 0x80, 0x10, 0x04, 0x0a, 0x0d, 0xf0, 0x05, 0xb1 }; +static const unsigned char pl081_id[] = +{ 0x81, 0x10, 0x04, 0x0a, 0x0d, 0xf0, 0x05, 0xb1 }; + static void pl080_update(pl080_state *s) { if ((s->tc_int & s->tc_mask) @@ -80,7 +84,7 @@ static void pl080_run(pl080_state *s) uint32_t req; s->tc_mask = 0; - for (c = 0; c < PL080_NUM_CHANNELS; c++) { + for (c = 0; c < s->nchannels; c++) { if (s->chan[c].conf & PL080_CCONF_ITC) s->tc_mask |= 1 << c; if (s->chan[c].conf & PL080_CCONF_IE) @@ -99,7 +103,7 @@ cpu_abort(cpu_single_env, "DMA active\n"); } s->running = 1; while (s->running) { - for (c = 0; c < PL080_NUM_CHANNELS; c++) { + for (c = 0; c < s->nchannels; c++) { ch = &s->chan[c]; again: /* Test if thiws channel has any pending DMA requests. */ @@ -185,10 +189,16 @@ static uint32_t pl080_read(void *opaque, target_phys_addr_t offset) offset -= s->base; if (offset >= 0xfe0 && offset < 0x1000) { - return pl080_id[(offset - 0xfe0) >> 2]; + if (s->nchannels == 8) { + return pl080_id[(offset - 0xfe0) >> 2]; + } else { + return pl081_id[(offset - 0xfe0) >> 2]; + } } if (offset >= 0x100 && offset < 0x200) { i = (offset & 0xe0) >> 5; + if (i >= s->nchannels) + goto bad_offset; switch (offset >> 2) { case 0: /* SrcAddr */ return s->chan[i].src; @@ -217,7 +227,7 @@ static uint32_t pl080_read(void *opaque, target_phys_addr_t offset) return s->err_int; case 7: /* EnbldChns */ mask = 0; - for (i = 0; i < PL080_NUM_CHANNELS; i++) { + for (i = 0; i < s->nchannels; i++) { if (s->chan[i].conf & PL080_CCONF_E) mask |= 1 << i; } @@ -248,6 +258,8 @@ static void pl080_write(void *opaque, target_phys_addr_t offset, offset -= s->base; if (offset >= 0x100 && offset < 0x200) { i = (offset & 0xe0) >> 5; + if (i >= s->nchannels) + goto bad_offset; switch (offset >> 2) { case 0: /* SrcAddr */ s->chan[i].src = value; @@ -293,6 +305,7 @@ static void pl080_write(void *opaque, target_phys_addr_t offset, s->sync = value; break; default: + bad_offset: cpu_abort(cpu_single_env, "pl080_write: Bad offset %x\n", offset); } pl080_update(s); @@ -310,7 +323,9 @@ static CPUWriteMemoryFunc *pl080_writefn[] = { pl080_write }; -void *pl080_init(uint32_t base, void *pic, int irq) +/* The PL080 and PL081 are the same except for the number of channels + they implement (8 and 2 respectively). */ +void *pl080_init(uint32_t base, void *pic, int irq, int nchannels) { int iomemtype; pl080_state *s; @@ -322,6 +337,7 @@ void *pl080_init(uint32_t base, void *pic, int irq) s->base = base; s->pic = pic; s->irq = irq; + s->nchannels = nchannels; /* ??? Save/restore. */ return s; } diff --git a/hw/realview.c b/hw/realview.c new file mode 100644 index 0000000000..11b0916089 --- /dev/null +++ b/hw/realview.c @@ -0,0 +1,138 @@ +/* + * ARM RealView Baseboard System emulation. + * + * Copyright (c) 2006 CodeSourcery. + * Written by Paul Brook + * + * This code is licenced under the GPL. + */ + +#include "vl.h" +#include "arm_pic.h" + +/* Board init. */ + +static void realview_init(int ram_size, int vga_ram_size, int boot_device, + DisplayState *ds, const char **fd_filename, int snapshot, + const char *kernel_filename, const char *kernel_cmdline, + const char *initrd_filename) +{ + CPUState *env; + void *pic; + void *scsi_hba; + PCIBus *pci_bus; + NICInfo *nd; + int n; + int done_smc = 0; + + env = cpu_init(); + cpu_arm_set_model(env, ARM_CPUID_ARM926); + //cpu_arm_set_model(env, ARM_CPUID_ARM11MPCORE); + /* ??? RAM shoud repeat to fill physical memory space. */ + /* SDRAM at address zero. */ + 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); + 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]); + + /* DMA controller is optional, apparently. */ + pl080_init(0x10030000, pic, 24, 2); + + sp804_init(0x10011000, pic, 4); + sp804_init(0x10012000, pic, 5); + + pl110_init(ds, 0x10020000, pic, 23, 1); + + pci_bus = pci_vpb_init(pic, 48, 1); + if (usb_enabled) { + usb_ohci_init(pci_bus, 3, -1); + } + scsi_hba = lsi_scsi_init(pci_bus, -1); + for (n = 0; n < MAX_DISKS; n++) { + if (bs_table[n]) { + lsi_scsi_attach(scsi_hba, bs_table[n], n); + } + } + for(n = 0; n < nb_nics; n++) { + nd = &nd_table[n]; + if (!nd->model) + nd->model = done_smc ? "rtl8139" : "smc91c111"; + if (strcmp(nd->model, "smc91c111") == 0) { + smc91c111_init(nd, 0x4e000000, pic, 28); + } else { + pci_nic_init(pci_bus, nd); + } + } + + /* Memory map for RealView Emulation Baseboard: */ + /* 0x10000000 System registers. */ + /* 0x10001000 System controller. */ + /* 0x10002000 Two-Wire Serial Bus. */ + /* 0x10003000 Reserved. */ + /* 0x10004000 AACI. */ + /* 0x10005000 MCI. */ + /* 0x10006000 KMI0. */ + /* 0x10007000 KMI1. */ + /* 0x10008000 Character LCD. */ + /* 0x10009000 UART0. */ + /* 0x1000a000 UART1. */ + /* 0x1000b000 UART2. */ + /* 0x1000c000 UART3. */ + /* 0x1000d000 SSPI. */ + /* 0x1000e000 SCI. */ + /* 0x1000f000 Reserved. */ + /* 0x10010000 Watchdog. */ + /* 0x10011000 Timer 0+1. */ + /* 0x10012000 Timer 2+3. */ + /* 0x10013000 GPIO 0. */ + /* 0x10014000 GPIO 1. */ + /* 0x10015000 GPIO 2. */ + /* 0x10016000 Reserved. */ + /* 0x10017000 RTC. */ + /* 0x10018000 DMC. */ + /* 0x10019000 PCI controller config. */ + /* 0x10020000 CLCD. */ + /* 0x10030000 DMA Controller. */ + /* 0x10040000 GIC1 (FIQ1). */ + /* 0x10050000 GIC2 (IRQ1). */ + /* 0x10060000 GIC3 (FIQ2). */ + /* 0x10070000 GIC4 (IRQ2). */ + /* 0x10080000 SMC. */ + /* 0x40000000 NOR flash. */ + /* 0x44000000 DoC flash. */ + /* 0x48000000 SRAM. */ + /* 0x4c000000 Configuration flash. */ + /* 0x4e000000 Ethernet. */ + /* 0x4f000000 USB. */ + /* 0x50000000 PISMO. */ + /* 0x54000000 PISMO. */ + /* 0x58000000 PISMO. */ + /* 0x5c000000 PISMO. */ + /* 0x60000000 PCI. */ + /* 0x61000000 PCI Self Config. */ + /* 0x62000000 PCI Config. */ + /* 0x63000000 PCI IO. */ + /* 0x64000000 PCI mem 0. */ + /* 0x68000000 PCI mem 1. */ + /* 0x6c000000 PCI mem 2. */ + + arm_load_kernel(ram_size, kernel_filename, kernel_cmdline, + initrd_filename, 0x33b); +} + +QEMUMachine realview_machine = { + "realview", + "ARM RealView Emulation Baseboard (ARM926EJ-S)", + realview_init +}; diff --git a/hw/versatile_pci.c b/hw/versatile_pci.c index 34a4bc6c5a..aac61c7b55 100644 --- a/hw/versatile_pci.c +++ b/hw/versatile_pci.c @@ -77,31 +77,49 @@ static CPUReadMemoryFunc *pci_vpb_config_read[] = { &pci_vpb_config_readl, }; +static int pci_vpb_irq; + static void pci_vpb_set_irq(PCIDevice *d, void *pic, int irq_num, int level) { - pic_set_irq_new(pic, 27 + irq_num, level); + pic_set_irq_new(pic, pci_vpb_irq + irq_num, level); } -PCIBus *pci_vpb_init(void *pic) +PCIBus *pci_vpb_init(void *pic, int irq, int realview) { PCIBus *s; PCIDevice *d; int mem_config; + uint32_t base; + const char * name; + pci_vpb_irq = irq; + if (realview) { + base = 0x60000000; + name = "RealView EB PCI Controller"; + } else { + base = 0x40000000; + name = "Versatile/PB PCI Controller"; + } s = pci_register_bus(pci_vpb_set_irq, pic, 11 << 3); /* ??? Register memory space. */ mem_config = cpu_register_io_memory(0, pci_vpb_config_read, pci_vpb_config_write, s); /* Selfconfig area. */ - cpu_register_physical_memory(0x41000000, 0x10000, mem_config); + cpu_register_physical_memory(base + 0x01000000, 0x10000, mem_config); /* Normal config area. */ - cpu_register_physical_memory(0x42000000, 0x10000, mem_config); + cpu_register_physical_memory(base + 0x02000000, 0x10000, mem_config); + + d = pci_register_device(s, name, sizeof(PCIDevice), -1, NULL, NULL); + + if (realview) { + /* IO memory area. */ + isa_mmio_init(base + 0x03000000, 0x00100000); + } - d = pci_register_device(s, "Versatile/PB PCI Controller", - sizeof(PCIDevice), -1, NULL, NULL); d->config[0x00] = 0xee; // vendor_id d->config[0x01] = 0x10; + /* Both boards have the same device ID. Oh well. */ d->config[0x02] = 0x00; // device_id d->config[0x03] = 0x03; d->config[0x04] = 0x00; diff --git a/hw/versatilepb.c b/hw/versatilepb.c index 28ec1374ba..475cb4892c 100644 --- a/hw/versatilepb.c +++ b/hw/versatilepb.c @@ -10,8 +10,6 @@ #include "vl.h" #include "arm_pic.h" -#define LOCK_VALUE 0xa05f - /* Primary interrupt controller. */ typedef struct vpb_sic_state @@ -75,7 +73,7 @@ static uint32_t vpb_sic_read(void *opaque, target_phys_addr_t offset) case 8: /* PICENABLE */ return s->pic_enable; default: - printf ("vpb_sic_read: Bad register offset 0x%x\n", offset); + printf ("vpb_sic_read: Bad register offset 0x%x\n", (int)offset); return 0; } } @@ -110,7 +108,7 @@ static void vpb_sic_write(void *opaque, target_phys_addr_t offset, vpb_sic_update_pic(s); break; default: - printf ("vpb_sic_write: Bad register offset 0x%x\n", offset); + printf ("vpb_sic_write: Bad register offset 0x%x\n", (int)offset); return; } vpb_sic_update(s); @@ -147,188 +145,6 @@ static vpb_sic_state *vpb_sic_init(uint32_t base, void *parent, int irq) return s; } -/* System controller. */ - -typedef struct { - uint32_t base; - uint32_t leds; - uint16_t lockval; - uint32_t cfgdata1; - uint32_t cfgdata2; - uint32_t flags; - uint32_t nvflags; - uint32_t resetlevel; -} vpb_sys_state; - -static uint32_t vpb_sys_read(void *opaque, target_phys_addr_t offset) -{ - vpb_sys_state *s = (vpb_sys_state *)opaque; - - offset -= s->base; - switch (offset) { - case 0x00: /* ID */ - return 0x41007004; - case 0x04: /* SW */ - /* General purpose hardware switches. - We don't have a useful way of exposing these to the user. */ - return 0; - case 0x08: /* LED */ - return s->leds; - case 0x20: /* LOCK */ - return s->lockval; - case 0x0c: /* OSC0 */ - case 0x10: /* OSC1 */ - case 0x14: /* OSC2 */ - case 0x18: /* OSC3 */ - case 0x1c: /* OSC4 */ - case 0x24: /* 100HZ */ - /* ??? Implement these. */ - return 0; - case 0x28: /* CFGDATA1 */ - return s->cfgdata1; - case 0x2c: /* CFGDATA2 */ - return s->cfgdata2; - case 0x30: /* FLAGS */ - return s->flags; - case 0x38: /* NVFLAGS */ - return s->nvflags; - case 0x40: /* RESETCTL */ - return s->resetlevel; - case 0x44: /* PCICTL */ - return 1; - case 0x48: /* MCI */ - return 0; - case 0x4c: /* FLASH */ - return 0; - case 0x50: /* CLCD */ - return 0x1000; - case 0x54: /* CLCDSER */ - return 0; - case 0x58: /* BOOTCS */ - return 0; - case 0x5c: /* 24MHz */ - /* ??? not implemented. */ - return 0; - case 0x60: /* MISC */ - return 0; - case 0x64: /* DMAPSR0 */ - case 0x68: /* DMAPSR1 */ - case 0x6c: /* DMAPSR2 */ - case 0x8c: /* OSCRESET0 */ - case 0x90: /* OSCRESET1 */ - case 0x94: /* OSCRESET2 */ - case 0x98: /* OSCRESET3 */ - case 0x9c: /* OSCRESET4 */ - case 0xc0: /* SYS_TEST_OSC0 */ - case 0xc4: /* SYS_TEST_OSC1 */ - case 0xc8: /* SYS_TEST_OSC2 */ - case 0xcc: /* SYS_TEST_OSC3 */ - case 0xd0: /* SYS_TEST_OSC4 */ - return 0; - default: - printf ("vpb_sys_read: Bad register offset 0x%x\n", offset); - return 0; - } -} - -static void vpb_sys_write(void *opaque, target_phys_addr_t offset, - uint32_t val) -{ - vpb_sys_state *s = (vpb_sys_state *)opaque; - offset -= s->base; - - switch (offset) { - case 0x08: /* LED */ - s->leds = val; - case 0x0c: /* OSC0 */ - case 0x10: /* OSC1 */ - case 0x14: /* OSC2 */ - case 0x18: /* OSC3 */ - case 0x1c: /* OSC4 */ - /* ??? */ - break; - case 0x20: /* LOCK */ - if (val == LOCK_VALUE) - s->lockval = val; - else - s->lockval = val & 0x7fff; - break; - case 0x28: /* CFGDATA1 */ - /* ??? Need to implement this. */ - s->cfgdata1 = val; - break; - case 0x2c: /* CFGDATA2 */ - /* ??? Need to implement this. */ - s->cfgdata2 = val; - break; - case 0x30: /* FLAGSSET */ - s->flags |= val; - break; - case 0x34: /* FLAGSCLR */ - s->flags &= ~val; - break; - case 0x38: /* NVFLAGSSET */ - s->nvflags |= val; - break; - case 0x3c: /* NVFLAGSCLR */ - s->nvflags &= ~val; - break; - case 0x40: /* RESETCTL */ - if (s->lockval == LOCK_VALUE) { - s->resetlevel = val; - if (val & 0x100) - cpu_abort(cpu_single_env, "Board reset\n"); - } - break; - case 0x44: /* PCICTL */ - /* nothing to do. */ - break; - case 0x4c: /* FLASH */ - case 0x50: /* CLCD */ - case 0x54: /* CLCDSER */ - case 0x64: /* DMAPSR0 */ - case 0x68: /* DMAPSR1 */ - case 0x6c: /* DMAPSR2 */ - case 0x8c: /* OSCRESET0 */ - case 0x90: /* OSCRESET1 */ - case 0x94: /* OSCRESET2 */ - case 0x98: /* OSCRESET3 */ - case 0x9c: /* OSCRESET4 */ - break; - default: - printf ("vpb_sys_write: Bad register offset 0x%x\n", offset); - return; - } -} - -static CPUReadMemoryFunc *vpb_sys_readfn[] = { - vpb_sys_read, - vpb_sys_read, - vpb_sys_read -}; - -static CPUWriteMemoryFunc *vpb_sys_writefn[] = { - vpb_sys_write, - vpb_sys_write, - vpb_sys_write -}; - -static vpb_sys_state *vpb_sys_init(uint32_t base) -{ - vpb_sys_state *s; - int iomemtype; - - s = (vpb_sys_state *)qemu_mallocz(sizeof(vpb_sys_state)); - if (!s) - return NULL; - s->base = base; - iomemtype = cpu_register_io_memory(0, vpb_sys_readfn, - vpb_sys_writefn, s); - cpu_register_physical_memory(base, 0x00000fff, iomemtype); - /* ??? Save/restore. */ - return s; -} - /* Board init. */ /* The AB and PB boards both use the same core, just with different @@ -355,14 +171,14 @@ static void versatile_init(int ram_size, int vga_ram_size, int boot_device, /* SDRAM at address zero. */ cpu_register_physical_memory(0, ram_size, IO_MEM_RAM); - vpb_sys_init(0x10000000); + arm_sysctl_init(0x10000000, 0x41007004); pic = arm_pic_init_cpu(env); pic = pl190_init(0x10140000, pic, ARM_PIC_CPU_IRQ, ARM_PIC_CPU_FIQ); sic = vpb_sic_init(0x10003000, pic, 31); pl050_init(0x10006000, sic, 3, 0); pl050_init(0x10007000, sic, 4, 1); - pci_bus = pci_vpb_init(sic); + pci_bus = pci_vpb_init(sic, 27, 0); /* The Versatile PCI bridge does not provide access to PCI IO space, so many of the qemu PCI devices are not useable. */ for(n = 0; n < nb_nics; n++) { @@ -390,7 +206,7 @@ static void versatile_init(int ram_size, int vga_ram_size, int boot_device, pl011_init(0x101f3000, pic, 14, serial_hds[2]); pl011_init(0x10009000, sic, 6, serial_hds[3]); - pl080_init(0x10130000, pic, 17); + pl080_init(0x10130000, pic, 17, 8); sp804_init(0x101e2000, pic, 4); sp804_init(0x101e3000, pic, 5); diff --git a/vl.c b/vl.c index d422fedcf0..d3efbbd4b7 100644 --- a/vl.c +++ b/vl.c @@ -6150,6 +6150,7 @@ void register_machines(void) qemu_register_machine(&integratorcp1026_machine); qemu_register_machine(&versatilepb_machine); qemu_register_machine(&versatileab_machine); + qemu_register_machine(&realview_machine); #elif defined(TARGET_SH4) qemu_register_machine(&shix_machine); #else diff --git a/vl.h b/vl.h index e16fd92859..fef2ef8c21 100644 --- a/vl.h +++ b/vl.h @@ -778,7 +778,7 @@ PCIBus *pci_pmac_init(void *pic); PCIBus *pci_apb_init(target_ulong special_base, target_ulong mem_base, void *pic); -PCIBus *pci_vpb_init(void *pic); +PCIBus *pci_vpb_init(void *pic, int irq, int realview); /* piix_pci.c */ PCIBus *i440fx_init(void); @@ -1222,6 +1222,9 @@ extern QEMUMachine integratorcp1026_machine; extern QEMUMachine versatilepb_machine; extern QEMUMachine versatileab_machine; +/* realview.c */ +extern QEMUMachine realview_machine; + /* ps2.c */ void *ps2_kbd_init(void (*update_irq)(void *, int), void *update_arg); void *ps2_mouse_init(void (*update_irq)(void *, int), void *update_arg); @@ -1244,7 +1247,7 @@ void pl011_init(uint32_t base, void *pic, int irq, CharDriverState *chr); void pl050_init(uint32_t base, void *pic, int irq, int is_mouse); /* pl080.c */ -void *pl080_init(uint32_t base, void *pic, int irq); +void *pl080_init(uint32_t base, void *pic, int irq, int nchannels); /* pl190.c */ void *pl190_init(uint32_t base, void *parent, int irq, int fiq); @@ -1253,6 +1256,12 @@ void *pl190_init(uint32_t base, void *parent, int irq, int fiq); void sp804_init(uint32_t base, void *pic, int irq); void icp_pit_init(uint32_t base, void *pic, int irq); +/* arm_sysctl.c */ +void arm_sysctl_init(uint32_t base, uint32_t sys_id); + +/* arm_gic.c */ +void *arm_gic_init(uint32_t base, void *parent, int parent_irq); + /* arm_boot.c */ void arm_load_kernel(int ram_size, const char *kernel_filename,