diff mbox

imx.31 and KZM board support

Message ID w4lir9m931.wl%peter@chubb.wattle.id.au
State New
Headers show

Commit Message

Peter Chubb Nov. 21, 2011, 9:58 p.m. UTC
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 <peter.chubb@nicta.com.au>
Signed-off-by: Hans Jang <hsjang@ok-labs.com>
Signed-off-by: Adam Clench <adamc@ok-labs.com>
---
 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

Comments

Andreas Färber Nov. 21, 2011, 10:45 p.m. UTC | #1
Hi Peter,

Am 21.11.2011 22:58, schrieb Peter Chubb:
> Hi Peter,
>    Please find appended a patch containing initial support for the
>    FreeScale i.MX31 and the KZM Arm11 evaluation board.

Your patch format is a bit unusual.

Please don't include personal messages in the description, keep it in a
format we can apply unchanged with git-am.

git-am complains:

/home/andreas/QEMU/qemu-arm/.git/rebase-apply/patch:43: new blank line
at EOF.
+
warning: 1 line adds whitespace errors.

>    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 <peter.chubb@nicta.com.au>
> Signed-off-by: Hans Jang <hsjang@ok-labs.com>
> Signed-off-by: Adam Clench <adamc@ok-labs.com>

If as you describe above, you polished up patches originally by OK-Labs
then your SoB should be placed last.

> 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.

If you can, it would be nice to clarify the "GPL" license:

    ... the GNU General Public License as published by
    the Free Software Foundation; either version 1, or (at your option)
    any later version.

> + *
> + * TODO: implement vectors and priorities.
> + */
> +
> +#include "hw.h"
> +#include "sysbus.h"
> +#include <string.h> /* ffsll */
> +
> +#define DEBUG_INT 1
> +#undef DEBUG_INT /* comment out for debugging */

Usually we just do //#define DEBUG_...

