From 20dcee9483361ee0621cf6d68d271ecde686fd9c Mon Sep 17 00:00:00 2001 From: pbrook Date: Sun, 3 Jun 2007 11:13:39 +0000 Subject: MCF5208 emulation. git-svn-id: svn://svn.savannah.nongnu.org/qemu/trunk@2924 c046a42c-6fe2-441c-8c8c-71466251a162 --- hw/an5206.c | 4 +- hw/mcf5206.c | 289 ++---------------------------------------------------- hw/mcf5208.c | 285 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ hw/mcf_intc.c | 156 ++++++++++++++++++++++++++++++ hw/mcf_uart.c | 306 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 758 insertions(+), 282 deletions(-) create mode 100644 hw/mcf5208.c create mode 100644 hw/mcf_intc.c create mode 100644 hw/mcf_uart.c (limited to 'hw') 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); +} -- cgit v1.2.3