diff options
author | pbrook <pbrook@c046a42c-6fe2-441c-8c8c-71466251a162> | 2006-04-09 01:32:52 +0000 |
---|---|---|
committer | pbrook <pbrook@c046a42c-6fe2-441c-8c8c-71466251a162> | 2006-04-09 01:32:52 +0000 |
commit | cdbdb648b7c2867f0bb7dce27efb1986f770dedb (patch) | |
tree | f838b39e8f30e4872a792638e532d8ac8db6fbfc /hw/integratorcp.c | |
parent | 95219897ff4e6d0502b920c521fccc612ad913dd (diff) | |
download | qemu-cdbdb648b7c2867f0bb7dce27efb1986f770dedb.zip |
ARM Versatile Platform Baseboard emulation.
git-svn-id: svn://svn.savannah.nongnu.org/qemu/trunk@1804 c046a42c-6fe2-441c-8c8c-71466251a162
Diffstat (limited to 'hw/integratorcp.c')
-rw-r--r-- | hw/integratorcp.c | 673 |
1 files changed, 22 insertions, 651 deletions
diff --git a/hw/integratorcp.c b/hw/integratorcp.c index 1bcd734b80..bce9b592c6 100644 --- a/hw/integratorcp.c +++ b/hw/integratorcp.c @@ -1,32 +1,19 @@ /* * ARM Integrator CP System emulation. * - * Copyright (c) 2005 CodeSourcery, LLC. + * Copyright (c) 2005-2006 CodeSourcery. * Written by Paul Brook * * This code is licenced under the GPL */ -#include <vl.h> +#include "vl.h" +#include "arm_pic.h" #define KERNEL_ARGS_ADDR 0x100 #define KERNEL_LOAD_ADDR 0x00010000 #define INITRD_LOAD_ADDR 0x00800000 -/* Stub functions for hardware that doesn't exist. */ -void pic_set_irq(int irq, int level) -{ - cpu_abort (cpu_single_env, "pic_set_irq"); -} - -void pic_info(void) -{ -} - -void irq_info(void) -{ -} - void DMA_run (void) { } @@ -284,41 +271,31 @@ static void integratorcm_init(int memsz, uint32_t flash_offset) typedef struct icp_pic_state { + arm_pic_handler handler; uint32_t base; uint32_t level; uint32_t irq_enabled; uint32_t fiq_enabled; void *parent; - /* -1 if parent is a cpu, otherwise IRQ number on parent PIC. */ int parent_irq; + int parent_fiq; } icp_pic_state; static void icp_pic_update(icp_pic_state *s) { - CPUState *env; - if (s->parent_irq != -1) { - uint32_t flags; + uint32_t flags; + if (s->parent_irq != -1) { flags = (s->level & s->irq_enabled); - pic_set_irq_new(s->parent, s->parent_irq, - flags != 0); - return; + pic_set_irq_new(s->parent, s->parent_irq, flags != 0); } - /* Raise CPU interrupt. */ - env = (CPUState *)s->parent; - if (s->level & s->fiq_enabled) { - cpu_interrupt (env, CPU_INTERRUPT_FIQ); - } else { - cpu_reset_interrupt (env, CPU_INTERRUPT_FIQ); - } - if (s->level & s->irq_enabled) { - cpu_interrupt (env, CPU_INTERRUPT_HARD); - } else { - cpu_reset_interrupt (env, CPU_INTERRUPT_HARD); + if (s->parent_fiq != -1) { + flags = (s->level & s->fiq_enabled); + pic_set_irq_new(s->parent, s->parent_fiq, flags != 0); } } -void pic_set_irq_new(void *opaque, int irq, int level) +static void icp_pic_set_irq(void *opaque, int irq, int level) { icp_pic_state *s = (icp_pic_state *)opaque; if (level) @@ -408,7 +385,7 @@ static CPUWriteMemoryFunc *icp_pic_writefn[] = { }; static icp_pic_state *icp_pic_init(uint32_t base, void *parent, - int parent_irq) + int parent_irq, int parent_fiq) { icp_pic_state *s; int iomemtype; @@ -416,10 +393,11 @@ static icp_pic_state *icp_pic_init(uint32_t base, void *parent, s = (icp_pic_state *)qemu_mallocz(sizeof(icp_pic_state)); if (!s) return NULL; - + s->handler = icp_pic_set_irq; s->base = base; s->parent = parent; s->parent_irq = parent_irq; + s->parent_fiq = parent_fiq; iomemtype = cpu_register_io_memory(0, icp_pic_readfn, icp_pic_writefn, s); cpu_register_physical_memory(base, 0x007fffff, iomemtype); @@ -427,499 +405,6 @@ static icp_pic_state *icp_pic_init(uint32_t base, void *parent, return s; } -/* Timers. */ - -/* System bus clock speed (40MHz) for timer 0. Not sure about this value. */ -#define ICP_BUS_FREQ 40000000 - -typedef struct { - int64_t next_time; - int64_t expires[3]; - int64_t loaded[3]; - QEMUTimer *timer; - icp_pic_state *pic; - uint32_t base; - uint32_t control[3]; - uint32_t count[3]; - uint32_t limit[3]; - int freq[3]; - int int_level[3]; -} icp_pit_state; - -/* Calculate the new expiry time of the given timer. */ - -static void icp_pit_reload(icp_pit_state *s, int n) -{ - int64_t delay; - - s->loaded[n] = s->expires[n]; - delay = muldiv64(s->count[n], ticks_per_sec, s->freq[n]); - if (delay == 0) - delay = 1; - s->expires[n] += delay; -} - -/* Check all active timers, and schedule the next timer interrupt. */ - -static void icp_pit_update(icp_pit_state *s, int64_t now) -{ - int n; - int64_t next; - - next = now; - for (n = 0; n < 3; n++) { - /* Ignore disabled timers. */ - if ((s->control[n] & 0x80) == 0) - continue; - /* Ignore expired one-shot timers. */ - if (s->count[n] == 0 && s->control[n] & 1) - continue; - if (s->expires[n] - now <= 0) { - /* Timer has expired. */ - s->int_level[n] = 1; - if (s->control[n] & 1) { - /* One-shot. */ - s->count[n] = 0; - } else { - if ((s->control[n] & 0x40) == 0) { - /* Free running. */ - if (s->control[n] & 2) - s->count[n] = 0xffffffff; - else - s->count[n] = 0xffff; - } else { - /* Periodic. */ - s->count[n] = s->limit[n]; - } - } - } - while (s->expires[n] - now <= 0) { - icp_pit_reload(s, n); - } - } - /* Update interrupts. */ - for (n = 0; n < 3; n++) { - if (s->int_level[n] && (s->control[n] & 0x20)) { - pic_set_irq_new(s->pic, 5 + n, 1); - } else { - pic_set_irq_new(s->pic, 5 + n, 0); - } - if (next - s->expires[n] < 0) - next = s->expires[n]; - } - /* Schedule the next timer interrupt. */ - if (next == now) { - qemu_del_timer(s->timer); - s->next_time = 0; - } else if (next != s->next_time) { - qemu_mod_timer(s->timer, next); - s->next_time = next; - } -} - -/* Return the current value of the timer. */ -static uint32_t icp_pit_getcount(icp_pit_state *s, int n, int64_t now) -{ - int64_t elapsed; - int64_t period; - - if (s->count[n] == 0) - return 0; - if ((s->control[n] & 0x80) == 0) - return s->count[n]; - elapsed = now - s->loaded[n]; - period = s->expires[n] - s->loaded[n]; - /* If the timer should have expired then return 0. This can happen - when the host timer signal doesnt occur immediately. It's better to - have a timer appear to sit at zero for a while than have it wrap - around before the guest interrupt is raised. */ - /* ??? Could we trigger the interrupt here? */ - if (elapsed > period) - return 0; - /* We need to calculate count * elapsed / period without overfowing. - Scale both elapsed and period so they fit in a 32-bit int. */ - while (period != (int32_t)period) { - period >>= 1; - elapsed >>= 1; - } - return ((uint64_t)s->count[n] * (uint64_t)(int32_t)elapsed) - / (int32_t)period; -} - -static uint32_t icp_pit_read(void *opaque, target_phys_addr_t offset) -{ - int n; - icp_pit_state *s = (icp_pit_state *)opaque; - - offset -= s->base; - n = offset >> 8; - if (n > 2) - cpu_abort (cpu_single_env, "icp_pit_read: Bad timer %x\n", offset); - switch ((offset & 0xff) >> 2) { - case 0: /* TimerLoad */ - case 6: /* TimerBGLoad */ - return s->limit[n]; - case 1: /* TimerValue */ - return icp_pit_getcount(s, n, qemu_get_clock(vm_clock)); - case 2: /* TimerControl */ - return s->control[n]; - case 4: /* TimerRIS */ - return s->int_level[n]; - case 5: /* TimerMIS */ - if ((s->control[n] & 0x20) == 0) - return 0; - return s->int_level[n]; - default: - cpu_abort (cpu_single_env, "icp_pit_read: Bad offset %x\n", offset); - return 0; - } -} - -static void icp_pit_write(void *opaque, target_phys_addr_t offset, - uint32_t value) -{ - icp_pit_state *s = (icp_pit_state *)opaque; - int n; - int64_t now; - - now = qemu_get_clock(vm_clock); - offset -= s->base; - n = offset >> 8; - if (n > 2) - cpu_abort (cpu_single_env, "icp_pit_write: Bad offset %x\n", offset); - - switch ((offset & 0xff) >> 2) { - case 0: /* TimerLoad */ - s->limit[n] = value; - s->count[n] = value; - s->expires[n] = now; - icp_pit_reload(s, n); - break; - case 1: /* TimerValue */ - /* ??? Linux seems to want to write to this readonly register. - Ignore it. */ - break; - case 2: /* TimerControl */ - if (s->control[n] & 0x80) { - /* Pause the timer if it is running. This may cause some - inaccuracy dure to rounding, but avoids a whole lot of other - messyness. */ - s->count[n] = icp_pit_getcount(s, n, now); - } - s->control[n] = value; - if (n == 0) - s->freq[n] = ICP_BUS_FREQ; - else - s->freq[n] = 1000000; - /* ??? Need to recalculate expiry time after changing divisor. */ - switch ((value >> 2) & 3) { - case 1: s->freq[n] >>= 4; break; - case 2: s->freq[n] >>= 8; break; - } - if (s->control[n] & 0x80) { - /* Restart the timer if still enabled. */ - s->expires[n] = now; - icp_pit_reload(s, n); - } - break; - case 3: /* TimerIntClr */ - s->int_level[n] = 0; - break; - case 6: /* TimerBGLoad */ - s->limit[n] = value; - break; - default: - cpu_abort (cpu_single_env, "icp_pit_write: Bad offset %x\n", offset); - } - icp_pit_update(s, now); -} - -static void icp_pit_tick(void *opaque) -{ - int64_t now; - - now = qemu_get_clock(vm_clock); - icp_pit_update((icp_pit_state *)opaque, now); -} - -static CPUReadMemoryFunc *icp_pit_readfn[] = { - icp_pit_read, - icp_pit_read, - icp_pit_read -}; - -static CPUWriteMemoryFunc *icp_pit_writefn[] = { - icp_pit_write, - icp_pit_write, - icp_pit_write -}; - -static void icp_pit_init(uint32_t base, icp_pic_state *pic) -{ - int iomemtype; - icp_pit_state *s; - int n; - - s = (icp_pit_state *)qemu_mallocz(sizeof(icp_pit_state)); - s->base = base; - s->pic = pic; - s->freq[0] = ICP_BUS_FREQ; - s->freq[1] = 1000000; - s->freq[2] = 1000000; - for (n = 0; n < 3; n++) { - s->control[n] = 0x20; - s->count[n] = 0xffffffff; - } - - iomemtype = cpu_register_io_memory(0, icp_pit_readfn, - icp_pit_writefn, s); - cpu_register_physical_memory(base, 0x007fffff, iomemtype); - s->timer = qemu_new_timer(vm_clock, icp_pit_tick, s); - /* ??? Save/restore. */ -} - -/* ARM PrimeCell PL011 UART */ - -typedef struct { - uint32_t base; - uint32_t readbuff; - uint32_t flags; - uint32_t lcr; - uint32_t cr; - uint32_t dmacr; - uint32_t int_enabled; - uint32_t int_level; - uint32_t read_fifo[16]; - uint32_t ilpr; - uint32_t ibrd; - uint32_t fbrd; - uint32_t ifl; - int read_pos; - int read_count; - int read_trigger; - CharDriverState *chr; - icp_pic_state *pic; - int irq; -} pl011_state; - -#define PL011_INT_TX 0x20 -#define PL011_INT_RX 0x10 - -#define PL011_FLAG_TXFE 0x80 -#define PL011_FLAG_RXFF 0x40 -#define PL011_FLAG_TXFF 0x20 -#define PL011_FLAG_RXFE 0x10 - -static const unsigned char pl011_id[] = -{ 0x11, 0x10, 0x14, 0x00, 0x0d, 0xf0, 0x05, 0xb1 }; - -static void pl011_update(pl011_state *s) -{ - uint32_t flags; - - flags = s->int_level & s->int_enabled; - pic_set_irq_new(s->pic, s->irq, flags != 0); -} - -static uint32_t pl011_read(void *opaque, target_phys_addr_t offset) -{ - pl011_state *s = (pl011_state *)opaque; - uint32_t c; - - offset -= s->base; - if (offset >= 0xfe0 && offset < 0x1000) { - return pl011_id[(offset - 0xfe0) >> 2]; - } - switch (offset >> 2) { - case 0: /* UARTDR */ - s->flags &= ~PL011_FLAG_RXFF; - c = s->read_fifo[s->read_pos]; - if (s->read_count > 0) { - s->read_count--; - if (++s->read_pos == 16) - s->read_pos = 0; - } - if (s->read_count == 0) { - s->flags |= PL011_FLAG_RXFE; - } - if (s->read_count == s->read_trigger - 1) - s->int_level &= ~ PL011_INT_RX; - pl011_update(s); - return c; - case 1: /* UARTCR */ - return 0; - case 6: /* UARTFR */ - return s->flags; - case 8: /* UARTILPR */ - return s->ilpr; - case 9: /* UARTIBRD */ - return s->ibrd; - case 10: /* UARTFBRD */ - return s->fbrd; - case 11: /* UARTLCR_H */ - return s->lcr; - case 12: /* UARTCR */ - return s->cr; - case 13: /* UARTIFLS */ - return s->ifl; - case 14: /* UARTIMSC */ - return s->int_enabled; - case 15: /* UARTRIS */ - return s->int_level; - case 16: /* UARTMIS */ - return s->int_level & s->int_enabled; - case 18: /* UARTDMACR */ - return s->dmacr; - default: - cpu_abort (cpu_single_env, "pl011_read: Bad offset %x\n", offset); - return 0; - } -} - -static void pl011_set_read_trigger(pl011_state *s) -{ -#if 0 - /* The docs say the RX interrupt is triggered when the FIFO exceeds - the threshold. However linux only reads the FIFO in response to an - interrupt. Triggering the interrupt when the FIFO is non-empty seems - to make things work. */ - if (s->lcr & 0x10) - s->read_trigger = (s->ifl >> 1) & 0x1c; - else -#endif - s->read_trigger = 1; -} - -static void pl011_write(void *opaque, target_phys_addr_t offset, - uint32_t value) -{ - pl011_state *s = (pl011_state *)opaque; - unsigned char ch; - - offset -= s->base; - switch (offset >> 2) { - case 0: /* UARTDR */ - /* ??? Check if transmitter is enabled. */ - ch = value; - if (s->chr) - qemu_chr_write(s->chr, &ch, 1); - s->int_level |= PL011_INT_TX; - pl011_update(s); - break; - case 1: /* UARTCR */ - s->cr = value; - break; - case 8: /* UARTUARTILPR */ - s->ilpr = value; - break; - case 9: /* UARTIBRD */ - s->ibrd = value; - break; - case 10: /* UARTFBRD */ - s->fbrd = value; - break; - case 11: /* UARTLCR_H */ - s->lcr = value; - pl011_set_read_trigger(s); - break; - case 12: /* UARTCR */ - /* ??? Need to implement the enable and loopback bits. */ - s->cr = value; - break; - case 13: /* UARTIFS */ - s->ifl = value; - pl011_set_read_trigger(s); - break; - case 14: /* UARTIMSC */ - s->int_enabled = value; - pl011_update(s); - break; - case 17: /* UARTICR */ - s->int_level &= ~value; - pl011_update(s); - break; - case 18: /* UARTDMACR */ - s->dmacr = value; - if (value & 3) - cpu_abort(cpu_single_env, "PL011: DMA not implemented\n"); - break; - default: - cpu_abort (cpu_single_env, "pl011_write: Bad offset %x\n", offset); - } -} - -static int pl011_can_recieve(void *opaque) -{ - pl011_state *s = (pl011_state *)opaque; - - if (s->lcr & 0x10) - return s->read_count < 16; - else - return s->read_count < 1; -} - -static void pl011_recieve(void *opaque, const uint8_t *buf, int size) -{ - pl011_state *s = (pl011_state *)opaque; - int slot; - - slot = s->read_pos + s->read_count; - if (slot >= 16) - slot -= 16; - s->read_fifo[slot] = *buf; - s->read_count++; - s->flags &= ~PL011_FLAG_RXFE; - if (s->cr & 0x10 || s->read_count == 16) { - s->flags |= PL011_FLAG_RXFF; - } - if (s->read_count == s->read_trigger) { - s->int_level |= PL011_INT_RX; - pl011_update(s); - } -} - -static void pl011_event(void *opaque, int event) -{ - /* ??? Should probably implement break. */ -} - -static CPUReadMemoryFunc *pl011_readfn[] = { - pl011_read, - pl011_read, - pl011_read -}; - -static CPUWriteMemoryFunc *pl011_writefn[] = { - pl011_write, - pl011_write, - pl011_write -}; - -static void pl011_init(uint32_t base, icp_pic_state *pic, int irq, - CharDriverState *chr) -{ - int iomemtype; - pl011_state *s; - - s = (pl011_state *)qemu_mallocz(sizeof(pl011_state)); - iomemtype = cpu_register_io_memory(0, pl011_readfn, - pl011_writefn, s); - cpu_register_physical_memory(base, 0x007fffff, iomemtype); - s->base = base; - s->pic = pic; - s->irq = irq; - s->chr = chr; - s->read_trigger = 1; - s->ifl = 0x12; - s->cr = 0x300; - s->flags = 0x90; - if (chr){ - qemu_chr_add_read_handler(chr, pl011_can_recieve, pl011_recieve, s); - qemu_chr_add_event_handler(chr, pl011_event); - } - /* ??? Save/restore. */ -} - /* CP control registers. */ typedef struct { uint32_t base; @@ -985,122 +470,6 @@ static void icp_control_init(uint32_t base) } -/* Keyboard/Mouse Interface. */ - -typedef struct { - void *dev; - uint32_t base; - uint32_t cr; - uint32_t clk; - uint32_t last; - icp_pic_state *pic; - int pending; - int irq; - int is_mouse; -} icp_kmi_state; - -static void icp_kmi_update(void *opaque, int level) -{ - icp_kmi_state *s = (icp_kmi_state *)opaque; - int raise; - - s->pending = level; - raise = (s->pending && (s->cr & 0x10) != 0) - || (s->cr & 0x08) != 0; - pic_set_irq_new(s->pic, s->irq, raise); -} - -static uint32_t icp_kmi_read(void *opaque, target_phys_addr_t offset) -{ - icp_kmi_state *s = (icp_kmi_state *)opaque; - offset -= s->base; - if (offset >= 0xfe0 && offset < 0x1000) - return 0; - - switch (offset >> 2) { - case 0: /* KMICR */ - return s->cr; - case 1: /* KMISTAT */ - /* KMIC and KMID bits not implemented. */ - if (s->pending) { - return 0x10; - } else { - return 0; - } - case 2: /* KMIDATA */ - if (s->pending) - s->last = ps2_read_data(s->dev); - return s->last; - case 3: /* KMICLKDIV */ - return s->clk; - case 4: /* KMIIR */ - return s->pending | 2; - default: - cpu_abort (cpu_single_env, "icp_kmi_read: Bad offset %x\n", offset); - return 0; - } -} - -static void icp_kmi_write(void *opaque, target_phys_addr_t offset, - uint32_t value) -{ - icp_kmi_state *s = (icp_kmi_state *)opaque; - offset -= s->base; - switch (offset >> 2) { - case 0: /* KMICR */ - s->cr = value; - icp_kmi_update(s, s->pending); - /* ??? Need to implement the enable/disable bit. */ - break; - case 2: /* KMIDATA */ - /* ??? This should toggle the TX interrupt line. */ - /* ??? This means kbd/mouse can block each other. */ - if (s->is_mouse) { - ps2_write_mouse(s->dev, value); - } else { - ps2_write_keyboard(s->dev, value); - } - break; - case 3: /* KMICLKDIV */ - s->clk = value; - return; - default: - cpu_abort (cpu_single_env, "icp_kmi_write: Bad offset %x\n", offset); - } -} -static CPUReadMemoryFunc *icp_kmi_readfn[] = { - icp_kmi_read, - icp_kmi_read, - icp_kmi_read -}; - -static CPUWriteMemoryFunc *icp_kmi_writefn[] = { - icp_kmi_write, - icp_kmi_write, - icp_kmi_write -}; - -static void icp_kmi_init(uint32_t base, icp_pic_state * pic, int irq, - int is_mouse) -{ - int iomemtype; - icp_kmi_state *s; - - s = (icp_kmi_state *)qemu_mallocz(sizeof(icp_kmi_state)); - iomemtype = cpu_register_io_memory(0, icp_kmi_readfn, - icp_kmi_writefn, s); - cpu_register_physical_memory(base, 0x007fffff, iomemtype); - s->base = base; - s->pic = pic; - s->irq = irq; - s->is_mouse = is_mouse; - if (is_mouse) - s->dev = ps2_mouse_init(icp_kmi_update, s); - else - s->dev = ps2_kbd_init(icp_kmi_update, s); - /* ??? Save/restore. */ -} - /* The worlds second smallest bootloader. Set r0-r2, then jump to kernel. */ static uint32_t bootloader[] = { 0xe3a00000, /* mov r0, #0 */ @@ -1162,6 +531,7 @@ static void integratorcp_init(int ram_size, int vga_ram_size, int boot_device, CPUState *env; uint32_t bios_offset; icp_pic_state *pic; + void *cpu_pic; int kernel_size; int initrd_size; int n; @@ -1177,14 +547,15 @@ static void integratorcp_init(int ram_size, int vga_ram_size, int boot_device, cpu_register_physical_memory(0x80000000, ram_size, IO_MEM_RAM); integratorcm_init(ram_size >> 20, bios_offset); - pic = icp_pic_init(0x14000000, env, -1); - icp_pic_init(0xca000000, pic, 26); - icp_pit_init(0x13000000, pic); + cpu_pic = arm_pic_init_cpu(env); + pic = icp_pic_init(0x14000000, cpu_pic, ARM_PIC_CPU_IRQ, ARM_PIC_CPU_FIQ); + icp_pic_init(0xca000000, pic, 26, -1); + icp_pit_init(0x13000000, pic, 5); pl011_init(0x16000000, pic, 1, serial_hds[0]); pl011_init(0x17000000, pic, 2, serial_hds[1]); icp_control_init(0xcb000000); - icp_kmi_init(0x18000000, pic, 3, 0); - icp_kmi_init(0x19000000, pic, 4, 1); + pl050_init(0x18000000, pic, 3, 0); + pl050_init(0x19000000, pic, 4, 1); if (nd_table[0].vlan) { if (nd_table[0].model == NULL || strcmp(nd_table[0].model, "smc91c111") == 0) { |