> +
> +#ifdef DEBUG_INT
> +#define DPRINTF(fmt, args...) \
> +do { printf("imx_int: " fmt , ##args); } while (0)
> +#else
> +#define DPRINTF(fmt, args...) do {} while (0)
> +#endif

> 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.

Dito.

> + * 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 */

Dito.

> +
> +#ifdef DEBUG_SERIAL
> +#define DPRINTF(fmt, args...) \
> +do { printf("imx_serial: " fmt , ##args); } while (0)
> +#else
> +#define DPRINTF(fmt, args...) do {} while (0)
> +#endif

> +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.  */

What does this comment tell us? :)

> +}

> 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.

Dito.

> + */
> +
> +#include "hw.h"
> +#include "qemu-timer.h"
> +#include "sysbus.h"
> +
> +#define DEBUG_TIMER 1
> +#undef DEBUG_TIMER  /* comment out for debugging */

Dito.

> +
> +#ifdef DEBUG_TIMER
> +#   define DPRINTF(fmt, args...) \
> +        do { printf("imx_timer: " fmt , ##args); } while (0)
> +#else
> +#   define DPRINTF(fmt, args...) do {} while (0)
> +#endif

> 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.

Dito.

> + * 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
> +     */

Nice overview!

> +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";

[ My "favorite" CPU... ;) ]

> +    }
> +
> +    env = cpu_init(cpu_model);
> +    if (!env) {
> +        fprintf(stderr, "Unable to find CPU definition\n");
> +        exit(1);
> +    }

My sharp eye also spotted an | without spaces somewhere. If you haven't
already, try running scripts/checkpatch.pl.

Regards,
Andreas
Peter Chubb Nov. 21, 2011, 11:04 p.m. UTC | #2
>>>>> "Andreas" == Andreas Färber <andreas.faerber@web.de> writes:

Andreas> Hi Peter, Am 21.11.2011 22:58, schrieb Peter Chubb:
>> Hi Peter, Please find appended a patch containing initial support
>> for the FreeScale i.MX31 and the KZM Arm11 evaluation board.

Andreas> Your patch format is a bit unusual.

Andreas> Please don't include personal messages in the description,
Andreas> keep it in a format we can apply unchanged with git-am.

OK, I'll try to fix it -- I'm using Quilt so it's not too hard.

Andreas> git-am complains:

Andreas> /home/andreas/QEMU/qemu-arm/.git/rebase-apply/patch:43: new
Andreas> blank line at EOF.  + warning: 1 line adds whitespace errors.

Dunno where that comes from.

>> 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 <peter.chubb@nicta.com.au>
>> Signed-off-by: Hans Jang <hsjang@ok-labs.com> Signed-off-by: Adam
>> Clench <adamc@ok-labs.com>

Andreas> If as you describe above, you polished up patches originally
Andreas> by OK-Labs then your SoB should be placed last.

Will fix.

>> 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.

Andreas> If you can, it would be nice to clarify the "GPL" license:

Andreas>     ... the GNU General Public License as published by the
Andreas> Free Software Foundation; either version 1, or (at your
Andreas> option) any later version.

I think it's version 2.0 or later.  I'll check and fix.

>> + * + * TODO: implement vectors and priorities.  + */ + +#include
>> "hw.h" +#include "sysbus.h" +#include <string.h> /* ffsll */ +
>> +#define DEBUG_INT 1 +#undef DEBUG_INT /* comment out for debugging
>> */

Andreas> Usually we just do //#define DEBUG_...

Checkpatch.pl complains about that!


>> ??? Save/restore.  */

Andreas> What does this comment tell us? :)

Dunno.  It was in the codebase I got; I think it means that save and
restore aren't implemented yet.

Andreas> My sharp eye also spotted an | without spaces somewhere. If
Andreas> you haven't already, try running scripts/checkpatch.pl.

I couldn't find one that was obvious, except in the big list of
reset-by-write-one bits in the imx_serial.c code:

 value &= (1<<15)|(1<<13)|(1<<12)|(1<<11)|(1<<10)|
            (1<<8)|(1<<7)|(1<<6)|(1<<4)|(1<<2)|(1<<1);

checkpatch.pl didn't complain about this!


Thanks for reviewing.


--
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
Peter Maydell Nov. 21, 2011, 11:10 p.m. UTC | #3
On 21 November 2011 23:04, Peter Chubb <peter.chubb@nicta.com.au> wrote:
>>>>>> "Andreas" == Andreas Färber <andreas.faerber@web.de> writes:

>>> ??? Save/restore.  */
>
> Andreas> What does this comment tell us? :)
>
> Dunno.  It was in the codebase I got; I think it means that save and
> restore aren't implemented yet.

I think what Andreas is trying to hint is that it means you need
to implement save/restore :-) (ditto in the interrupt controller)

-- PMM
Peter Maydell Nov. 21, 2011, 11:41 p.m. UTC | #4
On 21 November 2011 21:58, Peter Chubb <peterc@gelato.unsw.edu.au> wrote:
> 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 <peter.chubb@nicta.com.au>
> Signed-off-by: Hans Jang <hsjang@ok-labs.com>
> Signed-off-by: Adam Clench <adamc@ok-labs.com>
> ---
>  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(+)

This is a rather long patch. I think it would be easier to review
as a four patch series (1 patch per device plus 1 for the board model).

>  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
>
> 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 <string.h> /* 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));

If these are four bit fields (as suggested by the comment and the
size of the shift) why are we masking with 0xff rather than 0xf ?

Also can we avoid all this repetition of the (32/4) magic number?

> +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);

In the previous function we put the *4 in the shift; here we're
doing it in the calculation of part. Be consistent, please.

> +    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)) {

Is the use of likely() here really justified?

> +        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<<i)) {
> +            if (imx_int_prio(s, i) > 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;

ULL

> +    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;

0x

> +    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 */

Interrupt

> +        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);

Don't hw_error() for guest bad behaviour.

> +    }
> +    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;

The code setting member fields should be in a reset function.
Can't you just say "s->enabled = 0;" ?

> +    return 0;
> +}
> +
> +static void imx_int_register_devices(void)
> +{
> +    sysbus_register_dev("imx_int", sizeof(imx_int_state),
> +                        imx_int_init);

Use sysbus_register_withprop(). Provide a VMState so you get save/restore.


> +}
> +
> +device_init(imx_int_register_devices);

