From patchwork Mon Nov 21 21:58:26 2011 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Peter Chubb X-Patchwork-Id: 126950 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 A9375B71D4 for ; Tue, 22 Nov 2011 08:59:19 +1100 (EST) Received: from localhost ([::1]:50831 helo=lists.gnu.org) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1RSbtM-0000C7-UT for incoming@patchwork.ozlabs.org; Mon, 21 Nov 2011 16:59:08 -0500 Received: from eggs.gnu.org ([140.186.70.92]:60699) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1RSbtC-0000C0-D5 for qemu-devel@nongnu.org; Mon, 21 Nov 2011 16:59:03 -0500 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1RSbt7-0002nN-Gl for qemu-devel@nongnu.org; Mon, 21 Nov 2011 16:58:58 -0500 Received: from lemon.ertos.nicta.com.au ([203.143.174.143]:59716 helo=lemon.ken.nicta.com.au) by eggs.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1RSbt6-0002mT-C0 for qemu-devel@nongnu.org; Mon, 21 Nov 2011 16:58:53 -0500 Received: from [2402:1800:4000:2:224:d7ff:feaf:390] (port=54888 helo=Diprotodon.keg.ertos.in.nicta.com.au.chubb.wattle.id.au) by lemon.ken.nicta.com.au with esmtpsa (TLS1.0:DHE_RSA_AES_128_CBC_SHA1:16) (Exim 4.72) (envelope-from ) id 1RSbsk-0006Jb-IX; Tue, 22 Nov 2011 08:58:37 +1100 Date: Tue, 22 Nov 2011 08:58:26 +1100 Message-ID: From: Peter Chubb To: Peter Maydell In-Reply-To: References: 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:224:d7ff:feaf:390 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: Peter Chubb , qemu-devel@nongnu.org Subject: [Qemu-devel] [PATCH] imx.31 and KZM board support 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 Hi Peter, Please find appended a patch containing initial support for the FreeScale i.MX31 and the KZM Arm11 evaluation board. The implementation was originally written by Hans Jang and Adam Clench of OK-Labs; I've updated it to the current qdev and memory region paradigms and implemented enough extra that Linux will boot on the patched QEMU using a ram disk. The i.MX 31 Serial controller is found in most of the i.MX SoCs; the AVIC and timer implementations can also be shared, albeit with fewer chips. Signed-off-by: Peter Chubb Signed-off-by: Hans Jang Signed-off-by: Adam Clench --- Makefile.target | 1 hw/imx_avic.c | 294 ++++++++++++++++++++++++++++++++++++++ hw/imx_serial.c | 260 +++++++++++++++++++++++++++++++++ hw/imx_timer.c | 430 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ hw/kzm.c | 159 ++++++++++++++++++++ 5 files changed, 1144 insertions(+) create mode 100644 hw/imx_avic.c create mode 100644 hw/imx_serial.c create mode 100644 hw/imx_timer.c create mode 100644 hw/kzm.c -- Dr Peter Chubb http://www.gelato.unsw.edu.au peterc AT gelato.unsw.edu.au http://www.ertos.nicta.com.au ERTOS within National ICT Australia Index: qemu-working/Makefile.target =================================================================== --- qemu-working.orig/Makefile.target 2011-11-22 08:40:56.380128155 +1100 +++ qemu-working/Makefile.target 2011-11-22 08:42:26.288661513 +1100 @@ -336,20 +336,21 @@ obj-sparc-y = sun4m.o lance.o tcx.o sun4 obj-sparc-y += slavio_timer.o slavio_misc.o sparc32_dma.o obj-sparc-y += cs4231.o eccmemctl.o sbi.o sun4c_intctl.o leon3.o # GRLIB obj-sparc-y += grlib_gptimer.o grlib_irqmp.o grlib_apbuart.o 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_avic.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 obj-arm-y += arm-semi.o obj-arm-y += pxa2xx.o pxa2xx_pic.o pxa2xx_gpio.o pxa2xx_timer.o pxa2xx_dma.o obj-arm-y += pxa2xx_lcd.o pxa2xx_mmci.o pxa2xx_pcmcia.o pxa2xx_keypad.o obj-arm-y += gumstix.o obj-arm-y += zaurus.o ide/microdrive.o spitz.o tosa.o tc6393xb.o obj-arm-y += omap1.o omap_lcdc.o omap_dma.o omap_clk.o omap_mmc.o omap_i2c.o \ omap_gpio.o omap_intc.o omap_uart.o Index: qemu-working/hw/imx_avic.c =================================================================== --- /dev/null 1970-01-01 00:00:00.000000000 +0000 +++ qemu-working/hw/imx_avic.c 2011-11-22 08:51:09.733239638 +1100 @@ -0,0 +1,294 @@ +/* + * IMX31 Vectored Interrupt Controller + * + * Note this is NOT the PL192 provided by ARM, but + * a custom implementation by FreeScale. + * + * Copyright (c) 2008 OKL + * Written by Hans + * + * This code is licenced under the GPL. + * + * TODO: implement vectors and priorities. + */ + +#include "hw.h" +#include "sysbus.h" +#include /* ffsll */ + +#define DEBUG_INT 1 +#undef DEBUG_INT /* comment out for debugging */ + +#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 + +/* Interrupt Control Bits */ +#define ABFLAG (1<<25) +#define ABFEN (1<<24) +#define NIDIS (1<<22) /* Normal Interrupt disable */ +#define FIDIS (1<<21) /* Fast interrupt disable */ +#define NIAD (1<<20) /* Normal Interrupt Arbiter Rise ARM level */ +#define FIAD (1<<19) /* Fast Interrupt Arbiter Rise ARM level */ +#define NM (1<<18) /* Normal interrupt mode */ + +typedef struct { + SysBusDevice busdev; + MemoryRegion iomem; + uint64_t pending; + uint64_t enabled; + uint64_t is_fiq; + uint32_t intcntl; + uint32_t intmask; + qemu_irq irq; + qemu_irq fiq; + uint32_t prio[IMX_INT_NUM_IRQS/(32/4)]; /* Priorities are 4-bits each */ +} imx_int_state; + +static inline int imx_int_prio(imx_int_state *s, int irq) +{ + uint32_t word = irq / (32/4); + uint32_t part = irq % (32/4); + return 0xff & (s->prio[word] >> (4 * part)); +} + +static inline void imx_int_set_prio(imx_int_state *s, int irq, int prio) +{ + uint32_t word = irq / (32/4); + uint32_t part = 4 * (irq % (32/4)); + uint32_t mask = ~(0xff << part); + s->prio[word] &= mask; + s->prio[word] |= prio << part; +} + +/* Update interrupts. */ +static void imx_int_update(imx_int_state *s) +{ + int i; + uint64_t new = s->pending; + uint64_t flags; + + flags = new & s->enabled & s->is_fiq; + qemu_set_irq(s->fiq, !!flags); + + flags = new & s->enabled & ~s->is_fiq; + if (!flags || likely((s->intmask & 0x1f) == 0x1f)) { + qemu_set_irq(s->irq, !!flags); + return; + } + /* Take interrupt if prio lower than the value of intmask */ + + for (i = 0; i < IMX_INT_NUM_IRQS; i++) { + if (flags & (1< s->intmask) { + qemu_set_irq(s->irq, 1); + return; + } + } + } + +} + +static void imx_int_set_irq(void *opaque, int irq, int level) +{ + imx_int_state *s = (imx_int_state *)opaque; + + if (level) { + s->pending |= (1ULL << irq); + } else { + s->pending &= ~(1ULL << irq); + } + + imx_int_update(s); +} + + +static uint64_t imx_int_read(void *opaque, + target_phys_addr_t offset, unsigned size) +{ + imx_int_state *s = (imx_int_state *)opaque; + + + DPRINTF("read(offset = 0x%x)\n", offset >> 2); + switch (offset >> 2) { + case 0: /* INTCNTL */ + return s->intcntl; + + case 1: /* Normal Interrupt Mask Register, NIMASK */ + return s->intmask; + + case 2: /* Interrupt Enable Number Register, INTENNUM */ + case 3: /* Interrupt Disable Number Register, INTDISNUM */ + return 0; + + case 4: /* Interrupt Enabled Number Register High */ + return s->enabled >> 32; + case 5: /* Interrupt Enabled Number Register Low */ + return s->enabled & 0xffffffffULL; + case 6: /* Interrupt Type Register High */ + return s->is_fiq >> 32; + case 7: /* Interrupt Type Register Low */ + return s->is_fiq & 0xffffffffUll; + case 8: /* Normal Interrupt Priority Register 7 */ + case 9: /* Normal Interrupt Priority Register 6 */ + case 10:/* Normal Interrupt Priority Register 5 */ + case 11:/* Normal Interrupt Priority Register 4 */ + case 12:/* Normal Interrupt Priority Register 3 */ + case 13:/* Normal Interrupt Priority Register 2 */ + case 14:/* Normal Interrupt Priority Register 1 */ + case 15:/* Normal Interrupt Priority Register 0 */ + return s->prio[15-(offset>>2)]; + + case 16: /* Normal interrupt vector and status register */ + { + uint64_t flags = s->pending & s->enabled & ~s->is_fiq; + int i = ffsll(flags); + if (i) { + imx_int_set_irq(opaque, i-1, 0); + return (i-1) << 16; + } + return 0xFFFF<<16; + } + case 17:/* Fast Interrupt vector and status register */ + { + uint64_t flags = s->pending & s->enabled & s->is_fiq; + int i = ffsll(flags); + if (i) { + imx_int_set_irq(opaque, i-1, 0); + return (i-1) << 16; + } + return 0xFFFF<<16; + } + case 18:/* Interrupt source register high */ + return s->pending >> 32; + case 19:/* Interrupt source register low */ + return s->pending & 0xFFFFFFFFULL; + case 20:/* Interrupt Force Register high */ + case 21:/* Interrupt Force Register low */ + return 0; + case 22:/* Normal Interrupt Pending Register High */ + return (s->pending & s->enabled & ~s->is_fiq) >> 32; + case 23:/* Normal Interrupt Pending Register Low */ + return (s->pending & s->enabled & ~s->is_fiq) & 0XFFFFFFFFULL; + case 24: /* Fast Interrupt Pending Register High */ + return (s->pending & s->enabled & s->is_fiq) >> 32; + case 25: /* Fast Interrupt Pending Register Low */ + return (s->pending & s->enabled & s->is_fiq) & 0XFFFFFFFFULL; + case 0x40: /* AVIC vector 0, use for WFI WAR */ + return 0x4; + default: + printf("imx_int_read: Bad offset 0x%x\n", (int)offset); + return 0; + } +} + +static void imx_int_write(void *opaque, target_phys_addr_t offset, + uint64_t val, unsigned size) +{ + imx_int_state *s = (imx_int_state *)opaque; + + /* Vector Registers not yet supported */ + if (offset >= 0x100 && offset <= 0x2fc) { + DPRINTF("imx_int_write to vector register %d\n", + (offset - 0x100)>>2); + return; + } + + DPRINTF("imx_int_write(0x%x) = %x\n", + (unsigned int)offset>>2, (unsigned int)val); + switch (offset >> 2) { + case 0: /* Interrupt Control Register, INTCNTL */ + s->intcntl = val; + break; + case 1: /* Normal Interrupt Mask Register, NIMASK */ + s->intmask = val; + break; + case 2: /* Interrupt Enable Number Register, INTENNUM */ + DPRINTF("enable(%d)\n", (int)val); + s->enabled |= (1ULL << val); + break; + case 3: /* Interrupt Disable Number Register, INTDISNUM */ + s->enabled &= ~(1ULL << val); + DPRINTF("disabled(%d)\n", (int)val); + break; + case 4: /* Interrupt Enable Number Register High */ + s->enabled = (s->enabled & 0xffffffffULL) | (val << 32); + break; + case 5: /* Interrupt Enable Number Register Low */ + s->enabled = (s->enabled & 0xffffffff00000000ULL) | val; + break; + case 6: /* Interrypt Type Register High */ + s->is_fiq = (s->is_fiq & 0xffffffffULL) | (val << 32); + break; + case 7: /* Interrupt Type Register Low */ + s->is_fiq = (s->is_fiq & 0xffffffff00000000ULL) | val; + break; + case 8: /* Normal Interrupt Priority Register 7 */ + case 9: /* Normal Interrupt Priority Register 6 */ + case 10:/* Normal Interrupt Priority Register 5 */ + case 11:/* Normal Interrupt Priority Register 4 */ + case 12:/* Normal Interrupt Priority Register 3 */ + case 13:/* Normal Interrupt Priority Register 2 */ + case 14:/* Normal Interrupt Priority Register 1 */ + case 15:/* Normal Interrupt Priority Register 0 */ + s->prio[15-(offset>>2)] = val; + return; + /* Read-only registers, writes ignored */ + case 16:/* Normal Interrupt Vector and Status register */ + case 17:/* Fast Interrupt vector and status register */ + case 18:/* Interrupt source register high */ + case 19:/* Interrupt source register low */ + return; + case 20:/* Interrupt Force Register high */ + s->pending = (s->pending & 0xffffffffULL) | (val << 32); + break; + case 21:/* Interrupt Force Register low */ + s->pending = (s->pending & 0xffffffff00000000ULL) | val; + break; + case 22:/* Normal Interrupt Pending Register High */ + case 23:/* Normal Interrupt Pending Register Low */ + case 24: /* Fast Interrupt Pending Register High */ + case 25: /* Fast Interrupt Pending Register Low */ + return; + default: + hw_error("imx_int_write: Bad offset %x\n", (int)offset); + } + imx_int_update(s); +} + +static const MemoryRegionOps imx_int_ops = { + .read = imx_int_read, + .write = imx_int_write, + .endianness = DEVICE_NATIVE_ENDIAN, +}; + +static int imx_int_init(SysBusDevice *dev) +{ + imx_int_state *s = FROM_SYSBUS(imx_int_state, dev);; + + memory_region_init_io(&s->iomem, &imx_int_ops, s, "imx_int", 0x1000); + sysbus_init_mmio_region(dev, &s->iomem); + + qdev_init_gpio_in(&dev->qdev, imx_int_set_irq, IMX_INT_NUM_IRQS); + sysbus_init_irq(dev, &s->irq); + sysbus_init_irq(dev, &s->fiq); + + s->intmask = 0x1f; + s->enabled = 0ULL; + return 0; +} + +static void imx_int_register_devices(void) +{ + sysbus_register_dev("imx_int", sizeof(imx_int_state), + imx_int_init); +} + +device_init(imx_int_register_devices); + Index: qemu-working/hw/imx_serial.c =================================================================== --- /dev/null 1970-01-01 00:00:00.000000000 +0000 +++ qemu-working/hw/imx_serial.c 2011-11-22 08:49:36.084276219 +1100 @@ -0,0 +1,260 @@ +/* + * IMX31 UARTS + * + * Copyright (c) 2008 OKL + * Written by Hans + * + * This code is licenced under the GPL. + * This is a `bare-bones' implementation of the IMX series serial ports. + * TODO: + * -- implement FIFOs. The real hardware has 32 word transmit + * and receive FIFOs + * -- implement DMA + * -- implement BAUD-rate and modem lines, for when the backend + * is a real serial device. + */ + +#include "hw.h" +#include "sysbus.h" +#include "qemu-char.h" + +#define DEBUG_SERIAL 1 +#undef DEBUG_SERIAL /* comment out for debugging */ + +#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 { + SysBusDevice busdev; + MemoryRegion iomem; + int32_t readbuff; + + uint32_t usr1; + uint32_t usr2; + uint32_t ucr1; + uint32_t uts1; + + uint32_t ubrm; + uint32_t ubrc; + + qemu_irq irq; + CharDriverState *chr; +} imx_state; + +#define URXD_CHARRDY (1<<15) /* character read is valid */ + +#define USR1_TRDY (1<<13) /* Xmitter ready */ +#define USR1_RRDY (1<<9) /* receiver ready */ + +#define USR2_TXFE (1<<14) /* Transmit FIFO empty */ +#define USR2_RDR (1<<0) /* Receiove data ready */ +#define USR2_TXDC (1<<3) /* Transmission complete */ + +#define UCR1_UARTEN (1<<0) +#define UCR1_RRDYEN (1<<9) +#define UCR1_TRDYEN (1<<13) +#define UCR1_TXMPTYEN (1<<6) + +#define UTS1_TXEMPTY (1<<6) +#define UTS1_RXEMPTY (1<<5) +#define UTS1_TXFULL (1<<4) +#define UTS1_RXFULL (1<<3) + +static void imx_update(imx_state *s) +{ + uint32_t flags; + + flags = ((s->usr1 & s->ucr1)) & (USR1_TRDY|USR1_RRDY); + if (0 == (s->ucr1 & UCR1_TXMPTYEN)) { + flags &= ~USR1_TRDY; + } + + qemu_set_irq(s->irq, !!flags); +} + +static uint64_t imx_serial_read(void *opaque, target_phys_addr_t offset, + unsigned size) +{ + 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 | URXD_CHARRDY; + + case 0x20: /* UCR1 */ + return s->ucr1; + + case 0x21: /* UCR2 */ + return 1; /* reset complete */ + + case 0x25: /* USR1 */ + imx_update(s); + return s->usr1; + + case 0x26: /* USR2 */ + imx_update(s); + return s->usr2; + + + case 0x2A: /* BRM Modulator */ + return s->ubrm; + + case 0x2B: /* Baud Rate Count */ + return s->ubrc; + + case 0x2d: /* UTS1 */ + return s->uts1; + + + case 0x22: /* UCR3 */ + case 0x23: /* UCR4 */ + case 0x24: /* UFCR */ + case 0x29: /* BRM Incremental */ + return 0x0; /* TODO */ + + default: + hw_error("imx_serial_read: bad offset: 0x%x\n", (int)offset); + /* Keep gcc happy: notreached */ + return 0; + } +} + + +static void imx_serial_write(void *opaque, target_phys_addr_t offset, + uint64_t value, unsigned size) +{ + imx_state *s = (imx_state *)opaque; + unsigned char ch; + + DPRINTF("write(offset=%x, value = %x)\n", offset >> 2, (unsigned int)value); + switch (offset >> 2) { + case 0x10: /* UTXD */ + ch = value; + if (s->chr) { + qemu_chr_fe_write(s->chr, &ch, 1); + } + s->usr1 &= ~USR1_TRDY; + imx_update(s); + s->usr1 |= USR1_TRDY; + imx_update(s); + + break; + + case 0x20: /* UCR1 */ + s->ucr1 = value; + DPRINTF("write(ucr1=%x)\n", (unsigned int)value); + imx_update(s); + break; + + case 0x26: /* USR2 */ + /* + * Writing 1 to some bits clears them; all other + * values are ignored + */ + value &= (1<<15)|(1<<13)|(1<<12)|(1<<11)|(1<<10)| + (1<<8)|(1<<7)|(1<<6)|(1<<4)|(1<<2)|(1<<1); + s->usr2 &= ~value; + break; + + /* Linux expects to see what it writes here. */ + /* We don't currently alter the baud rate */ + case 0x29: /* UBIR */ + s->ubrc = value; + break; + + case 0x2a: /* UBRM */ + s->ubrm = value; + break; + + case 0x21: /* UCR2 */ + case 0x2d: /* UTS1 */ + case 0x22: /* UCR3 */ + case 0x23: /* UCR4 */ + case 0x24: /* UFCR */ + case 0x25: /* USR1 */ + case 0x2c: /* BIPR1 */ + /* TODO */ + break; + + default: + hw_error("imx_serial_write: Bad offset 0x%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 const struct MemoryRegionOps imx_serial_ops = { + .read = imx_serial_read, + .write = imx_serial_write, + .endianness = DEVICE_NATIVE_ENDIAN, +}; + +static int imx_serial_init(SysBusDevice *dev) +{ + imx_state *s = FROM_SYSBUS(imx_state, dev); + + memory_region_init_io(&s->iomem, &imx_serial_ops, s, "imx-serial", 0x1000); + sysbus_init_mmio_region(dev, &s->iomem); + sysbus_init_irq(dev, &s->irq); + s->chr = qdev_init_chardev(&dev->qdev); + + s->usr1 = USR1_TRDY; + s->usr2 = USR2_TXFE | USR2_TXDC; + s->ucr1 = UCR1_TRDYEN | UCR1_RRDYEN | UCR1_UARTEN; + s->uts1 = UTS1_RXEMPTY; + s->readbuff = 0; + if (s->chr) { + qemu_chr_add_handlers(s->chr, imx_can_receive, imx_receive, + imx_event, s); + } + return 0; + /* ??? Save/restore. */ +} + +static void imx_serial_register_devices(void) +{ + DPRINTF("imx_serial_register_devices\n"); + sysbus_register_dev("imx_serial", sizeof(imx_state), + imx_serial_init); +} + +device_init(imx_serial_register_devices); Index: qemu-working/hw/imx_timer.c =================================================================== --- /dev/null 1970-01-01 00:00:00.000000000 +0000 +++ qemu-working/hw/imx_timer.c 2011-11-22 08:51:12.589269454 +1100 @@ -0,0 +1,430 @@ +/* + * IMX31 Timer + * + * Copyright (c) 2008 OKL + * Written by Hans + * Updated by Peter Chubb + * + * This code is licenced under the GPL. + */ + +#include "hw.h" +#include "qemu-timer.h" +#include "sysbus.h" + +#define DEBUG_TIMER 1 +#undef DEBUG_TIMER /* comment out for debugging */ + +#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 0xFFFFFFFFUL +#define GPT_FREQ 50000000 /* Hz == 50 MHz */ + +/* Control register. Not all of these bits have any effect (yet) */ +#define GPT_CR_EN (1 << 0) /* GPT Enable */ +#define GPT_CR_ENMODE (1 << 1) /* GPT Enable Mode */ +#define GPT_CR_DBGEN (1 << 2) /* GPT Debug mode enable */ +#define GPT_CR_WAITEN (1 << 3) /* GPT Wait Mode Enable */ +#define GPT_CR_DOZEN (1 << 4) /* GPT Doze mode enable */ +#define GPT_CR_STOPEN (1 << 5) /* GPT Stop Mode Enable */ +#define GPT_CR_CLKSRC (7 << 6) /* Clock source select (3 bits) */ +#define GPT_CR_FRR (1 << 9) /* Freerun or Restart */ +#define GPT_CR_SWR (1 << 15) +#define GPT_CR_IM1 (3 << 16) /* Input capture channel 1 mode (2 bits) */ +#define GPT_CR_IM2 (3 << 18) /* Input capture channel 2 mode (2 bits) */ +#define GPT_CR_OM1 (7 << 20) /* Output Compare Channel 1 Mode (3 bits) */ +#define GPT_CR_OM2 (7 << 23) /* Output Compare Channel 2 Mode (3 bits) */ +#define GPT_CR_OM3 (7 << 26) /* Output Compare Channel 3 Mode (3 bits) */ +#define GPT_CR_FO1 (1 << 29) /* Force Output Compare Channel 1 */ +#define GPT_CR_FO2 (1 << 30) /* Force Output Compare Channel 2 */ +#define GPT_CR_FO3 (1 << 31) /* Force Output Compare Channel 3 */ + + + + +#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 { + SysBusDevice busdev; + QEMUTimer *timer; + MemoryRegion iomem; + 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; + +static const VMStateDescription vmstate_imxg_timer = { + .name = "imxg-timer", + .version_id = 1, + .minimum_version_id = 1, + .minimum_version_id_old = 1, + .fields = (VMStateField[]) { + VMSTATE_UINT32(cr, imxg_timer_state), + VMSTATE_UINT32(sr, imxg_timer_state), + VMSTATE_UINT32(ir, imxg_timer_state), + VMSTATE_UINT32(cnt, imxg_timer_state), + VMSTATE_UINT32(ocr1, imxg_timer_state), + VMSTATE_TIMER(timer, imxg_timer_state), + VMSTATE_END_OF_LIST() + } +}; + + +/* 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/1000000, + 1000)) % 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 + diff_cnt * 1000 / (GPT_FREQ/1000000)); +/* + clk + muldiv64(get_ticks_per_sec(), + diff_cnt, GPT_FREQ) +*/ +} + +static uint64_t imxg_timer_read(void *opaque, target_phys_addr_t offset, + unsigned size) +{ + 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, + uint64_t value, unsigned size) +{ + imxg_timer_state *s = (imxg_timer_state *)opaque; + DPRINTF("g-write(offset=%x, value = %x)\n", (unsigned int)offset >> 2, + (unsigned int)value); + + switch (offset >> 2) { + case 0: /* CR */ + if (value & GPT_CR_SWR) { /* force reset */ + value &= ~GPT_CR_SWR; + s->cr &= ~(GPT_CR_EN|GPT_CR_DOZEN|GPT_CR_WAITEN|GPT_CR_DBGEN); + s->sr = 0; + s->pr = 0; + s->ir = 0; + s->cnt = 0; + s->ocr1 = 0; + } + if (!(s->cr & GPT_CR_EN) && (value & GPT_CR_EN)) { + if (value & GPT_CR_ENMODE) { + s->cnt = 0; + } + imxg_timer_run(s, s->ocr1); + } else 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: + s->ocr1 = value; + if (s->cr & GPT_CR_EN) { + 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; + + DPRINTF("imxg_timer_timeout\n"); + 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 const MemoryRegionOps imxg_timer_ops = { + .read = imxg_timer_read, + .write = imxg_timer_write, + .endianness = DEVICE_NATIVE_ENDIAN, +}; + + +static int imxg_timer_init(SysBusDevice *dev) +{ + imxg_timer_state *s = FROM_SYSBUS(imxg_timer_state, dev); + + sysbus_init_irq(dev, &s->irq); + memory_region_init_io(&s->iomem, &imxg_timer_ops, + s, "imxg-timer", + 0x00001000); + sysbus_init_mmio_region(dev, &s->iomem); + + s->timer = qemu_new_timer_ns(vm_clock, imxg_timer_timeout, s); + s->cr = 0; + s->ir = 0; + s->pr = 0; + s->ocr1 = 0; + imxg_timer_update_count(s); + + return 0; +} + + + +/* + * 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 { + SysBusDevice busdev; + ptimer_state *timer; + MemoryRegion iomem; + 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 uint64_t imxp_timer_read(void *opaque, target_phys_addr_t offset, + unsigned size) +{ + 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, + uint64_t value, unsigned size) +{ + imxp_timer_state *s = (imxp_timer_state *)opaque; + DPRINTF("p-write(offset=%x, value = %x)\n", (unsigned int)offset >> 2, + (unsigned int)value); + + switch (offset >> 2) { + case 0: /* CR */ + 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 const MemoryRegionOps imxp_timer_ops = { + .read = imxp_timer_read, + .write = imxp_timer_write, + .endianness = DEVICE_NATIVE_ENDIAN, +}; + +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_UINT32(cmp, imxp_timer_state), + VMSTATE_INT32(int_level, imxp_timer_state), + VMSTATE_PTIMER(timer, imxp_timer_state), + VMSTATE_END_OF_LIST() + } +}; + + + +static int imxp_timer_init(SysBusDevice *dev) +{ + imxp_timer_state *s = FROM_SYSBUS(imxp_timer_state, dev); + QEMUBH *bh; + + DPRINTF("imxp_timer_init\n"); + + sysbus_init_irq(dev, &s->irq); + memory_region_init_io(&s->iomem, &imxp_timer_ops, + s, "imxp-timer", + 0x00001000); + sysbus_init_mmio_region(dev, &s->iomem); + + s->cr = 0; + s->lr = 0; + + bh = qemu_bh_new(imxp_timer_tick, s); + s->timer = ptimer_init(bh); + vmstate_register(&dev->qdev, -1, &vmstate_imxp_timer, s); + + return 0; +} + +static void imx_timer_register_devices(void) +{ + DPRINTF("Registering Timers\n"); + sysbus_register_dev("imx_timerp", sizeof(imxp_timer_state), + imxp_timer_init); + sysbus_register_dev("imx_timerg", sizeof(imxg_timer_state), + imxg_timer_init); +} + + +device_init(imx_timer_register_devices); Index: qemu-working/hw/kzm.c =================================================================== --- /dev/null 1970-01-01 00:00:00.000000000 +0000 +++ qemu-working/hw/kzm.c 2011-11-22 08:42:26.292661540 +1100 @@ -0,0 +1,159 @@ +/* + * KZM Board System emulation. + * + * Copyright (c) 2008 OKL and 2011 NICTA + * Written by Hans + * Updated by Peter Chubb. + * + * This code is licenced under the GPL. + * It (partially) emulates a Kyoto Microcomputer + * KZM-ARM11-01 evaluation board, with a FreeScale + * I.MX31 SoC + */ + +#include "sysbus.h" +#include "exec-memory.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" +#include "pc.h" /* for the FPGA UART that emulates a 16550 */ + + /* Memory map for Kzm Emulation Baseboard: + * 0x00000000-0x00003fff 16k secure ROM IGNORED + * 0x00004000-0x00407fff Reserved IGNORED + * 0x00404000-0x00407fff ROM IGNORED + * 0x00408000-0x0fffffff Reserved IGNORED + * 0x10000000-0x1fffBfff RAM aliasing IGNORED + * 0x1fffc000-0x1fffffff RAM EMULATED + * 0x20000000-0x2fffffff Reserved IGNORED + * 0x30000000-0x7fffffff I.MX31 Internal Register Space + * 0x43f00000 IO_AREA0 + * 0x43f90000 UART1 EMULATED + * 0x43f94000 UART2 EMULATED + * 0x68000000 PIC EMULATED + * 0x53f94000 PIT 1 EMULATED + * 0x53f98000 PIT 2 EMULATED + * 0x53f90000 GPT EMULATED + * 0x80000000-0x87ffffff RAM EMULATED + * 0x88000000-0x8fffffff RAM Aliasing EMULATED + * 0xa0000000-0xafffffff NAND Flash IGNORED + * 0xb0000000-0xb3ffffff Unavailable IGNORED + * 0xb4000000-0xb4000fff 8-bit free space IGNORED + * 0xb4001000-0xb400100f Board control IGNORED + * 0xb4001003 DIP switch + * 0xb4001010-0xb400101f 7-segment LED IGNORED + * 0xb4001020-0xb400102f LED IGNORED + * 0xb4001030-0xb400103f LED IGNORED + * 0xb4001040-0xb400104f FPGA, UART EMULATED + * 0xb4001050-0xb400105f FPGA, UART EMULATED + * 0xb4001060-0xb40fffff FPGA IGNORED + * 0xb6000000-0xb61fffff LAN controller EMULATED + * 0xb6200000-0xb62fffff FPGA NAND Controller IGNORED + * 0xb6300000-0xb7ffffff Free IGNORED + * 0xb8000000-0xb8004fff Memory control registers IGNORED + * 0xc0000000-0xc3ffffff PCMCIA/CF IGNORED + * 0xc4000000-0xffffffff Reserved IGNORED + */ + +#define KZM_RAMADDRESS (0x80000000) +#define KZM_FPGA (0xb4001040) + +/* Board init. */ + +static struct arm_boot_info kzm_binfo = { + .loader_start = KZM_RAMADDRESS, + .board_id = 1722, +}; + +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; + MemoryRegion *address_space_mem = get_system_memory(); + MemoryRegion *ram = g_new(MemoryRegion, 1); + MemoryRegion *sram = g_new(MemoryRegion, 1); + MemoryRegion *ram_alias = g_new(MemoryRegion, 1); + qemu_irq *cpu_pic; + DeviceState *dev; + + if (!cpu_model) { + cpu_model = "arm1136"; + } + + env = cpu_init(cpu_model); + if (!env) { + fprintf(stderr, "Unable to find CPU definition\n"); + exit(1); + } + + /* On a real system, the first 16k is a `secure boot rom' */ + + memory_region_init_ram(ram, NULL, "kzm.ram", ram_size); + memory_region_add_subregion(address_space_mem, KZM_RAMADDRESS, ram); + + memory_region_init_alias(ram_alias, "ram.alias", ram, 0, ram_size); + memory_region_add_subregion(address_space_mem, 0x88000000, ram_alias); + + memory_region_init_ram(sram, NULL, "kzm.sram", 0x4000); + memory_region_add_subregion(address_space_mem, 0x1FFFC000, sram); + + + cpu_pic = arm_pic_init_cpu(env); + dev = sysbus_create_varargs("imx_int", 0x68000000, + cpu_pic[ARM_PIC_CPU_IRQ], + cpu_pic[ARM_PIC_CPU_FIQ], NULL); + + + sysbus_create_simple("imx_serial", 0x43f90000, qdev_get_gpio_in(dev, 45)); + sysbus_create_simple("imx_serial", 0x43f94000, qdev_get_gpio_in(dev, 32)); + sysbus_create_simple("imx_timerp", 0x53f94000, qdev_get_gpio_in(dev, 28)); + sysbus_create_simple("imx_timerp", 0x53f98000, qdev_get_gpio_in(dev, 27)); + sysbus_create_simple("imx_timerg", 0x53f90000, qdev_get_gpio_in(dev, 29)); + + /* + if (nd_table[0].vlan) { + lan9118_init(&nd_table[0], 0xb6000000, qdev_get_gpio_in(dev, 51)); + } + */ + if (serial_hds[3]) { + printf("serial_hds[3]\n"); + serial_mm_init(address_space_mem, KZM_FPGA, 0, + qdev_get_gpio_in(dev, 52), + 14745600, serial_hds[3], + DEVICE_NATIVE_ENDIAN); + } + if (serial_hds[2]) { /* touchscreen */ + printf("serial_hds[2]\n"); + serial_mm_init(address_space_mem, KZM_FPGA+0x10, 0, + qdev_get_gpio_in(dev, 52), + 14745600, serial_hds[2], + DEVICE_NATIVE_ENDIAN); + } + + 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.nb_cpus = 1; + arm_load_kernel(first_cpu, &kzm_binfo); +} + +static 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);