From patchwork Fri Sep 23 00:50:32 2011 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Peter Chubb X-Patchwork-Id: 116023 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Received: from lists.gnu.org (lists.gnu.org [140.186.70.17]) (using TLSv1 with cipher AES256-SHA (256/256 bits)) (Client did not present a certificate) by ozlabs.org (Postfix) with ESMTPS id B7A46B6F8D for ; Fri, 23 Sep 2011 10:51:04 +1000 (EST) Received: from localhost ([::1]:52749 helo=lists.gnu.org) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1R6tyl-0003FV-9D for incoming@patchwork.ozlabs.org; Thu, 22 Sep 2011 20:50:59 -0400 Received: from eggs.gnu.org ([140.186.70.92]:42066) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1R6tyd-0003FQ-Jq for qemu-devel@nongnu.org; Thu, 22 Sep 2011 20:50:53 -0400 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1R6tya-0007kp-Np for qemu-devel@nongnu.org; Thu, 22 Sep 2011 20:50:51 -0400 Received: from lemon.ertos.nicta.com.au ([203.143.174.143]:40413 helo=lemon.ken.nicta.com.au) by eggs.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1R6tyZ-0007kK-Iq for qemu-devel@nongnu.org; Thu, 22 Sep 2011 20:50:48 -0400 Received: from [2402:1800:4000:2:21d:e0ff:fe35:2bed] (port=52121 helo=croc.chubb.wattle.id.au) by lemon.ken.nicta.com.au with esmtpsa (TLS1.0:DHE_RSA_AES_256_CBC_SHA1:32) (Exim 4.72) (envelope-from ) id 1R6tyN-0007yx-DP; Fri, 23 Sep 2011 10:50:39 +1000 Date: Fri, 23 Sep 2011 10:50:32 +1000 Message-ID: From: Peter Chubb To: qemu-devel@nongnu.org User-Agent: Wanderlust/2.15.9 (Almost Unreal) SEMI/1.14.6 (Maruoka) FLIM/1.14.9 (=?UTF-8?B?R29qxY0=?=) APEL/10.8 Emacs/23.3 (x86_64-pc-linux-gnu) MULE/6.0 (HANACHIRUSATO) X-Face: GgFg(Z>fx((4\32hvXq<)|jndSniCH~~$D)Ka:P@e@JR1P%Vr}EwUdfwf-4j\rUs#JR{'h# !]])6%Jh~b$VA|ALhnpPiHu[-x~@<"@Iv&|%R)Fq[[, (&Z'O)Q)xCqe1\M[F8#9l8~}#u$S$Rm`S9% \'T@`:&8>Sb*c5d'=eDYI&GF`+t[LfDH="MP5rwOO]w>ALi7'=QJHz&y&C&TE_3j! Organization: National ICT Australia MIME-Version: 1.0 (generated by SEMI 1.14.6 - "Maruoka") X-SA-Exim-Connect-IP: 2402:1800:4000:2:21d:e0ff:fe35:2bed X-SA-Exim-Mail-From: peter.chubb@nicta.com.au X-SA-Exim-Version: 4.2.1 (built Mon, 22 Mar 2010 06:52:44 +0000) X-SA-Exim-Scanned: Yes (on lemon.ken.nicta.com.au) X-detected-operating-system: by eggs.gnu.org: GNU/Linux 2.6 (newer, 3) X-Received-From: 203.143.174.143 Cc: hsjang@ok-labs.com, philipo@ok-labs.com, adamc@ok-labs.com Subject: [Qemu-devel] [PATCH] Add KZM board support to qemu X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.14 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: qemu-devel-bounces+incoming=patchwork.ozlabs.org@nongnu.org Sender: qemu-devel-bounces+incoming=patchwork.ozlabs.org@nongnu.org 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 Signed-Off-By: Hans Jang Signed-off-by: Adam Clench --- 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); + +