This macro doesn't need a trailing semicolon and mostly we don't
seem to give it one.



> +
> 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 */

Receive

> +#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)) {

We don't use the 0 == foo style in qemu.

> +        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;

Don't hw_error here.

> +    }
> +}
> +
> +
> +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;

Initialisation of fields => in a reset function.

> +    if (s->chr) {
> +        qemu_chr_add_handlers(s->chr, imx_can_receive, imx_receive,
> +                              imx_event, s);
> +    }
> +    return 0;
> +    /* ??? Save/restore.  */

Implement 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);

I'll come back and look at the timer and board later.

-- PMM
Peter Chubb Nov. 21, 2011, 11:54 p.m. UTC | #5
Thanks Peter,
I'll split the patches and resubmit.  It'll probably be tomorrow or Thursday.

Do you have a suggestion for an alternative to hw_error for guest bad
behaviour?  It seems to be used historically.  For emulating ARM,
causing qemu to stop isn't such a bad idea ... the guest does
something wrong and the qemu instance dies with a message.  It's not
as it that'll affect any other guests, each of which will have their
own qemu instance.

--
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
Peter Maydell Nov. 22, 2011, 12:12 a.m. UTC | #6
On 21 November 2011 23:54, Peter Chubb <peterc@gelato.unsw.edu.au> wrote:
> Do you have a suggestion for an alternative to hw_error for guest bad
> behaviour?  It seems to be used historically.  For emulating ARM,
> causing qemu to stop isn't such a bad idea ... the guest does
> something wrong and the qemu instance dies with a message.  It's not
> as it that'll affect any other guests, each of which will have their
> own qemu instance.

The question's come up before and we don't currently have a good
API for it. Personally I prefer "silently ignore" for the moment;
hardware doesn't chatter at you about guest OS misbehaviour...

-- PMM
Peter Chubb Nov. 22, 2011, 12:27 a.m. UTC | #7
>>>>> "Peter" == Peter Maydell <peter.maydell@linaro.org> writes:

Peter> On 21 November 2011 23:54, Peter Chubb
Peter> <peterc@gelato.unsw.edu.au> wrote:
>> Do you have a suggestion for an alternative to hw_error for guest
>> bad behaviour?  It seems to be used historically.  For emulating
>> ARM, causing qemu to stop isn't such a bad idea ... the guest does
>> something wrong and the qemu instance dies with a message.  It's
>> not as it that'll affect any other guests, each of which will have
>> their own qemu instance.

Peter> The question's come up before and we don't currently have a
Peter> good API for it. Personally I prefer "silently ignore" for the
Peter> moment; hardware doesn't chatter at you about guest OS
Peter> misbehaviour...

Speaking as an OS developer I'd much prefer to have the simulator yell
at me when my code tries to write to bad registers.  That's one of the
reasons I try to use the emulator rather than the real hardware --- it
allows one to find bugs otherwise invisible.

--
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
Peter Maydell Nov. 22, 2011, 12:32 a.m. UTC | #8
On 22 November 2011 00:27, Peter Chubb <peter.chubb@nicta.com.au> wrote:
>>>>>> "Peter" == Peter Maydell <peter.maydell@linaro.org> writes:
>
> Peter> On 21 November 2011 23:54, Peter Chubb
> Peter> <peterc@gelato.unsw.edu.au> wrote:
>>> Do you have a suggestion for an alternative to hw_error for guest
>>> bad behaviour?  It seems to be used historically.  For emulating
>>> ARM, causing qemu to stop isn't such a bad idea ... the guest does
>>> something wrong and the qemu instance dies with a message.  It's
>>> not as it that'll affect any other guests, each of which will have
>>> their own qemu instance.
>
> Peter> The question's come up before and we don't currently have a
> Peter> good API for it. Personally I prefer "silently ignore" for the
> Peter> moment; hardware doesn't chatter at you about guest OS
> Peter> misbehaviour...
>
> Speaking as an OS developer I'd much prefer to have the simulator yell
> at me when my code tries to write to bad registers.  That's one of the
> reasons I try to use the emulator rather than the real hardware --- it
> allows one to find bugs otherwise invisible.

