diff options
Diffstat (limited to 'qemu/hw/m68k')
-rw-r--r-- | qemu/hw/m68k/Makefile.objs | 4 | ||||
-rw-r--r-- | qemu/hw/m68k/an5206.c | 103 | ||||
-rw-r--r-- | qemu/hw/m68k/dummy_m68k.c | 86 | ||||
-rw-r--r-- | qemu/hw/m68k/mcf5206.c | 548 | ||||
-rw-r--r-- | qemu/hw/m68k/mcf5208.c | 309 | ||||
-rw-r--r-- | qemu/hw/m68k/mcf_intc.c | 168 |
6 files changed, 1218 insertions, 0 deletions
diff --git a/qemu/hw/m68k/Makefile.objs b/qemu/hw/m68k/Makefile.objs new file mode 100644 index 000000000..c4352e783 --- /dev/null +++ b/qemu/hw/m68k/Makefile.objs @@ -0,0 +1,4 @@ +obj-y += an5206.o mcf5208.o +obj-y += dummy_m68k.o + +obj-y += mcf5206.o mcf_intc.o diff --git a/qemu/hw/m68k/an5206.c b/qemu/hw/m68k/an5206.c new file mode 100644 index 000000000..f63ab2b94 --- /dev/null +++ b/qemu/hw/m68k/an5206.c @@ -0,0 +1,103 @@ +/* + * Arnewsh 5206 ColdFire system emulation. + * + * Copyright (c) 2007 CodeSourcery. + * + * This code is licensed under the GPL + */ + +#include "hw/hw.h" +#include "hw/m68k/mcf.h" +#include "hw/boards.h" +#include "hw/loader.h" +#include "elf.h" +#include "exec/address-spaces.h" +#include "sysemu/qtest.h" + +#define KERNEL_LOAD_ADDR 0x10000 +#define AN5206_MBAR_ADDR 0x10000000 +#define AN5206_RAMBAR_ADDR 0x20000000 + +/* Board init. */ + +static void an5206_init(MachineState *machine) +{ + ram_addr_t ram_size = machine->ram_size; + const char *cpu_model = machine->cpu_model; + const char *kernel_filename = machine->kernel_filename; + M68kCPU *cpu; + CPUM68KState *env; + int kernel_size; + uint64_t elf_entry; + hwaddr entry; + MemoryRegion *address_space_mem = get_system_memory(); + MemoryRegion *ram = g_new(MemoryRegion, 1); + MemoryRegion *sram = g_new(MemoryRegion, 1); + + if (!cpu_model) { + cpu_model = "m5206"; + } + cpu = cpu_m68k_init(cpu_model); + if (!cpu) { + hw_error("Unable to find m68k CPU definition\n"); + } + env = &cpu->env; + + /* Initialize CPU registers. */ + env->vbr = 0; + /* TODO: allow changing MBAR and RAMBAR. */ + env->mbar = AN5206_MBAR_ADDR | 1; + env->rambar0 = AN5206_RAMBAR_ADDR | 1; + + /* DRAM at address zero */ + memory_region_allocate_system_memory(ram, NULL, "an5206.ram", ram_size); + memory_region_add_subregion(address_space_mem, 0, ram); + + /* Internal SRAM. */ + memory_region_init_ram(sram, NULL, "an5206.sram", 512, &error_abort); + vmstate_register_ram_global(sram); + memory_region_add_subregion(address_space_mem, AN5206_RAMBAR_ADDR, sram); + + mcf5206_init(address_space_mem, AN5206_MBAR_ADDR, cpu); + + /* Load kernel. */ + if (!kernel_filename) { + if (qtest_enabled()) { + return; + } + fprintf(stderr, "Kernel image must be specified\n"); + exit(1); + } + + kernel_size = load_elf(kernel_filename, NULL, NULL, &elf_entry, + NULL, NULL, 1, ELF_MACHINE, 0); + entry = elf_entry; + if (kernel_size < 0) { + kernel_size = load_uimage(kernel_filename, &entry, NULL, NULL, + NULL, NULL); + } + if (kernel_size < 0) { + kernel_size = load_image_targphys(kernel_filename, KERNEL_LOAD_ADDR, + ram_size - KERNEL_LOAD_ADDR); + entry = KERNEL_LOAD_ADDR; + } + if (kernel_size < 0) { + fprintf(stderr, "qemu: could not load kernel '%s'\n", kernel_filename); + exit(1); + } + + env->pc = entry; +} + +static QEMUMachine an5206_machine = { + .name = "an5206", + .desc = "Arnewsh 5206", + .init = an5206_init, +}; + +static void an5206_machine_init(void) +{ + qemu_register_machine(&an5206_machine); +} + +machine_init(an5206_machine_init); diff --git a/qemu/hw/m68k/dummy_m68k.c b/qemu/hw/m68k/dummy_m68k.c new file mode 100644 index 000000000..5b77d930e --- /dev/null +++ b/qemu/hw/m68k/dummy_m68k.c @@ -0,0 +1,86 @@ +/* + * Dummy board with just RAM and CPU for use as an ISS. + * + * Copyright (c) 2007 CodeSourcery. + * + * This code is licensed under the GPL + */ + +#include "hw/hw.h" +#include "hw/boards.h" +#include "hw/loader.h" +#include "elf.h" +#include "exec/address-spaces.h" + +#define KERNEL_LOAD_ADDR 0x10000 + +/* Board init. */ + +static void dummy_m68k_init(MachineState *machine) +{ + ram_addr_t ram_size = machine->ram_size; + const char *cpu_model = machine->cpu_model; + const char *kernel_filename = machine->kernel_filename; + M68kCPU *cpu; + CPUM68KState *env; + MemoryRegion *address_space_mem = get_system_memory(); + MemoryRegion *ram = g_new(MemoryRegion, 1); + int kernel_size; + uint64_t elf_entry; + hwaddr entry; + + if (!cpu_model) + cpu_model = "cfv4e"; + cpu = cpu_m68k_init(cpu_model); + if (!cpu) { + fprintf(stderr, "Unable to find m68k CPU definition\n"); + exit(1); + } + env = &cpu->env; + + /* Initialize CPU registers. */ + env->vbr = 0; + + /* RAM at address zero */ + memory_region_allocate_system_memory(ram, NULL, "dummy_m68k.ram", + ram_size); + memory_region_add_subregion(address_space_mem, 0, ram); + + /* Load kernel. */ + if (kernel_filename) { + kernel_size = load_elf(kernel_filename, NULL, NULL, &elf_entry, + NULL, NULL, 1, ELF_MACHINE, 0); + entry = elf_entry; + if (kernel_size < 0) { + kernel_size = load_uimage(kernel_filename, &entry, NULL, NULL, + NULL, NULL); + } + if (kernel_size < 0) { + kernel_size = load_image_targphys(kernel_filename, + KERNEL_LOAD_ADDR, + ram_size - KERNEL_LOAD_ADDR); + entry = KERNEL_LOAD_ADDR; + } + if (kernel_size < 0) { + fprintf(stderr, "qemu: could not load kernel '%s'\n", + kernel_filename); + exit(1); + } + } else { + entry = 0; + } + env->pc = entry; +} + +static QEMUMachine dummy_m68k_machine = { + .name = "dummy", + .desc = "Dummy board", + .init = dummy_m68k_init, +}; + +static void dummy_m68k_machine_init(void) +{ + qemu_register_machine(&dummy_m68k_machine); +} + +machine_init(dummy_m68k_machine_init); diff --git a/qemu/hw/m68k/mcf5206.c b/qemu/hw/m68k/mcf5206.c new file mode 100644 index 000000000..1727a4685 --- /dev/null +++ b/qemu/hw/m68k/mcf5206.c @@ -0,0 +1,548 @@ +/* + * Motorola ColdFire MCF5206 SoC embedded peripheral emulation. + * + * Copyright (c) 2007 CodeSourcery. + * + * This code is licensed under the GPL + */ +#include "hw/hw.h" +#include "hw/m68k/mcf.h" +#include "qemu/timer.h" +#include "hw/ptimer.h" +#include "sysemu/sysemu.h" +#include "exec/address-spaces.h" + +/* General purpose timer module. */ +typedef struct { + uint16_t tmr; + uint16_t trr; + uint16_t tcr; + uint16_t ter; + ptimer_state *timer; + qemu_irq irq; + int irq_state; +} m5206_timer_state; + +#define TMR_RST 0x01 +#define TMR_CLK 0x06 +#define TMR_FRR 0x08 +#define TMR_ORI 0x10 +#define TMR_OM 0x20 +#define TMR_CE 0xc0 + +#define TER_CAP 0x01 +#define TER_REF 0x02 + +static void m5206_timer_update(m5206_timer_state *s) +{ + if ((s->tmr & TMR_ORI) != 0 && (s->ter & TER_REF)) + qemu_irq_raise(s->irq); + else + qemu_irq_lower(s->irq); +} + +static void m5206_timer_reset(m5206_timer_state *s) +{ + s->tmr = 0; + s->trr = 0; +} + +static void m5206_timer_recalibrate(m5206_timer_state *s) +{ + int prescale; + int mode; + + ptimer_stop(s->timer); + + if ((s->tmr & TMR_RST) == 0) + return; + + prescale = (s->tmr >> 8) + 1; + mode = (s->tmr >> 1) & 3; + if (mode == 2) + prescale *= 16; + + if (mode == 3 || mode == 0) + hw_error("m5206_timer: mode %d not implemented\n", mode); + if ((s->tmr & TMR_FRR) == 0) + hw_error("m5206_timer: free running mode not implemented\n"); + + /* Assume 66MHz system clock. */ + ptimer_set_freq(s->timer, 66000000 / prescale); + + ptimer_set_limit(s->timer, s->trr, 0); + + ptimer_run(s->timer, 0); +} + +static void m5206_timer_trigger(void *opaque) +{ + m5206_timer_state *s = (m5206_timer_state *)opaque; + s->ter |= TER_REF; + m5206_timer_update(s); +} + +static uint32_t m5206_timer_read(m5206_timer_state *s, uint32_t addr) +{ + switch (addr) { + case 0: + return s->tmr; + case 4: + return s->trr; + case 8: + return s->tcr; + case 0xc: + return s->trr - ptimer_get_count(s->timer); + case 0x11: + return s->ter; + default: + return 0; + } +} + +static void m5206_timer_write(m5206_timer_state *s, uint32_t addr, uint32_t val) +{ + switch (addr) { + case 0: + if ((s->tmr & TMR_RST) != 0 && (val & TMR_RST) == 0) { + m5206_timer_reset(s); + } + s->tmr = val; + m5206_timer_recalibrate(s); + break; + case 4: + s->trr = val; + m5206_timer_recalibrate(s); + break; + case 8: + s->tcr = val; + break; + case 0xc: + ptimer_set_count(s->timer, val); + break; + case 0x11: + s->ter &= ~val; + break; + default: + break; + } + m5206_timer_update(s); +} + +static m5206_timer_state *m5206_timer_init(qemu_irq irq) +{ + m5206_timer_state *s; + QEMUBH *bh; + + s = (m5206_timer_state *)g_malloc0(sizeof(m5206_timer_state)); + bh = qemu_bh_new(m5206_timer_trigger, s); + s->timer = ptimer_init(bh); + s->irq = irq; + m5206_timer_reset(s); + return s; +} + +/* System Integration Module. */ + +typedef struct { + M68kCPU *cpu; + MemoryRegion iomem; + m5206_timer_state *timer[2]; + void *uart[2]; + uint8_t scr; + uint8_t icr[14]; + uint16_t imr; /* 1 == interrupt is masked. */ + uint16_t ipr; + uint8_t rsr; + uint8_t swivr; + uint8_t par; + /* Include the UART vector registers here. */ + uint8_t uivr[2]; +} m5206_mbar_state; + +/* Interrupt controller. */ + +static int m5206_find_pending_irq(m5206_mbar_state *s) +{ + int level; + int vector; + uint16_t active; + int i; + + level = 0; + vector = 0; + active = s->ipr & ~s->imr; + if (!active) + return 0; + + for (i = 1; i < 14; i++) { + if (active & (1 << i)) { + if ((s->icr[i] & 0x1f) > level) { + level = s->icr[i] & 0x1f; + vector = i; + } + } + } + + if (level < 4) + vector = 0; + + return vector; +} + +static void m5206_mbar_update(m5206_mbar_state *s) +{ + int irq; + int vector; + int level; + + irq = m5206_find_pending_irq(s); + if (irq) { + int tmp; + tmp = s->icr[irq]; + level = (tmp >> 2) & 7; + if (tmp & 0x80) { + /* Autovector. */ + vector = 24 + level; + } else { + switch (irq) { + case 8: /* SWT */ + vector = s->swivr; + break; + case 12: /* UART1 */ + vector = s->uivr[0]; + break; + case 13: /* UART2 */ + vector = s->uivr[1]; + break; + default: + /* Unknown vector. */ + fprintf(stderr, "Unhandled vector for IRQ %d\n", irq); + vector = 0xf; + break; + } + } + } else { + level = 0; + vector = 0; + } + m68k_set_irq_level(s->cpu, level, vector); +} + +static void m5206_mbar_set_irq(void *opaque, int irq, int level) +{ + m5206_mbar_state *s = (m5206_mbar_state *)opaque; + if (level) { + s->ipr |= 1 << irq; + } else { + s->ipr &= ~(1 << irq); + } + m5206_mbar_update(s); +} + +/* System Integration Module. */ + +static void m5206_mbar_reset(m5206_mbar_state *s) +{ + s->scr = 0xc0; + s->icr[1] = 0x04; + s->icr[2] = 0x08; + s->icr[3] = 0x0c; + s->icr[4] = 0x10; + s->icr[5] = 0x14; + s->icr[6] = 0x18; + s->icr[7] = 0x1c; + s->icr[8] = 0x1c; + s->icr[9] = 0x80; + s->icr[10] = 0x80; + s->icr[11] = 0x80; + s->icr[12] = 0x00; + s->icr[13] = 0x00; + s->imr = 0x3ffe; + s->rsr = 0x80; + s->swivr = 0x0f; + s->par = 0; +} + +static uint64_t m5206_mbar_read(m5206_mbar_state *s, + uint64_t offset, unsigned size) +{ + if (offset >= 0x100 && offset < 0x120) { + return m5206_timer_read(s->timer[0], offset - 0x100); + } else if (offset >= 0x120 && offset < 0x140) { + return m5206_timer_read(s->timer[1], offset - 0x120); + } else if (offset >= 0x140 && offset < 0x160) { + return mcf_uart_read(s->uart[0], offset - 0x140, size); + } else if (offset >= 0x180 && offset < 0x1a0) { + return mcf_uart_read(s->uart[1], offset - 0x180, size); + } + switch (offset) { + case 0x03: return s->scr; + case 0x14 ... 0x20: return s->icr[offset - 0x13]; + case 0x36: return s->imr; + case 0x3a: return s->ipr; + case 0x40: return s->rsr; + case 0x41: return 0; + case 0x42: return s->swivr; + case 0x50: + /* DRAM mask register. */ + /* FIXME: currently hardcoded to 128Mb. */ + { + uint32_t mask = ~0; + while (mask > ram_size) + mask >>= 1; + return mask & 0x0ffe0000; + } + case 0x5c: return 1; /* DRAM bank 1 empty. */ + case 0xcb: return s->par; + case 0x170: return s->uivr[0]; + case 0x1b0: return s->uivr[1]; + } + hw_error("Bad MBAR read offset 0x%x", (int)offset); + return 0; +} + +static void m5206_mbar_write(m5206_mbar_state *s, uint32_t offset, + uint64_t value, unsigned size) +{ + if (offset >= 0x100 && offset < 0x120) { + m5206_timer_write(s->timer[0], offset - 0x100, value); + return; + } else if (offset >= 0x120 && offset < 0x140) { + m5206_timer_write(s->timer[1], offset - 0x120, value); + return; + } else if (offset >= 0x140 && offset < 0x160) { + mcf_uart_write(s->uart[0], offset - 0x140, value, size); + return; + } else if (offset >= 0x180 && offset < 0x1a0) { + mcf_uart_write(s->uart[1], offset - 0x180, value, size); + return; + } + switch (offset) { + case 0x03: + s->scr = value; + break; + case 0x14 ... 0x20: + s->icr[offset - 0x13] = value; + m5206_mbar_update(s); + break; + case 0x36: + s->imr = value; + m5206_mbar_update(s); + break; + case 0x40: + s->rsr &= ~value; + break; + case 0x41: + /* TODO: implement watchdog. */ + break; + case 0x42: + s->swivr = value; + break; + case 0xcb: + s->par = value; + break; + case 0x170: + s->uivr[0] = value; + break; + case 0x178: case 0x17c: case 0x1c8: case 0x1bc: + /* Not implemented: UART Output port bits. */ + break; + case 0x1b0: + s->uivr[1] = value; + break; + default: + hw_error("Bad MBAR write offset 0x%x", (int)offset); + break; + } +} + +/* Internal peripherals use a variety of register widths. + This lookup table allows a single routine to handle all of them. */ +static const uint8_t m5206_mbar_width[] = +{ + /* 000-040 */ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, + /* 040-080 */ 1, 2, 2, 2, 4, 1, 2, 4, 1, 2, 4, 2, 2, 4, 2, 2, + /* 080-0c0 */ 4, 2, 2, 4, 2, 2, 4, 2, 2, 4, 2, 2, 4, 2, 2, 4, + /* 0c0-100 */ 2, 2, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + /* 100-140 */ 2, 2, 2, 2, 1, 0, 0, 0, 2, 2, 2, 2, 1, 0, 0, 0, + /* 140-180 */ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + /* 180-1c0 */ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + /* 1c0-200 */ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, +}; + +static uint32_t m5206_mbar_readw(void *opaque, hwaddr offset); +static uint32_t m5206_mbar_readl(void *opaque, hwaddr offset); + +static uint32_t m5206_mbar_readb(void *opaque, hwaddr offset) +{ + m5206_mbar_state *s = (m5206_mbar_state *)opaque; + offset &= 0x3ff; + if (offset >= 0x200) { + hw_error("Bad MBAR read offset 0x%x", (int)offset); + } + if (m5206_mbar_width[offset >> 2] > 1) { + uint16_t val; + val = m5206_mbar_readw(opaque, offset & ~1); + if ((offset & 1) == 0) { + val >>= 8; + } + return val & 0xff; + } + return m5206_mbar_read(s, offset, 1); +} + +static uint32_t m5206_mbar_readw(void *opaque, hwaddr offset) +{ + m5206_mbar_state *s = (m5206_mbar_state *)opaque; + int width; + offset &= 0x3ff; + if (offset >= 0x200) { + hw_error("Bad MBAR read offset 0x%x", (int)offset); + } + width = m5206_mbar_width[offset >> 2]; + if (width > 2) { + uint32_t val; + val = m5206_mbar_readl(opaque, offset & ~3); + if ((offset & 3) == 0) + val >>= 16; + return val & 0xffff; + } else if (width < 2) { + uint16_t val; + val = m5206_mbar_readb(opaque, offset) << 8; + val |= m5206_mbar_readb(opaque, offset + 1); + return val; + } + return m5206_mbar_read(s, offset, 2); +} + +static uint32_t m5206_mbar_readl(void *opaque, hwaddr offset) +{ + m5206_mbar_state *s = (m5206_mbar_state *)opaque; + int width; + offset &= 0x3ff; + if (offset >= 0x200) { + hw_error("Bad MBAR read offset 0x%x", (int)offset); + } + width = m5206_mbar_width[offset >> 2]; + if (width < 4) { + uint32_t val; + val = m5206_mbar_readw(opaque, offset) << 16; + val |= m5206_mbar_readw(opaque, offset + 2); + return val; + } + return m5206_mbar_read(s, offset, 4); +} + +static void m5206_mbar_writew(void *opaque, hwaddr offset, + uint32_t value); +static void m5206_mbar_writel(void *opaque, hwaddr offset, + uint32_t value); + +static void m5206_mbar_writeb(void *opaque, hwaddr offset, + uint32_t value) +{ + m5206_mbar_state *s = (m5206_mbar_state *)opaque; + int width; + offset &= 0x3ff; + if (offset >= 0x200) { + hw_error("Bad MBAR write offset 0x%x", (int)offset); + } + width = m5206_mbar_width[offset >> 2]; + if (width > 1) { + uint32_t tmp; + tmp = m5206_mbar_readw(opaque, offset & ~1); + if (offset & 1) { + tmp = (tmp & 0xff00) | value; + } else { + tmp = (tmp & 0x00ff) | (value << 8); + } + m5206_mbar_writew(opaque, offset & ~1, tmp); + return; + } + m5206_mbar_write(s, offset, value, 1); +} + +static void m5206_mbar_writew(void *opaque, hwaddr offset, + uint32_t value) +{ + m5206_mbar_state *s = (m5206_mbar_state *)opaque; + int width; + offset &= 0x3ff; + if (offset >= 0x200) { + hw_error("Bad MBAR write offset 0x%x", (int)offset); + } + width = m5206_mbar_width[offset >> 2]; + if (width > 2) { + uint32_t tmp; + tmp = m5206_mbar_readl(opaque, offset & ~3); + if (offset & 3) { + tmp = (tmp & 0xffff0000) | value; + } else { + tmp = (tmp & 0x0000ffff) | (value << 16); + } + m5206_mbar_writel(opaque, offset & ~3, tmp); + return; + } else if (width < 2) { + m5206_mbar_writeb(opaque, offset, value >> 8); + m5206_mbar_writeb(opaque, offset + 1, value & 0xff); + return; + } + m5206_mbar_write(s, offset, value, 2); +} + +static void m5206_mbar_writel(void *opaque, hwaddr offset, + uint32_t value) +{ + m5206_mbar_state *s = (m5206_mbar_state *)opaque; + int width; + offset &= 0x3ff; + if (offset >= 0x200) { + hw_error("Bad MBAR write offset 0x%x", (int)offset); + } + width = m5206_mbar_width[offset >> 2]; + if (width < 4) { + m5206_mbar_writew(opaque, offset, value >> 16); + m5206_mbar_writew(opaque, offset + 2, value & 0xffff); + return; + } + m5206_mbar_write(s, offset, value, 4); +} + +static const MemoryRegionOps m5206_mbar_ops = { + .old_mmio = { + .read = { + m5206_mbar_readb, + m5206_mbar_readw, + m5206_mbar_readl, + }, + .write = { + m5206_mbar_writeb, + m5206_mbar_writew, + m5206_mbar_writel, + }, + }, + .endianness = DEVICE_NATIVE_ENDIAN, +}; + +qemu_irq *mcf5206_init(MemoryRegion *sysmem, uint32_t base, M68kCPU *cpu) +{ + m5206_mbar_state *s; + qemu_irq *pic; + + s = (m5206_mbar_state *)g_malloc0(sizeof(m5206_mbar_state)); + + memory_region_init_io(&s->iomem, NULL, &m5206_mbar_ops, s, + "mbar", 0x00001000); + memory_region_add_subregion(sysmem, base, &s->iomem); + + pic = qemu_allocate_irqs(m5206_mbar_set_irq, s, 14); + s->timer[0] = m5206_timer_init(pic[9]); + s->timer[1] = m5206_timer_init(pic[10]); + s->uart[0] = mcf_uart_init(pic[12], serial_hds[0]); + s->uart[1] = mcf_uart_init(pic[13], serial_hds[1]); + s->cpu = cpu; + + m5206_mbar_reset(s); + return pic; +} diff --git a/qemu/hw/m68k/mcf5208.c b/qemu/hw/m68k/mcf5208.c new file mode 100644 index 000000000..326a42d27 --- /dev/null +++ b/qemu/hw/m68k/mcf5208.c @@ -0,0 +1,309 @@ +/* + * Motorola ColdFire MCF5208 SoC emulation. + * + * Copyright (c) 2007 CodeSourcery. + * + * This code is licensed under the GPL + */ +#include "hw/hw.h" +#include "hw/m68k/mcf.h" +#include "qemu/timer.h" +#include "hw/ptimer.h" +#include "sysemu/sysemu.h" +#include "sysemu/qtest.h" +#include "net/net.h" +#include "hw/boards.h" +#include "hw/loader.h" +#include "elf.h" +#include "exec/address-spaces.h" + +#define SYS_FREQ 66000000 + +#define PCSR_EN 0x0001 +#define PCSR_RLD 0x0002 +#define PCSR_PIF 0x0004 +#define PCSR_PIE 0x0008 +#define PCSR_OVW 0x0010 +#define PCSR_DBG 0x0020 +#define PCSR_DOZE 0x0040 +#define PCSR_PRE_SHIFT 8 +#define PCSR_PRE_MASK 0x0f00 + +typedef struct { + MemoryRegion iomem; + qemu_irq irq; + ptimer_state *timer; + uint16_t pcsr; + uint16_t pmr; + uint16_t pcntr; +} m5208_timer_state; + +static void m5208_timer_update(m5208_timer_state *s) +{ + if ((s->pcsr & (PCSR_PIE | PCSR_PIF)) == (PCSR_PIE | PCSR_PIF)) + qemu_irq_raise(s->irq); + else + qemu_irq_lower(s->irq); +} + +static void m5208_timer_write(void *opaque, hwaddr offset, + uint64_t value, unsigned size) +{ + m5208_timer_state *s = (m5208_timer_state *)opaque; + int prescale; + int limit; + switch (offset) { + case 0: + /* The PIF bit is set-to-clear. */ + if (value & PCSR_PIF) { + s->pcsr &= ~PCSR_PIF; + value &= ~PCSR_PIF; + } + /* Avoid frobbing the timer if we're just twiddling IRQ bits. */ + if (((s->pcsr ^ value) & ~PCSR_PIE) == 0) { + s->pcsr = value; + m5208_timer_update(s); + return; + } + + if (s->pcsr & PCSR_EN) + ptimer_stop(s->timer); + + s->pcsr = value; + + prescale = 1 << ((s->pcsr & PCSR_PRE_MASK) >> PCSR_PRE_SHIFT); + ptimer_set_freq(s->timer, (SYS_FREQ / 2) / prescale); + if (s->pcsr & PCSR_RLD) + limit = s->pmr; + else + limit = 0xffff; + ptimer_set_limit(s->timer, limit, 0); + + if (s->pcsr & PCSR_EN) + ptimer_run(s->timer, 0); + break; + case 2: + s->pmr = value; + s->pcsr &= ~PCSR_PIF; + if ((s->pcsr & PCSR_RLD) == 0) { + if (s->pcsr & PCSR_OVW) + ptimer_set_count(s->timer, value); + } else { + ptimer_set_limit(s->timer, value, s->pcsr & PCSR_OVW); + } + break; + case 4: + break; + default: + hw_error("m5208_timer_write: Bad offset 0x%x\n", (int)offset); + break; + } + m5208_timer_update(s); +} + +static void m5208_timer_trigger(void *opaque) +{ + m5208_timer_state *s = (m5208_timer_state *)opaque; + s->pcsr |= PCSR_PIF; + m5208_timer_update(s); +} + +static uint64_t m5208_timer_read(void *opaque, hwaddr addr, + unsigned size) +{ + m5208_timer_state *s = (m5208_timer_state *)opaque; + switch (addr) { + case 0: + return s->pcsr; + case 2: + return s->pmr; + case 4: + return ptimer_get_count(s->timer); + default: + hw_error("m5208_timer_read: Bad offset 0x%x\n", (int)addr); + return 0; + } +} + +static const MemoryRegionOps m5208_timer_ops = { + .read = m5208_timer_read, + .write = m5208_timer_write, + .endianness = DEVICE_NATIVE_ENDIAN, +}; + +static uint64_t m5208_sys_read(void *opaque, hwaddr addr, + unsigned size) +{ + switch (addr) { + case 0x110: /* SDCS0 */ + { + int n; + for (n = 0; n < 32; n++) { + if (ram_size < (2u << n)) + break; + } + return (n - 1) | 0x40000000; + } + case 0x114: /* SDCS1 */ + return 0; + + default: + hw_error("m5208_sys_read: Bad offset 0x%x\n", (int)addr); + return 0; + } +} + +static void m5208_sys_write(void *opaque, hwaddr addr, + uint64_t value, unsigned size) +{ + hw_error("m5208_sys_write: Bad offset 0x%x\n", (int)addr); +} + +static const MemoryRegionOps m5208_sys_ops = { + .read = m5208_sys_read, + .write = m5208_sys_write, + .endianness = DEVICE_NATIVE_ENDIAN, +}; + +static void mcf5208_sys_init(MemoryRegion *address_space, qemu_irq *pic) +{ + MemoryRegion *iomem = g_new(MemoryRegion, 1); + m5208_timer_state *s; + QEMUBH *bh; + int i; + + /* SDRAMC. */ + memory_region_init_io(iomem, NULL, &m5208_sys_ops, NULL, "m5208-sys", 0x00004000); + memory_region_add_subregion(address_space, 0xfc0a8000, iomem); + /* Timers. */ + for (i = 0; i < 2; i++) { + s = (m5208_timer_state *)g_malloc0(sizeof(m5208_timer_state)); + bh = qemu_bh_new(m5208_timer_trigger, s); + s->timer = ptimer_init(bh); + memory_region_init_io(&s->iomem, NULL, &m5208_timer_ops, s, + "m5208-timer", 0x00004000); + memory_region_add_subregion(address_space, 0xfc080000 + 0x4000 * i, + &s->iomem); + s->irq = pic[4 + i]; + } +} + +static void mcf5208evb_init(MachineState *machine) +{ + ram_addr_t ram_size = machine->ram_size; + const char *cpu_model = machine->cpu_model; + const char *kernel_filename = machine->kernel_filename; + M68kCPU *cpu; + CPUM68KState *env; + int kernel_size; + uint64_t elf_entry; + hwaddr entry; + qemu_irq *pic; + MemoryRegion *address_space_mem = get_system_memory(); + MemoryRegion *ram = g_new(MemoryRegion, 1); + MemoryRegion *sram = g_new(MemoryRegion, 1); + + if (!cpu_model) { + cpu_model = "m5208"; + } + cpu = cpu_m68k_init(cpu_model); + if (!cpu) { + fprintf(stderr, "Unable to find m68k CPU definition\n"); + exit(1); + } + env = &cpu->env; + + /* Initialize CPU registers. */ + env->vbr = 0; + /* TODO: Configure BARs. */ + + /* DRAM at 0x40000000 */ + memory_region_allocate_system_memory(ram, NULL, "mcf5208.ram", ram_size); + memory_region_add_subregion(address_space_mem, 0x40000000, ram); + + /* Internal SRAM. */ + memory_region_init_ram(sram, NULL, "mcf5208.sram", 16384, &error_abort); + vmstate_register_ram_global(sram); + memory_region_add_subregion(address_space_mem, 0x80000000, sram); + + /* Internal peripherals. */ + pic = mcf_intc_init(address_space_mem, 0xfc048000, cpu); + + mcf_uart_mm_init(address_space_mem, 0xfc060000, pic[26], serial_hds[0]); + mcf_uart_mm_init(address_space_mem, 0xfc064000, pic[27], serial_hds[1]); + mcf_uart_mm_init(address_space_mem, 0xfc068000, pic[28], serial_hds[2]); + + mcf5208_sys_init(address_space_mem, pic); + + if (nb_nics > 1) { + fprintf(stderr, "Too many NICs\n"); + exit(1); + } + if (nd_table[0].used) + mcf_fec_init(address_space_mem, &nd_table[0], + 0xfc030000, pic + 36); + + /* 0xfc000000 SCM. */ + /* 0xfc004000 XBS. */ + /* 0xfc008000 FlexBus CS. */ + /* 0xfc030000 FEC. */ + /* 0xfc040000 SCM + Power management. */ + /* 0xfc044000 eDMA. */ + /* 0xfc048000 INTC. */ + /* 0xfc058000 I2C. */ + /* 0xfc05c000 QSPI. */ + /* 0xfc060000 UART0. */ + /* 0xfc064000 UART0. */ + /* 0xfc068000 UART0. */ + /* 0xfc070000 DMA timers. */ + /* 0xfc080000 PIT0. */ + /* 0xfc084000 PIT1. */ + /* 0xfc088000 EPORT. */ + /* 0xfc08c000 Watchdog. */ + /* 0xfc090000 clock module. */ + /* 0xfc0a0000 CCM + reset. */ + /* 0xfc0a4000 GPIO. */ + /* 0xfc0a8000 SDRAM controller. */ + + /* Load kernel. */ + if (!kernel_filename) { + if (qtest_enabled()) { + return; + } + fprintf(stderr, "Kernel image must be specified\n"); + exit(1); + } + + kernel_size = load_elf(kernel_filename, NULL, NULL, &elf_entry, + NULL, NULL, 1, ELF_MACHINE, 0); + entry = elf_entry; + if (kernel_size < 0) { + kernel_size = load_uimage(kernel_filename, &entry, NULL, NULL, + NULL, NULL); + } + if (kernel_size < 0) { + kernel_size = load_image_targphys(kernel_filename, 0x40000000, + ram_size); + entry = 0x40000000; + } + if (kernel_size < 0) { + fprintf(stderr, "qemu: could not load kernel '%s'\n", kernel_filename); + exit(1); + } + + env->pc = entry; +} + +static QEMUMachine mcf5208evb_machine = { + .name = "mcf5208evb", + .desc = "MCF5206EVB", + .init = mcf5208evb_init, + .is_default = 1, +}; + +static void mcf5208evb_machine_init(void) +{ + qemu_register_machine(&mcf5208evb_machine); +} + +machine_init(mcf5208evb_machine_init); diff --git a/qemu/hw/m68k/mcf_intc.c b/qemu/hw/m68k/mcf_intc.c new file mode 100644 index 000000000..f13c7f3ae --- /dev/null +++ b/qemu/hw/m68k/mcf_intc.c @@ -0,0 +1,168 @@ +/* + * ColdFire Interrupt Controller emulation. + * + * Copyright (c) 2007 CodeSourcery. + * + * This code is licensed under the GPL + */ +#include "hw/hw.h" +#include "hw/m68k/mcf.h" +#include "exec/address-spaces.h" + +typedef struct { + MemoryRegion iomem; + uint64_t ipr; + uint64_t imr; + uint64_t ifr; + uint64_t enabled; + uint8_t icr[64]; + M68kCPU *cpu; + int active_vector; +} mcf_intc_state; + +static void mcf_intc_update(mcf_intc_state *s) +{ + uint64_t active; + int i; + int best; + int best_level; + + active = (s->ipr | s->ifr) & s->enabled & ~s->imr; + best_level = 0; + best = 64; + if (active) { + for (i = 0; i < 64; i++) { + if ((active & 1) != 0 && s->icr[i] >= best_level) { + best_level = s->icr[i]; + best = i; + } + active >>= 1; + } + } + s->active_vector = ((best == 64) ? 24 : (best + 64)); + m68k_set_irq_level(s->cpu, best_level, s->active_vector); +} + +static uint64_t mcf_intc_read(void *opaque, hwaddr addr, + unsigned size) +{ + int offset; + mcf_intc_state *s = (mcf_intc_state *)opaque; + offset = addr & 0xff; + if (offset >= 0x40 && offset < 0x80) { + return s->icr[offset - 0x40]; + } + switch (offset) { + case 0x00: + return (uint32_t)(s->ipr >> 32); + case 0x04: + return (uint32_t)s->ipr; + case 0x08: + return (uint32_t)(s->imr >> 32); + case 0x0c: + return (uint32_t)s->imr; + case 0x10: + return (uint32_t)(s->ifr >> 32); + case 0x14: + return (uint32_t)s->ifr; + case 0xe0: /* SWIACK. */ + return s->active_vector; + case 0xe1: case 0xe2: case 0xe3: case 0xe4: + case 0xe5: case 0xe6: case 0xe7: + /* LnIACK */ + hw_error("mcf_intc_read: LnIACK not implemented\n"); + default: + return 0; + } +} + +static void mcf_intc_write(void *opaque, hwaddr addr, + uint64_t val, unsigned size) +{ + int offset; + mcf_intc_state *s = (mcf_intc_state *)opaque; + offset = addr & 0xff; + if (offset >= 0x40 && offset < 0x80) { + int n = offset - 0x40; + s->icr[n] = val; + if (val == 0) + s->enabled &= ~(1ull << n); + else + s->enabled |= (1ull << n); + mcf_intc_update(s); + return; + } + switch (offset) { + case 0x00: case 0x04: + /* Ignore IPR writes. */ + return; + case 0x08: + s->imr = (s->imr & 0xffffffff) | ((uint64_t)val << 32); + break; + case 0x0c: + s->imr = (s->imr & 0xffffffff00000000ull) | (uint32_t)val; + break; + case 0x1c: + if (val & 0x40) { + s->imr = ~0ull; + } else { + s->imr |= (0x1ull << (val & 0x3f)); + } + break; + case 0x1d: + if (val & 0x40) { + s->imr = 0ull; + } else { + s->imr &= ~(0x1ull << (val & 0x3f)); + } + break; + default: + hw_error("mcf_intc_write: Bad write offset %d\n", offset); + break; + } + mcf_intc_update(s); +} + +static void mcf_intc_set_irq(void *opaque, int irq, int level) +{ + mcf_intc_state *s = (mcf_intc_state *)opaque; + if (irq >= 64) + return; + if (level) + s->ipr |= 1ull << irq; + else + s->ipr &= ~(1ull << irq); + mcf_intc_update(s); +} + +static void mcf_intc_reset(mcf_intc_state *s) +{ + s->imr = ~0ull; + s->ipr = 0; + s->ifr = 0; + s->enabled = 0; + memset(s->icr, 0, 64); + s->active_vector = 24; +} + +static const MemoryRegionOps mcf_intc_ops = { + .read = mcf_intc_read, + .write = mcf_intc_write, + .endianness = DEVICE_NATIVE_ENDIAN, +}; + +qemu_irq *mcf_intc_init(MemoryRegion *sysmem, + hwaddr base, + M68kCPU *cpu) +{ + mcf_intc_state *s; + + s = g_malloc0(sizeof(mcf_intc_state)); + s->cpu = cpu; + mcf_intc_reset(s); + + memory_region_init_io(&s->iomem, NULL, &mcf_intc_ops, s, "mcf", 0x100); + memory_region_add_subregion(sysmem, base, &s->iomem); + + return qemu_allocate_irqs(mcf_intc_set_irq, s, 64); +} |