Message ID | w4wrd0xesn.wl%peter@chubb.wattle.id.au |
---|---|
State | New |
Headers | show |
On 09/22/2011 07:50 PM, Peter Chubb wrote: > > > The KZM board is an evaluation board for the ARM v6 i.mx31 processor. > It is about the only readily-available development board for that > processor, even though the imx.31 is used in many embedded devices. > > This patch was developed at OK-Labs. I have permission from them to > push it upstream. > > > > Signed-Off-By: Peter Chubb<peter.chubb@nicta.com.au> > Signed-Off-By: Hans Jang<hsjang@ok-labs.com> > Signed-off-by: Adam Clench<adamc@ok-labs.com> Peter, Could you bring this through your tree if appropriate? Thanks. Regards, Anthony Liguori > --- > Makefile.target | 1 + > hw/imx-int.c | 155 ++++++++++++++++++++++++ > hw/imx-serial.c | 195 ++++++++++++++++++++++++++++++ > hw/imx-timer.c | 355 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ > hw/imx.h | 14 ++ > hw/kzm.c | 86 +++++++++++++ > hw/kzm.h | 5 + > 7 files changed, 811 insertions(+), 0 deletions(-) > create mode 100644 hw/imx-int.c > create mode 100644 hw/imx-serial.c > create mode 100644 hw/imx-timer.c > create mode 100644 hw/imx.h > create mode 100644 hw/kzm.c > create mode 100644 hw/kzm.h > > diff --git a/Makefile.target b/Makefile.target > index 88d2f1f..e24dda8 100644 > --- a/Makefile.target > +++ b/Makefile.target > @@ -331,6 +331,7 @@ endif > obj-arm-y = integratorcp.o versatilepb.o arm_pic.o arm_timer.o > obj-arm-y += arm_boot.o pl011.o pl031.o pl050.o pl080.o pl110.o pl181.o pl190.o > obj-arm-y += versatile_pci.o > +obj-arm-y += kzm.o imx-int.o imx-serial.o imx-timer.o > obj-arm-y += realview_gic.o realview.o arm_sysctl.o arm11mpcore.o a9mpcore.o > obj-arm-y += armv7m.o armv7m_nvic.o stellaris.o pl022.o stellaris_enet.o > obj-arm-y += pl061.o > diff --git a/hw/imx-int.c b/hw/imx-int.c > new file mode 100644 > index 0000000..9674112 > --- /dev/null > +++ b/hw/imx-int.c > @@ -0,0 +1,155 @@ > +/* > + * IMX31 Interrupt Controller > + * > + * Copyright (c) 2008 OKL > + * Written by Hans Jang > + * > + * This code is licenced under the GPL. > + */ > + > +#include "imx.h" > + > +//#define DEBUG_INT > +#ifdef DEBUG_INT > +#define DPRINTF(fmt, args...) \ > +do { printf("imx_int: " fmt , ##args); } while (0) > +#else > +#define DPRINTF(fmt, args...) do {} while(0) > +#endif > + > + > +#define IMX_INT_NUM_IRQS 64 > + > +typedef struct { > + uint32_t base; > + uint32_t level; > + > + char pending[IMX_INT_NUM_IRQS]; > + char enable[IMX_INT_NUM_IRQS]; > + uint32_t intcntl; > + > + qemu_irq irq; > +} imx_int_state; > + > + > +/* Update interrupts. */ > +static void imx_int_update(imx_int_state *s) > +{ > + int new_level = 0; > + int i; > + for(i = 0; i< IMX_INT_NUM_IRQS; ++i) { > + if (s->pending[i]&& s->enable[i]) { > + new_level = 1; > + break; > + } > + } > + > + if (s->level != new_level) { > + DPRINTF("irq=%d, level=%d\n", i, new_level); > + qemu_set_irq(s->irq, new_level); > + s->level = new_level; > + } > +} > + > +static void imx_int_set_irq(void *opaque, int irq, int level) > +{ > + imx_int_state *s = (imx_int_state *)opaque; > + s->pending[irq] = level; > + imx_int_update(s); > +} > + > + > +static uint32_t imx_int_read(void *opaque, target_phys_addr_t offset) > +{ > + imx_int_state *s = (imx_int_state *)opaque; > + int i; > + > + DPRINTF("read(offset = 0x%x)\n", offset>> 2); > + switch (offset>> 2) { > + case 0: /* INTCNTL */ > + return s->intcntl; > + case 16: /* nivecsr : pending interrupt */ > + for(i = 0; i< IMX_INT_NUM_IRQS; ++i) { > + if (s->pending[i]&& s->enable[i]) { > + imx_int_set_irq(opaque, i, 0); > + //printf("return pending interrupt = irq = %d\n", i); > + return i<< 16; > + } > + } > + return 0xFFFF<<16; > + default: > + cpu_abort (cpu_single_env, "imx_int_read: Bad offset %x\n", (int)offset); > + return 0; > + } > +} > + > +static void imx_int_write(void *opaque, target_phys_addr_t offset, uint32_t val) > +{ > + imx_int_state *s = (imx_int_state *)opaque; > + > + DPRINTF("write(0x%x) = %x\n", offset>>2, val); > + switch (offset>> 2) { > + case 0: /* INTCNTL */ > + s->intcntl = val; > + break; > + case 2: /* INTENABLE */ > + DPRINTF("enable(%d)\n",val); > + s->enable[val] = 1; > + break; > + case 3: /* INTDISABLE */ > + s->enable[val] = 0; > + DPRINTF("disabled(%d)\n",val); > + imx_int_update(s); > + break; > + case 4: /* intenableh */ > + break; > + case 5: /* intenablel */ > + break; > + case 6: /* inttypeh */ > + break; > + case 7: /* inttypel */ > + case 8: /* NIPRIORITY */ > + case 9: > + case 10: > + case 11: > + case 12: > + case 13: > + case 14: > + case 15: > + /* ignore */ > + break; > + default: > + cpu_abort(cpu_single_env, "imx_int_write: Bad offset %x\n", (int)offset); > + return; > + } > + imx_int_update(s); > +} > + > +static CPUReadMemoryFunc *imx_int_readfn[] = { > + imx_int_read, > + imx_int_read, > + imx_int_read > +}; > + > +static CPUWriteMemoryFunc *imx_int_writefn[] = { > + imx_int_write, > + imx_int_write, > + imx_int_write > +}; > + > +qemu_irq *imx_int_init(uint32_t base, qemu_irq irq) > +{ > + imx_int_state *s; > + qemu_irq *qi; > + int iomemtype; > + > + s = (imx_int_state *)g_malloc0(sizeof *s); > + iomemtype = cpu_register_io_memory(imx_int_readfn, > + imx_int_writefn, s, DEVICE_NATIVE_ENDIAN); > + cpu_register_physical_memory(base, 0x00001000, iomemtype); > + qi = qemu_allocate_irqs(imx_int_set_irq, s, 64); > + s->base = base; > + s->irq = irq; > + /* ??? Save/restore. */ > + return qi; > +} > diff --git a/hw/imx-serial.c b/hw/imx-serial.c > new file mode 100644 > index 0000000..d14474d > --- /dev/null > +++ b/hw/imx-serial.c > @@ -0,0 +1,195 @@ > +/* > + * IMX31 Interrupt Controller > + * > + * Copyright (c) 2008 OKL > + * Written by Hans > + * > + * This code is licenced under the GPL. > + */ > + > +#include "imx.h" > +#include "qemu-char.h" > + > +//#define DEBUG_SERIAL > +#ifdef DEBUG_SERIAL > +#define DPRINTF(fmt, args...) \ > +do { printf("imx_serial: " fmt , ##args); } while (0) > +#else > +#define DPRINTF(fmt, args...) do {} while(0) > +#endif > + > +typedef struct { > + uint32_t base; > + int32_t readbuff; > + > + uint32_t usr1; > + uint32_t usr2; > + uint32_t ucr1; > + uint32_t uts1; > + > + qemu_irq irq; > + CharDriverState *chr; > +} imx_state; > + > +#define USR1_TRDY 0x2000 /* Xmitter ready */ > +#define USR1_RRDY 0x200 /* receiver ready */ > + > +#define USR2_TXFE 0x4000 > +#define USR2_RDR 0x1 > +#define USR2_TXDC 0x8 > + > +#define UCR1_RRDYEN 0x200 > +#define UCR1_TRDYEN 0x2000 > + > +#define UTS1_TXFULL 0x10 > +#define UTS1_RXEMPTY 0x20 > + > +static void imx_update(imx_state *s) > +{ > + uint32_t flags; > + flags = (s->usr1& s->ucr1); > + qemu_set_irq(s->irq, !!flags); > +} > + > +static uint32_t imx_read(void *opaque, target_phys_addr_t offset) > +{ > + imx_state *s = (imx_state *)opaque; > + uint32_t c; > + > + DPRINTF("read(offset=%x)\n", offset>> 2); > + switch (offset>> 2) { > + case 0x0: /* URXD */ > + c = s->readbuff; > + s->usr1&= ~USR1_RRDY; > + s->usr2&= ~USR2_RDR; > + s->uts1 |= UTS1_RXEMPTY; > + imx_update(s); > + qemu_chr_accept_input(s->chr); > + return c; > + case 0x25: /* USR1 */ > + imx_update(s); > + return s->usr1; > + case 0x26: /* USR2 */ > + imx_update(s); > + return s->usr2; > + > + case 0x20: /* UCR1 */ > + return s->ucr1; > + case 0x2d: /* UTS1 */ > + return s->uts1; > + > + case 0x21: /* UCR2 */ > + case 0x22: /* UCR3 */ > + case 0x23: /* UCR4 */ > + case 0x24: /* UFCR */ > + return 0x0; /* TODO */ > + > + default: > + cpu_abort (cpu_single_env, "imx_read: Bad offset %x\n", (int)offset); > + return 0; > + } > +} > + > + > +static void imx_write(void *opaque, target_phys_addr_t offset, > + uint32_t value) > +{ > + imx_state *s = (imx_state *)opaque; > + unsigned char ch; > + > + DPRINTF("write(offset=%x, value = %x)\n", offset>> 2, value); > + switch (offset>> 2) { > + case 0x10: /* UTXD */ > + ch = value; > + if (s->chr) > + qemu_chr_fe_write(s->chr,&ch, 1); > + // XXX imx_update(s); > + break; > + > + case 0x20: /* UCR1 */ > + s->ucr1 = value; > + DPRINTF("write(ucr1=%x)\n", value); > + imx_update(s); > + break; > + > + case 0x21: /* UCR2 */ > + case 0x22: /* UCR3 */ > + case 0x23: /* UCR4 */ > + case 0x24: /* UFCR */ > + case 0x25: /* USR1 */ > + case 0x29: /* UBIR */ > + case 0x2a: /* UBMR */ > + case 0x2c: /* BIPR1 */ > + /* TODO */ > + break; > + > + default: > + cpu_abort (cpu_single_env, "imx_write: Bad offset %x\n", (int)offset); > + } > +} > + > +static int imx_can_receive(void *opaque) > +{ > + imx_state *s = (imx_state *)opaque; > + return !(s->usr1& USR1_RRDY); > +} > + > +static void imx_put_data(void *opaque, uint32_t value) > +{ > + imx_state *s = (imx_state *)opaque; > + > + s->usr1 |= USR1_RRDY; > + s->usr2 |= USR2_RDR; > + s->uts1&= ~UTS1_RXEMPTY; > + s->readbuff = value; > + imx_update(s); > +} > + > +static void imx_receive(void *opaque, const uint8_t *buf, int size) > +{ > + imx_put_data(opaque, *buf); > +} > + > +static void imx_event(void *opaque, int event) > +{ > + if (event == CHR_EVENT_BREAK) > + imx_put_data(opaque, 0x400); > +} > + > +static CPUReadMemoryFunc *imx_readfn[] = { > + imx_read, > + imx_read, > + imx_read > +}; > + > +static CPUWriteMemoryFunc *imx_writefn[] = { > + imx_write, > + imx_write, > + imx_write > +}; > + > +void imx_serial_init(uint32_t base, qemu_irq irq, CharDriverState *chr) > +{ > + int iomemtype; > + imx_state *s; > + > + s = (imx_state *)g_malloc0(sizeof(imx_state)); > + iomemtype = cpu_register_io_memory(imx_readfn, > + imx_writefn, s, DEVICE_NATIVE_ENDIAN); > + cpu_register_physical_memory(base, 0x00001000, iomemtype); > + > + s->irq = irq; > + s->usr1 = USR1_TRDY; > + s->usr2 = USR2_TXFE | USR2_TXDC; > + s->ucr1 = UCR1_TRDYEN | UCR1_RRDYEN; > + s->uts1 = UTS1_RXEMPTY; > + s->readbuff = -1; > + s->base = base; > + s->chr = chr; > + if (chr){ > + qemu_chr_add_handlers(chr, imx_can_receive, imx_receive, > + imx_event, s); > + } > + /* ??? Save/restore. */ > +} > + > diff --git a/hw/imx-timer.c b/hw/imx-timer.c > new file mode 100644 > index 0000000..65a4b28 > --- /dev/null > +++ b/hw/imx-timer.c > @@ -0,0 +1,355 @@ > +/* > + * IMX31 Timer > + * > + * Copyright (c) 2008 OKL > + * Written by Hans > + * > + * This code is licenced under the GPL. > + */ > + > +#include "imx.h" > +#include "qemu-timer.h" > + > +//#define DEBUG_TIMER > +#ifdef DEBUG_TIMER > +#define DPRINTF(fmt, args...) \ > +do { printf("imx_timer: " fmt , ##args); } while (0) > +#else > +#define DPRINTF(fmt, args...) do {} while(0) > +#endif > + > +/* > + * GPT : General purpose timer > + */ > + > +#define TIMER_MAX 0xFFFFFFFF > +#define GPT_FREQ 50000000 > +#define GPT_CR_EN (1<< 0) > +#define GPT_SR_OF1 (1<< 0) > +#define GPT_SR_ROV (1<< 5) > +#define GPT_IR_OF1IE (1<< 0) > +#define GPT_IR_ROVIE (1<< 5) > + > +typedef struct { > + uint32_t base; > + QEMUTimer *timer; > + uint32_t cr; > + uint32_t sr; > + uint32_t pr; > + uint32_t ir; > + uint32_t ocr1; > + uint32_t cnt; > + > + int waiting_rov; > + qemu_irq irq; > +} imxg_timer_state; > + > +/* Check all active timers, and schedule the next timer interrupt. */ > +static void imxg_timer_update(imxg_timer_state *s) > +{ > + /* Update interrupts. */ > + if ((s->cr& GPT_CR_EN) > +&& (((s->sr& GPT_SR_OF1)&& (s->ir& GPT_IR_OF1IE)) || > + ((s->sr& GPT_SR_ROV)&& (s->ir& GPT_IR_ROVIE)))) { > + qemu_irq_raise(s->irq); > + } else { > + qemu_irq_lower(s->irq); > + } > +} > + > +static uint64_t imxg_timer_update_count(imxg_timer_state *s) > +{ > + uint64_t clk = qemu_get_clock_ns(vm_clock); > + s->cnt = ((uint32_t)muldiv64(clk, GPT_FREQ, > + get_ticks_per_sec())) % TIMER_MAX; > + return clk; > +} > + > +static void imxg_timer_run(imxg_timer_state *s, uint32_t timeout) > +{ > + uint64_t clk = imxg_timer_update_count(s); > + uint32_t diff_cnt; > + if (s->cnt< timeout) { > + diff_cnt = (timeout - s->cnt); > + s->waiting_rov = 0; > + } else { > + diff_cnt = (TIMER_MAX - s->cnt); > + s->waiting_rov = 1; > + } > + qemu_mod_timer(s->timer, clk + muldiv64(get_ticks_per_sec(), diff_cnt, GPT_FREQ)); > +} > + > +static uint32_t imxg_timer_read(void *opaque, target_phys_addr_t offset) > +{ > + imxg_timer_state *s = (imxg_timer_state *)opaque; > + > + DPRINTF("g-read(offset=%x)\n", offset>> 2); > + switch (offset>> 2) { > + case 0: /* CR */ > + return s->cr; > + > + case 1: /* prescaler */ > + return s->pr; > + > + case 2: > + return s->sr; > + > + case 3: > + return s->ir; > + > + case 4: > + return s->ocr1; > + > + case 9: /* cnt */ > + imxg_timer_update_count(s); > + return s->cnt; > + } > + > + cpu_abort (cpu_single_env, "imxg_timer_read: Bad offset %x\n", > + (int)offset>> 2); > +} > + > +static void imxg_timer_write(void *opaque, target_phys_addr_t offset, > + uint32_t value) > +{ > + imxg_timer_state *s = (imxg_timer_state *)opaque; > + DPRINTF("g-write(offset=%x, value = %x)\n", offset>> 2, value); > + > + switch (offset>> 2) { > + case 0: /* CR */ > + if (!(s->cr& GPT_CR_EN)&& (value& GPT_CR_EN)) { > + imxg_timer_run(s, s->ocr1); > + }; > + if ((s->cr& GPT_CR_EN)&& !(value& GPT_CR_EN)) { > + qemu_del_timer(s->timer); > + }; > + s->cr = value; > + return; > + case 1: > + s->pr = value; > + return; > + > + case 2: > + if (value& GPT_SR_OF1) { > + s->sr&= ~GPT_SR_OF1; > + } > + if (value& GPT_SR_ROV) { > + s->sr&= ~GPT_SR_ROV; > + } > + imxg_timer_update(s); > + return; > + > + case 3: > + s->ir = value; > + imxg_timer_update(s); > + return; > + > + case 4: > + if (s->cr& GPT_CR_EN) { > + s->ocr1 = value; > + imxg_timer_run(s, s->ocr1); > + } > + return; > + > + default: > + cpu_abort (cpu_single_env, "imxg_timer_write: Bad offset %x\n", > + (int)offset>> 2); > + } > +} > + > +static void imxg_timer_timeout(void *opaque) { > + imxg_timer_state *s = (imxg_timer_state *)opaque; > + > + if (s->waiting_rov) { > + s->sr |= GPT_SR_ROV; > + imxg_timer_run(s, s->ocr1); > + } else { > + s->sr |= GPT_SR_OF1; > + imxg_timer_run(s, 0); > + } > + imxg_timer_update(s); > +} > + > +static CPUReadMemoryFunc *imxg_timer_readfn[] = { > + imxg_timer_read, > + imxg_timer_read, > + imxg_timer_read > +}; > + > +static CPUWriteMemoryFunc *imxg_timer_writefn[] = { > + imxg_timer_write, > + imxg_timer_write, > + imxg_timer_write > +}; > + > +void imxg_timer_init(uint32_t base, qemu_irq irq) > +{ > + int iomemtype; > + imxg_timer_state *s; > + > + s = (imxg_timer_state *)g_malloc0(sizeof(imxg_timer_state)); > + s->base = base; > + s->cr = 0; > + s->ir = 0; > + s->pr = 0; > + s->ocr1 = 0; > + s->irq = irq; > + s->timer = qemu_new_timer_ns(vm_clock, imxg_timer_timeout, s); > + imxg_timer_update_count(s); > + > + iomemtype = cpu_register_io_memory(imxg_timer_readfn, > + imxg_timer_writefn, s, DEVICE_NATIVE_ENDIAN); > + cpu_register_physical_memory(base, 0x00001000, iomemtype); > +} > + > + > + > +/* > + * EPIT :Enhanced periodic interrupt timer > + */ > + > +#define EPIT_FREQ 1000000 > +#define TIMER_TICK_LENGTH 5000 > +#define IMX31_TICKS_PER_TIMESLICE (72 * TIMER_TICK_LENGTH) > +#define CR_EN (1<< 0) > +#define CR_SWR (1<< 16) > +typedef struct { > + uint32_t base; > + ptimer_state *timer; > + uint32_t cr; > + uint32_t lr; > + uint32_t cmp; > + int int_level; > + qemu_irq irq; > +} imxp_timer_state; > + > +/* Check all active timers, and schedule the next timer interrupt. */ > +static void imxp_timer_update(imxp_timer_state *s) > +{ > + /* Update interrupts. */ > + if (s->int_level&& (s->cr& CR_EN)) { > + qemu_irq_raise(s->irq); > + } else { > + qemu_irq_lower(s->irq); > + } > +} > + > +static uint32_t imxp_timer_read(void *opaque, target_phys_addr_t offset) > +{ > + imxp_timer_state *s = (imxp_timer_state *)opaque; > + > + DPRINTF("p-read(offset=%x)\n", offset); > + switch (offset>> 2) { > + case 0: /* CR */ > + return s->cr; > + > + case 1: /* SR */ > + return s->int_level; > + > + case 2: /* LR - set ticks*/ > + return s->lr; > + > + case 3: /* CMP */ > + return s->cmp; > + > + case 4: /* CNT */ > + return ptimer_get_count(s->timer); > + } > + cpu_abort (cpu_single_env, "imxp_timer_read: Bad offset %x\n", > + (int)offset>> 2); > +} > + > +static void imxp_timer_write(void *opaque, target_phys_addr_t offset, > + uint32_t value) > +{ > + imxp_timer_state *s = (imxp_timer_state *)opaque; > + DPRINTF("p-write(offset=%x, value = %x)\n", offset>> 2, value); > + > + switch (offset>> 2) { > + case 0: /* CR */ > + s->cr = value; > + if (s->cr& CR_EN) { > + ptimer_run(s->timer,0); > + } else { > + ptimer_stop(s->timer); > + } > + if (s->cr& CR_SWR) { > + s->cr = 0; > + s->lr = 0; > + ptimer_stop(s->timer); > + } > + break; > + case 1: /* SR - ACK*/ > + s->int_level=0; > + imxp_timer_update(s); > + break; > + > + case 2: /* LR - set ticks*/ > + s->lr = value; > + ptimer_set_freq(s->timer, EPIT_FREQ); > + ptimer_set_limit(s->timer, value, 1); > + break; > + > + case 3: /* CMP */ > + s->cmp = value; > + break; > + > + > + default: > + cpu_abort (cpu_single_env, "imxp_timer_write: Bad offset %x\n", > + (int)offset>> 2); > + } > +} > + > +static void imxp_timer_tick(void *opaque) > +{ > + imxp_timer_state *s = (imxp_timer_state *)opaque; > + s->int_level = 1; > + imxp_timer_update(s); > +} > + > +static CPUReadMemoryFunc *imxp_timer_readfn[] = { > + imxp_timer_read, > + imxp_timer_read, > + imxp_timer_read > +}; > + > +static CPUWriteMemoryFunc *imxp_timer_writefn[] = { > + imxp_timer_write, > + imxp_timer_write, > + imxp_timer_write > +}; > + > +static const VMStateDescription vmstate_imxp_timer = { > + .name = "imxp_timer", > + .version_id = 1, > + .minimum_version_id = 1, > + .minimum_version_id_old = 1, > + .fields = (VMStateField[]) { > + VMSTATE_UINT32(cr, imxp_timer_state), > + VMSTATE_UINT32(lr, imxp_timer_state), > + VMSTATE_INT32(int_level, imxp_timer_state), > + VMSTATE_PTIMER(timer, imxp_timer_state), > + VMSTATE_END_OF_LIST() > + } > +}; > + > +void imxp_timer_init(uint32_t base, qemu_irq irq) > +{ > + int iomemtype; > + imxp_timer_state *s; > + QEMUBH *bh; > + > + s = (imxp_timer_state *)g_malloc0(sizeof(imxp_timer_state)); > + s->base = base; > + s->cr = 0; > + s->lr = 0; > + s->irq = irq; > + > + bh = qemu_bh_new(imxp_timer_tick, s); > + s->timer = ptimer_init(bh); > + vmstate_register(NULL, -1,&vmstate_imxp_timer, s); > + iomemtype = cpu_register_io_memory(imxp_timer_readfn, > + imxp_timer_writefn, s, DEVICE_NATIVE_ENDIAN); > + cpu_register_physical_memory(base, 0x00001000, iomemtype); > +} > diff --git a/hw/imx.h b/hw/imx.h > new file mode 100644 > index 0000000..cf0088f > --- /dev/null > +++ b/hw/imx.h > @@ -0,0 +1,14 @@ > +#ifndef hw_imx_h > + > +#include "hw.h" > + > +/* > + * ARM11 IMX processor initial port. > + * > + */ > +void imx_serial_init(uint32_t base, qemu_irq irq, CharDriverState *chr); > +void imxp_timer_init(uint32_t base, qemu_irq irq); > +void imxg_timer_init(uint32_t base, qemu_irq irq); > +qemu_irq *imx_int_init(uint32_t base, qemu_irq irq); > + > +#endif /* hw_imx_h */ > diff --git a/hw/kzm.c b/hw/kzm.c > new file mode 100644 > index 0000000..3b9cca2 > --- /dev/null > +++ b/hw/kzm.c > @@ -0,0 +1,86 @@ > +/* > + * KZM Board System emulation. > + * > + * Copyright (c) 2008 OKL > + * Written by Hans > + * > + * This code is licenced under the GPL. > + */ > + > +#include "imx.h" > +#include "hw.h" > +#include "arm-misc.h" > +#include "primecell.h" > +#include "devices.h" > +#include "pci.h" > +#include "net.h" > +#include "sysemu.h" > +#include "boards.h" > + > +/* Board init. */ > + > +static struct arm_boot_info kzm_binfo = { > + .loader_start = 0x0, > + .board_id = 0x33b, > +}; > + > +static void kzm_init(ram_addr_t ram_size, > + const char *boot_device, > + const char *kernel_filename, const char *kernel_cmdline, > + const char *initrd_filename, const char *cpu_model) > +{ > + CPUState *env; > + ram_addr_t ram_offset; > + > + qemu_irq *pic; > + qemu_irq cpu_irq; > + > + if (!cpu_model) { > + cpu_model = "arm1136"; > + } > + > + env = cpu_init(cpu_model); > + if (!env) { > + fprintf(stderr, "Unable to find CPU definition\n"); > + exit(1); > + } > + pic = arm_pic_init_cpu(env); > + cpu_irq = pic[ARM_PIC_CPU_IRQ]; > + > + ram_offset = qemu_ram_alloc(NULL, "kzm.ram", ram_size); > + cpu_register_physical_memory(0x80000000, ram_size, ram_offset | IO_MEM_RAM); > + > + pic = imx_int_init(0x68000000, cpu_irq); > + > + imx_serial_init(0x43f90000, pic[45], serial_hds[0]); > + imxp_timer_init(0x53f94000, pic[28]); > + imxp_timer_init(0x53f98000, pic[27]); > + imxg_timer_init(0x53f90000, pic[29]); > + > + /* Memory map for Kzm Emulation Baseboard: */ > + > + /* 0x43f00000 IO_AREA0 */ > + /* 0x43f90000 UART1 */ > + /* 0x43f94000 UART2 */ > + > + kzm_binfo.ram_size = ram_size; > + kzm_binfo.kernel_filename = kernel_filename; > + kzm_binfo.kernel_cmdline = kernel_cmdline; > + kzm_binfo.initrd_filename = initrd_filename; > + kzm_binfo.initrd_filename = initrd_filename; > + kzm_binfo.nb_cpus = 1; > + arm_load_kernel(first_cpu,&kzm_binfo); > +} > + > +QEMUMachine kzm_machine = { > + .name = "kzm", > + .desc = "ARM KZM Emulation Baseboard (ARM1136)", > + .init = kzm_init, > +}; > + > +static void kzm_machine_init(void) > +{ > + qemu_register_machine(&kzm_machine); > +} > + > +machine_init(kzm_machine_init); > diff --git a/hw/kzm.h b/hw/kzm.h > new file mode 100644 > index 0000000..7fad04e > --- /dev/null > +++ b/hw/kzm.h > @@ -0,0 +1,5 @@ > +void imxp_timer_init(uint32_t base, qemu_irq irq); > +void imx_serial_init(uint32_t base, qemu_irq irq, CharDriverState *chr); > +qemu_irq *imx_int_init(uint32_t base, qemu_irq irq); > + > +
diff --git a/Makefile.target b/Makefile.target index 88d2f1f..e24dda8 100644 --- a/Makefile.target +++ b/Makefile.target @@ -331,6 +331,7 @@ endif obj-arm-y = integratorcp.o versatilepb.o arm_pic.o arm_timer.o obj-arm-y += arm_boot.o pl011.o pl031.o pl050.o pl080.o pl110.o pl181.o pl190.o obj-arm-y += versatile_pci.o +obj-arm-y += kzm.o imx-int.o imx-serial.o imx-timer.o obj-arm-y += realview_gic.o realview.o arm_sysctl.o arm11mpcore.o a9mpcore.o obj-arm-y += armv7m.o armv7m_nvic.o stellaris.o pl022.o stellaris_enet.o obj-arm-y += pl061.o diff --git a/hw/imx-int.c b/hw/imx-int.c new file mode 100644 index 0000000..9674112 --- /dev/null +++ b/hw/imx-int.c @@ -0,0 +1,155 @@ +/* + * IMX31 Interrupt Controller + * + * Copyright (c) 2008 OKL + * Written by Hans Jang + * + * This code is licenced under the GPL. + */ + +#include "imx.h" + +//#define DEBUG_INT +#ifdef DEBUG_INT +#define DPRINTF(fmt, args...) \ +do { printf("imx_int: " fmt , ##args); } while (0) +#else +#define DPRINTF(fmt, args...) do {} while(0) +#endif + + +#define IMX_INT_NUM_IRQS 64 + +typedef struct { + uint32_t base; + uint32_t level; + + char pending[IMX_INT_NUM_IRQS]; + char enable[IMX_INT_NUM_IRQS]; + uint32_t intcntl; + + qemu_irq irq; +} imx_int_state; + + +/* Update interrupts. */ +static void imx_int_update(imx_int_state *s) +{ + int new_level = 0; + int i; + for(i = 0; i < IMX_INT_NUM_IRQS; ++i) { + if (s->pending[i] && s->enable[i]) { + new_level = 1; + break; + } + } + + if (s->level != new_level) { + DPRINTF("irq=%d, level=%d\n", i, new_level); + qemu_set_irq(s->irq, new_level); + s->level = new_level; + } +} + +static void imx_int_set_irq(void *opaque, int irq, int level) +{ + imx_int_state *s = (imx_int_state *)opaque; + s->pending[irq] = level; + imx_int_update(s); +} + + +static uint32_t imx_int_read(void *opaque, target_phys_addr_t offset) +{ + imx_int_state *s = (imx_int_state *)opaque; + int i; + + DPRINTF("read(offset = 0x%x)\n", offset >> 2); + switch (offset >> 2) { + case 0: /* INTCNTL */ + return s->intcntl; + case 16: /* nivecsr : pending interrupt */ + for(i = 0; i < IMX_INT_NUM_IRQS; ++i) { + if (s->pending[i] && s->enable[i]) { + imx_int_set_irq(opaque, i, 0); + //printf("return pending interrupt = irq = %d\n", i); + return i << 16; + } + } + return 0xFFFF<<16; + default: + cpu_abort (cpu_single_env, "imx_int_read: Bad offset %x\n", (int)offset); + return 0; + } +} + +static void imx_int_write(void *opaque, target_phys_addr_t offset, uint32_t val) +{ + imx_int_state *s = (imx_int_state *)opaque; + + DPRINTF("write(0x%x) = %x\n", offset>>2, val); + switch (offset >> 2) { + case 0: /* INTCNTL */ + s->intcntl = val; + break; + case 2: /* INTENABLE */ + DPRINTF("enable(%d)\n",val); + s->enable[val] = 1; + break; + case 3: /* INTDISABLE */ + s->enable[val] = 0; + DPRINTF("disabled(%d)\n",val); + imx_int_update(s); + break; + case 4: /* intenableh */ + break; + case 5: /* intenablel */ + break; + case 6: /* inttypeh */ + break; + case 7: /* inttypel */ + case 8: /* NIPRIORITY */ + case 9: + case 10: + case 11: + case 12: + case 13: + case 14: + case 15: + /* ignore */ + break; + default: + cpu_abort(cpu_single_env, "imx_int_write: Bad offset %x\n", (int)offset); + return; + } + imx_int_update(s); +} + +static CPUReadMemoryFunc *imx_int_readfn[] = { + imx_int_read, + imx_int_read, + imx_int_read +}; + +static CPUWriteMemoryFunc *imx_int_writefn[] = { + imx_int_write, + imx_int_write, + imx_int_write +}; + +qemu_irq *imx_int_init(uint32_t base, qemu_irq irq) +{ + imx_int_state *s; + qemu_irq *qi; + int iomemtype; + + s = (imx_int_state *)g_malloc0(sizeof *s); + iomemtype = cpu_register_io_memory(imx_int_readfn, + imx_int_writefn, s, DEVICE_NATIVE_ENDIAN); + cpu_register_physical_memory(base, 0x00001000, iomemtype); + qi = qemu_allocate_irqs(imx_int_set_irq, s, 64); + s->base = base; + s->irq = irq; + /* ??? Save/restore. */ + return qi; +} diff --git a/hw/imx-serial.c b/hw/imx-serial.c new file mode 100644 index 0000000..d14474d --- /dev/null +++ b/hw/imx-serial.c @@ -0,0 +1,195 @@ +/* + * IMX31 Interrupt Controller + * + * Copyright (c) 2008 OKL + * Written by Hans + * + * This code is licenced under the GPL. + */ + +#include "imx.h" +#include "qemu-char.h" + +//#define DEBUG_SERIAL +#ifdef DEBUG_SERIAL +#define DPRINTF(fmt, args...) \ +do { printf("imx_serial: " fmt , ##args); } while (0) +#else +#define DPRINTF(fmt, args...) do {} while(0) +#endif + +typedef struct { + uint32_t base; + int32_t readbuff; + + uint32_t usr1; + uint32_t usr2; + uint32_t ucr1; + uint32_t uts1; + + qemu_irq irq; + CharDriverState *chr; +} imx_state; + +#define USR1_TRDY 0x2000 /* Xmitter ready */ +#define USR1_RRDY 0x200 /* receiver ready */ + +#define USR2_TXFE 0x4000 +#define USR2_RDR 0x1 +#define USR2_TXDC 0x8 + +#define UCR1_RRDYEN 0x200 +#define UCR1_TRDYEN 0x2000 + +#define UTS1_TXFULL 0x10 +#define UTS1_RXEMPTY 0x20 + +static void imx_update(imx_state *s) +{ + uint32_t flags; + flags = (s->usr1 & s->ucr1); + qemu_set_irq(s->irq, !!flags); +} + +static uint32_t imx_read(void *opaque, target_phys_addr_t offset) +{ + imx_state *s = (imx_state *)opaque; + uint32_t c; + + DPRINTF("read(offset=%x)\n", offset >> 2); + switch (offset >> 2) { + case 0x0: /* URXD */ + c = s->readbuff; + s->usr1 &= ~USR1_RRDY; + s->usr2 &= ~USR2_RDR; + s->uts1 |= UTS1_RXEMPTY; + imx_update(s); + qemu_chr_accept_input(s->chr); + return c; + case 0x25: /* USR1 */ + imx_update(s); + return s->usr1; + case 0x26: /* USR2 */ + imx_update(s); + return s->usr2; + + case 0x20: /* UCR1 */ + return s->ucr1; + case 0x2d: /* UTS1 */ + return s->uts1; + + case 0x21: /* UCR2 */ + case 0x22: /* UCR3 */ + case 0x23: /* UCR4 */ + case 0x24: /* UFCR */ + return 0x0; /* TODO */ + + default: + cpu_abort (cpu_single_env, "imx_read: Bad offset %x\n", (int)offset); + return 0; + } +} + + +static void imx_write(void *opaque, target_phys_addr_t offset, + uint32_t value) +{ + imx_state *s = (imx_state *)opaque; + unsigned char ch; + + DPRINTF("write(offset=%x, value = %x)\n", offset >> 2, value); + switch (offset >> 2) { + case 0x10: /* UTXD */ + ch = value; + if (s->chr) + qemu_chr_fe_write(s->chr, &ch, 1); + // XXX imx_update(s); + break; + + case 0x20: /* UCR1 */ + s->ucr1 = value; + DPRINTF("write(ucr1=%x)\n", value); + imx_update(s); + break; + + case 0x21: /* UCR2 */ + case 0x22: /* UCR3 */ + case 0x23: /* UCR4 */ + case 0x24: /* UFCR */ + case 0x25: /* USR1 */ + case 0x29: /* UBIR */ + case 0x2a: /* UBMR */ + case 0x2c: /* BIPR1 */ + /* TODO */ + break; + + default: + cpu_abort (cpu_single_env, "imx_write: Bad offset %x\n", (int)offset); + } +} + +static int imx_can_receive(void *opaque) +{ + imx_state *s = (imx_state *)opaque; + return !(s->usr1 & USR1_RRDY); +} + +static void imx_put_data(void *opaque, uint32_t value) +{ + imx_state *s = (imx_state *)opaque; + + s->usr1 |= USR1_RRDY; + s->usr2 |= USR2_RDR; + s->uts1 &= ~UTS1_RXEMPTY; + s->readbuff = value; + imx_update(s); +} + +static void imx_receive(void *opaque, const uint8_t *buf, int size) +{ + imx_put_data(opaque, *buf); +} + +static void imx_event(void *opaque, int event) +{ + if (event == CHR_EVENT_BREAK) + imx_put_data(opaque, 0x400); +} + +static CPUReadMemoryFunc *imx_readfn[] = { + imx_read, + imx_read, + imx_read +}; + +static CPUWriteMemoryFunc *imx_writefn[] = { + imx_write, + imx_write, + imx_write +}; + +void imx_serial_init(uint32_t base, qemu_irq irq, CharDriverState *chr) +{ + int iomemtype; + imx_state *s; + + s = (imx_state *)g_malloc0(sizeof(imx_state)); + iomemtype = cpu_register_io_memory(imx_readfn, + imx_writefn, s, DEVICE_NATIVE_ENDIAN); + cpu_register_physical_memory(base, 0x00001000, iomemtype); + + s->irq = irq; + s->usr1 = USR1_TRDY; + s->usr2 = USR2_TXFE | USR2_TXDC; + s->ucr1 = UCR1_TRDYEN | UCR1_RRDYEN; + s->uts1 = UTS1_RXEMPTY; + s->readbuff = -1; + s->base = base; + s->chr = chr; + if (chr){ + qemu_chr_add_handlers(chr, imx_can_receive, imx_receive, + imx_event, s); + } + /* ??? Save/restore. */ +} + diff --git a/hw/imx-timer.c b/hw/imx-timer.c new file mode 100644 index 0000000..65a4b28 --- /dev/null +++ b/hw/imx-timer.c @@ -0,0 +1,355 @@ +/* + * IMX31 Timer + * + * Copyright (c) 2008 OKL + * Written by Hans + * + * This code is licenced under the GPL. + */ + +#include "imx.h" +#include "qemu-timer.h" + +//#define DEBUG_TIMER +#ifdef DEBUG_TIMER +#define DPRINTF(fmt, args...) \ +do { printf("imx_timer: " fmt , ##args); } while (0) +#else +#define DPRINTF(fmt, args...) do {} while(0) +#endif + +/* + * GPT : General purpose timer + */ + +#define TIMER_MAX 0xFFFFFFFF +#define GPT_FREQ 50000000 +#define GPT_CR_EN (1 << 0) +#define GPT_SR_OF1 (1 << 0) +#define GPT_SR_ROV (1 << 5) +#define GPT_IR_OF1IE (1 << 0) +#define GPT_IR_ROVIE (1 << 5) + +typedef struct { + uint32_t base; + QEMUTimer *timer; + uint32_t cr; + uint32_t sr; + uint32_t pr; + uint32_t ir; + uint32_t ocr1; + uint32_t cnt; + + int waiting_rov; + qemu_irq irq; +} imxg_timer_state; + +/* Check all active timers, and schedule the next timer interrupt. */ +static void imxg_timer_update(imxg_timer_state *s) +{ + /* Update interrupts. */ + if ((s->cr & GPT_CR_EN) + && (((s->sr & GPT_SR_OF1) && (s->ir & GPT_IR_OF1IE)) || + ((s->sr & GPT_SR_ROV) && (s->ir & GPT_IR_ROVIE)))) { + qemu_irq_raise(s->irq); + } else { + qemu_irq_lower(s->irq); + } +} + +static uint64_t imxg_timer_update_count(imxg_timer_state *s) +{ + uint64_t clk = qemu_get_clock_ns(vm_clock); + s->cnt = ((uint32_t)muldiv64(clk, GPT_FREQ, + get_ticks_per_sec())) % TIMER_MAX; + return clk; +} + +static void imxg_timer_run(imxg_timer_state *s, uint32_t timeout) +{ + uint64_t clk = imxg_timer_update_count(s); + uint32_t diff_cnt; + if (s->cnt < timeout) { + diff_cnt = (timeout - s->cnt); + s->waiting_rov = 0; + } else { + diff_cnt = (TIMER_MAX - s->cnt); + s->waiting_rov = 1; + } + qemu_mod_timer(s->timer, clk + muldiv64(get_ticks_per_sec(), diff_cnt, GPT_FREQ)); +} + +static uint32_t imxg_timer_read(void *opaque, target_phys_addr_t offset) +{ + imxg_timer_state *s = (imxg_timer_state *)opaque; + + DPRINTF("g-read(offset=%x)\n", offset >> 2); + switch (offset >> 2) { + case 0: /* CR */ + return s->cr; + + case 1: /* prescaler */ + return s->pr; + + case 2: + return s->sr; + + case 3: + return s->ir; + + case 4: + return s->ocr1; + + case 9: /* cnt */ + imxg_timer_update_count(s); + return s->cnt; + } + + cpu_abort (cpu_single_env, "imxg_timer_read: Bad offset %x\n", + (int)offset >> 2); +} + +static void imxg_timer_write(void *opaque, target_phys_addr_t offset, + uint32_t value) +{ + imxg_timer_state *s = (imxg_timer_state *)opaque; + DPRINTF("g-write(offset=%x, value = %x)\n", offset >> 2, value); + + switch (offset >> 2) { + case 0: /* CR */ + if (!(s->cr & GPT_CR_EN) && (value & GPT_CR_EN)) { + imxg_timer_run(s, s->ocr1); + }; + if ((s->cr & GPT_CR_EN) && !(value & GPT_CR_EN)) { + qemu_del_timer(s->timer); + }; + s->cr = value; + return; + case 1: + s->pr = value; + return; + + case 2: + if (value & GPT_SR_OF1) { + s->sr &= ~GPT_SR_OF1; + } + if (value & GPT_SR_ROV) { + s->sr &= ~GPT_SR_ROV; + } + imxg_timer_update(s); + return; + + case 3: + s->ir = value; + imxg_timer_update(s); + return; + + case 4: + if (s->cr & GPT_CR_EN) { + s->ocr1 = value; + imxg_timer_run(s, s->ocr1); + } + return; + + default: + cpu_abort (cpu_single_env, "imxg_timer_write: Bad offset %x\n", + (int)offset >> 2); + } +} + +static void imxg_timer_timeout(void *opaque) { + imxg_timer_state *s = (imxg_timer_state *)opaque; + + if (s->waiting_rov) { + s->sr |= GPT_SR_ROV; + imxg_timer_run(s, s->ocr1); + } else { + s->sr |= GPT_SR_OF1; + imxg_timer_run(s, 0); + } + imxg_timer_update(s); +} + +static CPUReadMemoryFunc *imxg_timer_readfn[] = { + imxg_timer_read, + imxg_timer_read, + imxg_timer_read +}; + +static CPUWriteMemoryFunc *imxg_timer_writefn[] = { + imxg_timer_write, + imxg_timer_write, + imxg_timer_write +}; + +void imxg_timer_init(uint32_t base, qemu_irq irq) +{ + int iomemtype; + imxg_timer_state *s; + + s = (imxg_timer_state *)g_malloc0(sizeof(imxg_timer_state)); + s->base = base; + s->cr = 0; + s->ir = 0; + s->pr = 0; + s->ocr1 = 0; + s->irq = irq; + s->timer = qemu_new_timer_ns(vm_clock, imxg_timer_timeout, s); + imxg_timer_update_count(s); + + iomemtype = cpu_register_io_memory(imxg_timer_readfn, + imxg_timer_writefn, s, DEVICE_NATIVE_ENDIAN); + cpu_register_physical_memory(base, 0x00001000, iomemtype); +} + + + +/* + * EPIT :Enhanced periodic interrupt timer + */ + +#define EPIT_FREQ 1000000 +#define TIMER_TICK_LENGTH 5000 +#define IMX31_TICKS_PER_TIMESLICE (72 * TIMER_TICK_LENGTH) +#define CR_EN (1 << 0) +#define CR_SWR (1 << 16) +typedef struct { + uint32_t base; + ptimer_state *timer; + uint32_t cr; + uint32_t lr; + uint32_t cmp; + int int_level; + qemu_irq irq; +} imxp_timer_state; + +/* Check all active timers, and schedule the next timer interrupt. */ +static void imxp_timer_update(imxp_timer_state *s) +{ + /* Update interrupts. */ + if (s->int_level && (s->cr & CR_EN)) { + qemu_irq_raise(s->irq); + } else { + qemu_irq_lower(s->irq); + } +} + +static uint32_t imxp_timer_read(void *opaque, target_phys_addr_t offset) +{ + imxp_timer_state *s = (imxp_timer_state *)opaque; + + DPRINTF("p-read(offset=%x)\n", offset); + switch (offset >> 2) { + case 0: /* CR */ + return s->cr; + + case 1: /* SR */ + return s->int_level; + + case 2: /* LR - set ticks*/ + return s->lr; + + case 3: /* CMP */ + return s->cmp; + + case 4: /* CNT */ + return ptimer_get_count(s->timer); + } + cpu_abort (cpu_single_env, "imxp_timer_read: Bad offset %x\n", + (int)offset >> 2); +} + +static void imxp_timer_write(void *opaque, target_phys_addr_t offset, + uint32_t value) +{ + imxp_timer_state *s = (imxp_timer_state *)opaque; + DPRINTF("p-write(offset=%x, value = %x)\n", offset >> 2, value); + + switch (offset >> 2) { + case 0: /* CR */ + s->cr = value; + if (s->cr & CR_EN) { + ptimer_run(s->timer,0); + } else { + ptimer_stop(s->timer); + } + if (s->cr & CR_SWR) { + s->cr = 0; + s->lr = 0; + ptimer_stop(s->timer); + } + break; + case 1: /* SR - ACK*/ + s->int_level=0; + imxp_timer_update(s); + break; + + case 2: /* LR - set ticks*/ + s->lr = value; + ptimer_set_freq(s->timer, EPIT_FREQ); + ptimer_set_limit(s->timer, value, 1); + break; + + case 3: /* CMP */ + s->cmp = value; + break; + + + default: + cpu_abort (cpu_single_env, "imxp_timer_write: Bad offset %x\n", + (int)offset >> 2); + } +} + +static void imxp_timer_tick(void *opaque) +{ + imxp_timer_state *s = (imxp_timer_state *)opaque; + s->int_level = 1; + imxp_timer_update(s); +} + +static CPUReadMemoryFunc *imxp_timer_readfn[] = { + imxp_timer_read, + imxp_timer_read, + imxp_timer_read +}; + +static CPUWriteMemoryFunc *imxp_timer_writefn[] = { + imxp_timer_write, + imxp_timer_write, + imxp_timer_write +}; + +static const VMStateDescription vmstate_imxp_timer = { + .name = "imxp_timer", + .version_id = 1, + .minimum_version_id = 1, + .minimum_version_id_old = 1, + .fields = (VMStateField[]) { + VMSTATE_UINT32(cr, imxp_timer_state), + VMSTATE_UINT32(lr, imxp_timer_state), + VMSTATE_INT32(int_level, imxp_timer_state), + VMSTATE_PTIMER(timer, imxp_timer_state), + VMSTATE_END_OF_LIST() + } +}; + +void imxp_timer_init(uint32_t base, qemu_irq irq) +{ + int iomemtype; + imxp_timer_state *s; + QEMUBH *bh; + + s = (imxp_timer_state *)g_malloc0(sizeof(imxp_timer_state)); + s->base = base; + s->cr = 0; + s->lr = 0; + s->irq = irq; + + bh = qemu_bh_new(imxp_timer_tick, s); + s->timer = ptimer_init(bh); + vmstate_register(NULL, -1, &vmstate_imxp_timer, s); + iomemtype = cpu_register_io_memory(imxp_timer_readfn, + imxp_timer_writefn, s, DEVICE_NATIVE_ENDIAN); + cpu_register_physical_memory(base, 0x00001000, iomemtype); +} diff --git a/hw/imx.h b/hw/imx.h new file mode 100644 index 0000000..cf0088f --- /dev/null +++ b/hw/imx.h @@ -0,0 +1,14 @@ +#ifndef hw_imx_h + +#include "hw.h" + +/* + * ARM11 IMX processor initial port. + * + */ +void imx_serial_init(uint32_t base, qemu_irq irq, CharDriverState *chr); +void imxp_timer_init(uint32_t base, qemu_irq irq); +void imxg_timer_init(uint32_t base, qemu_irq irq); +qemu_irq *imx_int_init(uint32_t base, qemu_irq irq); + +#endif /* hw_imx_h */ diff --git a/hw/kzm.c b/hw/kzm.c new file mode 100644 index 0000000..3b9cca2 --- /dev/null +++ b/hw/kzm.c @@ -0,0 +1,86 @@ +/* + * KZM Board System emulation. + * + * Copyright (c) 2008 OKL + * Written by Hans + * + * This code is licenced under the GPL. + */ + +#include "imx.h" +#include "hw.h" +#include "arm-misc.h" +#include "primecell.h" +#include "devices.h" +#include "pci.h" +#include "net.h" +#include "sysemu.h" +#include "boards.h" + +/* Board init. */ + +static struct arm_boot_info kzm_binfo = { + .loader_start = 0x0, + .board_id = 0x33b, +}; + +static void kzm_init(ram_addr_t ram_size, + const char *boot_device, + const char *kernel_filename, const char *kernel_cmdline, + const char *initrd_filename, const char *cpu_model) +{ + CPUState *env; + ram_addr_t ram_offset; + + qemu_irq *pic; + qemu_irq cpu_irq; + + if (!cpu_model) { + cpu_model = "arm1136"; + } + + env = cpu_init(cpu_model); + if (!env) { + fprintf(stderr, "Unable to find CPU definition\n"); + exit(1); + } + pic = arm_pic_init_cpu(env); + cpu_irq = pic[ARM_PIC_CPU_IRQ]; + + ram_offset = qemu_ram_alloc(NULL, "kzm.ram", ram_size); + cpu_register_physical_memory(0x80000000, ram_size, ram_offset | IO_MEM_RAM); + + pic = imx_int_init(0x68000000, cpu_irq); + + imx_serial_init(0x43f90000, pic[45], serial_hds[0]); + imxp_timer_init(0x53f94000, pic[28]); + imxp_timer_init(0x53f98000, pic[27]); + imxg_timer_init(0x53f90000, pic[29]); + + /* Memory map for Kzm Emulation Baseboard: */ + + /* 0x43f00000 IO_AREA0 */ + /* 0x43f90000 UART1 */ + /* 0x43f94000 UART2 */ + + kzm_binfo.ram_size = ram_size; + kzm_binfo.kernel_filename = kernel_filename; + kzm_binfo.kernel_cmdline = kernel_cmdline; + kzm_binfo.initrd_filename = initrd_filename; + kzm_binfo.initrd_filename = initrd_filename; + kzm_binfo.nb_cpus = 1; + arm_load_kernel(first_cpu, &kzm_binfo); +} + +QEMUMachine kzm_machine = { + .name = "kzm", + .desc = "ARM KZM Emulation Baseboard (ARM1136)", + .init = kzm_init, +}; + +static void kzm_machine_init(void) +{ + qemu_register_machine(&kzm_machine); +} + +machine_init(kzm_machine_init); diff --git a/hw/kzm.h b/hw/kzm.h new file mode 100644 index 0000000..7fad04e --- /dev/null +++ b/hw/kzm.h @@ -0,0 +1,5 @@ +void imxp_timer_init(uint32_t base, qemu_irq irq); +void imx_serial_init(uint32_t base, qemu_irq irq, CharDriverState *chr); +qemu_irq *imx_int_init(uint32_t base, qemu_irq irq); + +