Yes, this is the trouble -- different audiences want different levels
of chattiness from qemu. An app developer/user basically doesn't care
if the OS is doing something slightly dubious because they have no
way to fix it anyhow. So you need a user-controllable verbosity level.
(And some OSes do dubious things at great rate, eg Linux on OMAP
deliberately touches a nonexistent register in its interrupt handler
path and if we warned every time we'd generate a huge volume of warnings.
So some kind of rate limit/"warned ten times about this already, don't
warn any more" would eb good too.)

But I think "kill qemu" is pretty definitely not the right thing
(unless the hardware bursts into flames if you access the bad
register, in which case maybe...)

-- PMM
Peter Chubb Nov. 22, 2011, 4:31 a.m. UTC | #9
Here follow four patches in separate emails, to implement basic
i.mx31 SoC support, and the KZM evaluation board built around this
chip.

The patch to the Makefile to build all the files is in the last of
the series, rather than changing Makefile.hw in each patch.

All comments received so far have been addressed --- I've added a
macro, `scream' that gives at most 10 lines of output for OS error
reporting.

Signed-off-by lines are in each individual patch.  The work was
originally done by people at OK-Labs sufficient to get OK-L4 running;
I've cleaned it up and added sufficient new functionality to get Linux
running with an initial RAM disk.
--
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
Juan Quintela Nov. 22, 2011, 11:06 a.m. UTC | #10
Peter Chubb <peterc@gelato.unsw.edu.au> wrote:
> 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 <peter.chubb@nicta.com.au>
> Signed-off-by: Hans Jang <hsjang@ok-labs.com>
> Signed-off-by: Adam Clench <adamc@ok-labs.com>

> +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()
> +    }
> +};

This is not used.  Shouldn't a call like this be needed?

> +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);

       vmstate_register(&dev->qdev, -1, &vmstate_imxg_timer, s);
???


I only looked at the vmstate bits, though.

Later, Juan.
Peter Maydell Nov. 22, 2011, 11:14 a.m. UTC | #11
On 22 November 2011 11:06, Juan Quintela <quintela@redhat.com> wrote:
> Peter Chubb <peterc@gelato.unsw.edu.au> wrote:
>> 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 <peter.chubb@nicta.com.au>
>> Signed-off-by: Hans Jang <hsjang@ok-labs.com>
>> Signed-off-by: Adam Clench <adamc@ok-labs.com>
>
>> +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()
>> +    }
>> +};
>
> This is not used.  Shouldn't a call like this be needed?
>
>> +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);
>
>       vmstate_register(&dev->qdev, -1, &vmstate_imxg_timer, s);
> ???

Any particular reason to prefer a vmstate_register() call
over just having a SysBusDeviceInfo struct with the .qdev.vmsd
field set? I tend to prefer the latter myself.

-- PMM
Peter Chubb Nov. 23, 2011, 12:48 a.m. UTC | #12
>>>>> "Juan" == Juan Quintela <quintela@redhat.com> writes:

Juan> Peter Chubb <peterc@gelato.unsw.edu.au> wrote:
Juan> This is not used.  Shouldn't a call like this be needed?


Juan>        vmstate_register(&dev->qdev, -1, &vmstate_imxg_timer, s);
Juan> ???

Yes!! Thanks for checking.

I've altered the source ready for the next rollout of patches, which
will be version 3.
--
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
Peter Chubb Nov. 23, 2011, 12:51 a.m. UTC | #13
>>>>> "Peter" == Peter Chubb <peter.chubb@nicta.com.au> writes:


Peter> All comments received so far have been addressed --- I've added
Peter> a macro, `scream' that gives at most 10 lines of output for OS
Peter> error reporting.

Except I noticed a thinko in the macro.  The decrement should be
inside the guard, thus:

#define scream(fmt, args...) \
    do { \
        static int printable = 10;\
        if (printable) { \
            printable--;\
            fprintf(stderr, fmt, ##args); \
        } \
    } while (0)


--
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
Juan Quintela Nov. 23, 2011, 2:24 p.m. UTC | #14
Peter Maydell <peter.maydell@linaro.org> wrote:
> On 22 November 2011 11:06, Juan Quintela <quintela@redhat.com> wrote:
>> Peter Chubb <peterc@gelato.unsw.edu.au> wrote:

>>
>>       vmstate_register(&dev->qdev, -1, &vmstate_imxg_timer, s);
>> ???
>
> Any particular reason to prefer a vmstate_register() call
> over just having a SysBusDeviceInfo struct with the .qdev.vmsd
> field set? I tend to prefer the latter myself.

I didn't saw a .qdev to hijack into on the patch O:-)
I also preffer to put it inside .qdev, we get
registration/unregistration working for free.

Later, Juan.
Andreas Färber Nov. 24, 2011, 7:07 p.m. UTC | #15
Am 23.11.2011 01:51, schrieb Peter Chubb:
>>>>>> "Peter" == Peter Chubb <peter.chubb@nicta.com.au> writes:
> 
> 
> Peter> All comments received so far have been addressed --- I've added
> Peter> a macro, `scream' that gives at most 10 lines of output for OS
> Peter> error reporting.
> 
> Except I noticed a thinko in the macro.  The decrement should be
> inside the guard, thus:
> 
> #define scream(fmt, args...) \
>     do { \
>         static int printable = 10;\
>         if (printable) { \
>             printable--;\
>             fprintf(stderr, fmt, ##args); \
>         } \
>     } while (0)

Another issue:

scream("black");
scream("black");
scream("black");
scream("black");
scream("black");
scream("black");
scream("black");
scream("black");
scream("black");
scream("black");
scream("red");

To show us "red", in addition to the integer count a duplicate of the
string contents would need to be stored and compared to the newly
formatted string.

If we want to do this, it should go into a central file so that it can
be reused and centrally maintained.

Andreas
Peter Chubb Nov. 26, 2011, 5:21 a.m. UTC | #16
>>>>> "Andreas" == Andreas Färber <afaerber@suse.de> writes:

Andreas> Am 23.11.2011 01:51, schrieb Peter Chubb:
>>>>>>> "Peter" == Peter Chubb <peter.chubb@nicta.com.au> writes:
>> 
>> 
Peter> All comments received so far have been addressed --- I've added
Peter> a macro, `scream' that gives at most 10 lines of output for OS
Peter> error reporting.
>> 
>> Except I noticed a thinko in the macro.  The decrement should be
>> inside the guard, thus:
>> 
>> #define scream(fmt, args...) \ do { \ static int printable = 10;\
>> if (printable) { \ printable--;\ fprintf(stderr, fmt, ##args); \ }
>> \ } while (0)

Andreas> Another issue:

Andreas> scream("black"); scream("black"); scream("black");
Andreas> scream("black"); scream("black"); scream("black");
Andreas> scream("black"); scream("black"); scream("black");
Andreas> scream("black"); scream("red");

Andreas> To show us "red", in addition to the integer count a
Andreas> duplicate of the string contents would need to be stored and
Andreas> compared to the newly formatted string.

No, because it's a macro.  Each time it appears the static count is
instantiated--- so there's a count for each call site.

Andreas> If we want to do this, it should go into a central file so
Andreas> that it can be reused and centrally maintained.

Agree -- but do we want to do this?

For this patch series, Peter M says to just use hw_error() (even
though he really doesn't like it).

Peter C
Peter Maydell Nov. 26, 2011, 7:35 p.m. UTC | #17
On 26 November 2011 05:21, Peter Chubb <peterc@gelato.unsw.edu.au> wrote:
> For this patch series, Peter M says to just use hw_error() (even
> though he really doesn't like it).

No, that was about the sp804 patch, which is just tweaking an existing
device. These are new devices.

Perhaps just having a second macro like DPRINTF [ie print to stderr,
don't kill qemu] but for guest-errors would be ok. Then we could convert
those to a general 'report guest bug' interface if/when we have one.

-- PMM
diff mbox

Patch

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 <string.h> /* 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<<i)) {
+            if (imx_int_prio(s, i) > 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);