qemu-devel.nongnu.org archive mirror
 help / color / mirror / Atom feed
From: "andrzej zaborowski" <balrog@zabor.org>
To: qemu-devel@nongnu.org
Subject: [Qemu-devel] [PATCH] ARM XScale core features and PXA270 and PXA255 emulation.
Date: Fri, 16 Mar 2007 21:54:33 +0100	[thread overview]
Message-ID: <fb249edb0703161354t32b01bd7rb0c7a464aa2f4d74@mail.gmail.com> (raw)

[-- Attachment #1: Type: text/plain, Size: 481 bytes --]

Implements basic differences between XScale and plain ARM. The patch
also adds the main on-chip peripherals of PXA2xx: interrupt
controller, DMA, GPIO controller, SSP, I2C, I2S busses, UARTs, FIR
port, RTC, Clock/Power/Memory managers.

There's also an api to hook funcitons to coprocessor writes and reads
becaue PXA uses those in various on-chip peripherals (and they're
supposed to be faster than memory IO access to the same peripherals,
on the real hardware).

Cheers,
Andrew

[-- Attachment #2: 0003-ARM-XScale-core-features.-PXA270-and-PXA255-emulation.txt --]
[-- Type: text/plain, Size: 112409 bytes --]

From e0700c2da24f97d10037e3f1517e3d3179b2ad8d Mon Sep 17 00:00:00 2001
From: Andrzej Zaborowski <balrog@zabor.org>
Date: Fri, 16 Mar 2007 18:03:11 +0100
Subject: [PATCH] ARM XScale core features. PXA270 and PXA255 emulation.

---
 hw/pxa.h               |  192 +++++
 hw/pxa2xx.c            | 1875 ++++++++++++++++++++++++++++++++++++++++++++++++
 hw/pxa2xx_dma.c        |  500 +++++++++++++
 hw/pxa2xx_gpio.c       |  290 ++++++++
 hw/pxa2xx_pic.c        |  284 ++++++++
 target-arm/cpu.h       |   37 +-
 target-arm/exec.h      |    2 +
 target-arm/helper.c    |  151 ++++-
 target-arm/op.c        |   14 +-
 target-arm/translate.c |   32 +
 vl.h                   |    2 +
 11 files changed, 3362 insertions(+), 17 deletions(-)

diff --git a/hw/pxa.h b/hw/pxa.h
new file mode 100644
index 0000000..16eb32f
--- /dev/null
+++ b/hw/pxa.h
@@ -0,0 +1,192 @@
+/*
+ * Intel XScale PXA255/270 processor support.
+ *
+ * Copyright (c) 2006 Openedhand Ltd.
+ * Written by Andrzej Zaborowski <balrog@zabor.org>
+ *
+ * This code is licenced under the GPL.
+ */
+#ifndef PXA_H
+# define PXA_H			"pxa.h"
+
+/* Interrupt numbers */
+# define PXA2XX_PIC_SSP3	0
+# define PXA2XX_PIC_USBH2	2
+# define PXA2XX_PIC_USBH1	3
+# define PXA2XX_PIC_PWRI2C	6
+# define PXA25X_PIC_HWUART	7
+# define PXA27X_PIC_OST_4_11	7
+# define PXA2XX_PIC_GPIO_0	8
+# define PXA2XX_PIC_GPIO_1	9
+# define PXA2XX_PIC_GPIO_X	10
+# define PXA2XX_PIC_I2S 	13
+# define PXA26X_PIC_ASSP	15
+# define PXA25X_PIC_NSSP	16
+# define PXA27X_PIC_SSP2	16
+# define PXA2XX_PIC_LCD		17
+# define PXA2XX_PIC_I2C		18
+# define PXA2XX_PIC_ICP		19
+# define PXA2XX_PIC_STUART	20
+# define PXA2XX_PIC_BTUART	21
+# define PXA2XX_PIC_FFUART	22
+# define PXA2XX_PIC_MMC		23
+# define PXA2XX_PIC_SSP		24
+# define PXA2XX_PIC_DMA		25
+# define PXA2XX_PIC_OST_0	26
+# define PXA2XX_PIC_RTC1HZ	30
+# define PXA2XX_PIC_RTCALARM	31
+
+/* DMA requests */
+# define PXA2XX_RX_RQ_I2S	2
+# define PXA2XX_TX_RQ_I2S	3
+# define PXA2XX_RX_RQ_BTUART	4
+# define PXA2XX_TX_RQ_BTUART	5
+# define PXA2XX_RX_RQ_FFUART	6
+# define PXA2XX_TX_RQ_FFUART	7
+# define PXA2XX_RX_RQ_SSP1	13
+# define PXA2XX_TX_RQ_SSP1	14
+# define PXA2XX_RX_RQ_SSP2	15
+# define PXA2XX_TX_RQ_SSP2	16
+# define PXA2XX_RX_RQ_ICP	17
+# define PXA2XX_TX_RQ_ICP	18
+# define PXA2XX_RX_RQ_STUART	19
+# define PXA2XX_TX_RQ_STUART	20
+# define PXA2XX_RX_RQ_MMCI	21
+# define PXA2XX_TX_RQ_MMCI	22
+# define PXA2XX_USB_RQ(x)	((x) + 24)
+# define PXA2XX_RX_RQ_SSP3	66
+# define PXA2XX_TX_RQ_SSP3	67
+
+# define PXA2XX_RAM_BASE	0xa0000000
+
+/* pxa2xx_pic.c */
+struct pxa2xx_pic_state_s;
+struct pxa2xx_pic_state_s *pxa2xx_pic_init(target_phys_addr_t base,
+                CPUState *env);
+
+/* pxa2xx_gpio.c */
+struct pxa2xx_gpio_info_s;
+struct pxa2xx_gpio_info_s *pxa2xx_gpio_init(target_phys_addr_t base,
+                CPUState *env, void *pic, int lines);
+void pxa2xx_gpio_set(struct pxa2xx_gpio_info_s *s, int line, int level);
+void pxa2xx_gpio_handler_set(struct pxa2xx_gpio_info_s *s, int line,
+                gpio_handler_t handler, void *opaque);
+void pxa2xx_gpio_read_notifier(struct pxa2xx_gpio_info_s *s,
+                void (*handler)(void *opaque), void *opaque);
+
+/* pxa2xx_dma.c */
+struct pxa2xx_dma_state_s;
+struct pxa2xx_dma_state_s *pxa255_dma_init(target_phys_addr_t base,
+                void *pic, int irq);
+struct pxa2xx_dma_state_s *pxa27x_dma_init(target_phys_addr_t base,
+                void *pic, int irq);
+void pxa2xx_dma_request(struct pxa2xx_dma_state_s *s, int req_num, int on);
+
+/* pxa2xx.c */
+struct pxa2xx_ssp_s;
+void pxa2xx_ssp_attach(struct pxa2xx_ssp_s *port,
+                uint32_t (*readfn)(void *opaque),
+                void (*writefn)(void *opaque, uint32_t value), void *opaque);
+
+struct pxa2xx_i2c_s;
+struct pxa2xx_i2s_s;
+struct pxa2xx_fir_s;
+
+struct pxa2xx_state_s {
+    CPUState *env;
+    struct pxa2xx_pic_state_s *pic;
+    struct pxa2xx_dma_state_s *dma;
+    struct pxa2xx_gpio_info_s *gpio;
+    struct pxa2xx_ssp_s **ssp;
+    struct pxa2xx_i2c_s *i2c[2];
+    struct pxa2xx_i2s_s *i2s;
+    struct pxa2xx_fir_s *fir;
+
+    /* Power management */
+    target_phys_addr_t pm_base;
+    uint32_t pm_regs[0x40];
+
+    /* Clock management */
+    target_phys_addr_t cm_base;
+    uint32_t cm_regs[4];
+    uint32_t clkcfg;
+
+    /* Memory management */
+    target_phys_addr_t mm_base;
+    uint32_t mm_regs[0x1a];
+
+    /* Performance monitoring */
+    uint32_t pmnc;
+
+    /* Real-Time clock */
+    target_phys_addr_t rtc_base;
+    uint32_t rttr;
+    uint32_t rtsr;
+    uint32_t rtar;
+    uint32_t rdar1;
+    uint32_t rdar2;
+    uint32_t ryar1;
+    uint32_t ryar2;
+    uint32_t swar1;
+    uint32_t swar2;
+    uint32_t piar;
+    uint32_t last_rcnr;
+    uint32_t last_rdcr;
+    uint32_t last_rycr;
+    uint32_t last_swcr;
+    uint32_t last_rtcpicr;
+    int64_t last_hz;
+    int64_t last_sw;
+    int64_t last_pi;
+    QEMUTimer *rtc_hz;
+    QEMUTimer *rtc_rdal1;
+    QEMUTimer *rtc_rdal2;
+    QEMUTimer *rtc_swal1;
+    QEMUTimer *rtc_swal2;
+    QEMUTimer *rtc_pi;
+};
+
+struct pxa2xx_i2c_s {
+    target_phys_addr_t base;
+    int irq;
+    struct pxa2xx_pic_state_s *pic;
+    struct i2c_master_s master;
+    struct i2c_slave_s slave;
+
+    uint16_t control;
+    uint16_t status;
+    uint8_t ibmr;
+};
+
+struct pxa2xx_i2s_s {
+    target_phys_addr_t base;
+    int irq;
+    struct pxa2xx_dma_state_s *dma;
+    struct pxa2xx_pic_state_s *pic;
+    void (*data_req)(void *, int, int);
+
+    uint32_t control[2];
+    uint32_t status;
+    uint32_t mask;
+    uint32_t clk;
+
+    int enable;
+    int rx_len;
+    int tx_len;
+    void (*codec_out)(void *, uint32_t);
+    uint32_t (*codec_in)(void *);
+    void *opaque;
+
+    int fifo_len;
+    uint32_t fifo[16];
+};
+
+# define PA_FMT			"0x%08lx"
+# define REG_FMT		"0x%lx"
+
+struct pxa2xx_state_s *pxa270_init(DisplayState *ds, const char *revision);
+struct pxa2xx_state_s *pxa255_init(DisplayState *ds);
+
+void pxa2xx_reset(int line, int level, void *opaque);
+
+#endif	/* PXA_H */
diff --git a/hw/pxa2xx.c b/hw/pxa2xx.c
new file mode 100644
index 0000000..57231ae
--- /dev/null
+++ b/hw/pxa2xx.c
@@ -0,0 +1,1875 @@
+/*
+ * Intel XScale PXA255/270 processor support.
+ *
+ * Copyright (c) 2006 Openedhand Ltd.
+ * Written by Andrzej Zaborowski <balrog@zabor.org>
+ *
+ * This code is licenced under the GPL.
+ */
+
+# include "vl.h"
+
+static struct {
+    target_phys_addr_t io_base;
+    int irq;
+} pxa255_serial[] = {
+    { 0x40100000, PXA2XX_PIC_FFUART },
+    { 0x40200000, PXA2XX_PIC_BTUART },
+    { 0x40700000, PXA2XX_PIC_STUART },
+    { 0x41600000, PXA25X_PIC_HWUART },
+    { 0, 0 }
+}, pxa270_serial[] = {
+    { 0x40100000, PXA2XX_PIC_FFUART },
+    { 0x40200000, PXA2XX_PIC_BTUART },
+    { 0x40700000, PXA2XX_PIC_STUART },
+    { 0, 0 }
+};
+
+static struct {
+    target_phys_addr_t io_base;
+    int irq;
+} pxa250_ssp[] = {
+    { 0x41000000, PXA2XX_PIC_SSP },
+    { 0, 0 }
+}, pxa255_ssp[] = {
+    { 0x41000000, PXA2XX_PIC_SSP },
+    { 0x41400000, PXA25X_PIC_NSSP },
+    { 0, 0 }
+}, pxa26x_ssp[] = {
+    { 0x41000000, PXA2XX_PIC_SSP },
+    { 0x41400000, PXA25X_PIC_NSSP },
+    { 0x41500000, PXA26X_PIC_ASSP },
+    { 0, 0 }
+}, pxa27x_ssp[] = {
+    { 0x41000000, PXA2XX_PIC_SSP },
+    { 0x41700000, PXA27X_PIC_SSP2 },
+    { 0x41900000, PXA2XX_PIC_SSP3 },
+    { 0, 0 }
+};
+
+#define PMCR	0x00	/* Power Manager Control register */
+#define PSSR	0x04	/* Power Manager Sleep Status register */
+#define PSPR	0x08	/* Power Manager Scratch-Pad register */
+#define PWER	0x0c	/* Power Manager Wake-Up Enable register */
+#define PRER	0x10	/* Power Manager Rising-Edge Detect Enable register */
+#define PFER	0x14	/* Power Manager Falling-Edge Detect Enable register */
+#define PEDR	0x18	/* Power Manager Edge-Detect Status register */
+#define PCFR	0x1c	/* Power Manager General Configuration register */
+#define PGSR0	0x20	/* Power Manager GPIO Sleep-State register 0 */
+#define PGSR1	0x24	/* Power Manager GPIO Sleep-State register 1 */
+#define PGSR2	0x28	/* Power Manager GPIO Sleep-State register 2 */
+#define PGSR3	0x2c	/* Power Manager GPIO Sleep-State register 3 */
+#define RCSR	0x30	/* Reset Controller Status register */
+#define PSLR	0x34	/* Power Manager Sleep Configuration register */
+#define PTSR	0x38	/* Power Manager Standby Configuration register */
+#define PVCR	0x40	/* Power Manager Voltage Change Control register */
+#define PUCR	0x4c	/* Power Manager USIM Card Control/Status register */
+#define PKWR	0x50	/* Power Manager Keyboard Wake-Up Enable register */
+#define PKSR	0x54	/* Power Manager Keyboard Level-Detect Status */
+#define PCMD0	0x80	/* Power Manager I2C Command register File 0 */
+#define PCMD31	0xfc	/* Power Manager I2C Command register File 31 */
+
+static uint32_t pxa2xx_i2c_read(void *, target_phys_addr_t);
+static void pxa2xx_i2c_write(void *, target_phys_addr_t, uint32_t);
+
+static uint32_t pxa2xx_pm_read(void *opaque, target_phys_addr_t addr)
+{
+    struct pxa2xx_state_s *s = (struct pxa2xx_state_s *) opaque;
+    if (addr > s->pm_base + PCMD31) {
+        /* Special case: PWRI2C registers appear in the same range.  */
+        return pxa2xx_i2c_read(s->i2c[1], addr);
+    }
+    addr -= s->pm_base;
+
+    switch (addr) {
+    case PMCR ... PCMD31:
+        if (addr & 3)
+            goto fail;
+
+        return s->pm_regs[addr >> 2];
+    default:
+    fail:
+        printf("%s: Bad register " REG_FMT "\n", __FUNCTION__, addr);
+        break;
+    }
+    return 0;
+}
+
+static void pxa2xx_pm_write(void *opaque, target_phys_addr_t addr,
+                uint32_t value)
+{
+    struct pxa2xx_state_s *s = (struct pxa2xx_state_s *) opaque;
+    if (addr > s->pm_base + PCMD31) {
+        /* Special case: PWRI2C registers appear in the same range.  */
+        pxa2xx_i2c_write(s->i2c[1], addr, value);
+        return;
+    }
+    addr -= s->pm_base;
+
+    switch (addr) {
+    case PMCR:
+        s->pm_regs[addr >> 2] &= 0x15 & ~(value & 0x2a);
+        s->pm_regs[addr >> 2] |= value & 0x15;
+        break;
+
+    case PSSR:	/* Read-clean registers */
+    case RCSR:
+    case PKSR:
+        s->pm_regs[addr >> 2] &= ~value;
+        break;
+
+    default:	/* Read-write registers */
+        if (addr >= PMCR && addr <= PCMD31 && !(addr & 3)) {
+            s->pm_regs[addr >> 2] = value;
+            break;
+        }
+
+        printf("%s: Bad register " REG_FMT "\n", __FUNCTION__, addr);
+        break;
+    }
+}
+
+static CPUReadMemoryFunc *pxa2xx_pm_readfn[] = {
+    pxa2xx_pm_read,
+    pxa2xx_pm_read,
+    pxa2xx_pm_read,
+};
+
+static CPUWriteMemoryFunc *pxa2xx_pm_writefn[] = {
+    pxa2xx_pm_write,
+    pxa2xx_pm_write,
+    pxa2xx_pm_write,
+};
+
+#define CCCR	0x00	/* Core Clock Configuration register */
+#define CKEN	0x04	/* Clock Enable register */
+#define OSCC	0x08	/* Oscillator Configuration register */
+#define CCSR	0x0c	/* Core Clock Status register */
+
+static uint32_t pxa2xx_cm_read(void *opaque, target_phys_addr_t addr)
+{
+    struct pxa2xx_state_s *s = (struct pxa2xx_state_s *) opaque;
+    addr -= s->cm_base;
+
+    switch (addr) {
+    case CCCR:
+    case CKEN:
+    case OSCC:
+        return s->cm_regs[addr >> 2];
+
+    case CCSR:
+        return s->cm_regs[CCCR >> 2] | (3 << 28);
+
+    default:
+        printf("%s: Bad register " REG_FMT "\n", __FUNCTION__, addr);
+        break;
+    }
+    return 0;
+}
+
+static void pxa2xx_cm_write(void *opaque, target_phys_addr_t addr,
+                uint32_t value)
+{
+    struct pxa2xx_state_s *s = (struct pxa2xx_state_s *) opaque;
+    addr -= s->cm_base;
+
+    switch (addr) {
+    case CCCR:
+    case CKEN:
+        s->cm_regs[addr >> 2] = value;
+        break;
+
+    case OSCC:
+        s->cm_regs[addr >> 2] &= ~0x6e;
+        s->cm_regs[addr >> 2] |= value & 0x6e;
+        break;
+
+    default:
+        printf("%s: Bad register " REG_FMT "\n", __FUNCTION__, addr);
+        break;
+    }
+}
+
+static CPUReadMemoryFunc *pxa2xx_cm_readfn[] = {
+    pxa2xx_cm_read,
+    pxa2xx_cm_read,
+    pxa2xx_cm_read,
+};
+
+static CPUWriteMemoryFunc *pxa2xx_cm_writefn[] = {
+    pxa2xx_cm_write,
+    pxa2xx_cm_write,
+    pxa2xx_cm_write,
+};
+
+static uint32_t pxa2xx_clkpwr_read(void *opaque, int op2, int reg, int crm)
+{
+    struct pxa2xx_state_s *s = (struct pxa2xx_state_s *) opaque;
+
+    switch (reg) {
+    case 6:	/* Clock Configuration register */
+        return s->clkcfg;
+
+    case 7:	/* Power Mode register */
+        return 0;
+
+    default:
+        printf("%s: Bad register 0x%x\n", __FUNCTION__, reg);
+        break;
+    }
+    return 0;
+}
+
+static void pxa2xx_clkpwr_write(void *opaque, int op2, int reg, int crm,
+                uint32_t value)
+{
+    struct pxa2xx_state_s *s = (struct pxa2xx_state_s *) opaque;
+    static const char *pwrmode[8] = {
+        "Normal", "Idle", "Deep-idle", "Standby",
+        "Sleep", "reserved (!)", "reserved (!)", "Deep-sleep",
+    };
+
+    switch (reg) {
+    case 6:	/* Clock Configuration register */
+        s->clkcfg = value & 0xf;
+        if (value & 2)
+            printf("%s: CPU frequency change attempt\n", __FUNCTION__);
+        break;
+
+    case 7:	/* Power Mode register */
+        if (value & 8)
+            printf("%s: CPU voltage change attempt\n", __FUNCTION__);
+        switch (value & 7) {
+        case 0:
+            /* Do nothing */
+            break;
+
+        case 1:
+            /* Idle */
+            if (!(s->cm_regs[CCCR] & (1 << 31))) {	/* CPDIS */
+                cpu_interrupt(s->env, CPU_INTERRUPT_HALT);
+                break;
+            }
+            /* Fall through.  */
+
+        case 2:
+            /* Deep-Idle */
+            cpu_interrupt(s->env, CPU_INTERRUPT_HALT);
+            s->pm_regs[RCSR >> 2] |= 0x8;	/* Set GPR */
+            goto message;
+
+        case 3:
+            cpu_reset(s->env);
+            s->env->cp15.c1_sys = 0;
+            s->env->cp15.c1_coproc = 0;
+            s->env->cp15.c2 = 0;
+            s->env->cp15.c3 = 0;
+            s->pm_regs[PSSR >> 2] |= 0x8;	/* Set STS */
+            s->pm_regs[RCSR >> 2] |= 0x8;	/* Set GPR */
+
+            /*
+             * The scratch-pad register is almost universally used
+             * for storing the return address on suspend.  For the
+             * lack of a resuming bootloader, perform a jump
+             * directly to that address.
+             */
+            memset(s->env->regs, 0, 4 * 15);
+            s->env->regs[15] = s->pm_regs[PSPR >> 2];
+
+#if 0
+            buffer = 0xe59ff000;	/* ldr     pc, [pc, #0] */
+            cpu_physical_memory_write(0, &buffer, 4);
+            buffer = s->pm_regs[PSPR >> 2];
+            cpu_physical_memory_write(8, &buffer, 4);
+#endif
+
+            /* Suspend */
+            cpu_interrupt(cpu_single_env, CPU_INTERRUPT_HALT);
+
+            goto message;
+
+        default:
+        message:
+            printf("%s: machine entered %s mode\n", __FUNCTION__,
+                            pwrmode[value & 7]);
+        }
+        break;
+
+    default:
+        printf("%s: Bad register 0x%x\n", __FUNCTION__, reg);
+        break;
+    }
+}
+
+/* Performace Monitoring Registers */
+#define CPPMNC		0	/* Performance Monitor Control register */
+#define CPCCNT		1	/* Clock Counter register */
+#define CPINTEN		4	/* Interrupt Enable register */
+#define CPFLAG		5	/* Overflow Flag register */
+#define CPEVTSEL	8	/* Event Selection register */
+
+#define CPPMN0		0	/* Performance Count register 0 */
+#define CPPMN1		1	/* Performance Count register 1 */
+#define CPPMN2		2	/* Performance Count register 2 */
+#define CPPMN3		3	/* Performance Count register 3 */
+
+static uint32_t pxa2xx_perf_read(void *opaque, int op2, int reg, int crm)
+{
+    struct pxa2xx_state_s *s = (struct pxa2xx_state_s *) opaque;
+
+    switch (reg) {
+    case CPPMNC:
+        return s->pmnc;
+    case CPCCNT:
+        if (s->pmnc & 1)
+            return qemu_get_clock(vm_clock);
+        else
+            return 0;
+    case CPINTEN:
+    case CPFLAG:
+    case CPEVTSEL:
+        return 0;
+
+    default:
+        printf("%s: Bad register 0x%x\n", __FUNCTION__, reg);
+        break;
+    }
+    return 0;
+}
+
+static void pxa2xx_perf_write(void *opaque, int op2, int reg, int crm,
+                uint32_t value)
+{
+    struct pxa2xx_state_s *s = (struct pxa2xx_state_s *) opaque;
+
+    switch (reg) {
+    case CPPMNC:
+        s->pmnc = value;
+        break;
+
+    case CPCCNT:
+    case CPINTEN:
+    case CPFLAG:
+    case CPEVTSEL:
+        break;
+
+    default:
+        printf("%s: Bad register 0x%x\n", __FUNCTION__, reg);
+        break;
+    }
+}
+
+static uint32_t pxa2xx_cp14_read(void *opaque, int op2, int reg, int crm)
+{
+    switch (crm) {
+    case 0:
+        return pxa2xx_clkpwr_read(opaque, op2, reg, crm);
+    case 1:
+        return pxa2xx_perf_read(opaque, op2, reg, crm);
+    case 2:
+        switch (reg) {
+        case CPPMN0:
+        case CPPMN1:
+        case CPPMN2:
+        case CPPMN3:
+            return 0;
+        }
+        /* Fall through */
+    default:
+        printf("%s: Bad register 0x%x\n", __FUNCTION__, reg);
+        break;
+    }
+    return 0;
+}
+
+static void pxa2xx_cp14_write(void *opaque, int op2, int reg, int crm,
+                uint32_t value)
+{
+    switch (crm) {
+    case 0:
+        pxa2xx_clkpwr_write(opaque, op2, reg, crm, value);
+        break;
+    case 1:
+        pxa2xx_perf_write(opaque, op2, reg, crm, value);
+        break;
+    case 2:
+        switch (reg) {
+        case CPPMN0:
+        case CPPMN1:
+        case CPPMN2:
+        case CPPMN3:
+            return;
+        }
+        /* Fall through */
+    default:
+        printf("%s: Bad register 0x%x\n", __FUNCTION__, reg);
+        break;
+    }
+}
+
+#define MDCNFG		0x00	/* SDRAM Configuration register */
+#define MDREFR		0x04	/* SDRAM Refresh Control register */
+#define MSC0		0x08	/* Static Memory Control register 0 */
+#define MSC1		0x0c	/* Static Memory Control register 1 */
+#define MSC2		0x10	/* Static Memory Control register 2 */
+#define MECR		0x14	/* Expansion Memory Bus Config register */
+#define SXCNFG		0x1c	/* Synchronous Static Memory Config register */
+#define MCMEM0		0x28	/* PC Card Memory Socket 0 Timing register */
+#define MCMEM1		0x2c	/* PC Card Memory Socket 1 Timing register */
+#define MCATT0		0x30	/* PC Card Attribute Socket 0 register */
+#define MCATT1		0x34	/* PC Card Attribute Socket 1 register */
+#define MCIO0		0x38	/* PC Card I/O Socket 0 Timing register */
+#define MCIO1		0x3c	/* PC Card I/O Socket 1 Timing register */
+#define MDMRS		0x40	/* SDRAM Mode Register Set Config register */
+#define BOOT_DEF	0x44	/* Boot-time Default Configuration register */
+#define ARB_CNTL	0x48	/* Arbiter Control register */
+#define BSCNTR0		0x4c	/* Memory Buffer Strength Control register 0 */
+#define BSCNTR1		0x50	/* Memory Buffer Strength Control register 1 */
+#define LCDBSCNTR	0x54	/* LCD Buffer Strength Control register */
+#define MDMRSLP		0x58	/* Low Power SDRAM Mode Set Config register */
+#define BSCNTR2		0x5c	/* Memory Buffer Strength Control register 2 */
+#define BSCNTR3		0x60	/* Memory Buffer Strength Control register 3 */
+#define SA1110		0x64	/* SA-1110 Memory Compatibility register */
+
+static uint32_t pxa2xx_mm_read(void *opaque, target_phys_addr_t addr)
+{
+    struct pxa2xx_state_s *s = (struct pxa2xx_state_s *) opaque;
+    addr -= s->mm_base;
+
+    switch (addr) {
+    case MDCNFG ... SA1110:
+        if ((addr & 3) == 0)
+            return s->mm_regs[addr >> 2];
+
+    default:
+        printf("%s: Bad register " REG_FMT "\n", __FUNCTION__, addr);
+        break;
+    }
+    return 0;
+}
+
+static void pxa2xx_mm_write(void *opaque, target_phys_addr_t addr,
+                uint32_t value)
+{
+    struct pxa2xx_state_s *s = (struct pxa2xx_state_s *) opaque;
+    addr -= s->mm_base;
+
+    switch (addr) {
+    case MDCNFG ... SA1110:
+        if ((addr & 3) == 0) {
+            s->mm_regs[addr >> 2] = value;
+            break;
+        }
+
+    default:
+        printf("%s: Bad register " REG_FMT "\n", __FUNCTION__, addr);
+        break;
+    }
+}
+
+static CPUReadMemoryFunc *pxa2xx_mm_readfn[] = {
+    pxa2xx_mm_read,
+    pxa2xx_mm_read,
+    pxa2xx_mm_read,
+};
+
+static CPUWriteMemoryFunc *pxa2xx_mm_writefn[] = {
+    pxa2xx_mm_write,
+    pxa2xx_mm_write,
+    pxa2xx_mm_write,
+};
+
+/* Synchronous Serial Ports */
+struct pxa2xx_ssp_s {
+    target_phys_addr_t base;
+    int irq;
+    struct pxa2xx_pic_state_s *pic;
+    int enable;
+
+    uint32_t sscr[2];
+    uint32_t sspsp;
+    uint32_t ssto;
+    uint32_t ssitr;
+    uint32_t sssr;
+    uint8_t sstsa;
+    uint8_t ssrsa;
+    uint8_t ssacd;
+
+    uint32_t rx_fifo[16];
+    int rx_level;
+    int rx_start;
+
+    uint32_t (*readfn)(void *opaque);
+    void (*writefn)(void *opaque, uint32_t value);
+    void *opaque;
+};
+
+#define SSCR0	0x00	/* SSP Control register 0 */
+#define SSCR1	0x04	/* SSP Control register 1 */
+#define SSSR	0x08	/* SSP Status register */
+#define SSITR	0x0c	/* SSP Interrupt Test register */
+#define SSDR	0x10	/* SSP Data register */
+#define SSTO	0x28	/* SSP Time-Out register */
+#define SSPSP	0x2c	/* SSP Programmable Serial Protocol register */
+#define SSTSA	0x30	/* SSP TX Time Slot Active register */
+#define SSRSA	0x34	/* SSP RX Time Slot Active register */
+#define SSTSS	0x38	/* SSP Time Slot Status register */
+#define SSACD	0x3c	/* SSP Audio Clock Divider register */
+
+/* Bitfields for above registers */
+#define SSCR0_SPI(x)	(((x) & 0x30) == 0x00)
+#define SSCR0_SSP(x)	(((x) & 0x30) == 0x10)
+#define SSCR0_UWIRE(x)	(((x) & 0x30) == 0x20)
+#define SSCR0_PSP(x)	(((x) & 0x30) == 0x30)
+#define SSCR0_SSE	(1 << 7)
+#define SSCR0_RIM	(1 << 22)
+#define SSCR0_TIM	(1 << 23)
+#define SSCR0_MOD	(1 << 31)
+#define SSCR0_DSS(x)	(((((x) >> 16) & 0x10) | ((x) & 0xf)) + 1)
+#define SSCR1_RIE	(1 << 0)
+#define SSCR1_TIE	(1 << 1)
+#define SSCR1_LBM	(1 << 2)
+#define SSCR1_MWDS	(1 << 5)
+#define SSCR1_TFT(x)	((((x) >> 6) & 0xf) + 1)
+#define SSCR1_RFT(x)	((((x) >> 10) & 0xf) + 1)
+#define SSCR1_EFWR	(1 << 14)
+#define SSCR1_PINTE	(1 << 18)
+#define SSCR1_TINTE	(1 << 19)
+#define SSCR1_RSRE	(1 << 20)
+#define SSCR1_TSRE	(1 << 21)
+#define SSCR1_EBCEI	(1 << 29)
+#define SSITR_INT	(7 << 5)
+#define SSSR_TNF	(1 << 2)
+#define SSSR_RNE	(1 << 3)
+#define SSSR_TFS	(1 << 5)
+#define SSSR_RFS	(1 << 6)
+#define SSSR_ROR	(1 << 7)
+#define SSSR_PINT	(1 << 18)
+#define SSSR_TINT	(1 << 19)
+#define SSSR_EOC	(1 << 20)
+#define SSSR_TUR	(1 << 21)
+#define SSSR_BCE	(1 << 23)
+#define SSSR_RW		0x00bc0080
+
+static void pxa2xx_ssp_int_update(struct pxa2xx_ssp_s *s)
+{
+    int level = 0;
+
+    level |= s->ssitr & SSITR_INT;
+    level |= (s->sssr & SSSR_BCE)  &&  (s->sscr[1] & SSCR1_EBCEI);
+    level |= (s->sssr & SSSR_TUR)  && !(s->sscr[0] & SSCR0_TIM);
+    level |= (s->sssr & SSSR_EOC)  &&  (s->sssr & (SSSR_TINT | SSSR_PINT));
+    level |= (s->sssr & SSSR_TINT) &&  (s->sscr[1] & SSCR1_TINTE);
+    level |= (s->sssr & SSSR_PINT) &&  (s->sscr[1] & SSCR1_PINTE);
+    level |= (s->sssr & SSSR_ROR)  && !(s->sscr[0] & SSCR0_RIM);
+    level |= (s->sssr & SSSR_RFS)  &&  (s->sscr[1] & SSCR1_RIE);
+    level |= (s->sssr & SSSR_TFS)  &&  (s->sscr[1] & SSCR1_TIE);
+    pic_set_irq_new(s->pic, s->irq, !!level);
+}
+
+static void pxa2xx_ssp_fifo_update(struct pxa2xx_ssp_s *s)
+{
+    s->sssr &= ~(0xf << 12);	/* Clear RFL */
+    s->sssr &= ~(0xf << 8);	/* Clear TFL */
+    s->sssr &= ~SSSR_TNF;
+    if (s->enable) {
+        s->sssr |= ((s->rx_level - 1) & 0xf) << 12;
+        if (s->rx_level >= SSCR1_RFT(s->sscr[1]))
+            s->sssr |= SSSR_RFS;
+        else
+            s->sssr &= ~SSSR_RFS;
+        if (0 <= SSCR1_TFT(s->sscr[1]))
+            s->sssr |= SSSR_TFS;
+        else
+            s->sssr &= ~SSSR_TFS;
+        if (s->rx_level)
+            s->sssr |= SSSR_RNE;
+        else
+            s->sssr &= ~SSSR_RNE;
+        s->sssr |= SSSR_TNF;
+    }
+
+    pxa2xx_ssp_int_update(s);
+}
+
+static uint32_t pxa2xx_ssp_read(void *opaque, target_phys_addr_t addr)
+{
+    struct pxa2xx_ssp_s *s = (struct pxa2xx_ssp_s *) opaque;
+    uint32_t retval;
+    addr -= s->base;
+
+    switch (addr) {
+    case SSCR0:
+        return s->sscr[0];
+    case SSCR1:
+        return s->sscr[1];
+    case SSPSP:
+        return s->sspsp;
+    case SSTO:
+        return s->ssto;
+    case SSITR:
+        return s->ssitr;
+    case SSSR:
+        return s->sssr | s->ssitr;
+    case SSDR:
+        if (!s->enable)
+            return 0xffffffff;
+        if (s->rx_level < 1) {
+            printf("%s: SSP Rx Underrun\n", __FUNCTION__);
+            return 0xffffffff;
+        }
+        s->rx_level --;
+        retval = s->rx_fifo[s->rx_start ++];
+        s->rx_start &= 0xf;
+        pxa2xx_ssp_fifo_update(s);
+        return retval;
+    case SSTSA:
+        return s->sstsa;
+    case SSRSA:
+        return s->ssrsa;
+    case SSTSS:
+        return 0;
+    case SSACD:
+        return s->ssacd;
+    default:
+        printf("%s: Bad register " REG_FMT "\n", __FUNCTION__, addr);
+        break;
+    }
+    return 0;
+}
+
+static void pxa2xx_ssp_write(void *opaque, target_phys_addr_t addr,
+                uint32_t value)
+{
+    struct pxa2xx_ssp_s *s = (struct pxa2xx_ssp_s *) opaque;
+    addr -= s->base;
+
+    switch (addr) {
+    case SSCR0:
+        s->sscr[0] = value & 0xc7ffffff;
+        s->enable = value & SSCR0_SSE;
+        if (value & SSCR0_MOD)
+            printf("%s: Attempt to use network mode\n", __FUNCTION__);
+        if (s->enable && SSCR0_DSS(value) < 4)
+            printf("%s: Wrong data size: %i bits\n", __FUNCTION__,
+                            SSCR0_DSS(value));
+        if (!(value & SSCR0_SSE)) {
+            s->sssr = 0;
+            s->ssitr = 0;
+            s->rx_level = 0;
+        }
+        pxa2xx_ssp_fifo_update(s);
+        break;
+
+    case SSCR1:
+        s->sscr[1] = value;
+        if (value & (SSCR1_LBM | SSCR1_EFWR))
+            printf("%s: Attempt to use SSP test mode\n", __FUNCTION__);
+        pxa2xx_ssp_fifo_update(s);
+        break;
+
+    case SSPSP:
+        s->sspsp = value;
+        break;
+
+    case SSTO:
+        s->ssto = value;
+        break;
+
+    case SSITR:
+        s->ssitr = value & SSITR_INT;
+        pxa2xx_ssp_int_update(s);
+        break;
+
+    case SSSR:
+        s->sssr &= ~(value & SSSR_RW);
+        pxa2xx_ssp_int_update(s);
+        break;
+
+    case SSDR:
+        if (SSCR0_UWIRE(s->sscr[0])) {
+            if (s->sscr[1] & SSCR1_MWDS)
+                value &= 0xffff;
+            else
+                value &= 0xff;
+        } else
+            /* Note how 32bits overflow does no harm here */
+            value &= (1 << SSCR0_DSS(s->sscr[0])) - 1;
+
+        /* Data goes from here to the Tx FIFO and is shifted out from
+         * there directly to the slave, no need to buffer it.
+         */
+        if (s->enable) {
+            if (s->writefn)
+                s->writefn(s->opaque, value);
+
+            if (s->rx_level < 0x10) {
+                if (s->readfn)
+                    s->rx_fifo[(s->rx_start + s->rx_level ++) & 0xf] =
+                            s->readfn(s->opaque);
+                else
+                    s->rx_fifo[(s->rx_start + s->rx_level ++) & 0xf] = 0x0;
+            } else
+                s->sssr |= SSSR_ROR;
+        }
+        pxa2xx_ssp_fifo_update(s);
+        break;
+
+    case SSTSA:
+        s->sstsa = value;
+        break;
+
+    case SSRSA:
+        s->ssrsa = value;
+        break;
+
+    case SSACD:
+        s->ssacd = value;
+        break;
+
+    default:
+        printf("%s: Bad register " REG_FMT "\n", __FUNCTION__, addr);
+        break;
+    }
+}
+
+void pxa2xx_ssp_attach(struct pxa2xx_ssp_s *port,
+                uint32_t (*readfn)(void *opaque),
+                void (*writefn)(void *opaque, uint32_t value), void *opaque)
+{
+    if (!port) {
+        printf("%s: no such SSP\n", __FUNCTION__);
+        exit(-1);
+    }
+
+    port->opaque = opaque;
+    port->readfn = readfn;
+    port->writefn = writefn;
+}
+
+static CPUReadMemoryFunc *pxa2xx_ssp_readfn[] = {
+    pxa2xx_ssp_read,
+    pxa2xx_ssp_read,
+    pxa2xx_ssp_read,
+};
+
+static CPUWriteMemoryFunc *pxa2xx_ssp_writefn[] = {
+    pxa2xx_ssp_write,
+    pxa2xx_ssp_write,
+    pxa2xx_ssp_write,
+};
+
+/* Real-Time Clock */
+#define RCNR		0x00	/* RTC Counter register */
+#define RTAR		0x04	/* RTC Alarm register */
+#define RTSR		0x08	/* RTC Status register */
+#define RTTR		0x0c	/* RTC Timer Trim register */
+#define RDCR		0x10	/* RTC Day Counter register */
+#define RYCR		0x14	/* RTC Year Counter register */
+#define RDAR1		0x18	/* RTC Wristwatch Day Alarm register 1 */
+#define RYAR1		0x1c	/* RTC Wristwatch Year Alarm register 1 */
+#define RDAR2		0x20	/* RTC Wristwatch Day Alarm register 2 */
+#define RYAR2		0x24	/* RTC Wristwatch Year Alarm register 2 */
+#define SWCR		0x28	/* RTC Stopwatch Counter register */
+#define SWAR1		0x2c	/* RTC Stopwatch Alarm register 1 */
+#define SWAR2		0x30	/* RTC Stopwatch Alarm register 2 */
+#define RTCPICR		0x34	/* RTC Periodic Interrupt Counter register */
+#define PIAR		0x38	/* RTC Periodic Interrupt Alarm register */
+
+static inline void pxa2xx_rtc_int_update(struct pxa2xx_state_s *s)
+{
+    pic_set_irq_new(s->pic, PXA2XX_PIC_RTCALARM, !!(s->rtsr & 0x2553));
+}
+
+static void pxa2xx_rtc_hzupdate(struct pxa2xx_state_s *s)
+{
+    int64_t rt = qemu_get_clock(rt_clock);
+    s->last_rcnr += ((rt - s->last_hz) << 15) /
+            (1000 * ((s->rttr & 0xffff) + 1));
+    s->last_rdcr += ((rt - s->last_hz) << 15) /
+            (1000 * ((s->rttr & 0xffff) + 1));
+    s->last_hz = rt;
+}
+
+static void pxa2xx_rtc_swupdate(struct pxa2xx_state_s *s)
+{
+    int64_t rt = qemu_get_clock(rt_clock);
+    if (s->rtsr & (1 << 12))
+        s->last_swcr += (rt - s->last_sw) / 10;
+    s->last_sw = rt;
+}
+
+static void pxa2xx_rtc_piupdate(struct pxa2xx_state_s *s)
+{
+    int64_t rt = qemu_get_clock(rt_clock);
+    if (s->rtsr & (1 << 15))
+        s->last_swcr += rt - s->last_pi;
+    s->last_pi = rt;
+}
+
+static inline void pxa2xx_rtc_alarm_update(struct pxa2xx_state_s *s,
+                uint32_t rtsr)
+{
+    if ((rtsr & (1 << 2)) && !(rtsr & (1 << 0)))
+        qemu_mod_timer(s->rtc_hz, s->last_hz +
+                (((s->rtar - s->last_rcnr) * 1000 *
+                  ((s->rttr & 0xffff) + 1)) >> 15));
+    else
+        qemu_del_timer(s->rtc_hz);
+
+    if ((rtsr & (1 << 5)) && !(rtsr & (1 << 4)))
+        qemu_mod_timer(s->rtc_rdal1, s->last_hz +
+                (((s->rdar1 - s->last_rdcr) * 1000 *
+                  ((s->rttr & 0xffff) + 1)) >> 15)); /* TODO: fixup */
+    else
+        qemu_del_timer(s->rtc_rdal1);
+
+    if ((rtsr & (1 << 7)) && !(rtsr & (1 << 6)))
+        qemu_mod_timer(s->rtc_rdal2, s->last_hz +
+                (((s->rdar2 - s->last_rdcr) * 1000 *
+                  ((s->rttr & 0xffff) + 1)) >> 15)); /* TODO: fixup */
+    else
+        qemu_del_timer(s->rtc_rdal2);
+
+    if ((rtsr & 0x1200) == 0x1200 && !(rtsr & (1 << 8)))
+        qemu_mod_timer(s->rtc_swal1, s->last_sw +
+                        (s->swar1 - s->last_swcr) * 10); /* TODO: fixup */
+    else
+        qemu_del_timer(s->rtc_swal1);
+
+    if ((rtsr & 0x1800) == 0x1800 && !(rtsr & (1 << 10)))
+        qemu_mod_timer(s->rtc_swal2, s->last_sw +
+                        (s->swar2 - s->last_swcr) * 10); /* TODO: fixup */
+    else
+        qemu_del_timer(s->rtc_swal2);
+
+    if ((rtsr & 0xc000) == 0xc000 && !(rtsr & (1 << 13)))
+        qemu_mod_timer(s->rtc_pi, s->last_pi +
+                        (s->piar & 0xffff) - s->last_rtcpicr);
+    else
+        qemu_del_timer(s->rtc_pi);
+}
+
+static inline void pxa2xx_rtc_hz_tick(void *opaque)
+{
+    struct pxa2xx_state_s *s = (struct pxa2xx_state_s *) opaque;
+    s->rtsr |= (1 << 0);
+    pxa2xx_rtc_alarm_update(s, s->rtsr);
+    pxa2xx_rtc_int_update(s);
+}
+
+static inline void pxa2xx_rtc_rdal1_tick(void *opaque)
+{
+    struct pxa2xx_state_s *s = (struct pxa2xx_state_s *) opaque;
+    s->rtsr |= (1 << 4);
+    pxa2xx_rtc_alarm_update(s, s->rtsr);
+    pxa2xx_rtc_int_update(s);
+}
+
+static inline void pxa2xx_rtc_rdal2_tick(void *opaque)
+{
+    struct pxa2xx_state_s *s = (struct pxa2xx_state_s *) opaque;
+    s->rtsr |= (1 << 6);
+    pxa2xx_rtc_alarm_update(s, s->rtsr);
+    pxa2xx_rtc_int_update(s);
+}
+
+static inline void pxa2xx_rtc_swal1_tick(void *opaque)
+{
+    struct pxa2xx_state_s *s = (struct pxa2xx_state_s *) opaque;
+    s->rtsr |= (1 << 8);
+    pxa2xx_rtc_alarm_update(s, s->rtsr);
+    pxa2xx_rtc_int_update(s);
+}
+
+static inline void pxa2xx_rtc_swal2_tick(void *opaque)
+{
+    struct pxa2xx_state_s *s = (struct pxa2xx_state_s *) opaque;
+    s->rtsr |= (1 << 10);
+    pxa2xx_rtc_alarm_update(s, s->rtsr);
+    pxa2xx_rtc_int_update(s);
+}
+
+static inline void pxa2xx_rtc_pi_tick(void *opaque)
+{
+    struct pxa2xx_state_s *s = (struct pxa2xx_state_s *) opaque;
+    s->rtsr |= (1 << 13);
+    pxa2xx_rtc_piupdate(s);
+    s->last_rtcpicr = 0;
+    pxa2xx_rtc_alarm_update(s, s->rtsr);
+    pxa2xx_rtc_int_update(s);
+}
+
+static uint32_t pxa2xx_rtc_read(void *opaque, target_phys_addr_t addr)
+{
+    struct pxa2xx_state_s *s = (struct pxa2xx_state_s *) opaque;
+    addr -= s->rtc_base;
+
+    switch (addr) {
+    case RTTR:
+        return s->rttr;
+    case RTSR:
+        return s->rtsr;
+    case RTAR:
+        return s->rtar;
+    case RDAR1:
+        return s->rdar1;
+    case RDAR2:
+        return s->rdar2;
+    case RYAR1:
+        return s->ryar1;
+    case RYAR2:
+        return s->ryar2;
+    case SWAR1:
+        return s->swar1;
+    case SWAR2:
+        return s->swar2;
+    case PIAR:
+        return s->piar;
+    case RCNR:
+        return s->last_rcnr + ((qemu_get_clock(rt_clock) - s->last_hz) << 15) /
+                (1000 * ((s->rttr & 0xffff) + 1));
+    case RDCR:
+        return s->last_rdcr + ((qemu_get_clock(rt_clock) - s->last_hz) << 15) /
+                (1000 * ((s->rttr & 0xffff) + 1));
+    case RYCR:
+        return s->last_rycr;
+    case SWCR:
+        if (s->rtsr & (1 << 12))
+            return s->last_swcr + (qemu_get_clock(rt_clock) - s->last_sw) / 10;
+        else
+            return s->last_swcr;
+    default:
+        printf("%s: Bad register " REG_FMT "\n", __FUNCTION__, addr);
+        break;
+    }
+    return 0;
+}
+
+static void pxa2xx_rtc_write(void *opaque, target_phys_addr_t addr,
+                uint32_t value)
+{
+    struct pxa2xx_state_s *s = (struct pxa2xx_state_s *) opaque;
+    addr -= s->rtc_base;
+
+    switch (addr) {
+    case RTTR:
+        if (!(s->rttr & (1 << 31))) {
+            pxa2xx_rtc_hzupdate(s);
+            s->rttr = value;
+            pxa2xx_rtc_alarm_update(s, s->rtsr);
+        }
+        break;
+
+    case RTSR:
+        if ((s->rtsr ^ value) & (1 << 15))
+            pxa2xx_rtc_piupdate(s);
+
+        if ((s->rtsr ^ value) & (1 << 12))
+            pxa2xx_rtc_swupdate(s);
+
+        if (((s->rtsr ^ value) & 0x4aac) | (value & ~0xdaac))
+            pxa2xx_rtc_alarm_update(s, value);
+
+        s->rtsr = (value & 0xdaac) | (s->rtsr & ~(value & ~0xdaac));
+        pxa2xx_rtc_int_update(s);
+        break;
+
+    case RTAR:
+        s->rtar = value;
+        pxa2xx_rtc_alarm_update(s, s->rtsr);
+        break;
+
+    case RDAR1:
+        s->rdar1 = value;
+        pxa2xx_rtc_alarm_update(s, s->rtsr);
+        break;
+
+    case RDAR2:
+        s->rdar2 = value;
+        pxa2xx_rtc_alarm_update(s, s->rtsr);
+        break;
+
+    case RYAR1:
+        s->ryar1 = value;
+        pxa2xx_rtc_alarm_update(s, s->rtsr);
+        break;
+
+    case RYAR2:
+        s->ryar2 = value;
+        pxa2xx_rtc_alarm_update(s, s->rtsr);
+        break;
+
+    case SWAR1:
+        pxa2xx_rtc_swupdate(s);
+        s->swar1 = value;
+        s->last_swcr = 0;
+        pxa2xx_rtc_alarm_update(s, s->rtsr);
+        break;
+
+    case SWAR2:
+        s->swar2 = value;
+        pxa2xx_rtc_alarm_update(s, s->rtsr);
+        break;
+
+    case PIAR:
+        s->piar = value;
+        pxa2xx_rtc_alarm_update(s, s->rtsr);
+        break;
+
+    case RCNR:
+        pxa2xx_rtc_hzupdate(s);
+        s->last_rcnr = value;
+        pxa2xx_rtc_alarm_update(s, s->rtsr);
+        break;
+
+    case RDCR:
+        pxa2xx_rtc_hzupdate(s);
+        s->last_rdcr = value;
+        pxa2xx_rtc_alarm_update(s, s->rtsr);
+        break;
+
+    case RYCR:
+        s->last_rycr = value;
+        break;
+
+    case SWCR:
+        pxa2xx_rtc_swupdate(s);
+        s->last_swcr = value;
+        pxa2xx_rtc_alarm_update(s, s->rtsr);
+        break;
+
+    case RTCPICR:
+        pxa2xx_rtc_piupdate(s);
+        s->last_rtcpicr = value & 0xffff;
+        pxa2xx_rtc_alarm_update(s, s->rtsr);
+        break;
+
+    default:
+        printf("%s: Bad register " REG_FMT "\n", __FUNCTION__, addr);
+    }
+}
+
+static void pxa2xx_rtc_reset(struct pxa2xx_state_s *s)
+{
+    struct tm *tm;
+    time_t ti;
+    int wom;
+
+    s->rttr = 0x7fff;
+    s->rtsr = 0;
+
+    time(&ti);
+    if (rtc_utc)
+        tm = gmtime(&ti);
+    else
+        tm = localtime(&ti);
+    wom = ((tm->tm_mday - 1) / 7) + 1;
+
+    s->last_rcnr = (uint32_t) ti;
+    s->last_rdcr = (wom << 20) | ((tm->tm_wday + 1) << 17) |
+            (tm->tm_hour << 12) | (tm->tm_min << 6) | tm->tm_sec;
+    s->last_rycr = ((tm->tm_year + 1900) << 9) |
+            ((tm->tm_mon + 1) << 5) | tm->tm_mday;
+    s->last_swcr = (tm->tm_hour << 19) |
+            (tm->tm_min << 13) | (tm->tm_sec << 7);
+    s->last_rtcpicr = 0;
+    s->last_hz = s->last_sw = s->last_pi = qemu_get_clock(rt_clock);
+
+    s->rtc_hz    = qemu_new_timer(rt_clock, pxa2xx_rtc_hz_tick,    s);
+    s->rtc_rdal1 = qemu_new_timer(rt_clock, pxa2xx_rtc_rdal1_tick, s);
+    s->rtc_rdal2 = qemu_new_timer(rt_clock, pxa2xx_rtc_rdal2_tick, s);
+    s->rtc_swal1 = qemu_new_timer(rt_clock, pxa2xx_rtc_swal1_tick, s);
+    s->rtc_swal2 = qemu_new_timer(rt_clock, pxa2xx_rtc_swal2_tick, s);
+    s->rtc_pi    = qemu_new_timer(rt_clock, pxa2xx_rtc_pi_tick,    s);
+}
+
+static CPUReadMemoryFunc *pxa2xx_rtc_readfn[] = {
+    pxa2xx_rtc_read,
+    pxa2xx_rtc_read,
+    pxa2xx_rtc_read,
+};
+
+static CPUWriteMemoryFunc *pxa2xx_rtc_writefn[] = {
+    pxa2xx_rtc_write,
+    pxa2xx_rtc_write,
+    pxa2xx_rtc_write,
+};
+
+/* I2C Interface */
+#define IBMR	0x80	/* I2C Bus Monitor register */
+#define IDBR	0x88	/* I2C Data Buffer register */
+#define ICR	0x90	/* I2C Control register */
+#define ISR	0x98	/* I2C Status register */
+#define ISAR	0xa0	/* I2C Slave Address register */
+
+static void pxa2xx_i2c_update(struct pxa2xx_i2c_s *s)
+{
+    uint16_t level = 0;
+    level |= s->status & s->control & (1 << 10);		/* BED */
+    level |= (s->status & (1 << 7)) && (s->control & (1 << 9));	/* IRF */
+    level |= (s->status & (1 << 6)) && (s->control & (1 << 8));	/* ITE */
+    level |= s->status & (1 << 9);				/* SAD */
+    pic_set_irq_new(s->pic, s->irq, !!level);
+}
+
+static void pxa2xx_i2c_start(void *opaque, int dir)
+{
+    struct pxa2xx_i2c_s *s = (struct pxa2xx_i2c_s *) opaque;
+    s->status |= (1 << 9);				/* set SAD */
+    if (dir)
+        s->status |= 1 << 0;				/* set RWM */
+    else
+        s->status &= ~(1 << 0);				/* clear RWM */
+}
+
+static void pxa2xx_i2c_stop(void *opaque)
+{
+    struct pxa2xx_i2c_s *s = (struct pxa2xx_i2c_s *) opaque;
+    s->status |= (1 << 4);				/* set SSD */
+}
+
+static int pxa2xx_i2c_tx(void *opaque, uint8_t *message, int len)
+{
+    struct pxa2xx_i2c_s *s = (struct pxa2xx_i2c_s *) opaque;
+    if ((s->control & (1 << 14)) || !(s->control & (1 << 6)))
+        return 1;
+
+    if (len) {
+        if (s->status & (1 << 0)) {			/* RWM */
+            s->status |= 1 << 6;			/* set ITE */
+            s->master.data = message[0];		/* TODO */
+	} else {
+            s->status |= 1 << 7;			/* set IRF */
+            message[0] = s->master.data;		/* TODO */
+        }
+    }
+    pxa2xx_i2c_update(s);
+
+    return !s->master.ack;
+}
+
+static uint32_t pxa2xx_i2c_read(void *opaque, target_phys_addr_t addr)
+{
+    struct pxa2xx_i2c_s *s = (struct pxa2xx_i2c_s *) opaque;
+    addr -= s->base;
+
+    switch (addr) {
+    case ICR:
+        return s->control;
+    case ISR:
+        return s->status;
+    case ISAR:
+        return s->slave.address;
+    case IDBR:
+        return s->master.data;
+    case IBMR:
+        if (s->status & (1 << 2))
+            s->ibmr ^= 3;	/* Fake SCL and SDA pin changes */
+        else
+            s->ibmr = 0;
+        return s->ibmr;
+    default:
+        printf("%s: Bad register " REG_FMT "\n", __FUNCTION__, addr);
+        break;
+    }
+    return 0;
+}
+
+static void pxa2xx_i2c_write(void *opaque, target_phys_addr_t addr,
+                uint32_t value)
+{
+    struct pxa2xx_i2c_s *s = (struct pxa2xx_i2c_s *) opaque;
+    addr -= s->base;
+
+    switch (addr) {
+    case ICR:
+        s->control = value & 0xfff7;
+        if ((value & (1 << 3)) && (value & (1 << 6))) {	/* TB and IUE */
+            /* TODO: slave mode */
+            if (value & (1 << 0)) {			/* START condition */
+                if (s->master.data & 1)
+                    s->status |= 1 << 0;		/* set RWM */
+                else
+                    s->status &= ~(1 << 0);		/* clear RWM */
+                s->status |= 1 << 2;			/* set UB */
+            }
+
+            i2c_master_submit(&s->master,
+                            (value >> 0) & 1,		/* START condition */
+                            (value >> 1) & 1);		/* STOP condition */
+
+            if (s->master.ack) {
+                if (s->status & (1 << 0))		/* RWM */
+                    s->status |= 1 << 7;		/* set IRF */
+                else
+                    s->status |= 1 << 6;		/* set ITE */
+                s->status &= ~(1 << 1);			/* clear ACKNAK */
+            } else {
+                s->status |= 1 << 6;			/* set ITE */
+                s->status |= 1 << 10;			/* set BED */
+                s->status |= 1 << 1;			/* set ACKNAK */
+                s->status &= ~(1 << 2);			/* clear UB */
+            }
+            if (value & (1 << 1))			/* STOP condition */
+                s->status &= ~(1 << 2);			/* clear UB */
+        }
+        if (value & (1 << 4))				/* MA */
+            s->status &= ~(1 << 2);			/* clear UB */
+        if (!(value & (1 << 3)) && (value & (1 << 6)))	/* !TB and IUE */
+            s->status &= ~(1 << 2);			/* clear UB */
+        pxa2xx_i2c_update(s);
+        break;
+
+    case ISR:
+        s->status &= ~(value & 0x07f0);
+        pxa2xx_i2c_update(s);
+        break;
+
+    case ISAR:
+        if (s->master.bus)
+            i2c_slave_attach(s->master.bus, value & 0x7f, &s->slave);
+        break;
+
+    case IDBR:
+        s->master.data = value & 0xff;
+        break;
+
+    default:
+        printf("%s: Bad register " REG_FMT "\n", __FUNCTION__, addr);
+    }
+}
+
+static CPUReadMemoryFunc *pxa2xx_i2c_readfn[] = {
+    pxa2xx_i2c_read,
+    pxa2xx_i2c_read,
+    pxa2xx_i2c_read,
+};
+
+static CPUWriteMemoryFunc *pxa2xx_i2c_writefn[] = {
+    pxa2xx_i2c_write,
+    pxa2xx_i2c_write,
+    pxa2xx_i2c_write,
+};
+
+static struct pxa2xx_i2c_s *pxa2xx_i2c_init(target_phys_addr_t base, int irq,
+                struct pxa2xx_pic_state_s *pic)
+{
+    int iomemtype;
+    struct pxa2xx_i2c_s *s = (struct pxa2xx_i2c_s *)
+            qemu_mallocz(sizeof(struct pxa2xx_i2c_s));
+
+    s->base = base;
+    s->irq = irq;
+    s->pic = pic;
+    s->slave.tx = pxa2xx_i2c_tx;
+    s->slave.start = pxa2xx_i2c_start;
+    s->slave.stop = pxa2xx_i2c_stop;
+    s->slave.opaque = s;
+
+    iomemtype = cpu_register_io_memory(0, pxa2xx_i2c_readfn,
+                    pxa2xx_i2c_writefn, s);
+    cpu_register_physical_memory(s->base & 0xfffff000, 0xfff, iomemtype);
+
+    return s;
+}
+
+/* PXA Inter-IC Sound Controller */
+static void pxa2xx_i2s_reset(struct pxa2xx_i2s_s *i2s)
+{
+    i2s->rx_len = 0;
+    i2s->tx_len = 0;
+    i2s->fifo_len = 0;
+    i2s->clk = 0x1a;
+    i2s->control[0] = 0x00;
+    i2s->control[1] = 0x00;
+    i2s->status = 0x00;
+    i2s->mask = 0x00;
+}
+
+#define SACR_TFTH(val)	((val >> 8) & 0xf)
+#define SACR_RFTH(val)	((val >> 12) & 0xf)
+#define SACR_DREC(val)	(val & (1 << 3))
+#define SACR_DPRL(val)	(val & (1 << 4))
+
+static inline void pxa2xx_i2s_update(struct pxa2xx_i2s_s *i2s)
+{
+    int rfs, tfs;
+    rfs = SACR_RFTH(i2s->control[0]) < i2s->rx_len &&
+            !SACR_DREC(i2s->control[1]);
+    tfs = (i2s->tx_len || i2s->fifo_len < SACR_TFTH(i2s->control[0])) &&
+            i2s->enable && !SACR_DPRL(i2s->control[1]);
+
+    pxa2xx_dma_request(i2s->dma, PXA2XX_RX_RQ_I2S, rfs);
+    pxa2xx_dma_request(i2s->dma, PXA2XX_TX_RQ_I2S, tfs);
+
+    i2s->status &= 0xe0;
+    if (i2s->rx_len)
+        i2s->status |= 1 << 1;			/* RNE */
+    if (i2s->enable)
+        i2s->status |= 1 << 2;			/* BSY */
+    if (tfs)
+        i2s->status |= 1 << 3;			/* TFS */
+    if (rfs)
+        i2s->status |= 1 << 4;			/* RFS */
+    if (!(i2s->tx_len && i2s->enable))
+        i2s->status |= i2s->fifo_len << 8;	/* TFL */
+    i2s->status |= MAX(i2s->rx_len, 0xf) << 12;	/* RFL */
+
+    pic_set_irq_new(i2s->pic, i2s->irq, i2s->status & i2s->mask);
+}
+
+#define SACR0	0x00	/* Serial Audio Global Control register */
+#define SACR1	0x04	/* Serial Audio I2S/MSB-Justified Control register */
+#define SASR0	0x0c	/* Serial Audio Interface and FIFO Status register */
+#define SAIMR	0x14	/* Serial Audio Interrupt Mask register */
+#define SAICR	0x18	/* Serial Audio Interrupt Clear register */
+#define SADIV	0x60	/* Serial Audio Clock Divider register */
+#define SADR	0x80	/* Serial Audio Data register */
+
+static uint32_t pxa2xx_i2s_read(void *opaque, target_phys_addr_t addr)
+{
+    struct pxa2xx_i2s_s *s = (struct pxa2xx_i2s_s *) opaque;
+    addr -= s->base;
+
+    switch (addr) {
+    case SACR0:
+        return s->control[0];
+    case SACR1:
+        return s->control[1];
+    case SASR0:
+        return s->status;
+    case SAIMR:
+        return s->mask;
+    case SAICR:
+        return 0;
+    case SADIV:
+        return s->clk;
+    case SADR:
+        if (s->rx_len > 0) {
+            s->rx_len --;
+            pxa2xx_i2s_update(s);
+            return s->codec_in(s->opaque);
+        }
+        return 0;
+    default:
+        printf("%s: Bad register " REG_FMT "\n", __FUNCTION__, addr);
+        break;
+    }
+    return 0;
+}
+
+static void pxa2xx_i2s_write(void *opaque, target_phys_addr_t addr,
+                uint32_t value)
+{
+    struct pxa2xx_i2s_s *s = (struct pxa2xx_i2s_s *) opaque;
+    uint32_t *sample;
+    addr -= s->base;
+
+    switch (addr) {
+    case SACR0:
+        if (value & (1 << 3))				/* RST */
+            pxa2xx_i2s_reset(s);
+        s->control[0] = value & 0xff3d;
+        if (!s->enable && (value & 1) && s->tx_len) {	/* ENB */
+            for (sample = s->fifo; s->fifo_len > 0; s->fifo_len --, sample ++)
+                s->codec_out(s->opaque, *sample);
+            s->status &= ~(1 << 7);			/* I2SOFF */
+        }
+        if (value & (1 << 4))				/* EFWR */
+            printf("%s: Attempt to use special function\n", __FUNCTION__);
+        s->enable = ((value ^ 4) & 5) == 5;		/* ENB && !RST*/
+        pxa2xx_i2s_update(s);
+        break;
+    case SACR1:
+        s->control[1] = value & 0x0039;
+        if (value & (1 << 5))				/* ENLBF */
+            printf("%s: Attempt to use loopback function\n", __FUNCTION__);
+        if (value & (1 << 4))				/* DPRL */
+            s->fifo_len = 0;
+        pxa2xx_i2s_update(s);
+        break;
+    case SAIMR:
+        s->mask = value & 0x0078;
+        pxa2xx_i2s_update(s);
+        break;
+    case SAICR:
+        s->status &= ~(value & (3 << 5));
+        pxa2xx_i2s_update(s);
+        break;
+    case SADIV:
+        s->clk = value & 0x007f;
+        break;
+    case SADR:
+        if (s->tx_len && s->enable) {
+            s->tx_len --;
+            pxa2xx_i2s_update(s);
+            s->codec_out(s->opaque, value);
+        } else if (s->fifo_len < 16) {
+            s->fifo[s->fifo_len ++] = value;
+            pxa2xx_i2s_update(s);
+        }
+        break;
+    default:
+        printf("%s: Bad register " REG_FMT "\n", __FUNCTION__, addr);
+    }
+}
+
+static CPUReadMemoryFunc *pxa2xx_i2s_readfn[] = {
+    pxa2xx_i2s_read,
+    pxa2xx_i2s_read,
+    pxa2xx_i2s_read,
+};
+
+static CPUWriteMemoryFunc *pxa2xx_i2s_writefn[] = {
+    pxa2xx_i2s_write,
+    pxa2xx_i2s_write,
+    pxa2xx_i2s_write,
+};
+
+static void pxa2xx_i2s_data_req(void *opaque, int tx, int rx)
+{
+    struct pxa2xx_i2s_s *s = (struct pxa2xx_i2s_s *) opaque;
+    uint32_t *sample;
+
+    /* Signal FIFO errors */
+    if (s->enable && s->tx_len)
+        s->status |= 1 << 5;		/* TUR */
+    if (s->enable && s->rx_len)
+        s->status |= 1 << 6;		/* ROR */
+
+    /* Should be tx - MIN(tx, s->fifo_len) but we don't really need to
+     * handle the cases where it makes a difference.  */
+    s->tx_len = tx - s->fifo_len;
+    s->rx_len = rx;
+    /* Note that is s->codec_out wasn't set, we wouldn't get called.  */
+    if (s->enable)
+        for (sample = s->fifo; s->fifo_len; s->fifo_len --, sample ++)
+            s->codec_out(s->opaque, *sample);
+    pxa2xx_i2s_update(s);
+}
+
+static struct pxa2xx_i2s_s *pxa2xx_i2s_init(target_phys_addr_t base, int irq,
+                struct pxa2xx_pic_state_s *pic, struct pxa2xx_dma_state_s *dma)
+{
+    int iomemtype;
+    struct pxa2xx_i2s_s *s = (struct pxa2xx_i2s_s *)
+            qemu_mallocz(sizeof(struct pxa2xx_i2s_s));
+
+    s->base = base;
+    s->irq = irq;
+    s->pic = pic;
+    s->dma = dma;
+    s->data_req = pxa2xx_i2s_data_req;
+
+    pxa2xx_i2s_reset(s);
+
+    iomemtype = cpu_register_io_memory(0, pxa2xx_i2s_readfn,
+                    pxa2xx_i2s_writefn, s);
+    cpu_register_physical_memory(s->base & 0xfff00000, 0xfffff, iomemtype);
+
+    return s;
+}
+
+/* PXA Fast Infra-red Communications Port */
+struct pxa2xx_fir_s {
+    target_phys_addr_t base;
+    int irq;
+    struct pxa2xx_dma_state_s *dma;
+    struct pxa2xx_pic_state_s *pic;
+    int enable;
+    CharDriverState *chr;
+
+    uint8_t control[3];
+    uint8_t status[2];
+
+    int rx_len;
+    int rx_start;
+    uint8_t rx_fifo[64];
+};
+
+static void pxa2xx_fir_reset(struct pxa2xx_fir_s *s)
+{
+    s->control[0] = 0x00;
+    s->control[1] = 0x00;
+    s->control[2] = 0x00;
+    s->status[0] = 0x00;
+    s->status[1] = 0x00;
+    s->enable = 0;
+}
+
+static inline void pxa2xx_fir_update(struct pxa2xx_fir_s *s)
+{
+    static const int tresh[4] = { 8, 16, 32, 0 };
+    int intr = 0;
+    if ((s->control[0] & (1 << 4)) &&			/* RXE */
+                    s->rx_len >= tresh[s->control[2] & 3])	/* TRIG */
+        s->status[0] |= 1 << 4;				/* RFS */
+    else
+        s->status[0] &= ~(1 << 4);			/* RFS */
+    if (s->control[0] & (1 << 3))			/* TXE */
+        s->status[0] |= 1 << 3;				/* TFS */
+    else
+        s->status[0] &= ~(1 << 3);			/* TFS */
+    if (s->rx_len)
+        s->status[1] |= 1 << 2;				/* RNE */
+    else
+        s->status[1] &= ~(1 << 2);			/* RNE */
+    if (s->control[0] & (1 << 4))			/* RXE */
+        s->status[1] |= 1 << 0;				/* RSY */
+    else
+        s->status[1] &= ~(1 << 0);			/* RSY */
+
+    intr |= (s->control[0] & (1 << 5)) &&		/* RIE */
+            (s->status[0] & (1 << 4));			/* RFS */
+    intr |= (s->control[0] & (1 << 6)) &&		/* TIE */
+            (s->status[0] & (1 << 3));			/* TFS */
+    intr |= (s->control[2] & (1 << 4)) &&		/* TRAIL */
+            (s->status[0] & (1 << 6));			/* EOC */
+    intr |= (s->control[0] & (1 << 2)) &&		/* TUS */
+            (s->status[0] & (1 << 1));			/* TUR */
+    intr |= s->status[0] & 0x25;			/* FRE, RAB, EIF */
+
+    pxa2xx_dma_request(s->dma, PXA2XX_RX_RQ_ICP, (s->status[0] >> 4) & 1);
+    pxa2xx_dma_request(s->dma, PXA2XX_TX_RQ_ICP, (s->status[0] >> 3) & 1);
+
+    pic_set_irq_new(s->pic, s->irq, intr && s->enable);
+}
+
+#define ICCR0	0x00	/* FICP Control register 0 */
+#define ICCR1	0x04	/* FICP Control register 1 */
+#define ICCR2	0x08	/* FICP Control register 2 */
+#define ICDR	0x0c	/* FICP Data register */
+#define ICSR0	0x14	/* FICP Status register 0 */
+#define ICSR1	0x18	/* FICP Status register 1 */
+#define ICFOR	0x1c	/* FICP FIFO Occupancy Status register */
+
+static uint32_t pxa2xx_fir_read(void *opaque, target_phys_addr_t addr)
+{
+    struct pxa2xx_fir_s *s = (struct pxa2xx_fir_s *) opaque;
+    uint8_t ret;
+    addr -= s->base;
+
+    switch (addr) {
+    case ICCR0:
+        return s->control[0];
+    case ICCR1:
+        return s->control[1];
+    case ICCR2:
+        return s->control[2];
+    case ICDR:
+        s->status[0] &= ~0x01;
+        s->status[1] &= ~0x72;
+        if (s->rx_len) {
+            s->rx_len --;
+            ret = s->rx_fifo[s->rx_start ++];
+            s->rx_start &= 63;
+            pxa2xx_fir_update(s);
+            return ret;
+        }
+        printf("%s: Rx FIFO underrun.\n", __FUNCTION__);
+        break;
+    case ICSR0:
+        return s->status[0];
+    case ICSR1:
+        return s->status[1] | (1 << 3);			/* TNF */
+    case ICFOR:
+        return s->rx_len;
+    default:
+        printf("%s: Bad register " REG_FMT "\n", __FUNCTION__, addr);
+        break;
+    }
+    return 0;
+}
+
+static void pxa2xx_fir_write(void *opaque, target_phys_addr_t addr,
+                uint32_t value)
+{
+    struct pxa2xx_fir_s *s = (struct pxa2xx_fir_s *) opaque;
+    uint8_t ch;
+    addr -= s->base;
+
+    switch (addr) {
+    case ICCR0:
+        s->control[0] = value;
+        if (!(value & (1 << 4)))			/* RXE */
+            s->rx_len = s->rx_start = 0;
+        if (!(value & (1 << 3)))			/* TXE */
+            /* Nop */;
+        s->enable = value & 1;				/* ITR */
+        if (!s->enable)
+            s->status[0] = 0;
+        pxa2xx_fir_update(s);
+        break;
+    case ICCR1:
+        s->control[1] = value;
+        break;
+    case ICCR2:
+        s->control[2] = value & 0x3f;
+        pxa2xx_fir_update(s);
+        break;
+    case ICDR:
+        if (s->control[2] & (1 << 2))			/* TXP */
+            ch = value;
+        else
+            ch = ~value;
+        if (s->chr && s->enable && (s->control[0] & (1 << 3)))	/* TXE */
+            qemu_chr_write(s->chr, &ch, 1);
+        break;
+    case ICSR0:
+        s->status[0] &= ~(value & 0x66);
+        pxa2xx_fir_update(s);
+        break;
+    case ICFOR:
+        break;
+    default:
+        printf("%s: Bad register " REG_FMT "\n", __FUNCTION__, addr);
+    }
+}
+
+static CPUReadMemoryFunc *pxa2xx_fir_readfn[] = {
+    pxa2xx_fir_read,
+    pxa2xx_fir_read,
+    pxa2xx_fir_read,
+};
+
+static CPUWriteMemoryFunc *pxa2xx_fir_writefn[] = {
+    pxa2xx_fir_write,
+    pxa2xx_fir_write,
+    pxa2xx_fir_write,
+};
+
+static int pxa2xx_fir_is_empty(void *opaque)
+{
+    struct pxa2xx_fir_s *s = (struct pxa2xx_fir_s *) opaque;
+    return (s->rx_len < 64);
+}
+
+static void pxa2xx_fir_rx(void *opaque, const uint8_t *buf, int size)
+{
+    struct pxa2xx_fir_s *s = (struct pxa2xx_fir_s *) opaque;
+    if (!(s->control[0] & (1 << 4)))			/* RXE */
+        return;
+
+    while (size --) {
+        s->status[1] |= 1 << 4;				/* EOF */
+        if (s->rx_len >= 64) {
+            s->status[1] |= 1 << 6;			/* ROR */
+            break;
+        }
+
+        if (s->control[2] & (1 << 3))			/* RXP */
+            s->rx_fifo[(s->rx_start + s->rx_len ++) & 63] = *(buf ++);
+        else
+            s->rx_fifo[(s->rx_start + s->rx_len ++) & 63] = ~*(buf ++);
+    }
+
+    pxa2xx_fir_update(s);
+}
+
+static void pxa2xx_fir_event(void *opaque, int event)
+{
+}
+
+static struct pxa2xx_fir_s *pxa2xx_fir_init(target_phys_addr_t base, int irq,
+                struct pxa2xx_pic_state_s *pic, struct pxa2xx_dma_state_s *dma,
+                CharDriverState *chr)
+{
+    int iomemtype;
+    struct pxa2xx_fir_s *s = (struct pxa2xx_fir_s *)
+            qemu_mallocz(sizeof(struct pxa2xx_fir_s));
+
+    s->base = base;
+    s->irq = irq;
+    s->pic = pic;
+    s->dma = dma;
+    s->chr = chr;
+
+    pxa2xx_fir_reset(s);
+
+    iomemtype = cpu_register_io_memory(0, pxa2xx_fir_readfn,
+                    pxa2xx_fir_writefn, s);
+    cpu_register_physical_memory(s->base, 0xfff, iomemtype);
+
+    if (chr)
+        qemu_chr_add_handlers(chr, pxa2xx_fir_is_empty,
+                        pxa2xx_fir_rx, pxa2xx_fir_event, s);
+
+    return s;
+}
+
+void pxa2xx_reset(int line, int level, void *opaque)
+{
+    struct pxa2xx_state_s *s = (struct pxa2xx_state_s *) opaque;
+    if (level && (s->pm_regs[PCFR >> 2] & 0x10)) {	/* GPR_EN */
+        cpu_reset(s->env);
+        /* TODO: reset peripherals */
+    }
+}
+
+/* Initialise a PXA270 integrated chip (ARM based core).  */
+struct pxa2xx_state_s *pxa270_init(DisplayState *ds, const char *revision)
+{
+    struct pxa2xx_state_s *s;
+    struct pxa2xx_ssp_s *ssp;
+    char *cpu_model;
+    int iomemtype, i;
+    s = (struct pxa2xx_state_s *) qemu_mallocz(sizeof(struct pxa2xx_state_s));
+
+    s->env = cpu_init();
+    asprintf(&cpu_model, "pxa270-%s", revision);
+    cpu_arm_set_model(s->env, cpu_model);
+    free(cpu_model);
+
+    s->pic = pxa2xx_pic_init(0x40d00000, s->env);
+
+    s->dma = pxa27x_dma_init(0x40000000, s->pic, PXA2XX_PIC_DMA);
+
+    s->gpio = pxa2xx_gpio_init(0x40e00000, s->env, s->pic, 121);
+
+    for (i = 0; pxa270_serial[i].io_base; i ++)
+        if (serial_hds[i])
+            serial_mm_init(*(SetIRQFunc **) s->pic, s->pic,
+                            pxa270_serial[i].io_base, 2,
+                            pxa270_serial[i].irq, serial_hds[i]);
+    if (serial_hds[i])
+        s->fir = pxa2xx_fir_init(0x40800000, PXA2XX_PIC_ICP,
+                        s->pic, s->dma, serial_hds[i]);
+
+    s->cm_base = 0x41300000;
+    s->cm_regs[CCCR >> 4] = 0x02000210;	/* 416.0 MHz */
+    s->clkcfg = 0x00000009;		/* Turbo mode active */
+    iomemtype = cpu_register_io_memory(0, pxa2xx_cm_readfn,
+                    pxa2xx_cm_writefn, s);
+    cpu_register_physical_memory(s->cm_base, 0xfff, iomemtype);
+
+    cpu_arm_set_cp_io(s->env, 14, pxa2xx_cp14_read, pxa2xx_cp14_write, s);
+
+    s->mm_base = 0x48000000;
+    s->mm_regs[MDMRS >> 2] = 0x00020002;
+    s->mm_regs[MDREFR >> 2] = 0x03ca4000;
+    s->mm_regs[MECR >> 2] = 0x00000001;	/* Two PC Card sockets */
+    iomemtype = cpu_register_io_memory(0, pxa2xx_mm_readfn,
+                    pxa2xx_mm_writefn, s);
+    cpu_register_physical_memory(s->mm_base, 0xfff, iomemtype);
+
+    for (i = 0; pxa27x_ssp[i].io_base; i ++);
+    s->ssp = (struct pxa2xx_ssp_s **)
+            qemu_mallocz(sizeof(struct pxa2xx_ssp_s *) * i);
+    ssp = (struct pxa2xx_ssp_s *)
+            qemu_mallocz(sizeof(struct pxa2xx_ssp_s) * i);
+    for (i = 0; pxa27x_ssp[i].io_base; i ++) {
+        s->ssp[i] = &ssp[i];
+        ssp[i].base = pxa27x_ssp[i].io_base;
+        ssp[i].irq = pxa27x_ssp[i].irq;
+        ssp[i].pic = s->pic;
+
+        iomemtype = cpu_register_io_memory(0, pxa2xx_ssp_readfn,
+                        pxa2xx_ssp_writefn, &ssp[i]);
+        cpu_register_physical_memory(ssp[i].base, 0xfff, iomemtype);
+    }
+
+    s->rtc_base = 0x40900000;
+    iomemtype = cpu_register_io_memory(0, pxa2xx_rtc_readfn,
+                    pxa2xx_rtc_writefn, s);
+    cpu_register_physical_memory(s->rtc_base, 0xfff, iomemtype);
+    pxa2xx_rtc_reset(s);
+
+    s->i2c[0] = pxa2xx_i2c_init(0x40301600, PXA2XX_PIC_I2C, s->pic);
+    s->i2c[1] = pxa2xx_i2c_init(0x40f00100, PXA2XX_PIC_PWRI2C, s->pic);
+
+    /* Register PM after PWRI2C to get the register mappings right.  */
+    s->pm_base = 0x40f00000;
+    iomemtype = cpu_register_io_memory(0, pxa2xx_pm_readfn,
+                    pxa2xx_pm_writefn, s);
+    cpu_register_physical_memory(s->pm_base, 0xfff, iomemtype);
+
+    s->i2s = pxa2xx_i2s_init(0x40400000, PXA2XX_PIC_I2S, s->pic, s->dma);
+
+    /* GPIO1 resets the processor */
+    /* The handler can be overriden by board-specific code */
+    pxa2xx_gpio_handler_set(s->gpio, 1, pxa2xx_reset, s);
+    return s;
+}
+
+/* Initialise a PXA255 integrated chip (ARM based core).  */
+struct pxa2xx_state_s *pxa255_init(DisplayState *ds)
+{
+    struct pxa2xx_state_s *s;
+    struct pxa2xx_ssp_s *ssp;
+    int iomemtype, i;
+    s = (struct pxa2xx_state_s *) qemu_mallocz(sizeof(struct pxa2xx_state_s));
+
+    s->env = cpu_init();
+    cpu_arm_set_model(s->env, "pxa255");
+
+    s->pic = pxa2xx_pic_init(0x40d00000, s->env);
+
+    s->dma = pxa255_dma_init(0x40000000, s->pic, PXA2XX_PIC_DMA);
+
+    s->gpio = pxa2xx_gpio_init(0x40e00000, s->env, s->pic, 121);
+
+    for (i = 0; pxa255_serial[i].io_base; i ++)
+        if (serial_hds[i])
+            serial_mm_init(*(SetIRQFunc **) s->pic, s->pic,
+                            pxa255_serial[i].io_base, 2,
+                            pxa255_serial[i].irq, serial_hds[i]);
+    if (serial_hds[i])
+        s->fir = pxa2xx_fir_init(0x40800000, PXA2XX_PIC_ICP,
+                        s->pic, s->dma, serial_hds[i]);
+
+    s->cm_base = 0x41300000;
+    s->cm_regs[CCCR >> 4] = 0x02000210;	/* 416.0 MHz */
+    s->clkcfg = 0x00000009;		/* Turbo mode active */
+    iomemtype = cpu_register_io_memory(0, pxa2xx_cm_readfn,
+                    pxa2xx_cm_writefn, s);
+    cpu_register_physical_memory(s->cm_base, 0xfff, iomemtype);
+
+    cpu_arm_set_cp_io(s->env, 14, pxa2xx_cp14_read, pxa2xx_cp14_write, s);
+
+    s->mm_base = 0x48000000;
+    s->mm_regs[MDMRS >> 2] = 0x00020002;
+    s->mm_regs[MDREFR >> 2] = 0x03ca4000;
+    s->mm_regs[MECR >> 2] = 0x00000001;	/* Two PC Card sockets */
+    iomemtype = cpu_register_io_memory(0, pxa2xx_mm_readfn,
+                    pxa2xx_mm_writefn, s);
+    cpu_register_physical_memory(s->mm_base, 0xfff, iomemtype);
+
+    for (i = 0; pxa255_ssp[i].io_base; i ++);
+    s->ssp = (struct pxa2xx_ssp_s **)
+            qemu_mallocz(sizeof(struct pxa2xx_ssp_s *) * i);
+    ssp = (struct pxa2xx_ssp_s *)
+            qemu_mallocz(sizeof(struct pxa2xx_ssp_s) * i);
+    for (i = 0; pxa255_ssp[i].io_base; i ++) {
+        s->ssp[i] = &ssp[i];
+        ssp[i].base = pxa255_ssp[i].io_base;
+        ssp[i].irq = pxa255_ssp[i].irq;
+        ssp[i].pic = s->pic;
+
+        iomemtype = cpu_register_io_memory(0, pxa2xx_ssp_readfn,
+                        pxa2xx_ssp_writefn, &ssp[i]);
+        cpu_register_physical_memory(ssp[i].base, 0xfff, iomemtype);
+    }
+
+    s->rtc_base = 0x40900000;
+    iomemtype = cpu_register_io_memory(0, pxa2xx_rtc_readfn,
+                    pxa2xx_rtc_writefn, s);
+    cpu_register_physical_memory(s->rtc_base, 0xfff, iomemtype);
+    pxa2xx_rtc_reset(s);
+
+    s->i2c[0] = pxa2xx_i2c_init(0x40301600, PXA2XX_PIC_I2C, s->pic);
+    s->i2c[1] = pxa2xx_i2c_init(0x40f00100, PXA2XX_PIC_PWRI2C, s->pic);
+
+    /* Register PM after PWRI2C to get the register mappings right.  */
+    s->pm_base = 0x40f00000;
+    iomemtype = cpu_register_io_memory(0, pxa2xx_pm_readfn,
+                    pxa2xx_pm_writefn, s);
+    cpu_register_physical_memory(s->pm_base, 0xfff, iomemtype);
+
+    s->i2s = pxa2xx_i2s_init(0x40400000, PXA2XX_PIC_I2S, s->pic, s->dma);
+
+    /* GPIO1 resets the processor */
+    /* The handler can be overriden by board-specific code */
+    pxa2xx_gpio_handler_set(s->gpio, 1, pxa2xx_reset, s);
+    return s;
+}
diff --git a/hw/pxa2xx_dma.c b/hw/pxa2xx_dma.c
new file mode 100644
index 0000000..4b44d8d
--- /dev/null
+++ b/hw/pxa2xx_dma.c
@@ -0,0 +1,500 @@
+/*
+ * Intel XScale PXA255/270 DMA controller.
+ *
+ * Copyright (c) 2006 Openedhand Ltd.
+ * Copyright (c) 2006 Thorsten Zitterell
+ * Written by Andrzej Zaborowski <balrog@zabor.org>
+ *
+ * This code is licenced under the GPL.
+ */
+
+#include "vl.h"
+
+struct pxa2xx_dma_channel_s {
+    target_phys_addr_t descr;
+    target_phys_addr_t src;
+    target_phys_addr_t dest;
+    uint32_t cmd;
+    uint32_t state;
+    int request;
+};
+
+/* The first element of an individual PIC state structures should
+ * be a pointer to the handler routine.  We allow the DMA to be used
+ * as a PIC. */
+typedef void (*pxa2xx_dma_handler_t)(void *opaque, int irq, int level);
+
+struct pxa2xx_dma_state_s {
+    pxa2xx_dma_handler_t handler;
+    target_phys_addr_t base;
+    void *pic;
+    int irq;
+
+    uint32_t stopintr;
+    uint32_t eorintr;
+    uint32_t rasintr;
+    uint32_t startintr;
+    uint32_t endintr;
+
+    uint32_t align;
+    uint32_t pio;
+
+    int channels;
+    struct pxa2xx_dma_channel_s *chan;
+
+    uint8_t *req;
+
+    /* Flag to avoid recursive DMA invocations.  */
+    int running;
+};
+
+#define PXA255_DMA_NUM_CHANNELS	16
+#define PXA27X_DMA_NUM_CHANNELS	32
+
+#define PXA2XX_DMA_NUM_REQUESTS	75
+
+#define DCSR0	0x0000	/* DMA Control / Status register for Channel 0 */
+#define DCSR31	0x007c	/* DMA Control / Status register for Channel 31 */
+#define DALGN	0x00a0	/* DMA Alignment register */
+#define DPCSR	0x00a4	/* DMA Programmed I/O Control Status register */
+#define DRQSR0	0x00e0	/* DMA DREQ<0> Status register */
+#define DRQSR1	0x00e4	/* DMA DREQ<1> Status register */
+#define DRQSR2	0x00e8	/* DMA DREQ<2> Status register */
+#define DINT	0x00f0	/* DMA Interrupt register */
+#define DRCMR0	0x0100	/* Request to Channel Map register 0 */
+#define DRCMR63	0x01fc	/* Request to Channel Map register 63 */
+#define D_CH0	0x0200	/* Channel 0 Descriptor start */
+#define DRCMR64	0x1100	/* Request to Channel Map register 64 */
+#define DRCMR74	0x1128	/* Request to Channel Map register 74 */
+
+/* Per-channel register */
+#define DDADR	0x00
+#define DSADR	0x01
+#define DTADR	0x02
+#define DCMD	0x03
+
+/* Bit-field masks */
+#define DRCMR_CHLNUM		0x1f
+#define DRCMR_MAPVLD		(1 << 7)
+#define DDADR_STOP		(1 << 0)
+#define DDADR_BREN		(1 << 1)
+#define DCMD_LEN		0x1fff
+#define DCMD_WIDTH(x)		(1 << ((((x) >> 14) & 3) - 1))
+#define DCMD_SIZE(x)		(4 << (((x) >> 16) & 3))
+#define DCMD_FLYBYT		(1 << 19)
+#define DCMD_FLYBYS		(1 << 20)
+#define DCMD_ENDIRQEN		(1 << 21)
+#define DCMD_STARTIRQEN		(1 << 22)
+#define DCMD_CMPEN		(1 << 25)
+#define DCMD_FLOWTRG		(1 << 28)
+#define DCMD_FLOWSRC		(1 << 29)
+#define DCMD_INCTRGADDR		(1 << 30)
+#define DCMD_INCSRCADDR		(1 << 31)
+#define DCSR_BUSERRINTR		(1 << 0)
+#define DCSR_STARTINTR		(1 << 1)
+#define DCSR_ENDINTR		(1 << 2)
+#define DCSR_STOPINTR		(1 << 3)
+#define DCSR_RASINTR		(1 << 4)
+#define DCSR_REQPEND		(1 << 8)
+#define DCSR_EORINT		(1 << 9)
+#define DCSR_CMPST		(1 << 10)
+#define DCSR_MASKRUN		(1 << 22)
+#define DCSR_RASIRQEN		(1 << 23)
+#define DCSR_CLRCMPST		(1 << 24)
+#define DCSR_SETCMPST		(1 << 25)
+#define DCSR_EORSTOPEN		(1 << 26)
+#define DCSR_EORJMPEN		(1 << 27)
+#define DCSR_EORIRQEN		(1 << 28)
+#define DCSR_STOPIRQEN		(1 << 29)
+#define DCSR_NODESCFETCH	(1 << 30)
+#define DCSR_RUN		(1 << 31)
+
+static inline void pxa2xx_dma_update(struct pxa2xx_dma_state_s *s, int ch)
+{
+    if (ch >= 0) {
+        if ((s->chan[ch].state & DCSR_STOPIRQEN) &&
+                (s->chan[ch].state & DCSR_STOPINTR))
+            s->stopintr |= 1 << ch;
+        else
+            s->stopintr &= ~(1 << ch);
+
+        if ((s->chan[ch].state & DCSR_EORIRQEN) &&
+                (s->chan[ch].state & DCSR_EORINT))
+            s->eorintr |= 1 << ch;
+        else
+            s->eorintr &= ~(1 << ch);
+
+        if ((s->chan[ch].state & DCSR_RASIRQEN) &&
+                (s->chan[ch].state & DCSR_RASINTR))
+            s->rasintr |= 1 << ch;
+        else
+            s->rasintr &= ~(1 << ch);
+
+        if (s->chan[ch].state & DCSR_STARTINTR)
+            s->startintr |= 1 << ch;
+        else
+            s->startintr &= ~(1 << ch);
+
+        if (s->chan[ch].state & DCSR_ENDINTR)
+            s->endintr |= 1 << ch;
+        else
+            s->endintr &= ~(1 << ch);
+    }
+
+    if (s->stopintr | s->eorintr | s->rasintr | s->startintr | s->endintr)
+        pic_set_irq_new(s->pic, s->irq, 1);
+    else
+        pic_set_irq_new(s->pic, s->irq, 0);
+}
+
+static inline void pxa2xx_dma_descriptor_fetch(
+                struct pxa2xx_dma_state_s *s, int ch)
+{
+    uint32_t desc[4];
+    target_phys_addr_t daddr = s->chan[ch].descr & ~0xf;
+    if ((s->chan[ch].descr & DDADR_BREN) && (s->chan[ch].state & DCSR_CMPST))
+        daddr += 32;
+
+    cpu_physical_memory_read(daddr, (uint8_t *) desc, 16);
+    s->chan[ch].descr = desc[DDADR];
+    s->chan[ch].src = desc[DSADR];
+    s->chan[ch].dest = desc[DTADR];
+    s->chan[ch].cmd = desc[DCMD];
+
+    if (s->chan[ch].cmd & DCMD_FLOWSRC)
+        s->chan[ch].src &= ~3;
+    if (s->chan[ch].cmd & DCMD_FLOWTRG)
+        s->chan[ch].dest &= ~3;
+
+    if (s->chan[ch].cmd & (DCMD_CMPEN | DCMD_FLYBYS | DCMD_FLYBYT))
+        printf("%s: unsupported mode in channel %i\n", __FUNCTION__, ch);
+
+    if (s->chan[ch].cmd & DCMD_STARTIRQEN)
+        s->chan[ch].state |= DCSR_STARTINTR;
+}
+
+static void pxa2xx_dma_run(struct pxa2xx_dma_state_s *s)
+{
+    int c, srcinc, destinc;
+    uint32_t n, size;
+    uint32_t width;
+    uint32_t length;
+    char buffer[32];
+    struct pxa2xx_dma_channel_s *ch;
+
+    if (s->running ++)
+        return;
+
+    while (s->running) {
+        s->running = 1;
+        for (c = 0; c < s->channels; c ++) {
+            ch = &s->chan[c];
+
+            while ((ch->state & DCSR_RUN) && !(ch->state & DCSR_STOPINTR)) {
+                /* Test for pending requests */
+                if ((ch->cmd & (DCMD_FLOWSRC | DCMD_FLOWTRG)) && !ch->request)
+                    break;
+
+                length = ch->cmd & DCMD_LEN;
+                size = DCMD_SIZE(ch->cmd);
+                width = DCMD_WIDTH(ch->cmd);
+
+                srcinc = (ch->cmd & DCMD_INCSRCADDR) ? width : 0;
+                destinc = (ch->cmd & DCMD_INCTRGADDR) ? width : 0;
+
+                while (length) {
+                    size = MIN(length, size);
+
+                    for (n = 0; n < size; n += width) {
+                        cpu_physical_memory_read(ch->src, buffer + n, width);
+                        ch->src += srcinc;
+                    }
+
+                    for (n = 0; n < size; n += width) {
+                        cpu_physical_memory_write(ch->dest, buffer + n, width);
+                        ch->dest += destinc;
+                    }
+
+                    length -= size;
+
+                    if ((ch->cmd & (DCMD_FLOWSRC | DCMD_FLOWTRG)) &&
+                            !ch->request) {
+                        ch->state |= DCSR_EORINT;
+                        if (ch->state & DCSR_EORSTOPEN)
+                            ch->state |= DCSR_STOPINTR;
+                        if ((ch->state & DCSR_EORJMPEN) &&
+                                        !(ch->state & DCSR_NODESCFETCH))
+                            pxa2xx_dma_descriptor_fetch(s, c);
+                        break;
+		    }
+                }
+
+                ch->cmd = (ch->cmd & ~DCMD_LEN) | length;
+
+                /* Is the transfer complete now? */
+                if (!length) {
+                    if (ch->cmd & DCMD_ENDIRQEN)
+                        ch->state |= DCSR_ENDINTR;
+
+                    if ((ch->state & DCSR_NODESCFETCH) ||
+                                (ch->descr & DDADR_STOP) ||
+                                (ch->state & DCSR_EORSTOPEN)) {
+                        ch->state |= DCSR_STOPINTR;
+                        ch->state &= ~DCSR_RUN;
+
+                        break;
+                    }
+
+                    ch->state |= DCSR_STOPINTR;
+                    break;
+                }
+            }
+        }
+
+        s->running --;
+    }
+}
+
+static uint32_t pxa2xx_dma_read(void *opaque, target_phys_addr_t offset)
+{
+    struct pxa2xx_dma_state_s *s = (struct pxa2xx_dma_state_s *) opaque;
+    unsigned int channel;
+    offset -= s->base;
+
+    switch (offset) {
+    case DRCMR64 ... DRCMR74:
+        offset -= DRCMR64 - DRCMR0 - (64 << 2);
+        /* Fall through */
+    case DRCMR0 ... DRCMR63:
+        channel = (offset - DRCMR0) >> 2;
+        return s->req[channel];
+
+    case DRQSR0:
+    case DRQSR1:
+    case DRQSR2:
+        return 0;
+
+    case DCSR0 ... DCSR31:
+        channel = offset >> 2;
+	if (s->chan[channel].request)
+            return s->chan[channel].state | DCSR_REQPEND;
+        return s->chan[channel].state;
+
+    case DINT:
+        return s->stopintr | s->eorintr | s->rasintr |
+                s->startintr | s->endintr;
+
+    case DALGN:
+        return s->align;
+
+    case DPCSR:
+        return s->pio;
+    }
+
+    if (offset >= D_CH0 && offset < D_CH0 + (s->channels << 4)) {
+        channel = (offset - D_CH0) >> 4;
+        switch ((offset & 0x0f) >> 2) {
+        case DDADR:
+            return s->chan[channel].descr;
+        case DSADR:
+            return s->chan[channel].src;
+        case DTADR:
+            return s->chan[channel].dest;
+        case DCMD:
+            return s->chan[channel].cmd;
+        }
+    }
+
+    cpu_abort(cpu_single_env,
+                    "%s: Bad offset 0x%04lx\n", __FUNCTION__, offset);
+    return 7;
+}
+
+static void pxa2xx_dma_write(void *opaque,
+                 target_phys_addr_t offset, uint32_t value)
+{
+    struct pxa2xx_dma_state_s *s = (struct pxa2xx_dma_state_s *) opaque;
+    unsigned int channel;
+    offset -= s->base;
+
+    switch (offset) {
+    case DRCMR64 ... DRCMR74:
+        offset -= DRCMR64 - DRCMR0 - (64 << 2);
+        /* Fall through */
+    case DRCMR0 ... DRCMR63:
+        channel = (offset - DRCMR0) >> 2;
+
+        if (value & DRCMR_MAPVLD)
+            if ((value & DRCMR_CHLNUM) > s->channels)
+                cpu_abort(cpu_single_env, "%s: Bad DMA channel %i\n",
+                        __FUNCTION__, value & DRCMR_CHLNUM);
+
+        s->req[channel] = value;
+        break;
+
+    case DRQSR0:
+    case DRQSR1:
+    case DRQSR2:
+        /* Nothing to do */
+        break;
+
+    case DCSR0 ... DCSR31:
+        channel = offset >> 2;
+        s->chan[channel].state &= 0x0000071f & ~(value &
+                        (DCSR_EORINT | DCSR_ENDINTR |
+                         DCSR_STARTINTR | DCSR_BUSERRINTR));
+        s->chan[channel].state |= value & 0xfc800000;
+
+        if (s->chan[channel].state & DCSR_STOPIRQEN)
+            s->chan[channel].state &= ~DCSR_STOPINTR;
+
+        if (value & DCSR_NODESCFETCH) {
+            /* No-descriptor-fetch mode */
+            if (value & DCSR_RUN)
+                pxa2xx_dma_run(s);
+        } else {
+            /* Descriptor-fetch mode */
+            if (value & DCSR_RUN) {
+                s->chan[channel].state &= ~DCSR_STOPINTR;
+                pxa2xx_dma_descriptor_fetch(s, channel);
+                pxa2xx_dma_run(s);
+            }
+        }
+
+        /* Shouldn't matter as our DMA is synchronous.  */
+        if (!(value & (DCSR_RUN | DCSR_MASKRUN)))
+            s->chan[channel].state |= DCSR_STOPINTR;
+
+        if (value & DCSR_CLRCMPST)
+            s->chan[channel].state &= ~DCSR_CMPST;
+        if (value & DCSR_SETCMPST)
+            s->chan[channel].state |= DCSR_CMPST;
+
+        pxa2xx_dma_update(s, channel);
+        break;
+
+    case DALGN:
+        s->align = value;
+        break;
+
+    case DPCSR:
+        s->pio = value & 0x80000001;
+        break;
+
+    default:
+        if (offset >= D_CH0 && offset < D_CH0 + (s->channels << 4)) {
+            channel = (offset - D_CH0) >> 4;
+            switch ((offset & 0x0f) >> 2) {
+            case DDADR:
+                s->chan[channel].descr = value;
+                break;
+            case DSADR:
+                s->chan[channel].src = value;
+                break;
+            case DTADR:
+                s->chan[channel].dest = value;
+                break;
+            case DCMD:
+                s->chan[channel].cmd = value;
+                break;
+            default:
+                goto fail;
+            }
+
+            break;
+        }
+    fail:
+        cpu_abort(cpu_single_env, "%s: Bad offset 0x%04lx\n",
+                __FUNCTION__, offset);
+    }
+}
+
+static uint32_t pxa2xx_dma_readbad(void *opaque, target_phys_addr_t offset)
+{
+    cpu_abort(cpu_single_env, "%s: Bad access width\n", __FUNCTION__);
+    return 5;
+}
+
+static void pxa2xx_dma_writebad(void *opaque,
+                 target_phys_addr_t offset, uint32_t value)
+{
+    cpu_abort(cpu_single_env, "%s: Bad access width\n", __FUNCTION__);
+}
+
+static CPUReadMemoryFunc *pxa2xx_dma_readfn[] = {
+    pxa2xx_dma_readbad,
+    pxa2xx_dma_readbad,
+    pxa2xx_dma_read
+};
+
+static CPUWriteMemoryFunc *pxa2xx_dma_writefn[] = {
+    pxa2xx_dma_writebad,
+    pxa2xx_dma_writebad,
+    pxa2xx_dma_write
+};
+
+static struct pxa2xx_dma_state_s *pxa2xx_dma_init(target_phys_addr_t base,
+                void *pic, int irq, int channels)
+{
+    int i, iomemtype;
+    struct pxa2xx_dma_state_s *s;
+    s = (struct pxa2xx_dma_state_s *)
+            qemu_mallocz(sizeof(struct pxa2xx_dma_state_s));
+
+    s->channels = channels;
+    s->chan = qemu_mallocz(sizeof(struct pxa2xx_dma_channel_s) * s->channels);
+    s->base = base;
+    s->pic = pic;
+    s->irq = irq;
+    s->handler = (pxa2xx_dma_handler_t) pxa2xx_dma_request;
+    s->req = qemu_mallocz(sizeof(int) * PXA2XX_DMA_NUM_REQUESTS);
+
+    memset(s->chan, 0, sizeof(struct pxa2xx_dma_channel_s) * s->channels);
+    for (i = 0; i < s->channels; i ++)
+        s->chan[i].state = DCSR_STOPINTR;
+
+    memset(s->req, 0, sizeof(int) * PXA2XX_DMA_NUM_REQUESTS);
+
+    iomemtype = cpu_register_io_memory(0, pxa2xx_dma_readfn,
+                   pxa2xx_dma_writefn, s);
+    cpu_register_physical_memory(base, 0x0000ffff, iomemtype);
+
+    return s;
+}
+
+struct pxa2xx_dma_state_s *pxa27x_dma_init(target_phys_addr_t base,
+                void *pic, int irq)
+{
+    return pxa2xx_dma_init(base, pic, irq, PXA27X_DMA_NUM_CHANNELS);
+}
+
+struct pxa2xx_dma_state_s *pxa255_dma_init(target_phys_addr_t base,
+                void *pic, int irq)
+{
+    return pxa2xx_dma_init(base, pic, irq, PXA255_DMA_NUM_CHANNELS);
+}
+
+void pxa2xx_dma_request(struct pxa2xx_dma_state_s *s, int req_num, int on)
+{
+    int ch;
+    if (req_num < 0 || req_num >= PXA2XX_DMA_NUM_REQUESTS)
+        cpu_abort(cpu_single_env,
+              "%s: Bad DMA request %i\n", __FUNCTION__, req_num);
+
+    if (!(s->req[req_num] & DRCMR_MAPVLD))
+        return;
+    ch = s->req[req_num] & DRCMR_CHLNUM;
+
+    if (!s->chan[ch].request && on)
+        s->chan[ch].state |= DCSR_RASINTR;
+    else
+        s->chan[ch].state &= ~DCSR_RASINTR;
+    if (s->chan[ch].request && !on)
+        s->chan[ch].state |= DCSR_EORINT;
+
+    s->chan[ch].request = on;
+    if (on) {
+        pxa2xx_dma_run(s);
+        pxa2xx_dma_update(s, ch);
+    }
+}
diff --git a/hw/pxa2xx_gpio.c b/hw/pxa2xx_gpio.c
new file mode 100644
index 0000000..0c56cf1
--- /dev/null
+++ b/hw/pxa2xx_gpio.c
@@ -0,0 +1,290 @@
+/*
+ * Intel XScale PXA255/270 GPIO controller emulation.
+ *
+ * Copyright (c) 2006 Openedhand Ltd.
+ * Written by Andrzej Zaborowski <balrog@zabor.org>
+ *
+ * This code is licensed under the GPL.
+ */
+
+#include "vl.h"
+
+#define PXA2XX_GPIO_BANKS	4
+
+struct pxa2xx_gpio_info_s {
+    target_phys_addr_t base;
+    void *pic;
+    int lines;
+    CPUState *cpu_env;
+
+    /* XXX: GNU C vectors are more suitable */
+    uint32_t ilevel[PXA2XX_GPIO_BANKS];
+    uint32_t olevel[PXA2XX_GPIO_BANKS];
+    uint32_t dir[PXA2XX_GPIO_BANKS];
+    uint32_t rising[PXA2XX_GPIO_BANKS];
+    uint32_t falling[PXA2XX_GPIO_BANKS];
+    uint32_t status[PXA2XX_GPIO_BANKS];
+    uint32_t gafr[PXA2XX_GPIO_BANKS * 2];
+
+    uint32_t prev_level[PXA2XX_GPIO_BANKS];
+    struct {
+        gpio_handler_t fn;
+        void *opaque;
+    } handler[PXA2XX_GPIO_BANKS * 32];
+
+    void (*read_notify)(void *opaque);
+    void *opaque;
+};
+
+static struct {
+    enum {
+        GPIO_NONE,
+        GPLR,
+        GPSR,
+        GPCR,
+        GPDR,
+        GRER,
+        GFER,
+        GEDR,
+        GAFR_L,
+        GAFR_U,
+    } reg;
+    int bank;
+} pxa2xx_gpio_regs[0x200] = {
+    [0 ... 0x1ff] = { GPIO_NONE, 0 },
+#define PXA2XX_REG(reg, a0, a1, a2, a3)	\
+    [a0] = { reg, 0 }, [a1] = { reg, 1 }, [a2] = { reg, 2 }, [a3] = { reg, 3 }, 
+
+    PXA2XX_REG(GPLR, 0x000, 0x004, 0x008, 0x100)
+    PXA2XX_REG(GPSR, 0x018, 0x01c, 0x020, 0x118)
+    PXA2XX_REG(GPCR, 0x024, 0x028, 0x02c, 0x124)
+    PXA2XX_REG(GPDR, 0x00c, 0x010, 0x014, 0x10c)
+    PXA2XX_REG(GRER, 0x030, 0x034, 0x038, 0x130)
+    PXA2XX_REG(GFER, 0x03c, 0x040, 0x044, 0x13c)
+    PXA2XX_REG(GEDR, 0x048, 0x04c, 0x050, 0x148)
+    PXA2XX_REG(GAFR_L, 0x054, 0x05c, 0x064, 0x06c)
+    PXA2XX_REG(GAFR_U, 0x058, 0x060, 0x068, 0x070)
+};
+
+static void pxa2xx_gpio_irq_update(struct pxa2xx_gpio_info_s *s)
+{
+    if (s->status[0] & (1 << 0))
+        pic_set_irq_new(s->pic, PXA2XX_PIC_GPIO_0, 1);
+    else
+        pic_set_irq_new(s->pic, PXA2XX_PIC_GPIO_0, 0);
+
+    if (s->status[0] & (1 << 1))
+        pic_set_irq_new(s->pic, PXA2XX_PIC_GPIO_1, 1);
+    else
+        pic_set_irq_new(s->pic, PXA2XX_PIC_GPIO_1, 0);
+
+    if ((s->status[0] & ~3) | s->status[1] | s->status[2] | s->status[3])
+        pic_set_irq_new(s->pic, PXA2XX_PIC_GPIO_X, 1);
+    else
+        pic_set_irq_new(s->pic, PXA2XX_PIC_GPIO_X, 0);
+}
+
+/* Bitmap of pins used as standby and sleep wake-up sources.  */
+const int pxa2xx_gpio_wake[PXA2XX_GPIO_BANKS] = {
+    0x8003fe1b, 0x002001fc, 0xec080000, 0x0012007f,
+};
+
+void pxa2xx_gpio_set(struct pxa2xx_gpio_info_s *s, int line, int level)
+{
+    int bank;
+    uint32_t mask;
+
+    if (line >= s->lines) {
+        printf("%s: No GPIO pin %i\n", __FUNCTION__, line);
+        return;
+    }
+
+    bank = line >> 5;
+    mask = 1 << (line & 31);
+
+    if (level) {
+        s->status[bank] |= s->rising[bank] & mask &
+                ~s->ilevel[bank] & ~s->dir[bank];
+        s->ilevel[bank] |= mask;
+    } else {
+        s->status[bank] |= s->falling[bank] & mask &
+                s->ilevel[bank] & ~s->dir[bank];
+        s->ilevel[bank] &= ~mask;
+    }
+
+    if (s->status[bank] & mask)
+        pxa2xx_gpio_irq_update(s);
+
+    /* Wake-up GPIOs */
+    if (s->cpu_env->halted && (mask & ~s->dir[bank] & pxa2xx_gpio_wake[bank]))
+        cpu_interrupt(s->cpu_env, CPU_INTERRUPT_EXITTB);
+}
+
+static void pxa2xx_gpio_handler_update(struct pxa2xx_gpio_info_s *s) {
+    uint32_t level, diff;
+    int i, bit, line;
+    for (i = 0; i < PXA2XX_GPIO_BANKS; i ++) {
+        level = s->olevel[i] & s->dir[i];
+
+        for (diff = s->prev_level[i] ^ level; diff; diff ^= 1 << bit) {
+            bit = ffs(diff) - 1;
+            line = bit + 32 * i;
+            if (s->handler[line].fn)
+                s->handler[line].fn(line, (level >> bit) & 1,
+                                s->handler[line].opaque);
+        }
+
+        s->prev_level[i] = level;
+    }
+}
+
+static uint32_t pxa2xx_gpio_read(void *opaque, target_phys_addr_t offset)
+{
+    struct pxa2xx_gpio_info_s *s = (struct pxa2xx_gpio_info_s *) opaque;
+    uint32_t ret;
+    int bank;
+    offset -= s->base;
+    if (offset >= 0x200)
+        return 0;
+
+    bank = pxa2xx_gpio_regs[offset].bank;
+    switch (pxa2xx_gpio_regs[offset].reg) {
+    case GPDR:		/* GPIO Pin-Direction registers */
+        return s->dir[bank];
+
+    case GRER:		/* GPIO Rising-Edge Detect Enable registers */
+        return s->rising[bank];
+
+    case GFER:		/* GPIO Falling-Edge Detect Enable registers */
+        return s->falling[bank];
+
+    case GAFR_L:	/* GPIO Alternate Function registers */
+        return s->gafr[bank * 2];
+
+    case GAFR_U:	/* GPIO Alternate Function registers */
+        return s->gafr[bank * 2 + 1];
+
+    case GPLR:		/* GPIO Pin-Level registers */
+        ret = (s->olevel[bank] & s->dir[bank]) |
+                (s->ilevel[bank] & ~s->dir[bank]);
+        if (s->read_notify)
+            s->read_notify(s->opaque);
+        return ret;
+
+    case GEDR:		/* GPIO Edge Detect Status registers */
+        return s->status[bank];
+
+    default:
+        cpu_abort(cpu_single_env,
+                "%s: Bad offset " REG_FMT "\n", __FUNCTION__, offset);
+    }
+
+    return 0;
+}
+
+static void pxa2xx_gpio_write(void *opaque,
+                target_phys_addr_t offset, uint32_t value)
+{
+    struct pxa2xx_gpio_info_s *s = (struct pxa2xx_gpio_info_s *) opaque;
+    int bank;
+    offset -= s->base;
+    if (offset >= 0x200)
+        return;
+
+    bank = pxa2xx_gpio_regs[offset].bank;
+    switch (pxa2xx_gpio_regs[offset].reg) {
+    case GPDR:		/* GPIO Pin-Direction registers */
+        s->dir[bank] = value;
+        pxa2xx_gpio_handler_update(s);
+        break;
+
+    case GPSR:		/* GPIO Pin-Output Set registers */
+        s->olevel[bank] |= value;
+        pxa2xx_gpio_handler_update(s);
+        break;
+
+    case GPCR:		/* GPIO Pin-Output Clear registers */
+        s->olevel[bank] &= ~value;
+        pxa2xx_gpio_handler_update(s);
+        break;
+
+    case GRER:		/* GPIO Rising-Edge Detect Enable registers */
+        s->rising[bank] = value;
+        break;
+
+    case GFER:		/* GPIO Falling-Edge Detect Enable registers */
+        s->falling[bank] = value;
+        break;
+
+    case GAFR_L:	/* GPIO Alternate Function registers */
+        s->gafr[bank * 2] = value;
+        break;
+
+    case GAFR_U:	/* GPIO Alternate Function registers */
+        s->gafr[bank * 2 + 1] = value;
+        break;
+
+    case GEDR:		/* GPIO Edge Detect Status registers */
+        s->status[bank] &= ~value;
+        pxa2xx_gpio_irq_update(s);
+        break;
+
+    default:
+        cpu_abort(cpu_single_env,
+                "%s: Bad offset " REG_FMT "\n", __FUNCTION__, offset);
+    }
+}
+
+static CPUReadMemoryFunc *pxa2xx_gpio_readfn[] = {
+    pxa2xx_gpio_read,
+    pxa2xx_gpio_read,
+    pxa2xx_gpio_read
+};
+
+static CPUWriteMemoryFunc *pxa2xx_gpio_writefn[] = {
+    pxa2xx_gpio_write,
+    pxa2xx_gpio_write,
+    pxa2xx_gpio_write
+};
+
+struct pxa2xx_gpio_info_s *pxa2xx_gpio_init(target_phys_addr_t base,
+                CPUState *env, void *pic, int lines)
+{
+    int iomemtype;
+    struct pxa2xx_gpio_info_s *s;
+
+    s = (struct pxa2xx_gpio_info_s *)
+            qemu_mallocz(sizeof(struct pxa2xx_gpio_info_s));
+    memset(s, 0, sizeof(struct pxa2xx_gpio_info_s));
+    s->base = base;
+    s->pic = pic;
+    s->lines = lines;
+    s->cpu_env = env;
+
+    iomemtype = cpu_register_io_memory(0, pxa2xx_gpio_readfn,
+                    pxa2xx_gpio_writefn, s);
+    cpu_register_physical_memory(base, 0x00000fff, iomemtype);
+
+    return s;
+}
+
+void pxa2xx_gpio_handler_set(struct pxa2xx_gpio_info_s *s, int line,
+                gpio_handler_t handler, void *opaque) {
+    if (line >= s->lines) {
+        printf("%s: No GPIO pin %i\n", __FUNCTION__, line);
+        return;
+    }
+
+    s->handler[line].fn = handler;
+    s->handler[line].opaque = opaque;
+}
+
+/*
+ * Registers a callback to notify on GPLR reads.  This normally
+ * shouldn't be needed but it is used for the hack on Spitz machines.
+ */
+void pxa2xx_gpio_read_notifier(struct pxa2xx_gpio_info_s *s,
+                void (*handler)(void *opaque), void *opaque) {
+    s->read_notify = handler;
+    s->opaque = opaque;
+}
diff --git a/hw/pxa2xx_pic.c b/hw/pxa2xx_pic.c
new file mode 100644
index 0000000..752fe35
--- /dev/null
+++ b/hw/pxa2xx_pic.c
@@ -0,0 +1,284 @@
+/*
+ * Intel XScale PXA Programmable Interrupt Controller.
+ *
+ * Copyright (c) 2006 Openedhand Ltd.
+ * Copyright (c) 2006 Thorsten Zitterell
+ * Written by Andrzej Zaborowski <balrog@zabor.org>
+ *
+ * This code is licenced under the GPL.
+ */
+
+#include "vl.h"
+
+#define ICIP	0x00	/* Interrupt Controller IRQ Pending register */
+#define ICMR	0x04	/* Interrupt Controller Mask register */
+#define ICLR	0x08	/* Interrupt Controller Level register */
+#define ICFP	0x0c	/* Interrupt Controller FIQ Pending register */
+#define ICPR	0x10	/* Interrupt Controller Pending register */
+#define ICCR	0x14	/* Interrupt Controller Control register */
+#define ICHP	0x18	/* Interrupt Controller Highest Priority register */
+#define IPR0	0x1c	/* Interrupt Controller Priority register 0 */
+#define IPR31	0x98	/* Interrupt Controller Priority register 31 */
+#define ICIP2	0x9c	/* Interrupt Controller IRQ Pending register 2 */
+#define ICMR2	0xa0	/* Interrupt Controller Mask register 2 */
+#define ICLR2	0xa4	/* Interrupt Controller Level register 2 */
+#define ICFP2	0xa8	/* Interrupt Controller FIQ Pending register 2 */
+#define ICPR2	0xac	/* Interrupt Controller Pending register 2 */
+#define IPR32	0xb0	/* Interrupt Controller Priority register 32 */
+#define IPR39	0xcc	/* Interrupt Controller Priority register 39 */
+
+/* The first element of an individual PIC state structures should
+ * be a pointer to the handler routine.  */
+typedef void (*pxa2xx_pic_handler_t)(void *opaque, int irq, int level);
+
+#define PXA2XX_PIC_SRCS	40
+
+struct pxa2xx_pic_state_s {
+    pxa2xx_pic_handler_t handler;
+    target_phys_addr_t base;
+    CPUState *cpu_env;
+    uint32_t int_enabled[2];
+    uint32_t int_pending[2];
+    uint32_t is_fiq[2];
+    uint32_t int_idle;
+    uint32_t priority[PXA2XX_PIC_SRCS];
+};
+
+static void pxa2xx_pic_update(void *opaque)
+{
+    uint32_t mask[2];
+    struct pxa2xx_pic_state_s *s = (struct pxa2xx_pic_state_s *) opaque;
+
+    if (s->cpu_env->halted) {
+        mask[0] = s->int_pending[0] & (s->int_enabled[0] | s->int_idle);
+        mask[1] = s->int_pending[1] & (s->int_enabled[1] | s->int_idle);
+        if (mask[0] || mask[1])
+            cpu_interrupt(s->cpu_env, CPU_INTERRUPT_EXITTB);
+    }
+
+    mask[0] = s->int_pending[0] & s->int_enabled[0];
+    mask[1] = s->int_pending[1] & s->int_enabled[1];
+
+    if ((mask[0] & s->is_fiq[0]) || (mask[1] & s->is_fiq[1]))
+        cpu_interrupt(s->cpu_env, CPU_INTERRUPT_FIQ);
+    else
+        cpu_reset_interrupt(s->cpu_env, CPU_INTERRUPT_FIQ);
+
+    if ((mask[0] & ~s->is_fiq[0]) || (mask[1] & ~s->is_fiq[1]))
+        cpu_interrupt(s->cpu_env, CPU_INTERRUPT_HARD);
+    else
+        cpu_reset_interrupt(s->cpu_env, CPU_INTERRUPT_HARD);
+}
+
+/* Note: Here level means state of the signal on a pin, not
+ * IRQ/FIQ distinction as in PXA Developer Manual.  */
+static void pxa2xx_pic_set_irq(void *opaque, int irq, int level)
+{
+    struct pxa2xx_pic_state_s *s = (struct pxa2xx_pic_state_s *) opaque;
+    int int_set = (irq >= 32);
+    irq &= 31;
+
+    if (level)
+        s->int_pending[int_set] |= 1 << irq;
+    else
+        s->int_pending[int_set] &= ~(1 << irq);
+
+    pxa2xx_pic_update(opaque);
+}
+
+static inline uint32_t pxa2xx_pic_highest(struct pxa2xx_pic_state_s *s) {
+    int i, int_set, irq;
+    uint32_t bit, mask[2];
+    uint32_t ichp = 0x003f003f;	/* Both IDs invalid */
+
+    mask[0] = s->int_pending[0] & s->int_enabled[0];
+    mask[1] = s->int_pending[1] & s->int_enabled[1];
+
+    for (i = PXA2XX_PIC_SRCS - 1; i >= 0; i --) {
+        irq = s->priority[i] & 0x3f;
+        if ((s->priority[i] & (1 << 31)) && irq < PXA2XX_PIC_SRCS) {
+            /* Source peripheral ID is valid.  */
+            bit = 1 << (irq & 31);
+            int_set = (irq >= 32);
+
+            if (mask[int_set] & bit & s->is_fiq[int_set]) {
+                /* FIQ asserted */
+                ichp &= 0xffff0000;
+                ichp |= (1 << 15) | irq;
+            }
+
+            if (mask[int_set] & bit & ~s->is_fiq[int_set]) {
+                /* IRQ asserted */
+                ichp &= 0x0000ffff;
+                ichp |= (1 << 31) | (irq << 16);
+            }
+        }
+    }
+
+    return ichp;
+}
+
+static uint32_t pxa2xx_pic_mem_read(void *opaque, target_phys_addr_t offset)
+{
+    struct pxa2xx_pic_state_s *s = (struct pxa2xx_pic_state_s *) opaque;
+    offset -= s->base;
+
+    switch (offset) {
+    case ICIP:	/* IRQ Pending register */
+        return s->int_pending[0] & ~s->is_fiq[0] & s->int_enabled[0];
+    case ICIP2:	/* IRQ Pending register 2 */
+        return s->int_pending[1] & ~s->is_fiq[1] & s->int_enabled[1];
+    case ICMR:	/* Mask register */
+        return s->int_enabled[0];
+    case ICMR2:	/* Mask register 2 */
+        return s->int_enabled[1];
+    case ICLR:	/* Level register */
+        return s->is_fiq[0];
+    case ICLR2:	/* Level register 2 */
+        return s->is_fiq[1];
+    case ICCR:	/* Idle mask */
+        return (s->int_idle == 0);
+    case ICFP:	/* FIQ Pending register */
+        return s->int_pending[0] & s->is_fiq[0] & s->int_enabled[0];
+    case ICFP2:	/* FIQ Pending register 2 */
+        return s->int_pending[1] & s->is_fiq[1] & s->int_enabled[1];
+    case ICPR:	/* Pending register */
+        return s->int_pending[0];
+    case ICPR2:	/* Pending register 2 */
+        return s->int_pending[1];
+    case IPR0  ... IPR31:
+        return s->priority[0  + ((offset - IPR0 ) >> 2)];
+    case IPR32 ... IPR39:
+        return s->priority[32 + ((offset - IPR32) >> 2)];
+    case ICHP:	/* Highest Priority register */
+        return pxa2xx_pic_highest(s);
+    default:
+        printf("%s: Bad register offset " REG_FMT "\n", __FUNCTION__, offset);
+        return 0;
+    }
+}
+
+static void pxa2xx_pic_mem_write(void *opaque, target_phys_addr_t offset,
+                uint32_t value)
+{
+    struct pxa2xx_pic_state_s *s = (struct pxa2xx_pic_state_s *) opaque;
+    offset -= s->base;
+
+    switch (offset) {
+    case ICMR:	/* Mask register */
+        s->int_enabled[0] = value;
+        break;
+    case ICMR2:	/* Mask register 2 */
+        s->int_enabled[1] = value;
+        break;
+    case ICLR:	/* Level register */
+        s->is_fiq[0] = value;
+        break;
+    case ICLR2:	/* Level register 2 */
+        s->is_fiq[1] = value;
+        break;
+    case ICCR:	/* Idle mask */
+        s->int_idle = (value & 1) ? 0 : ~0;
+        break;
+    case IPR0  ... IPR31:
+        s->priority[0  + ((offset - IPR0 ) >> 2)] = value & 0x8000003f;
+        break;
+    case IPR32 ... IPR39:
+        s->priority[32 + ((offset - IPR32) >> 2)] = value & 0x8000003f;
+        break;
+    default:
+        printf("%s: Bad register offset " REG_FMT "\n", __FUNCTION__, offset);
+        return;
+    }
+    pxa2xx_pic_update(opaque);
+}
+
+/* Interrupt Controller Coprocessor Space Register Mapping */
+static const int pxa2xx_cp_reg_map[0x10] = {
+    [0x0 ... 0xf] = -1,
+    [0x0] = ICIP,
+    [0x1] = ICMR,
+    [0x2] = ICLR,
+    [0x3] = ICFP,
+    [0x4] = ICPR,
+    [0x5] = ICHP,
+    [0x6] = ICIP2,
+    [0x7] = ICMR2,
+    [0x8] = ICLR2,
+    [0x9] = ICFP2,
+    [0xa] = ICPR2,
+};
+
+static uint32_t pxa2xx_pic_cp_read(void *opaque, int op2, int reg, int crm)
+{
+    struct pxa2xx_pic_state_s *s = (struct pxa2xx_pic_state_s *) opaque;
+    target_phys_addr_t offset;
+
+    if (pxa2xx_cp_reg_map[reg] == -1) {
+        printf("%s: Bad register 0x%x\n", __FUNCTION__, reg);
+        return 0;
+    }
+
+    offset = s->base + pxa2xx_cp_reg_map[reg];
+    return pxa2xx_pic_mem_read(opaque, offset);
+}
+
+static void pxa2xx_pic_cp_write(void *opaque, int op2, int reg, int crm,
+                uint32_t value)
+{
+    struct pxa2xx_pic_state_s *s = (struct pxa2xx_pic_state_s *) opaque;
+    target_phys_addr_t offset;
+
+    if (pxa2xx_cp_reg_map[reg] == -1) {
+        printf("%s: Bad register 0x%x\n", __FUNCTION__, reg);
+        return;
+    }
+
+    offset = s->base + pxa2xx_cp_reg_map[reg];
+    pxa2xx_pic_mem_write(opaque, offset, value);
+}
+
+static CPUReadMemoryFunc *pxa2xx_pic_readfn[] = {
+    pxa2xx_pic_mem_read,
+    pxa2xx_pic_mem_read,
+    pxa2xx_pic_mem_read,
+};
+
+static CPUWriteMemoryFunc *pxa2xx_pic_writefn[] = {
+    pxa2xx_pic_mem_write,
+    pxa2xx_pic_mem_write,
+    pxa2xx_pic_mem_write,
+};
+
+struct pxa2xx_pic_state_s *pxa2xx_pic_init(target_phys_addr_t base,
+                CPUState *env)
+{
+    struct pxa2xx_pic_state_s *s;
+    int iomemtype;
+
+    s = (struct pxa2xx_pic_state_s *)
+            qemu_mallocz(sizeof(struct pxa2xx_pic_state_s));
+    if (!s)
+        return NULL;
+
+    s->cpu_env = env;
+    s->base = base;
+
+    s->int_pending[0] = 0;
+    s->int_pending[1] = 0;
+    s->int_enabled[0] = 0;
+    s->int_enabled[1] = 0;
+    s->is_fiq[0] = 0;
+    s->is_fiq[1] = 0;
+    s->handler = pxa2xx_pic_set_irq;
+
+    /* Enable IC memory-mapped registers access.  */
+    iomemtype = cpu_register_io_memory(0, pxa2xx_pic_readfn,
+                    pxa2xx_pic_writefn, s);
+    cpu_register_physical_memory(base, 0x000fffff, iomemtype);
+
+    /* Enable IC coprocessor access.  */
+    cpu_arm_set_cp_io(env, 6, pxa2xx_pic_cp_read, pxa2xx_pic_cp_write, s);
+
+    return s;
+}
diff --git a/target-arm/cpu.h b/target-arm/cpu.h
index 68bf3fd..738bb3a 100644
--- a/target-arm/cpu.h
+++ b/target-arm/cpu.h
@@ -38,6 +38,10 @@
 #define EXCP_FIQ             6
 #define EXCP_BKPT            7
 
+typedef void ARMWriteCPFunc(void *opaque,
+                int op2, int reg, int crm, uint32_t value);
+typedef uint32_t ARMReadCPFunc(void *opaque, int op2, int reg, int crm);
+
 /* We currently assume float and double are IEEE single and double
    precision respectively.
    Doing runtime conversions is tricky because VFP registers may contain
@@ -75,6 +79,7 @@ typedef struct CPUARMState {
     /* System control coprocessor (cp15) */
     struct {
         uint32_t c0_cpuid;
+        uint32_t c0_cachetype;
         uint32_t c1_sys; /* System control register.  */
         uint32_t c1_coproc; /* Coprocessor access register.  */
         uint32_t c2; /* MMU translation table base.  */
@@ -87,8 +92,16 @@ typedef struct CPUARMState {
         uint32_t c9_data;
         uint32_t c13_fcse; /* FCSE PID.  */
         uint32_t c13_context; /* Context ID.  */
+        uint32_t c15_cpar; /* XScale Coprocessor Access Register */
     } cp15;
 
+    /* Coprocessor IO used by peripherals */
+    struct {
+        ARMReadCPFunc *cp_read;
+        ARMWriteCPFunc *cp_write;
+        void *opaque;
+    } cp[15];
+
     /* Internal CPU feature flags.  */
     uint32_t features;
 
@@ -204,10 +217,10 @@ enum arm_cpu_mode {
 #define ARM_VFP_FPINST  9
 #define ARM_VFP_FPINST2 10
 
-
 enum arm_features {
     ARM_FEATURE_VFP,
-    ARM_FEATURE_AUXCR /* ARM1026 Auxiliary control register.  */
+    ARM_FEATURE_AUXCR,  /* ARM1026 Auxiliary control register.  */
+    ARM_FEATURE_XSCALE, /* Intel XScale extensions.  */
 };
 
 static inline int arm_feature(CPUARMState *env, int feature)
@@ -218,8 +231,24 @@ static inline int arm_feature(CPUARMState *env, int feature)
 void arm_cpu_list(void);
 void cpu_arm_set_model(CPUARMState *env, const char *name);
 
-#define ARM_CPUID_ARM1026 0x4106a262
-#define ARM_CPUID_ARM926  0x41069265
+void cpu_arm_set_cp_io(CPUARMState *env, int cpnum,
+                ARMReadCPFunc *cp_read, ARMWriteCPFunc *cp_write,
+                void *opaque);
+
+#define ARM_CPUID_ARM1026   0x4106a262
+#define ARM_CPUID_ARM926    0x41069265
+#define ARM_CPUID_PXA250    0x69052100
+#define ARM_CPUID_PXA255    0x69052d00
+#define ARM_CPUID_PXA260    0x69052903
+#define ARM_CPUID_PXA261    0x69052d05
+#define ARM_CPUID_PXA262    0x69052d06
+#define ARM_CPUID_PXA270    0x69054110
+#define ARM_CPUID_PXA270_A0 0x69054110
+#define ARM_CPUID_PXA270_A1 0x69054111
+#define ARM_CPUID_PXA270_B0 0x69054112
+#define ARM_CPUID_PXA270_B1 0x69054113
+#define ARM_CPUID_PXA270_C0 0x69054114
+#define ARM_CPUID_PXA270_C5 0x69054117
 
 #if defined(CONFIG_USER_ONLY)
 #define TARGET_PAGE_BITS 12
diff --git a/target-arm/exec.h b/target-arm/exec.h
index deba893..af06f48 100644
--- a/target-arm/exec.h
+++ b/target-arm/exec.h
@@ -60,6 +60,8 @@ int cpu_arm_handle_mmu_fault (CPUState *env, target_ulong address, int rw,
 
 void cpu_lock(void);
 void cpu_unlock(void);
+void helper_set_cp(CPUState *, uint32_t, uint32_t);
+uint32_t helper_get_cp(CPUState *, uint32_t);
 void helper_set_cp15(CPUState *, uint32_t, uint32_t);
 uint32_t helper_get_cp15(CPUState *, uint32_t);
 
diff --git a/target-arm/helper.c b/target-arm/helper.c
index 242dd28..a2eb646 100644
--- a/target-arm/helper.c
+++ b/target-arm/helper.c
@@ -17,11 +17,32 @@ static void cpu_reset_model_id(CPUARMState *env, uint32_t id)
     case ARM_CPUID_ARM926:
         set_feature(env, ARM_FEATURE_VFP);
         env->vfp.xregs[ARM_VFP_FPSID] = 0x41011090;
+        env->cp15.c0_cachetype = 0x1dd20d2;
         break;
     case ARM_CPUID_ARM1026:
         set_feature(env, ARM_FEATURE_VFP);
         set_feature(env, ARM_FEATURE_AUXCR);
         env->vfp.xregs[ARM_VFP_FPSID] = 0x410110a0;
+        env->cp15.c0_cachetype = 0x1dd20d2;
+        break;
+    case ARM_CPUID_PXA250:
+    case ARM_CPUID_PXA255:
+    case ARM_CPUID_PXA260:
+    case ARM_CPUID_PXA261:
+    case ARM_CPUID_PXA262:
+        set_feature(env, ARM_FEATURE_XSCALE);
+        /* JTAG_ID is ((id << 28) | 0x09265013) */
+        env->cp15.c0_cachetype = 0xd172172;
+        break;
+    case ARM_CPUID_PXA270_A0:
+    case ARM_CPUID_PXA270_A1:
+    case ARM_CPUID_PXA270_B0:
+    case ARM_CPUID_PXA270_B1:
+    case ARM_CPUID_PXA270_C0:
+    case ARM_CPUID_PXA270_C5:
+        set_feature(env, ARM_FEATURE_XSCALE);
+        /* JTAG_ID is ((id << 28) | 0x09265013) */
+        env->cp15.c0_cachetype = 0xd172172;
         break;
     default:
         cpu_abort(env, "Bad CPU ID: %x\n", id);
@@ -68,6 +89,18 @@ struct arm_cpu_t {
 static const struct arm_cpu_t arm_cpu_names[] = {
     { ARM_CPUID_ARM926, "arm926"},
     { ARM_CPUID_ARM1026, "arm1026"},
+    { ARM_CPUID_PXA250, "pxa250" },
+    { ARM_CPUID_PXA255, "pxa255" },
+    { ARM_CPUID_PXA260, "pxa260" },
+    { ARM_CPUID_PXA261, "pxa261" },
+    { ARM_CPUID_PXA262, "pxa262" },
+    { ARM_CPUID_PXA270, "pxa270" },
+    { ARM_CPUID_PXA270_A0, "pxa270-a0" },
+    { ARM_CPUID_PXA270_A1, "pxa270-a1" },
+    { ARM_CPUID_PXA270_B0, "pxa270-b0" },
+    { ARM_CPUID_PXA270_B1, "pxa270-b1" },
+    { ARM_CPUID_PXA270_C0, "pxa270-c0" },
+    { ARM_CPUID_PXA270_C5, "pxa270-c5" },
     { 0, NULL}
 };
 
@@ -132,6 +165,19 @@ target_ulong cpu_get_phys_page_debug(CPUState *env, target_ulong addr)
 }
 
 /* These should probably raise undefined insn exceptions.  */
+void helper_set_cp(CPUState *env, uint32_t insn, uint32_t val)
+{
+    int op1 = (insn >> 8) & 0xf;
+    cpu_abort(env, "cp%i insn %08x\n", op1, insn);
+}
+
+uint32_t helper_get_cp(CPUState *env, uint32_t insn)
+{
+    int op1 = (insn >> 8) & 0xf;
+    cpu_abort(env, "cp%i insn %08x\n", op1, insn);
+    return 0;
+}
+
 void helper_set_cp15(CPUState *env, uint32_t insn, uint32_t val)
 {
     cpu_abort(env, "cp15 insn %08x\n", insn);
@@ -370,7 +417,7 @@ static int get_phys_addr(CPUState *env, uint32_t address, int access_type,
             phys_addr = (desc & 0xfff00000) | (address & 0x000fffff);
             ap = (desc >> 10) & 3;
             code = 13;
-        } else {
+        } else if (type == 1) {
             /* Lookup l2 entry.  */
             table = (desc & 0xfffffc00) | ((address >> 10) & 0x3fc);
             desc = ldl_phys(table);
@@ -387,12 +434,16 @@ static int get_phys_addr(CPUState *env, uint32_t address, int access_type,
                 ap = (desc >> (4 + ((address >> 13) & 6))) & 3;
                 break;
             case 3: /* 1k page.  */
-                if (type == 1) {
-                    /* Page translation fault.  */
-                    code = 7;
-                    goto do_fault;
+                if (arm_feature(env, ARM_FEATURE_XSCALE))
+                    phys_addr = (desc & 0xfffff000) | (address & 0xfff);
+                else {
+                    if (type == 1) {
+                        /* Page translation fault.  */
+                        code = 7;
+                        goto do_fault;
+                    }
+                    phys_addr = (desc & 0xfffffc00) | (address & 0x3ff);
                 }
-                phys_addr = (desc & 0xfffffc00) | (address & 0x3ff);
                 ap = (desc >> 4) & 3;
                 break;
             default:
@@ -400,7 +451,11 @@ static int get_phys_addr(CPUState *env, uint32_t address, int access_type,
                 abort();
             }
             code = 15;
+        } else {
+            code = 15;
+            goto do_fault;
         }
+
         *prot = check_ap(env, ap, domain, access_type, is_user);
         if (!*prot) {
             /* Access permission fault.  */
@@ -455,6 +510,41 @@ target_ulong cpu_get_phys_page_debug(CPUState *env, target_ulong addr)
     return phys_addr;
 }
 
+void helper_set_cp(CPUState *env, uint32_t insn, uint32_t val)
+{
+    int op1 = (insn >> 8) & 0xf;
+    int op2 = (insn >> 5) & 7;
+    int reg = (insn >> 16) & 0xf;
+    int crm = insn & 0xf;
+
+    /* On XScale coprocessors [0, 13] have to be enabled in CPAR.  */
+    if (arm_feature(env, ARM_FEATURE_XSCALE)) {
+        if (op1 <= 13 && (env->cp15.c15_cpar & (1 << op1)) == 0)
+            return;	/* Permission Denied */
+    }
+
+    if (env->cp[op1].cp_write)
+        env->cp[op1].cp_write(env->cp[op1].opaque, op2, reg, crm, val);
+}
+
+uint32_t helper_get_cp(CPUState *env, uint32_t insn)
+{
+    int op1 = (insn >> 8) & 0xf;
+    int op2 = (insn >> 5) & 7;
+    int reg = (insn >> 16) & 0xf;
+    int crm = insn & 0xf;
+
+    /* On XScale coprocessors [0, 13] have to be enabled in CPAR.  */
+    if (arm_feature(env, ARM_FEATURE_XSCALE)) {
+        if (op1 <= 13 && (env->cp15.c15_cpar & (1 << op1)) == 0)
+            return 0;	/* Permission Denied */
+    }
+
+    if (env->cp[op1].cp_read)
+        return env->cp[op1].cp_read(env->cp[op1].opaque, op2, reg, crm);
+    return 0;
+}
+
 void helper_set_cp15(CPUState *env, uint32_t insn, uint32_t val)
 {
     uint32_t op2;
@@ -466,15 +556,23 @@ void helper_set_cp15(CPUState *env, uint32_t insn, uint32_t val)
     case 1: /* System configuration.  */
         switch (op2) {
         case 0:
-            env->cp15.c1_sys = val;
+            if (!arm_feature(env, ARM_FEATURE_XSCALE) || (insn & 0xf) == 0)
+                env->cp15.c1_sys = val;
             /* ??? Lots of these bits are not implemented.  */
             /* This may enable/disable the MMU, so do a TLB flush.  */
             tlb_flush(env, 1);
             break;
+        case 1:
+            /* XScale doesn't implement AUX CR (P-Bit) but allows
+             * writing with zero and reading.  */
+            if (arm_feature(env, ARM_FEATURE_XSCALE))
+                break;
+            goto bad_reg;
         case 2:
             env->cp15.c1_coproc = val;
             /* ??? Is this safe when called from within a TB?  */
             tb_flush(env);
+            break;
         default:
             goto bad_reg;
         }
@@ -578,13 +676,21 @@ void helper_set_cp15(CPUState *env, uint32_t insn, uint32_t val)
     case 14: /* Reserved.  */
         goto bad_reg;
     case 15: /* Implementation specific.  */
-        /* ??? Internal registers not implemented.  */
+        if (arm_feature(env, ARM_FEATURE_XSCALE)) {
+            if (op2 == 0 && (insn & 0xf) == 1) {
+                /* Changes cp0 to cp13 behavior, so needs a TB flush.  */
+                tb_flush(env);
+                env->cp15.c15_cpar = (val & 0x3fff) | 2;
+                break;
+            }
+            goto bad_reg;
+        }
         break;
     }
     return;
 bad_reg:
     /* ??? For debugging only.  Should raise illegal instruction exception.  */
-    cpu_abort(env, "Unimplemented cp15 register read\n");
+    cpu_abort(env, "Unimplemented cp15 register write\n");
 }
 
 uint32_t helper_get_cp15(CPUState *env, uint32_t insn)
@@ -598,7 +704,7 @@ uint32_t helper_get_cp15(CPUState *env, uint32_t insn)
         default: /* Device ID.  */
             return env->cp15.c0_cpuid;
         case 1: /* Cache Type.  */
-            return 0x1dd20d2;
+            return env->cp15.c0_cachetype;
         case 2: /* TCM status.  */
             return 0;
         }
@@ -609,6 +715,8 @@ uint32_t helper_get_cp15(CPUState *env, uint32_t insn)
         case 1: /* Auxiliary control register.  */
             if (arm_feature(env, ARM_FEATURE_AUXCR))
                 return 1;
+            if (arm_feature(env, ARM_FEATURE_XSCALE))
+                return 0;
             goto bad_reg;
         case 2: /* Coprocessor access register.  */
             return env->cp15.c1_coproc;
@@ -643,7 +751,7 @@ uint32_t helper_get_cp15(CPUState *env, uint32_t insn)
         }
     case 7: /* Cache control.  */
         /* ??? This is for test, clean and invaidate operations that set the
-           Z flag.  We can't represent N = Z = 1, so it also clears clears
+           Z flag.  We can't represent N = Z = 1, so it also clears
            the N flag.  Oh well.  */
         env->NZF = 0;
         return 0;
@@ -676,7 +784,12 @@ uint32_t helper_get_cp15(CPUState *env, uint32_t insn)
     case 14: /* Reserved.  */
         goto bad_reg;
     case 15: /* Implementation specific.  */
-        /* ??? Internal registers not implemented.  */
+        if (arm_feature(env, ARM_FEATURE_XSCALE)) {
+            if (op2 == 0 && (insn & 0xf) == 1)
+                return env->cp15.c15_cpar;
+
+            goto bad_reg;
+        }
         return 0;
     }
 bad_reg:
@@ -685,4 +798,18 @@ bad_reg:
     return 0;
 }
 
+void cpu_arm_set_cp_io(CPUARMState *env, int cpnum,
+                ARMReadCPFunc *cp_read, ARMWriteCPFunc *cp_write,
+                void *opaque)
+{
+    if (cpnum < 0 || cpnum > 14) {
+        cpu_abort(env, "Bad coprocessor number: %i\n", cpnum);
+        return;
+    }
+
+    env->cp[cpnum].cp_read = cp_read;
+    env->cp[cpnum].cp_write = cp_write;
+    env->cp[cpnum].opaque = opaque;
+}
+
 #endif
diff --git a/target-arm/op.c b/target-arm/op.c
index f17b812..9cfb462 100644
--- a/target-arm/op.c
+++ b/target-arm/op.c
@@ -1142,12 +1142,24 @@ void OPPROTO op_vfp_mdrr(void)
     FT0d = u.d;
 }
 
-/* Copy the most significant bit to T0 to all bits of T1.  */
+/* Copy the most significant bit of T0 to all bits of T1.  */
 void OPPROTO op_signbit_T1_T0(void)
 {
     T1 = (int32_t)T0 >> 31;
 }
 
+void OPPROTO op_movl_cp_T0(void)
+{
+    helper_set_cp(env, PARAM1, T0);
+    FORCE_RET();
+}
+
+void OPPROTO op_movl_T0_cp(void)
+{
+    T0 = helper_get_cp(env, PARAM1);
+    FORCE_RET();
+}
+
 void OPPROTO op_movl_cp15_T0(void)
 {
     helper_set_cp15(env, PARAM1, T0);
diff --git a/target-arm/translate.c b/target-arm/translate.c
index 055ccfa..24e8eee 100644
--- a/target-arm/translate.c
+++ b/target-arm/translate.c
@@ -492,6 +492,29 @@ static inline void gen_mov_vreg_F0(int dp, int reg)
         gen_op_vfp_setreg_F0s(vfp_reg_offset(dp, reg));
 }
 
+/* Disassemble system coprocessor instruction.  Return nonzero if
+   instruction is not defined.  */
+static int disas_cp_insn(DisasContext *s, uint32_t insn)
+{
+    uint32_t rd = (insn >> 12) & 0xf;
+    if (IS_USER(s)) {
+        return 1;
+    }
+
+    if (insn & (1 << 20)) {
+        gen_op_movl_T0_cp(insn);
+        gen_movl_reg_T0(s, rd);
+    } else {
+        gen_op_movl_T0_im((uint32_t) s->pc);
+        gen_op_movl_reg_TN[0][15]();
+        gen_movl_T0_reg(s, rd);
+        gen_op_movl_cp_T0(insn);
+        s->is_jmp = DISAS_UPDATE;
+    }
+
+    return 0;
+}
+
 /* Disassemble system coprocessor (cp15) instruction.  Return nonzero if
    instruction is not defined.  */
 static int disas_cp15_insn(DisasContext *s, uint32_t insn)
@@ -1797,7 +1820,16 @@ static void disas_arm_insn(CPUState * env, DisasContext *s)
         case 0xe:
             /* Coprocessor.  */
             op1 = (insn >> 8) & 0xf;
+            if (arm_feature(env, ARM_FEATURE_XSCALE) &&
+                    ((env->cp15.c15_cpar ^ 0x3fff) & (1 << op1)))
+                goto illegal_op;
             switch (op1) {
+            case 0 ... 1:
+            case 2 ... 9:
+            case 12 ... 14:
+                if (disas_cp_insn(s, insn))
+                    goto illegal_op;
+                break;
             case 10:
             case 11:
                 if (disas_vfp_insn (env, s, insn))
diff --git a/vl.h b/vl.h
index b30fb87..563f437 100644
--- a/vl.h
+++ b/vl.h
@@ -1442,6 +1442,8 @@ typedef void (*gpio_handler_t)(int line, int level, void *opaque);
 
 #include "hw/i2c.h"
 
+#include "hw/pxa.h"
+
 #include "gdbstub.h"
 
 #endif /* defined(QEMU_TOOL) */
-- 
1.4.4.3


             reply	other threads:[~2007-03-16 20:55 UTC|newest]

Thread overview: 3+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2007-03-16 20:54 andrzej zaborowski [this message]
2007-04-16 22:10 ` [Qemu-devel] [PATCH] ARM XScale core features and PXA270 and PXA255 emulation Christian MICHON
2007-04-17 17:52   ` andrzej zaborowski

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=fb249edb0703161354t32b01bd7rb0c7a464aa2f4d74@mail.gmail.com \
    --to=balrog@zabor.org \
    --cc=balrogg@gmail.com \
    --cc=qemu-devel@nongnu.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).