From 20dcee9483361ee0621cf6d68d271ecde686fd9c Mon Sep 17 00:00:00 2001 From: pbrook Date: Sun, 3 Jun 2007 11:13:39 +0000 Subject: [PATCH] MCF5208 emulation. git-svn-id: svn://svn.savannah.nongnu.org/qemu/trunk@2924 c046a42c-6fe2-441c-8c8c-71466251a162 --- Makefile.target | 2 +- hw/an5206.c | 4 +- hw/mcf5206.c | 289 ++----------------------------------- hw/mcf5208.c | 285 +++++++++++++++++++++++++++++++++++++ hw/mcf_intc.c | 156 ++++++++++++++++++++ hw/mcf_uart.c | 306 ++++++++++++++++++++++++++++++++++++++++ target-m68k/cpu.h | 13 ++ target-m68k/helper.c | 26 +++- target-m68k/op.c | 7 + target-m68k/op_helper.c | 18 +-- target-m68k/translate.c | 9 +- vl.c | 1 + vl.h | 13 ++ 13 files changed, 833 insertions(+), 296 deletions(-) create mode 100644 hw/mcf5208.c create mode 100644 hw/mcf_intc.c create mode 100644 hw/mcf_uart.c diff --git a/Makefile.target b/Makefile.target index 224474ba7d..1d1dacfb03 100644 --- a/Makefile.target +++ b/Makefile.target @@ -467,7 +467,7 @@ ifeq ($(TARGET_BASE_ARCH), sh4) VL_OBJS+= shix.o sh7750.o sh7750_regnames.o tc58128.o endif ifeq ($(TARGET_BASE_ARCH), m68k) -VL_OBJS+= an5206.o mcf5206.o ptimer.o +VL_OBJS+= an5206.o mcf5206.o ptimer.o mcf_uart.o mcf_intc.o mcf5208.o VL_OBJS+= m68k-semi.o endif ifdef CONFIG_GDBSTUB diff --git a/hw/an5206.c b/hw/an5206.c index 1306d1964d..379f48e786 100644 --- a/hw/an5206.c +++ b/hw/an5206.c @@ -40,7 +40,9 @@ static void an5206_init(int ram_size, int vga_ram_size, int boot_device, env = cpu_init(); if (!cpu_model) cpu_model = "m5206"; - cpu_m68k_set_model(env, cpu_model); + if (cpu_m68k_set_model(env, cpu_model)) { + cpu_abort(env, "Unable to find m68k CPU definition\n"); + } /* Initialize CPU registers. */ env->vbr = 0; diff --git a/hw/mcf5206.c b/hw/mcf5206.c index 0da791293d..ce4676bdf1 100644 --- a/hw/mcf5206.c +++ b/hw/mcf5206.c @@ -139,285 +139,12 @@ static m5206_timer_state *m5206_timer_init(qemu_irq irq) return s; } -/* UART */ - -typedef struct { - uint8_t mr[2]; - uint8_t sr; - uint8_t isr; - uint8_t imr; - uint8_t bg1; - uint8_t bg2; - uint8_t fifo[4]; - uint8_t tb; - int current_mr; - int fifo_len; - int tx_enabled; - int rx_enabled; - qemu_irq irq; - CharDriverState *chr; -} m5206_uart_state; - -/* UART Status Register bits. */ -#define M5206_UART_RxRDY 0x01 -#define M5206_UART_FFULL 0x02 -#define M5206_UART_TxRDY 0x04 -#define M5206_UART_TxEMP 0x08 -#define M5206_UART_OE 0x10 -#define M5206_UART_PE 0x20 -#define M5206_UART_FE 0x40 -#define M5206_UART_RB 0x80 - -/* Interrupt flags. */ -#define M5206_UART_TxINT 0x01 -#define M5206_UART_RxINT 0x02 -#define M5206_UART_DBINT 0x04 -#define M5206_UART_COSINT 0x80 - -/* UMR1 flags. */ -#define M5206_UART_BC0 0x01 -#define M5206_UART_BC1 0x02 -#define M5206_UART_PT 0x04 -#define M5206_UART_PM0 0x08 -#define M5206_UART_PM1 0x10 -#define M5206_UART_ERR 0x20 -#define M5206_UART_RxIRQ 0x40 -#define M5206_UART_RxRTS 0x80 - -static void m5206_uart_update(m5206_uart_state *s) -{ - s->isr &= ~(M5206_UART_TxINT | M5206_UART_RxINT); - if (s->sr & M5206_UART_TxRDY) - s->isr |= M5206_UART_TxINT; - if ((s->sr & ((s->mr[0] & M5206_UART_RxIRQ) - ? M5206_UART_FFULL : M5206_UART_RxRDY)) != 0) - s->isr |= M5206_UART_RxINT; - - qemu_set_irq(s->irq, (s->isr & s->imr) != 0); -} - -static uint32_t m5206_uart_read(m5206_uart_state *s, uint32_t addr) -{ - switch (addr) { - case 0x00: - return s->mr[s->current_mr]; - case 0x04: - return s->sr; - case 0x0c: - { - uint8_t val; - int i; - - if (s->fifo_len == 0) - return 0; - - val = s->fifo[0]; - s->fifo_len--; - for (i = 0; i < s->fifo_len; i++) - s->fifo[i] = s->fifo[i + 1]; - s->sr &= ~M5206_UART_FFULL; - if (s->fifo_len == 0) - s->sr &= ~M5206_UART_RxRDY; - m5206_uart_update(s); - return val; - } - case 0x10: - /* TODO: Implement IPCR. */ - return 0; - case 0x14: - return s->isr; - case 0x18: - return s->bg1; - case 0x1c: - return s->bg2; - default: - return 0; - } -} - -/* Update TxRDY flag and set data if present and enabled. */ -static void m5206_uart_do_tx(m5206_uart_state *s) -{ - if (s->tx_enabled && (s->sr & M5206_UART_TxEMP) == 0) { - if (s->chr) - qemu_chr_write(s->chr, (unsigned char *)&s->tb, 1); - s->sr |= M5206_UART_TxEMP; - } - if (s->tx_enabled) { - s->sr |= M5206_UART_TxRDY; - } else { - s->sr &= ~M5206_UART_TxRDY; - } -} - -static void m5206_do_command(m5206_uart_state *s, uint8_t cmd) -{ - /* Misc command. */ - switch ((cmd >> 4) & 3) { - case 0: /* No-op. */ - break; - case 1: /* Reset mode register pointer. */ - s->current_mr = 0; - break; - case 2: /* Reset receiver. */ - s->rx_enabled = 0; - s->fifo_len = 0; - s->sr &= ~(M5206_UART_RxRDY | M5206_UART_FFULL); - break; - case 3: /* Reset transmitter. */ - s->tx_enabled = 0; - s->sr |= M5206_UART_TxEMP; - s->sr &= ~M5206_UART_TxRDY; - break; - case 4: /* Reset error status. */ - break; - case 5: /* Reset break-change interrupt. */ - s->isr &= ~M5206_UART_DBINT; - break; - case 6: /* Start break. */ - case 7: /* Stop break. */ - break; - } - - /* Transmitter command. */ - switch ((cmd >> 2) & 3) { - case 0: /* No-op. */ - break; - case 1: /* Enable. */ - s->tx_enabled = 1; - m5206_uart_do_tx(s); - break; - case 2: /* Disable. */ - s->tx_enabled = 0; - m5206_uart_do_tx(s); - break; - case 3: /* Reserved. */ - fprintf(stderr, "m5206_uart: Bad TX command\n"); - break; - } - - /* Receiver command. */ - switch (cmd & 3) { - case 0: /* No-op. */ - break; - case 1: /* Enable. */ - s->rx_enabled = 1; - break; - case 2: - s->rx_enabled = 0; - break; - case 3: /* Reserved. */ - fprintf(stderr, "m5206_uart: Bad RX command\n"); - break; - } -} - -static void m5206_uart_write(m5206_uart_state *s, uint32_t addr, uint32_t val) -{ - switch (addr) { - case 0x00: - s->mr[s->current_mr] = val; - s->current_mr = 1; - break; - case 0x04: - /* CSR is ignored. */ - break; - case 0x08: /* Command Register. */ - m5206_do_command(s, val); - break; - case 0x0c: /* Transmit Buffer. */ - s->sr &= ~M5206_UART_TxEMP; - s->tb = val; - m5206_uart_do_tx(s); - break; - case 0x10: - /* ACR is ignored. */ - break; - case 0x14: - s->imr = val; - break; - default: - break; - } - m5206_uart_update(s); -} - -static void m5206_uart_reset(m5206_uart_state *s) -{ - s->fifo_len = 0; - s->mr[0] = 0; - s->mr[1] = 0; - s->sr = M5206_UART_TxEMP; - s->tx_enabled = 0; - s->rx_enabled = 0; - s->isr = 0; - s->imr = 0; -} - -static void m5206_uart_push_byte(m5206_uart_state *s, uint8_t data) -{ - /* Break events overwrite the last byte if the fifo is full. */ - if (s->fifo_len == 4) - s->fifo_len--; - - s->fifo[s->fifo_len] = data; - s->fifo_len++; - s->sr |= M5206_UART_RxRDY; - if (s->fifo_len == 4) - s->sr |= M5206_UART_FFULL; - - m5206_uart_update(s); -} - -static void m5206_uart_event(void *opaque, int event) -{ - m5206_uart_state *s = (m5206_uart_state *)opaque; - - switch (event) { - case CHR_EVENT_BREAK: - s->isr |= M5206_UART_DBINT; - m5206_uart_push_byte(s, 0); - break; - default: - break; - } -} - -static int m5206_uart_can_receive(void *opaque) -{ - m5206_uart_state *s = (m5206_uart_state *)opaque; - - return s->rx_enabled && (s->sr & M5206_UART_FFULL) == 0; -} - -static void m5206_uart_receive(void *opaque, const uint8_t *buf, int size) -{ - m5206_uart_state *s = (m5206_uart_state *)opaque; - - m5206_uart_push_byte(s, buf[0]); -} - -static m5206_uart_state *m5206_uart_init(qemu_irq irq, CharDriverState *chr) -{ - m5206_uart_state *s; - - s = qemu_mallocz(sizeof(m5206_uart_state)); - s->chr = chr; - s->irq = irq; - if (chr) { - qemu_chr_add_handlers(chr, m5206_uart_can_receive, m5206_uart_receive, - m5206_uart_event, s); - } - m5206_uart_reset(s); - return s; -} - /* System Integration Module. */ typedef struct { CPUState *env; m5206_timer_state *timer[2]; - m5206_uart_state *uart[2]; + void *uart[2]; uint8_t scr; uint8_t icr[14]; uint16_t imr; /* 1 == interrupt is masked. */ @@ -540,9 +267,9 @@ static uint32_t m5206_mbar_read(m5206_mbar_state *s, uint32_t offset) } else if (offset >= 0x120 && offset < 0x140) { return m5206_timer_read(s->timer[1], offset - 0x120); } else if (offset >= 0x140 && offset < 0x160) { - return m5206_uart_read(s->uart[0], offset - 0x140); + return mcf_uart_read(s->uart[0], offset - 0x140); } else if (offset >= 0x180 && offset < 0x1a0) { - return m5206_uart_read(s->uart[1], offset - 0x180); + return mcf_uart_read(s->uart[1], offset - 0x180); } switch (offset) { case 0x03: return s->scr; @@ -580,10 +307,10 @@ static void m5206_mbar_write(m5206_mbar_state *s, uint32_t offset, m5206_timer_write(s->timer[1], offset - 0x120, value); return; } else if (offset >= 0x140 && offset < 0x160) { - m5206_uart_write(s->uart[0], offset - 0x140, value); + mcf_uart_write(s->uart[0], offset - 0x140, value); return; } else if (offset >= 0x180 && offset < 0x1a0) { - m5206_uart_write(s->uart[1], offset - 0x180, value); + mcf_uart_write(s->uart[1], offset - 0x180, value); return; } switch (offset) { @@ -798,13 +525,13 @@ qemu_irq *mcf5206_init(uint32_t base, CPUState *env) s = (m5206_mbar_state *)qemu_mallocz(sizeof(m5206_mbar_state)); iomemtype = cpu_register_io_memory(0, m5206_mbar_readfn, m5206_mbar_writefn, s); - cpu_register_physical_memory(base, 0x00000fff, iomemtype); + cpu_register_physical_memory(base, 0x00001000, iomemtype); 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] = m5206_uart_init(pic[12], serial_hds[0]); - s->uart[1] = m5206_uart_init(pic[13], serial_hds[1]); + s->uart[0] = mcf_uart_init(pic[12], serial_hds[0]); + s->uart[1] = mcf_uart_init(pic[13], serial_hds[1]); s->env = env; m5206_mbar_reset(s); diff --git a/hw/mcf5208.c b/hw/mcf5208.c new file mode 100644 index 0000000000..49108e0929 --- /dev/null +++ b/hw/mcf5208.c @@ -0,0 +1,285 @@ +/* + * Motorola ColdFire MCF5208 SoC emulation. + * + * Copyright (c) 2007 CodeSourcery. + * + * This code is licenced under the GPL + */ +#include "vl.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 { + 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(m5208_timer_state *s, int offset, + uint32_t value) +{ + 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 = 0xffff; + else + limit = s->pmr; + 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) + value = 0xffff; + ptimer_set_limit(s->timer, value, s->pcsr & PCSR_OVW); + break; + case 4: + break; + default: + /* Should never happen. */ + abort(); + } + 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); +} + +typedef struct { + m5208_timer_state timer[2]; +} m5208_sys_state; + +static uint32_t m5208_sys_read(void *opaque, target_phys_addr_t addr) +{ + m5208_sys_state *s = (m5208_sys_state *)opaque; + switch (addr) { + /* PIT0 */ + case 0xfc080000: + return s->timer[0].pcsr; + case 0xfc080002: + return s->timer[0].pmr; + case 0xfc080004: + return ptimer_get_count(s->timer[0].timer); + /* PIT1 */ + case 0xfc084000: + return s->timer[1].pcsr; + case 0xfc084002: + return s->timer[1].pmr; + case 0xfc084004: + return ptimer_get_count(s->timer[1].timer); + + /* SDRAM Controller. */ + case 0xfc0a8110: /* SDCS0 */ + { + int n; + for (n = 0; n < 32; n++) { + if (ram_size < (2u << n)) + break; + } + return (n - 1) | 0x40000000; + } + case 0xfc0a8114: /* SDCS1 */ + return 0; + + default: + cpu_abort(cpu_single_env, "m5208_sys_read: Bad offset 0x%x\n", + (int)addr); + return 0; + } +} + +static void m5208_sys_write(void *opaque, target_phys_addr_t addr, + uint32_t value) +{ + m5208_sys_state *s = (m5208_sys_state *)opaque; + switch (addr) { + /* PIT0 */ + case 0xfc080000: + case 0xfc080002: + case 0xfc080004: + m5208_timer_write(&s->timer[0], addr & 0xf, value); + return; + /* PIT1 */ + case 0xfc084000: + case 0xfc084002: + case 0xfc084004: + m5208_timer_write(&s->timer[1], addr & 0xf, value); + return; + default: + cpu_abort(cpu_single_env, "m5208_sys_write: Bad offset 0x%x\n", + (int)addr); + break; + } +} + +static CPUReadMemoryFunc *m5208_sys_readfn[] = { + m5208_sys_read, + m5208_sys_read, + m5208_sys_read +}; + +static CPUWriteMemoryFunc *m5208_sys_writefn[] = { + m5208_sys_write, + m5208_sys_write, + m5208_sys_write +}; + +static void mcf5208_sys_init(qemu_irq *pic) +{ + int iomemtype; + m5208_sys_state *s; + QEMUBH *bh; + int i; + + s = (m5208_sys_state *)qemu_mallocz(sizeof(m5208_sys_state)); + iomemtype = cpu_register_io_memory(0, m5208_sys_readfn, + m5208_sys_writefn, s); + /* SDRAMC. */ + cpu_register_physical_memory(0xfc0a8000, 0x00004000, iomemtype); + /* Timers. */ + for (i = 0; i < 2; i++) { + bh = qemu_bh_new(m5208_timer_trigger, &s->timer[i]); + s->timer[i].timer = ptimer_init(bh); + cpu_register_physical_memory(0xfc080000 + 0x4000 * i, 0x00004000, + iomemtype); + s->timer[i].irq = pic[4 + i]; + } +} + +static void mcf5208evb_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, const char *cpu_model) +{ + CPUState *env; + int kernel_size; + uint64_t elf_entry; + target_ulong entry; + qemu_irq *pic; + + env = cpu_init(); + if (!cpu_model) + cpu_model = "m5208"; + if (cpu_m68k_set_model(env, cpu_model)) { + cpu_abort(env, "Unable to find m68k CPU definition\n"); + } + + /* Initialize CPU registers. */ + env->vbr = 0; + /* TODO: Configure BARs. */ + + /* DRAM at 0x20000000 */ + cpu_register_physical_memory(0x40000000, ram_size, + qemu_ram_alloc(ram_size) | IO_MEM_RAM); + + /* Internal SRAM. */ + cpu_register_physical_memory(0x80000000, 16384, + qemu_ram_alloc(16384) | IO_MEM_RAM); + + /* Internal peripherals. */ + pic = mcf_intc_init(0xfc048000, env); + + mcf_uart_mm_init(0xfc060000, pic[26], serial_hds[0]); + mcf_uart_mm_init(0xfc064000, pic[27], serial_hds[1]); + mcf_uart_mm_init(0xfc068000, pic[28], serial_hds[2]); + + mcf5208_sys_init(pic); + + /* 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) { + fprintf(stderr, "Kernel image must be specified\n"); + exit(1); + } + + kernel_size = load_elf(kernel_filename, 0, &elf_entry, NULL, NULL); + entry = elf_entry; + if (kernel_size < 0) { + kernel_size = load_uboot(kernel_filename, &entry, NULL); + } + if (kernel_size < 0) { + kernel_size = load_image(kernel_filename, phys_ram_base); + entry = 0x20000000; + } + if (kernel_size < 0) { + fprintf(stderr, "qemu: could not load kernel '%s'\n", kernel_filename); + exit(1); + } + + env->pc = entry; +} + +QEMUMachine mcf5208evb_machine = { + "mcf5208evb", + "MCF5206EVB", + mcf5208evb_init, +}; diff --git a/hw/mcf_intc.c b/hw/mcf_intc.c new file mode 100644 index 0000000000..5bdebf8bbe --- /dev/null +++ b/hw/mcf_intc.c @@ -0,0 +1,156 @@ +/* + * ColdFire Interrupt Controller emulation. + * + * Copyright (c) 2007 CodeSourcery. + * + * This code is licenced under the GPL + */ +#include "vl.h" + +typedef struct { + uint64_t ipr; + uint64_t imr; + uint64_t ifr; + uint64_t enabled; + uint8_t icr[64]; + CPUState *env; + 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->env, best_level, s->active_vector); +} + +static uint32_t mcf_intc_read(void *opaque, target_phys_addr_t addr) +{ + 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 */ + cpu_abort(cpu_single_env, "mcf_intc_read: LnIACK not implemented\n"); + default: + return 0; + } +} + +static void mcf_intc_write(void *opaque, target_phys_addr_t addr, uint32_t val) +{ + 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; + default: + cpu_abort(cpu_single_env, "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 CPUReadMemoryFunc *mcf_intc_readfn[] = { + mcf_intc_read, + mcf_intc_read, + mcf_intc_read +}; + +static CPUWriteMemoryFunc *mcf_intc_writefn[] = { + mcf_intc_write, + mcf_intc_write, + mcf_intc_write +}; + +qemu_irq *mcf_intc_init(target_phys_addr_t base, CPUState *env) +{ + mcf_intc_state *s; + int iomemtype; + + s = qemu_mallocz(sizeof(mcf_intc_state)); + s->env = env; + mcf_intc_reset(s); + + iomemtype = cpu_register_io_memory(0, mcf_intc_readfn, + mcf_intc_writefn, s); + cpu_register_physical_memory(base, 0x100, iomemtype); + + return qemu_allocate_irqs(mcf_intc_set_irq, s, 64); +} diff --git a/hw/mcf_uart.c b/hw/mcf_uart.c new file mode 100644 index 0000000000..fcdfc4453f --- /dev/null +++ b/hw/mcf_uart.c @@ -0,0 +1,306 @@ +/* + * ColdFire UART emulation. + * + * Copyright (c) 2007 CodeSourcery. + * + * This code is licenced under the GPL + */ +#include "vl.h" + +typedef struct { + uint8_t mr[2]; + uint8_t sr; + uint8_t isr; + uint8_t imr; + uint8_t bg1; + uint8_t bg2; + uint8_t fifo[4]; + uint8_t tb; + int current_mr; + int fifo_len; + int tx_enabled; + int rx_enabled; + qemu_irq irq; + CharDriverState *chr; +} mcf_uart_state; + +/* UART Status Register bits. */ +#define MCF_UART_RxRDY 0x01 +#define MCF_UART_FFULL 0x02 +#define MCF_UART_TxRDY 0x04 +#define MCF_UART_TxEMP 0x08 +#define MCF_UART_OE 0x10 +#define MCF_UART_PE 0x20 +#define MCF_UART_FE 0x40 +#define MCF_UART_RB 0x80 + +/* Interrupt flags. */ +#define MCF_UART_TxINT 0x01 +#define MCF_UART_RxINT 0x02 +#define MCF_UART_DBINT 0x04 +#define MCF_UART_COSINT 0x80 + +/* UMR1 flags. */ +#define MCF_UART_BC0 0x01 +#define MCF_UART_BC1 0x02 +#define MCF_UART_PT 0x04 +#define MCF_UART_PM0 0x08 +#define MCF_UART_PM1 0x10 +#define MCF_UART_ERR 0x20 +#define MCF_UART_RxIRQ 0x40 +#define MCF_UART_RxRTS 0x80 + +static void mcf_uart_update(mcf_uart_state *s) +{ + s->isr &= ~(MCF_UART_TxINT | MCF_UART_RxINT); + if (s->sr & MCF_UART_TxRDY) + s->isr |= MCF_UART_TxINT; + if ((s->sr & ((s->mr[0] & MCF_UART_RxIRQ) + ? MCF_UART_FFULL : MCF_UART_RxRDY)) != 0) + s->isr |= MCF_UART_RxINT; + + qemu_set_irq(s->irq, (s->isr & s->imr) != 0); +} + +uint32_t mcf_uart_read(void *opaque, target_phys_addr_t addr) +{ + mcf_uart_state *s = (mcf_uart_state *)opaque; + switch (addr & 0x3f) { + case 0x00: + return s->mr[s->current_mr]; + case 0x04: + return s->sr; + case 0x0c: + { + uint8_t val; + int i; + + if (s->fifo_len == 0) + return 0; + + val = s->fifo[0]; + s->fifo_len--; + for (i = 0; i < s->fifo_len; i++) + s->fifo[i] = s->fifo[i + 1]; + s->sr &= ~MCF_UART_FFULL; + if (s->fifo_len == 0) + s->sr &= ~MCF_UART_RxRDY; + mcf_uart_update(s); + return val; + } + case 0x10: + /* TODO: Implement IPCR. */ + return 0; + case 0x14: + return s->isr; + case 0x18: + return s->bg1; + case 0x1c: + return s->bg2; + default: + return 0; + } +} + +/* Update TxRDY flag and set data if present and enabled. */ +static void mcf_uart_do_tx(mcf_uart_state *s) +{ + if (s->tx_enabled && (s->sr & MCF_UART_TxEMP) == 0) { + if (s->chr) + qemu_chr_write(s->chr, (unsigned char *)&s->tb, 1); + s->sr |= MCF_UART_TxEMP; + } + if (s->tx_enabled) { + s->sr |= MCF_UART_TxRDY; + } else { + s->sr &= ~MCF_UART_TxRDY; + } +} + +static void mcf_do_command(mcf_uart_state *s, uint8_t cmd) +{ + /* Misc command. */ + switch ((cmd >> 4) & 3) { + case 0: /* No-op. */ + break; + case 1: /* Reset mode register pointer. */ + s->current_mr = 0; + break; + case 2: /* Reset receiver. */ + s->rx_enabled = 0; + s->fifo_len = 0; + s->sr &= ~(MCF_UART_RxRDY | MCF_UART_FFULL); + break; + case 3: /* Reset transmitter. */ + s->tx_enabled = 0; + s->sr |= MCF_UART_TxEMP; + s->sr &= ~MCF_UART_TxRDY; + break; + case 4: /* Reset error status. */ + break; + case 5: /* Reset break-change interrupt. */ + s->isr &= ~MCF_UART_DBINT; + break; + case 6: /* Start break. */ + case 7: /* Stop break. */ + break; + } + + /* Transmitter command. */ + switch ((cmd >> 2) & 3) { + case 0: /* No-op. */ + break; + case 1: /* Enable. */ + s->tx_enabled = 1; + mcf_uart_do_tx(s); + break; + case 2: /* Disable. */ + s->tx_enabled = 0; + mcf_uart_do_tx(s); + break; + case 3: /* Reserved. */ + fprintf(stderr, "mcf_uart: Bad TX command\n"); + break; + } + + /* Receiver command. */ + switch (cmd & 3) { + case 0: /* No-op. */ + break; + case 1: /* Enable. */ + s->rx_enabled = 1; + break; + case 2: + s->rx_enabled = 0; + break; + case 3: /* Reserved. */ + fprintf(stderr, "mcf_uart: Bad RX command\n"); + break; + } +} + +void mcf_uart_write(void *opaque, target_phys_addr_t addr, uint32_t val) +{ + mcf_uart_state *s = (mcf_uart_state *)opaque; + switch (addr & 0x3f) { + case 0x00: + s->mr[s->current_mr] = val; + s->current_mr = 1; + break; + case 0x04: + /* CSR is ignored. */ + break; + case 0x08: /* Command Register. */ + mcf_do_command(s, val); + break; + case 0x0c: /* Transmit Buffer. */ + s->sr &= ~MCF_UART_TxEMP; + s->tb = val; + mcf_uart_do_tx(s); + break; + case 0x10: + /* ACR is ignored. */ + break; + case 0x14: + s->imr = val; + break; + default: + break; + } + mcf_uart_update(s); +} + +static void mcf_uart_reset(mcf_uart_state *s) +{ + s->fifo_len = 0; + s->mr[0] = 0; + s->mr[1] = 0; + s->sr = MCF_UART_TxEMP; + s->tx_enabled = 0; + s->rx_enabled = 0; + s->isr = 0; + s->imr = 0; +} + +static void mcf_uart_push_byte(mcf_uart_state *s, uint8_t data) +{ + /* Break events overwrite the last byte if the fifo is full. */ + if (s->fifo_len == 4) + s->fifo_len--; + + s->fifo[s->fifo_len] = data; + s->fifo_len++; + s->sr |= MCF_UART_RxRDY; + if (s->fifo_len == 4) + s->sr |= MCF_UART_FFULL; + + mcf_uart_update(s); +} + +static void mcf_uart_event(void *opaque, int event) +{ + mcf_uart_state *s = (mcf_uart_state *)opaque; + + switch (event) { + case CHR_EVENT_BREAK: + s->isr |= MCF_UART_DBINT; + mcf_uart_push_byte(s, 0); + break; + default: + break; + } +} + +static int mcf_uart_can_receive(void *opaque) +{ + mcf_uart_state *s = (mcf_uart_state *)opaque; + + return s->rx_enabled && (s->sr & MCF_UART_FFULL) == 0; +} + +static void mcf_uart_receive(void *opaque, const uint8_t *buf, int size) +{ + mcf_uart_state *s = (mcf_uart_state *)opaque; + + mcf_uart_push_byte(s, buf[0]); +} + +void *mcf_uart_init(qemu_irq irq, CharDriverState *chr) +{ + mcf_uart_state *s; + + s = qemu_mallocz(sizeof(mcf_uart_state)); + s->chr = chr; + s->irq = irq; + if (chr) { + qemu_chr_add_handlers(chr, mcf_uart_can_receive, mcf_uart_receive, + mcf_uart_event, s); + } + mcf_uart_reset(s); + return s; +} + + +static CPUReadMemoryFunc *mcf_uart_readfn[] = { + mcf_uart_read, + mcf_uart_read, + mcf_uart_read +}; + +static CPUWriteMemoryFunc *mcf_uart_writefn[] = { + mcf_uart_write, + mcf_uart_write, + mcf_uart_write +}; + +void mcf_uart_mm_init(target_phys_addr_t base, qemu_irq irq, + CharDriverState *chr) +{ + mcf_uart_state *s; + int iomemtype; + + s = mcf_uart_init(irq, chr); + iomemtype = cpu_register_io_memory(0, mcf_uart_readfn, + mcf_uart_writefn, s); + cpu_register_physical_memory(base, 0x40, iomemtype); +} diff --git a/target-m68k/cpu.h b/target-m68k/cpu.h index acbb51690d..6f29d5e5c4 100644 --- a/target-m68k/cpu.h +++ b/target-m68k/cpu.h @@ -59,6 +59,10 @@ typedef struct CPUM68KState { uint32_t pc; uint32_t sr; + /* SSP and USP. The current_sp is stored in aregs[7], the other here. */ + int current_sp; + uint32_t sp[2]; + /* Condition flags. */ uint32_t cc_op; uint32_t cc_dest; @@ -92,6 +96,7 @@ typedef struct CPUM68KState { uint32_t vbr; uint32_t mbar; uint32_t rambar0; + uint32_t cacr; uint32_t features; @@ -151,6 +156,12 @@ enum { #define SR_S 0x2000 #define SR_T 0x8000 +#define M68K_SSP 0 +#define M68K_USP 1 + +/* CACR fields are implementation defined, but some bits are common. */ +#define M68K_CACR_EUSP 0x10 + #define MACSR_PAV0 0x100 #define MACSR_OMC 0x080 #define MACSR_SU 0x040 @@ -167,6 +178,7 @@ int cpu_m68k_set_model(CPUM68KState *env, const char * name); void m68k_set_irq_level(CPUM68KState *env, int level, uint8_t vector); void m68k_set_macsr(CPUM68KState *env, uint32_t val); +void m68k_switch_sp(CPUM68KState *env); #define M68K_FPCR_PREC (1 << 6) @@ -179,6 +191,7 @@ enum m68k_features { M68K_FEATURE_CF_FPU, M68K_FEATURE_CF_MAC, M68K_FEATURE_CF_EMAC, + M68K_FEATURE_USP, M68K_FEATURE_EXT_FULL, /* 68020+ full extension word. */ M68K_FEATURE_WORD_INDEX /* word sized address index registers. */ }; diff --git a/target-m68k/helper.c b/target-m68k/helper.c index bdbc1de396..bb213a7603 100644 --- a/target-m68k/helper.c +++ b/target-m68k/helper.c @@ -28,6 +28,7 @@ enum m68k_cpuid { M68K_CPUID_M5206, + M68K_CPUID_M5208, M68K_CPUID_CFV4E, M68K_CPUID_ANY, }; @@ -39,6 +40,7 @@ struct m68k_def_t { static m68k_def_t m68k_cpu_defs[] = { {"m5206", M68K_CPUID_M5206}, + {"m5208", M68K_CPUID_M5208}, {"cfv4e", M68K_CPUID_CFV4E}, {"any", M68K_CPUID_ANY}, {NULL, 0}, @@ -64,12 +66,18 @@ int cpu_m68k_set_model(CPUM68KState *env, const char * name) case M68K_CPUID_M5206: m68k_set_feature(env, M68K_FEATURE_CF_ISA_A); break; + case M68K_CPUID_M5208: + m68k_set_feature(env, M68K_FEATURE_CF_ISA_A); + m68k_set_feature(env, M68K_FEATURE_CF_EMAC); + m68k_set_feature(env, M68K_FEATURE_USP); + break; case M68K_CPUID_CFV4E: m68k_set_feature(env, M68K_FEATURE_CF_ISA_A); m68k_set_feature(env, M68K_FEATURE_CF_ISA_B); m68k_set_feature(env, M68K_FEATURE_CF_ISA_C); m68k_set_feature(env, M68K_FEATURE_CF_FPU); m68k_set_feature(env, M68K_FEATURE_CF_EMAC); + m68k_set_feature(env, M68K_FEATURE_USP); break; case M68K_CPUID_ANY: m68k_set_feature(env, M68K_FEATURE_CF_ISA_A); @@ -79,6 +87,7 @@ int cpu_m68k_set_model(CPUM68KState *env, const char * name) /* MAC and EMAC are mututally exclusive, so pick EMAC. It's mostly backwards compatible. */ m68k_set_feature(env, M68K_FEATURE_CF_EMAC); + m68k_set_feature(env, M68K_FEATURE_USP); m68k_set_feature(env, M68K_FEATURE_EXT_FULL); break; } @@ -215,7 +224,11 @@ void helper_movec(CPUM68KState *env, int reg, uint32_t val) { switch (reg) { case 0x02: /* CACR */ - /* Ignored. */ + env->cacr = val; + m68k_switch_sp(env); + break; + case 0x04: case 0x05: case 0x06: case 0x07: /* ACR[0-3] */ + /* TODO: Implement Access Control Registers. */ break; case 0x801: /* VBR */ env->vbr = val; @@ -261,6 +274,17 @@ void m68k_set_macsr(CPUM68KState *env, uint32_t val) env->macsr = val; } +void m68k_switch_sp(CPUM68KState *env) +{ + int new_sp; + + env->sp[env->current_sp] = env->aregs[7]; + new_sp = (env->sr & SR_S && env->cacr & M68K_CACR_EUSP) + ? M68K_SSP : M68K_USP; + env->aregs[7] = env->sp[new_sp]; + env->current_sp = new_sp; +} + /* MMU */ /* TODO: This will need fixing once the MMU is implemented. */ diff --git a/target-m68k/op.c b/target-m68k/op.c index febfe034c1..932c994357 100644 --- a/target-m68k/op.c +++ b/target-m68k/op.c @@ -478,6 +478,13 @@ OP(fp_result) FORCE_RET(); } +OP(set_sr) +{ + env->sr = get_op(PARAM1); + m68k_switch_sp(env); + FORCE_RET(); +} + OP(jmp) { GOTO_LABEL_PARAM(1); diff --git a/target-m68k/op_helper.c b/target-m68k/op_helper.c index 8086238db4..4c423ca984 100644 --- a/target-m68k/op_helper.c +++ b/target-m68k/op_helper.c @@ -87,6 +87,7 @@ static void do_rte(void) env->pc = ldl_kernel(sp + 4); sp |= (fmt >> 28) & 3; env->sr = fmt & 0xffff; + m68k_switch_sp(env); env->aregs[7] = sp + 8; } @@ -128,9 +129,6 @@ void do_interrupt(int is_hw) } } - /* TODO: Implement USP. */ - sp = env->aregs[7]; - vector = env->exception_index << 2; fmt |= 0x40000000; @@ -138,6 +136,15 @@ void do_interrupt(int is_hw) fmt |= vector << 16; fmt |= env->sr; + env->sr |= SR_S; + if (is_hw) { + env->sr = (env->sr & ~SR_I) | (env->pending_level << SR_I_SHIFT); + env->sr &= ~SR_M; + } + m68k_switch_sp(env); + + sp = env->aregs[7]; + /* ??? This could cause MMU faults. */ sp &= ~3; sp -= 4; @@ -145,11 +152,6 @@ void do_interrupt(int is_hw) sp -= 4; stl_kernel(sp, fmt); env->aregs[7] = sp; - env->sr |= SR_S; - if (is_hw) { - env->sr = (env->sr & ~SR_I) | (env->pending_level << SR_I_SHIFT); - env->sr &= ~SR_M; - } /* Jump to vector. */ env->pc = ldl_kernel(env->vbr + vector); } diff --git a/target-m68k/translate.c b/target-m68k/translate.c index 1d32e8f0f7..da3e72a473 100644 --- a/target-m68k/translate.c +++ b/target-m68k/translate.c @@ -1345,7 +1345,7 @@ static void gen_set_sr_im(DisasContext *s, uint16_t val, int ccr_only) gen_op_logic_cc(gen_im32(val & 0xf)); gen_op_update_xflag_tst(gen_im32((val & 0x10) >> 4)); if (!ccr_only) { - gen_op_mov32(QREG_SR, gen_im32(val & 0xff00)); + gen_op_set_sr(gen_im32(val & 0xff00)); } } @@ -1365,7 +1365,7 @@ static void gen_set_sr(DisasContext *s, uint16_t insn, int ccr_only) gen_op_and32(src1, src1, gen_im32(1)); gen_op_update_xflag_tst(src1); if (!ccr_only) { - gen_op_and32(QREG_SR, reg, gen_im32(0xff00)); + gen_op_set_sr(reg); } } else if ((insn & 0x3f) == 0x3c) @@ -2797,8 +2797,8 @@ void register_m68k_insns (CPUM68KState *env) INSN(trap, 4e40, fff0, CF_ISA_A); INSN(link, 4e50, fff8, CF_ISA_A); INSN(unlk, 4e58, fff8, CF_ISA_A); - INSN(move_to_usp, 4e60, fff8, CF_ISA_B); - INSN(move_from_usp, 4e68, fff8, CF_ISA_B); + INSN(move_to_usp, 4e60, fff8, USP); + INSN(move_from_usp, 4e68, fff8, USP); INSN(nop, 4e71, ffff, CF_ISA_A); INSN(stop, 4e72, ffff, CF_ISA_A); INSN(rte, 4e73, ffff, CF_ISA_A); @@ -3261,6 +3261,7 @@ void cpu_reset(CPUM68KState *env) #if !defined (CONFIG_USER_ONLY) env->sr = 0x2700; #endif + m68k_switch_sp(env); /* ??? FP regs should be initialized to NaN. */ env->cc_op = CC_OP_FLAGS; /* TODO: We should set PC from the interrupt vector. */ diff --git a/vl.c b/vl.c index c949a4752e..513ab4649f 100644 --- a/vl.c +++ b/vl.c @@ -6972,6 +6972,7 @@ void register_machines(void) #elif defined(TARGET_ALPHA) /* XXX: TODO */ #elif defined(TARGET_M68K) + qemu_register_machine(&mcf5208evb_machine); qemu_register_machine(&an5206_machine); #else #error unsupported CPU diff --git a/vl.h b/vl.h index b881668fdd..916daf3912 100644 --- a/vl.h +++ b/vl.h @@ -1593,12 +1593,25 @@ void qemu_get_ptimer(QEMUFile *f, ptimer_state *s); #include "hw/pxa.h" +/* mcf_uart.c */ +uint32_t mcf_uart_read(void *opaque, target_phys_addr_t addr); +void mcf_uart_write(void *opaque, target_phys_addr_t addr, uint32_t val); +void *mcf_uart_init(qemu_irq irq, CharDriverState *chr); +void mcf_uart_mm_init(target_phys_addr_t base, qemu_irq irq, + CharDriverState *chr); + +/* mcf_intc.c */ +qemu_irq *mcf_intc_init(target_phys_addr_t base, CPUState *env); + /* mcf5206.c */ qemu_irq *mcf5206_init(uint32_t base, CPUState *env); /* an5206.c */ extern QEMUMachine an5206_machine; +/* mcf5208.c */ +extern QEMUMachine mcf5208evb_machine; + #include "gdbstub.h" #endif /* defined(QEMU_TOOL) */