]> git.proxmox.com Git - mirror_qemu.git/commitdiff
Basic OMAP310 support. Basic Palm Tungsten|E machine emulation.
authorbalrog <balrog@c046a42c-6fe2-441c-8c8c-71466251a162>
Sun, 29 Jul 2007 17:57:26 +0000 (17:57 +0000)
committerbalrog <balrog@c046a42c-6fe2-441c-8c8c-71466251a162>
Sun, 29 Jul 2007 17:57:26 +0000 (17:57 +0000)
git-svn-id: svn://svn.savannah.nongnu.org/qemu/trunk@3091 c046a42c-6fe2-441c-8c8c-71466251a162

12 files changed:
Makefile.target
cpu-all.h
hw/omap.c [new file with mode: 0644]
hw/omap.h [new file with mode: 0644]
hw/omap1_clk.c [new file with mode: 0644]
hw/omap_lcd_template.h [new file with mode: 0644]
hw/omap_lcdc.c [new file with mode: 0644]
hw/palm.c [new file with mode: 0644]
target-arm/cpu.h
target-arm/helper.c
vl.c
vl.h

index 18b7949c60b62a571844eb6befc589c9c5184feb..f99573d1dc420b3653c57f8be8b0cece6deaf24e 100644 (file)
@@ -463,6 +463,7 @@ VL_OBJS+= arm-semi.o
 VL_OBJS+= pxa2xx.o pxa2xx_pic.o pxa2xx_gpio.o pxa2xx_timer.o pxa2xx_dma.o
 VL_OBJS+= pxa2xx_lcd.o pxa2xx_mmci.o pxa2xx_pcmcia.o max111x.o max7310.o
 VL_OBJS+= spitz.o ads7846.o ide.o serial.o nand.o $(AUDIODRV) wm8750.o
+VL_OBJS+= omap.o omap_lcdc.o omap1_clk.o palm.o
 CPPFLAGS += -DHAS_AUDIO
 endif
 ifeq ($(TARGET_BASE_ARCH), sh4)
index 289d660f14db11d0b463edaa644f6b1e9b36911f..54d5dc36aead29ee77615801200565efce37fb98 100644 (file)
--- a/cpu-all.h
+++ b/cpu-all.h
@@ -702,7 +702,8 @@ void cpu_dump_statistics (CPUState *env, FILE *f,
                           int flags);
 
 void cpu_abort(CPUState *env, const char *fmt, ...)
-    __attribute__ ((__format__ (__printf__, 2, 3)));
+    __attribute__ ((__format__ (__printf__, 2, 3)))
+    __attribute__ ((__noreturn__));
 extern CPUState *first_cpu;
 extern CPUState *cpu_single_env;
 extern int code_copy_enabled;
diff --git a/hw/omap.c b/hw/omap.c
new file mode 100644 (file)
index 0000000..fe4f9c4
--- /dev/null
+++ b/hw/omap.c
@@ -0,0 +1,2914 @@
+/*
+ * TI OMAP processors emulation.
+ *
+ * Copyright (C) 2006-2007 Andrzej Zaborowski  <balrog@zabor.org>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
+ * MA 02111-1307 USA
+ */
+#include "vl.h"
+#include "arm_pic.h"
+
+/* Should signal the TCMI */
+static uint32_t omap_badwidth_read16(void *opaque, target_phys_addr_t addr)
+{
+    OMAP_16B_REG(addr);
+    return 0;
+}
+
+static void omap_badwidth_write16(void *opaque, target_phys_addr_t addr,
+                uint32_t value)
+{
+    OMAP_16B_REG(addr);
+}
+
+static uint32_t omap_badwidth_read32(void *opaque, target_phys_addr_t addr)
+{
+    OMAP_32B_REG(addr);
+    return 0;
+}
+
+static void omap_badwidth_write32(void *opaque, target_phys_addr_t addr,
+                uint32_t value)
+{
+    OMAP_32B_REG(addr);
+}
+
+#define likely
+#define unlikely
+
+/* Interrupt Handlers */
+struct omap_intr_handler_s {
+    qemu_irq *pins;
+    qemu_irq *parent_pic;
+    target_phys_addr_t base;
+
+    /* state */
+    uint32_t irqs;
+    uint32_t mask;
+    uint32_t sens_edge;
+    uint32_t fiq;
+    int priority[32];
+    uint32_t new_irq_agr;
+    uint32_t new_fiq_agr;
+    int sir_irq;
+    int sir_fiq;
+    int stats[32];
+};
+
+static void omap_inth_update(struct omap_intr_handler_s *s)
+{
+    uint32_t irq = s->new_irq_agr & s->irqs & ~s->mask & ~s->fiq;
+    uint32_t fiq = s->new_fiq_agr & s->irqs & ~s->mask & s->fiq;
+
+    qemu_set_irq(s->parent_pic[ARM_PIC_CPU_IRQ], irq);
+    if (irq)
+        s->new_irq_agr = 0;
+
+    qemu_set_irq(s->parent_pic[ARM_PIC_CPU_FIQ], fiq);
+    if (fiq)
+        s->new_fiq_agr = 0;
+}
+
+static void omap_inth_sir_update(struct omap_intr_handler_s *s)
+{
+    int i, intr_irq, intr_fiq, p_irq, p_fiq, p, f;
+    uint32_t level = s->irqs & ~s->mask;
+
+    intr_irq = 0;
+    intr_fiq = 0;
+    p_irq = -1;
+    p_fiq = -1;
+    /* Find the interrupt line with the highest dynamic priority */
+    for (f = ffs(level), i = f - 1, level >>= f - 1; f; i += f, level >>= f) {
+        p = s->priority[i];
+        if (s->fiq & (1 << i)) {
+            if (p > p_fiq) {
+                p_fiq = p;
+                intr_fiq = i;
+            }
+        } else {
+            if (p > p_irq) {
+                p_irq = p;
+                intr_irq = i;
+            }
+        }
+
+        f = ffs(level >> 1);
+    }
+
+    s->sir_irq = intr_irq;
+    s->sir_fiq = intr_fiq;
+}
+
+#define INT_FALLING_EDGE       0
+#define INT_LOW_LEVEL          1
+
+static void omap_set_intr(void *opaque, int irq, int req)
+{
+    struct omap_intr_handler_s *ih = (struct omap_intr_handler_s *) opaque;
+    uint32_t rise;
+
+    if (req) {
+        rise = ~ih->irqs & (1 << irq);
+        ih->irqs |= rise;
+        ih->stats[irq] ++;
+    } else {
+        rise = ih->sens_edge & ih->irqs & (1 << irq);
+        ih->irqs &= ~rise;
+    }
+
+    if (rise & ~ih->mask) {
+        omap_inth_sir_update(ih);
+
+        omap_inth_update(ih);
+    }
+}
+
+static uint32_t omap_inth_read(void *opaque, target_phys_addr_t addr)
+{
+    struct omap_intr_handler_s *s = (struct omap_intr_handler_s *) opaque;
+    int i, offset = addr - s->base;
+
+    switch (offset) {
+    case 0x00: /* ITR */
+        return s->irqs;
+
+    case 0x04: /* MIR */
+        return s->mask;
+
+    case 0x10: /* SIR_IRQ_CODE */
+        i = s->sir_irq;
+        if (((s->sens_edge >> i) & 1) == INT_FALLING_EDGE && i) {
+            s->irqs &= ~(1 << i);
+            omap_inth_sir_update(s);
+            omap_inth_update(s);
+        }
+        return i;
+
+    case 0x14: /* SIR_FIQ_CODE */
+        i = s->sir_fiq;
+        if (((s->sens_edge >> i) & 1) == INT_FALLING_EDGE && i) {
+            s->irqs &= ~(1 << i);
+            omap_inth_sir_update(s);
+            omap_inth_update(s);
+        }
+        return i;
+
+    case 0x18: /* CONTROL_REG */
+        return 0;
+
+    case 0x1c: /* ILR0 */
+    case 0x20: /* ILR1 */
+    case 0x24: /* ILR2 */
+    case 0x28: /* ILR3 */
+    case 0x2c: /* ILR4 */
+    case 0x30: /* ILR5 */
+    case 0x34: /* ILR6 */
+    case 0x38: /* ILR7 */
+    case 0x3c: /* ILR8 */
+    case 0x40: /* ILR9 */
+    case 0x44: /* ILR10 */
+    case 0x48: /* ILR11 */
+    case 0x4c: /* ILR12 */
+    case 0x50: /* ILR13 */
+    case 0x54: /* ILR14 */
+    case 0x58: /* ILR15 */
+    case 0x5c: /* ILR16 */
+    case 0x60: /* ILR17 */
+    case 0x64: /* ILR18 */
+    case 0x68: /* ILR19 */
+    case 0x6c: /* ILR20 */
+    case 0x70: /* ILR21 */
+    case 0x74: /* ILR22 */
+    case 0x78: /* ILR23 */
+    case 0x7c: /* ILR24 */
+    case 0x80: /* ILR25 */
+    case 0x84: /* ILR26 */
+    case 0x88: /* ILR27 */
+    case 0x8c: /* ILR28 */
+    case 0x90: /* ILR29 */
+    case 0x94: /* ILR30 */
+    case 0x98: /* ILR31 */
+        i = (offset - 0x1c) >> 2;
+        return (s->priority[i] << 2) |
+                (((s->sens_edge >> i) & 1) << 1) |
+                ((s->fiq >> i) & 1);
+
+    case 0x9c: /* ISR */
+        return 0x00000000;
+
+    default:
+        OMAP_BAD_REG(addr);
+        break;
+    }
+    return 0;
+}
+
+static void omap_inth_write(void *opaque, target_phys_addr_t addr,
+                uint32_t value)
+{
+    struct omap_intr_handler_s *s = (struct omap_intr_handler_s *) opaque;
+    int i, offset = addr - s->base;
+
+    switch (offset) {
+    case 0x00: /* ITR */
+        s->irqs &= value;
+        omap_inth_sir_update(s);
+        omap_inth_update(s);
+        return;
+
+    case 0x04: /* MIR */
+        s->mask = value;
+        omap_inth_sir_update(s);
+        omap_inth_update(s);
+        return;
+
+    case 0x10: /* SIR_IRQ_CODE */
+    case 0x14: /* SIR_FIQ_CODE */
+        OMAP_RO_REG(addr);
+        break;
+
+    case 0x18: /* CONTROL_REG */
+        if (value & 2)
+            s->new_fiq_agr = ~0;
+        if (value & 1)
+            s->new_irq_agr = ~0;
+        omap_inth_update(s);
+        return;
+
+    case 0x1c: /* ILR0 */
+    case 0x20: /* ILR1 */
+    case 0x24: /* ILR2 */
+    case 0x28: /* ILR3 */
+    case 0x2c: /* ILR4 */
+    case 0x30: /* ILR5 */
+    case 0x34: /* ILR6 */
+    case 0x38: /* ILR7 */
+    case 0x3c: /* ILR8 */
+    case 0x40: /* ILR9 */
+    case 0x44: /* ILR10 */
+    case 0x48: /* ILR11 */
+    case 0x4c: /* ILR12 */
+    case 0x50: /* ILR13 */
+    case 0x54: /* ILR14 */
+    case 0x58: /* ILR15 */
+    case 0x5c: /* ILR16 */
+    case 0x60: /* ILR17 */
+    case 0x64: /* ILR18 */
+    case 0x68: /* ILR19 */
+    case 0x6c: /* ILR20 */
+    case 0x70: /* ILR21 */
+    case 0x74: /* ILR22 */
+    case 0x78: /* ILR23 */
+    case 0x7c: /* ILR24 */
+    case 0x80: /* ILR25 */
+    case 0x84: /* ILR26 */
+    case 0x88: /* ILR27 */
+    case 0x8c: /* ILR28 */
+    case 0x90: /* ILR29 */
+    case 0x94: /* ILR30 */
+    case 0x98: /* ILR31 */
+        i = (offset - 0x1c) >> 2;
+        s->priority[i] = (value >> 2) & 0x1f;
+        s->sens_edge &= ~(1 << i);
+        s->sens_edge |= ((value >> 1) & 1) << i;
+        s->fiq &= ~(1 << i);
+        s->fiq |= (value & 1) << i;
+        return;
+
+    case 0x9c: /* ISR */
+        for (i = 0; i < 32; i ++)
+            if (value & (1 << i)) {
+                omap_set_intr(s, i, 1);
+                return;
+            }
+        return;
+
+    default:
+        OMAP_BAD_REG(addr);
+    }
+}
+
+static CPUReadMemoryFunc *omap_inth_readfn[] = {
+    omap_badwidth_read32,
+    omap_badwidth_read32,
+    omap_inth_read,
+};
+
+static CPUWriteMemoryFunc *omap_inth_writefn[] = {
+    omap_inth_write,
+    omap_inth_write,
+    omap_inth_write,
+};
+
+static void omap_inth_reset(struct omap_intr_handler_s *s)
+{
+    s->irqs = 0x00000000;
+    s->mask = 0xffffffff;
+    s->sens_edge = 0x00000000;
+    s->fiq = 0x00000000;
+    memset(s->priority, 0, sizeof(s->priority));
+    s->new_irq_agr = ~0;
+    s->new_fiq_agr = ~0;
+    s->sir_irq = 0;
+    s->sir_fiq = 0;
+
+    omap_inth_update(s);
+}
+
+struct omap_intr_handler_s *omap_inth_init(target_phys_addr_t base,
+                unsigned long size, qemu_irq parent[2], omap_clk clk)
+{
+    int iomemtype;
+    struct omap_intr_handler_s *s = (struct omap_intr_handler_s *)
+            qemu_mallocz(sizeof(struct omap_intr_handler_s));
+
+    s->parent_pic = parent;
+    s->base = base;
+    s->pins = qemu_allocate_irqs(omap_set_intr, s, 32);
+    omap_inth_reset(s);
+
+    iomemtype = cpu_register_io_memory(0, omap_inth_readfn,
+                    omap_inth_writefn, s);
+    cpu_register_physical_memory(s->base, size, iomemtype);
+
+    return s;
+}
+
+/* OMAP1 DMA module */
+typedef enum {
+    constant = 0,
+    post_incremented,
+    single_index,
+    double_index,
+} omap_dma_addressing_t;
+
+struct omap_dma_channel_s {
+    int burst[2];
+    int pack[2];
+    enum omap_dma_port port[2];
+    target_phys_addr_t addr[2];
+    omap_dma_addressing_t mode[2];
+    int data_type;
+    int end_prog;
+    int repeat;
+    int auto_init;
+    int priority;
+    int fs;
+    int sync;
+    int running;
+    int interrupts;
+    int status;
+    int signalled;
+    int post_sync;
+    int transfer;
+    uint16_t elements;
+    uint16_t frames;
+    uint16_t frame_index;
+    uint16_t element_index;
+    uint16_t cpc;
+
+    struct omap_dma_reg_set_s {
+        target_phys_addr_t src, dest;
+        int frame;
+        int element;
+        int frame_delta[2];
+        int elem_delta[2];
+        int frames;
+        int elements;
+    } active_set;
+};
+
+struct omap_dma_s {
+    qemu_irq *ih;
+    QEMUTimer *tm;
+    struct omap_mpu_state_s *mpu;
+    target_phys_addr_t base;
+    omap_clk clk;
+    int64_t delay;
+
+    uint16_t gcr;
+    int run_count;
+
+    int chans;
+    struct omap_dma_channel_s ch[16];
+    struct omap_dma_lcd_channel_s lcd_ch;
+};
+
+static void omap_dma_interrupts_update(struct omap_dma_s *s)
+{
+    /* First three interrupts are shared between two channels each.  */
+    qemu_set_irq(s->ih[OMAP_INT_DMA_CH0_6],
+                    (s->ch[0].status | s->ch[6].status) & 0x3f);
+    qemu_set_irq(s->ih[OMAP_INT_DMA_CH1_7],
+                    (s->ch[1].status | s->ch[7].status) & 0x3f);
+    qemu_set_irq(s->ih[OMAP_INT_DMA_CH2_8],
+                    (s->ch[2].status | s->ch[8].status) & 0x3f);
+    qemu_set_irq(s->ih[OMAP_INT_DMA_CH3],
+                    (s->ch[3].status) & 0x3f);
+    qemu_set_irq(s->ih[OMAP_INT_DMA_CH4],
+                    (s->ch[4].status) & 0x3f);
+    qemu_set_irq(s->ih[OMAP_INT_DMA_CH5],
+                    (s->ch[5].status) & 0x3f);
+}
+
+static void omap_dma_channel_load(struct omap_dma_s *s, int ch)
+{
+    struct omap_dma_reg_set_s *a = &s->ch[ch].active_set;
+    int i;
+
+    /*
+     * TODO: verify address ranges and alignment
+     * TODO: port endianness
+     */
+
+    a->src = s->ch[ch].addr[0];
+    a->dest = s->ch[ch].addr[1];
+    a->frames = s->ch[ch].frames;
+    a->elements = s->ch[ch].elements;
+    a->frame = 0;
+    a->element = 0;
+
+    if (unlikely(!s->ch[ch].elements || !s->ch[ch].frames)) {
+        printf("%s: bad DMA request\n", __FUNCTION__);
+        return;
+    }
+
+    for (i = 0; i < 2; i ++)
+        switch (s->ch[ch].mode[i]) {
+        case constant:
+            a->elem_delta[i] = 0;
+            a->frame_delta[i] = 0;
+            break;
+        case post_incremented:
+            a->elem_delta[i] = s->ch[ch].data_type;
+            a->frame_delta[i] = 0;
+            break;
+        case single_index:
+            a->elem_delta[i] = s->ch[ch].data_type +
+                s->ch[ch].element_index - 1;
+            if (s->ch[ch].element_index > 0x7fff)
+                a->elem_delta[i] -= 0x10000;
+            a->frame_delta[i] = 0;
+            break;
+        case double_index:
+            a->elem_delta[i] = s->ch[ch].data_type +
+                s->ch[ch].element_index - 1;
+            if (s->ch[ch].element_index > 0x7fff)
+                a->elem_delta[i] -= 0x10000;
+            a->frame_delta[i] = s->ch[ch].frame_index -
+                s->ch[ch].element_index;
+            if (s->ch[ch].frame_index > 0x7fff)
+                a->frame_delta[i] -= 0x10000;
+            break;
+        default:
+            break;
+        }
+}
+
+static inline void omap_dma_request_run(struct omap_dma_s *s,
+                int channel, int request)
+{
+next_channel:
+    if (request > 0)
+        for (; channel < 9; channel ++)
+            if (s->ch[channel].sync == request && s->ch[channel].running)
+                break;
+    if (channel >= 9)
+        return;
+
+    if (s->ch[channel].transfer) {
+        if (request > 0) {
+            s->ch[channel ++].post_sync = request;
+            goto next_channel;
+        }
+        s->ch[channel].status |= 0x02; /* Synchronisation drop */
+        omap_dma_interrupts_update(s);
+        return;
+    }
+
+    if (!s->ch[channel].signalled)
+        s->run_count ++;
+    s->ch[channel].signalled = 1;
+
+    if (request > 0)
+        s->ch[channel].status |= 0x40; /* External request */
+
+    if (s->delay)
+        qemu_mod_timer(s->tm, qemu_get_clock(vm_clock) + s->delay);
+
+    if (request > 0) {
+        channel ++;
+        goto next_channel;
+    }
+}
+
+static inline void omap_dma_request_stop(struct omap_dma_s *s, int channel)
+{
+    if (s->ch[channel].signalled)
+        s->run_count --;
+    s->ch[channel].signalled = 0;
+
+    if (!s->run_count)
+        qemu_del_timer(s->tm);
+}
+
+static void omap_dma_channel_run(struct omap_dma_s *s)
+{
+    int ch;
+    uint16_t status;
+    uint8_t value[4];
+    struct omap_dma_port_if_s *src_p, *dest_p;
+    struct omap_dma_reg_set_s *a;
+
+    for (ch = 0; ch < 9; ch ++) {
+        a = &s->ch[ch].active_set;
+
+        src_p = &s->mpu->port[s->ch[ch].port[0]];
+        dest_p = &s->mpu->port[s->ch[ch].port[1]];
+        if (s->ch[ch].signalled && (!src_p->addr_valid(s->mpu, a->src) ||
+                    !dest_p->addr_valid(s->mpu, a->dest))) {
+#if 0
+            /* Bus time-out */
+            if (s->ch[ch].interrupts & 0x01)
+                s->ch[ch].status |= 0x01;
+            omap_dma_request_stop(s, ch);
+            continue;
+#endif
+            printf("%s: Bus time-out in DMA%i operation\n", __FUNCTION__, ch);
+        }
+
+        status = s->ch[ch].status;
+        while (status == s->ch[ch].status && s->ch[ch].signalled) {
+            /* Transfer a single element */
+            s->ch[ch].transfer = 1;
+            cpu_physical_memory_read(a->src, value, s->ch[ch].data_type);
+            cpu_physical_memory_write(a->dest, value, s->ch[ch].data_type);
+            s->ch[ch].transfer = 0;
+
+            a->src += a->elem_delta[0];
+            a->dest += a->elem_delta[1];
+            a->element ++;
+
+            /* Check interrupt conditions */
+            if (a->element == a->elements) {
+                a->element = 0;
+                a->src += a->frame_delta[0];
+                a->dest += a->frame_delta[1];
+                a->frame ++;
+
+                if (a->frame == a->frames) {
+                    if (!s->ch[ch].repeat || !s->ch[ch].auto_init)
+                        s->ch[ch].running = 0;
+
+                    if (s->ch[ch].auto_init &&
+                            (s->ch[ch].repeat ||
+                             s->ch[ch].end_prog))
+                        omap_dma_channel_load(s, ch);
+
+                    if (s->ch[ch].interrupts & 0x20)
+                        s->ch[ch].status |= 0x20;
+
+                    if (!s->ch[ch].sync)
+                        omap_dma_request_stop(s, ch);
+                }
+
+                if (s->ch[ch].interrupts & 0x08)
+                    s->ch[ch].status |= 0x08;
+
+                if (s->ch[ch].sync && s->ch[ch].fs) {
+                    s->ch[ch].status &= ~0x40;
+                    omap_dma_request_stop(s, ch);
+                }
+            }
+
+            if (a->element == 1 && a->frame == a->frames - 1)
+                if (s->ch[ch].interrupts & 0x10)
+                    s->ch[ch].status |= 0x10;
+
+            if (a->element == (a->elements >> 1))
+                if (s->ch[ch].interrupts & 0x04)
+                    s->ch[ch].status |= 0x04;
+
+            if (s->ch[ch].sync && !s->ch[ch].fs) {
+                s->ch[ch].status &= ~0x40;
+                omap_dma_request_stop(s, ch);
+            }
+
+            /*
+             * Process requests made while the element was
+             * being transferred.
+             */
+            if (s->ch[ch].post_sync) {
+                omap_dma_request_run(s, 0, s->ch[ch].post_sync);
+                s->ch[ch].post_sync = 0;
+            }
+
+#if 0
+            break;
+#endif
+        }
+
+        s->ch[ch].cpc = a->dest & 0x0000ffff;
+    }
+
+    omap_dma_interrupts_update(s);
+    if (s->run_count && s->delay)
+        qemu_mod_timer(s->tm, qemu_get_clock(vm_clock) + s->delay);
+}
+
+static int omap_dma_ch_reg_read(struct omap_dma_s *s,
+                int ch, int reg, uint16_t *value) {
+    switch (reg) {
+    case 0x00: /* SYS_DMA_CSDP_CH0 */
+        *value = (s->ch[ch].burst[1] << 14) |
+                (s->ch[ch].pack[1] << 13) |
+                (s->ch[ch].port[1] << 9) |
+                (s->ch[ch].burst[0] << 7) |
+                (s->ch[ch].pack[0] << 6) |
+                (s->ch[ch].port[0] << 2) |
+                (s->ch[ch].data_type >> 1);
+        break;
+
+    case 0x02: /* SYS_DMA_CCR_CH0 */
+        *value = (s->ch[ch].mode[1] << 14) |
+                (s->ch[ch].mode[0] << 12) |
+                (s->ch[ch].end_prog << 11) |
+                (s->ch[ch].repeat << 9) |
+                (s->ch[ch].auto_init << 8) |
+                (s->ch[ch].running << 7) |
+                (s->ch[ch].priority << 6) |
+                (s->ch[ch].fs << 5) | s->ch[ch].sync;
+        break;
+
+    case 0x04: /* SYS_DMA_CICR_CH0 */
+        *value = s->ch[ch].interrupts;
+        break;
+
+    case 0x06: /* SYS_DMA_CSR_CH0 */
+        /* FIXME: shared CSR for channels sharing the interrupts */
+        *value = s->ch[ch].status;
+        s->ch[ch].status &= 0x40;
+        omap_dma_interrupts_update(s);
+        break;
+
+    case 0x08: /* SYS_DMA_CSSA_L_CH0 */
+        *value = s->ch[ch].addr[0] & 0x0000ffff;
+        break;
+
+    case 0x0a: /* SYS_DMA_CSSA_U_CH0 */
+        *value = s->ch[ch].addr[0] >> 16;
+        break;
+
+    case 0x0c: /* SYS_DMA_CDSA_L_CH0 */
+        *value = s->ch[ch].addr[1] & 0x0000ffff;
+        break;
+
+    case 0x0e: /* SYS_DMA_CDSA_U_CH0 */
+        *value = s->ch[ch].addr[1] >> 16;
+        break;
+
+    case 0x10: /* SYS_DMA_CEN_CH0 */
+        *value = s->ch[ch].elements;
+        break;
+
+    case 0x12: /* SYS_DMA_CFN_CH0 */
+        *value = s->ch[ch].frames;
+        break;
+
+    case 0x14: /* SYS_DMA_CFI_CH0 */
+        *value = s->ch[ch].frame_index;
+        break;
+
+    case 0x16: /* SYS_DMA_CEI_CH0 */
+        *value = s->ch[ch].element_index;
+        break;
+
+    case 0x18: /* SYS_DMA_CPC_CH0 */
+        *value = s->ch[ch].cpc;
+        break;
+
+    default:
+        return 1;
+    }
+    return 0;
+}
+
+static int omap_dma_ch_reg_write(struct omap_dma_s *s,
+                int ch, int reg, uint16_t value) {
+    switch (reg) {
+    case 0x00: /* SYS_DMA_CSDP_CH0 */
+        s->ch[ch].burst[1] = (value & 0xc000) >> 14;
+        s->ch[ch].pack[1] = (value & 0x2000) >> 13;
+        s->ch[ch].port[1] = (enum omap_dma_port) ((value & 0x1e00) >> 9);
+        s->ch[ch].burst[0] = (value & 0x0180) >> 7;
+        s->ch[ch].pack[0] = (value & 0x0040) >> 6;
+        s->ch[ch].port[0] = (enum omap_dma_port) ((value & 0x003c) >> 2);
+        s->ch[ch].data_type = (1 << (value & 3));
+        if (s->ch[ch].port[0] >= omap_dma_port_last)
+            printf("%s: invalid DMA port %i\n", __FUNCTION__,
+                            s->ch[ch].port[0]);
+        if (s->ch[ch].port[1] >= omap_dma_port_last)
+            printf("%s: invalid DMA port %i\n", __FUNCTION__,
+                            s->ch[ch].port[1]);
+        if ((value & 3) == 3)
+            printf("%s: bad data_type for DMA channel %i\n", __FUNCTION__, ch);
+        break;
+
+    case 0x02: /* SYS_DMA_CCR_CH0 */
+        s->ch[ch].mode[1] = (omap_dma_addressing_t) ((value & 0xc000) >> 14);
+        s->ch[ch].mode[0] = (omap_dma_addressing_t) ((value & 0x3000) >> 12);
+        s->ch[ch].end_prog = (value & 0x0800) >> 11;
+        s->ch[ch].repeat = (value & 0x0200) >> 9;
+        s->ch[ch].auto_init = (value & 0x0100) >> 8;
+        s->ch[ch].priority = (value & 0x0040) >> 6;
+        s->ch[ch].fs = (value & 0x0020) >> 5;
+        s->ch[ch].sync = value & 0x001f;
+        if (value & 0x0080) {
+            if (s->ch[ch].running) {
+                if (!s->ch[ch].signalled &&
+                                s->ch[ch].auto_init && s->ch[ch].end_prog)
+                    omap_dma_channel_load(s, ch);
+            } else {
+                s->ch[ch].running = 1;
+                omap_dma_channel_load(s, ch);
+            }
+            if (!s->ch[ch].sync)
+                omap_dma_request_run(s, ch, 0);
+        } else {
+            s->ch[ch].running = 0;
+            omap_dma_request_stop(s, ch);
+        }
+        break;
+
+    case 0x04: /* SYS_DMA_CICR_CH0 */
+        s->ch[ch].interrupts = value & 0x003f;
+        break;
+
+    case 0x06: /* SYS_DMA_CSR_CH0 */
+        return 1;
+
+    case 0x08: /* SYS_DMA_CSSA_L_CH0 */
+        s->ch[ch].addr[0] &= 0xffff0000;
+        s->ch[ch].addr[0] |= value;
+        break;
+
+    case 0x0a: /* SYS_DMA_CSSA_U_CH0 */
+        s->ch[ch].addr[0] &= 0x0000ffff;
+        s->ch[ch].addr[0] |= value << 16;
+        break;
+
+    case 0x0c: /* SYS_DMA_CDSA_L_CH0 */
+        s->ch[ch].addr[1] &= 0xffff0000;
+        s->ch[ch].addr[1] |= value;
+        break;
+
+    case 0x0e: /* SYS_DMA_CDSA_U_CH0 */
+        s->ch[ch].addr[1] &= 0x0000ffff;
+        s->ch[ch].addr[1] |= value << 16;
+        break;
+
+    case 0x10: /* SYS_DMA_CEN_CH0 */
+        s->ch[ch].elements = value & 0xffff;
+        break;
+
+    case 0x12: /* SYS_DMA_CFN_CH0 */
+        s->ch[ch].frames = value & 0xffff;
+        break;
+
+    case 0x14: /* SYS_DMA_CFI_CH0 */
+        s->ch[ch].frame_index = value & 0xffff;
+        break;
+
+    case 0x16: /* SYS_DMA_CEI_CH0 */
+        s->ch[ch].element_index = value & 0xffff;
+        break;
+
+    case 0x18: /* SYS_DMA_CPC_CH0 */
+        return 1;
+
+    default:
+        OMAP_BAD_REG((unsigned long) reg);
+    }
+    return 0;
+}
+
+static uint32_t omap_dma_read(void *opaque, target_phys_addr_t addr)
+{
+    struct omap_dma_s *s = (struct omap_dma_s *) opaque;
+    int i, reg, ch, offset = addr - s->base;
+    uint16_t ret;
+
+    switch (offset) {
+    case 0x000 ... 0x2fe:
+        reg = offset & 0x3f;
+        ch = (offset >> 6) & 0x0f;
+        if (omap_dma_ch_reg_read(s, ch, reg, &ret))
+            break;
+        return ret;
+
+    case 0x300:        /* SYS_DMA_LCD_CTRL */
+        i = s->lcd_ch.condition;
+        s->lcd_ch.condition = 0;
+        qemu_irq_lower(s->lcd_ch.irq);
+        return ((s->lcd_ch.src == imif) << 6) | (i << 3) |
+                (s->lcd_ch.interrupts << 1) | s->lcd_ch.dual;
+
+    case 0x302:        /* SYS_DMA_LCD_TOP_F1_L */
+        return s->lcd_ch.src_f1_top & 0xffff;
+
+    case 0x304:        /* SYS_DMA_LCD_TOP_F1_U */
+        return s->lcd_ch.src_f1_top >> 16;
+
+    case 0x306:        /* SYS_DMA_LCD_BOT_F1_L */
+        return s->lcd_ch.src_f1_bottom & 0xffff;
+
+    case 0x308:        /* SYS_DMA_LCD_BOT_F1_U */
+        return s->lcd_ch.src_f1_bottom >> 16;
+
+    case 0x30a:        /* SYS_DMA_LCD_TOP_F2_L */
+        return s->lcd_ch.src_f2_top & 0xffff;
+
+    case 0x30c:        /* SYS_DMA_LCD_TOP_F2_U */
+        return s->lcd_ch.src_f2_top >> 16;
+
+    case 0x30e:        /* SYS_DMA_LCD_BOT_F2_L */
+        return s->lcd_ch.src_f2_bottom & 0xffff;
+
+    case 0x310:        /* SYS_DMA_LCD_BOT_F2_U */
+        return s->lcd_ch.src_f2_bottom >> 16;
+
+    case 0x400:        /* SYS_DMA_GCR */
+        return s->gcr;
+    }
+
+    OMAP_BAD_REG(addr);
+    return 0;
+}
+
+static void omap_dma_write(void *opaque, target_phys_addr_t addr,
+                uint32_t value)
+{
+    struct omap_dma_s *s = (struct omap_dma_s *) opaque;
+    int reg, ch, offset = addr - s->base;
+
+    switch (offset) {
+    case 0x000 ... 0x2fe:
+        reg = offset & 0x3f;
+        ch = (offset >> 6) & 0x0f;
+        if (omap_dma_ch_reg_write(s, ch, reg, value))
+            OMAP_RO_REG(addr);
+        break;
+
+    case 0x300:        /* SYS_DMA_LCD_CTRL */
+        s->lcd_ch.src = (value & 0x40) ? imif : emiff;
+        s->lcd_ch.condition = 0;
+        /* Assume no bus errors and thus no BUS_ERROR irq bits.  */
+        s->lcd_ch.interrupts = (value >> 1) & 1;
+        s->lcd_ch.dual = value & 1;
+        break;
+
+    case 0x302:        /* SYS_DMA_LCD_TOP_F1_L */
+        s->lcd_ch.src_f1_top &= 0xffff0000;
+        s->lcd_ch.src_f1_top |= 0x0000ffff & value;
+        break;
+
+    case 0x304:        /* SYS_DMA_LCD_TOP_F1_U */
+        s->lcd_ch.src_f1_top &= 0x0000ffff;
+        s->lcd_ch.src_f1_top |= value << 16;
+        break;
+
+    case 0x306:        /* SYS_DMA_LCD_BOT_F1_L */
+        s->lcd_ch.src_f1_bottom &= 0xffff0000;
+        s->lcd_ch.src_f1_bottom |= 0x0000ffff & value;
+        break;
+
+    case 0x308:        /* SYS_DMA_LCD_BOT_F1_U */
+        s->lcd_ch.src_f1_bottom &= 0x0000ffff;
+        s->lcd_ch.src_f1_bottom |= value << 16;
+        break;
+
+    case 0x30a:        /* SYS_DMA_LCD_TOP_F2_L */
+        s->lcd_ch.src_f2_top &= 0xffff0000;
+        s->lcd_ch.src_f2_top |= 0x0000ffff & value;
+        break;
+
+    case 0x30c:        /* SYS_DMA_LCD_TOP_F2_U */
+        s->lcd_ch.src_f2_top &= 0x0000ffff;
+        s->lcd_ch.src_f2_top |= value << 16;
+        break;
+
+    case 0x30e:        /* SYS_DMA_LCD_BOT_F2_L */
+        s->lcd_ch.src_f2_bottom &= 0xffff0000;
+        s->lcd_ch.src_f2_bottom |= 0x0000ffff & value;
+        break;
+
+    case 0x310:        /* SYS_DMA_LCD_BOT_F2_U */
+        s->lcd_ch.src_f2_bottom &= 0x0000ffff;
+        s->lcd_ch.src_f2_bottom |= value << 16;
+        break;
+
+    case 0x400:        /* SYS_DMA_GCR */
+        s->gcr = value & 0x000c;
+        break;
+
+    default:
+        OMAP_BAD_REG(addr);
+    }
+}
+
+static CPUReadMemoryFunc *omap_dma_readfn[] = {
+    omap_badwidth_read16,
+    omap_dma_read,
+    omap_badwidth_read16,
+};
+
+static CPUWriteMemoryFunc *omap_dma_writefn[] = {
+    omap_badwidth_write16,
+    omap_dma_write,
+    omap_badwidth_write16,
+};
+
+static void omap_dma_request(void *opaque, int drq, int req)
+{
+    struct omap_dma_s *s = (struct omap_dma_s *) opaque;
+    /* All the request pins are edge triggered.  */
+    if (req)
+        omap_dma_request_run(s, 0, drq);
+}
+
+static void omap_dma_clk_update(void *opaque, int line, int on)
+{
+    struct omap_dma_s *s = (struct omap_dma_s *) opaque;
+
+    if (on) {
+        s->delay = ticks_per_sec >> 5;
+        if (s->run_count)
+            qemu_mod_timer(s->tm, qemu_get_clock(vm_clock) + s->delay);
+    } else {
+        s->delay = 0;
+        qemu_del_timer(s->tm);
+    }
+}
+
+static void omap_dma_reset(struct omap_dma_s *s)
+{
+    int i;
+
+    qemu_del_timer(s->tm);
+    s->gcr = 0x0004;
+    s->run_count = 0;
+    s->lcd_ch.src = emiff;
+    s->lcd_ch.condition = 0;
+    s->lcd_ch.interrupts = 0;
+    s->lcd_ch.dual = 0;
+    memset(s->ch, 0, sizeof(s->ch));
+    for (i = 0; i < s->chans; i ++)
+        s->ch[i].interrupts = 0x0003;
+}
+
+struct omap_dma_s *omap_dma_init(target_phys_addr_t base,
+                qemu_irq pic[], struct omap_mpu_state_s *mpu, omap_clk clk)
+{
+    int iomemtype;
+    struct omap_dma_s *s = (struct omap_dma_s *)
+            qemu_mallocz(sizeof(struct omap_dma_s));
+
+    s->ih = pic;
+    s->base = base;
+    s->chans = 9;
+    s->mpu = mpu;
+    s->clk = clk;
+    s->lcd_ch.irq = pic[OMAP_INT_DMA_LCD];
+    s->lcd_ch.mpu = mpu;
+    s->tm = qemu_new_timer(vm_clock, (QEMUTimerCB *) omap_dma_channel_run, s);
+    omap_clk_adduser(s->clk, qemu_allocate_irqs(omap_dma_clk_update, s, 1)[0]);
+    mpu->drq = qemu_allocate_irqs(omap_dma_request, s, 32);
+    omap_dma_reset(s);
+
+    iomemtype = cpu_register_io_memory(0, omap_dma_readfn,
+                    omap_dma_writefn, s);
+    cpu_register_physical_memory(s->base, 0x800, iomemtype);
+
+    return s;
+}
+
+/* DMA ports */
+int omap_validate_emiff_addr(struct omap_mpu_state_s *s,
+                target_phys_addr_t addr)
+{
+    return addr >= OMAP_EMIFF_BASE && addr < OMAP_EMIFF_BASE + s->sdram_size;
+}
+
+int omap_validate_emifs_addr(struct omap_mpu_state_s *s,
+                target_phys_addr_t addr)
+{
+    return addr >= OMAP_EMIFS_BASE && addr < OMAP_EMIFF_BASE;
+}
+
+int omap_validate_imif_addr(struct omap_mpu_state_s *s,
+                target_phys_addr_t addr)
+{
+    return addr >= OMAP_IMIF_BASE && addr < OMAP_IMIF_BASE + s->sram_size;
+}
+
+int omap_validate_tipb_addr(struct omap_mpu_state_s *s,
+                target_phys_addr_t addr)
+{
+    return addr >= 0xfffb0000 && addr < 0xffff0000;
+}
+
+int omap_validate_local_addr(struct omap_mpu_state_s *s,
+                target_phys_addr_t addr)
+{
+    return addr >= OMAP_LOCALBUS_BASE && addr < OMAP_LOCALBUS_BASE + 0x1000000;
+}
+
+int omap_validate_tipb_mpui_addr(struct omap_mpu_state_s *s,
+                target_phys_addr_t addr)
+{
+    return addr >= 0xe1010000 && addr < 0xe1020004;
+}
+
+/* MPU OS timers */
+struct omap_mpu_timer_s {
+    qemu_irq irq;
+    omap_clk clk;
+    target_phys_addr_t base;
+    uint32_t val;
+    int64_t time;
+    QEMUTimer *timer;
+    int64_t rate;
+    int it_ena;
+
+    int enable;
+    int ptv;
+    int ar;
+    int st;
+    uint32_t reset_val;
+};
+
+static inline uint32_t omap_timer_read(struct omap_mpu_timer_s *timer)
+{
+    uint64_t distance = qemu_get_clock(vm_clock) - timer->time;
+
+    if (timer->st && timer->enable && timer->rate)
+        return timer->val - muldiv64(distance >> (timer->ptv + 1),
+                        timer->rate, ticks_per_sec);
+    else
+        return timer->val;
+}
+
+static inline void omap_timer_sync(struct omap_mpu_timer_s *timer)
+{
+    timer->val = omap_timer_read(timer);
+    timer->time = qemu_get_clock(vm_clock);
+}
+
+static inline void omap_timer_update(struct omap_mpu_timer_s *timer)
+{
+    int64_t expires;
+
+    if (timer->enable && timer->st && timer->rate) {
+        timer->val = timer->reset_val; /* Should skip this on clk enable */
+        expires = timer->time + muldiv64(timer->val << (timer->ptv + 1),
+                        ticks_per_sec, timer->rate);
+        qemu_mod_timer(timer->timer, expires);
+    } else
+        qemu_del_timer(timer->timer);
+}
+
+static void omap_timer_tick(void *opaque)
+{
+    struct omap_mpu_timer_s *timer = (struct omap_mpu_timer_s *) opaque;
+    omap_timer_sync(timer);
+
+    if (!timer->ar) {
+        timer->val = 0;
+        timer->st = 0;
+    }
+
+    if (timer->it_ena)
+        qemu_irq_raise(timer->irq);
+    omap_timer_update(timer);
+}
+
+static void omap_timer_clk_update(void *opaque, int line, int on)
+{
+    struct omap_mpu_timer_s *timer = (struct omap_mpu_timer_s *) opaque;
+
+    omap_timer_sync(timer);
+    timer->rate = on ? omap_clk_getrate(timer->clk) : 0;
+    omap_timer_update(timer);
+}
+
+static void omap_timer_clk_setup(struct omap_mpu_timer_s *timer)
+{
+    omap_clk_adduser(timer->clk,
+                    qemu_allocate_irqs(omap_timer_clk_update, timer, 1)[0]);
+    timer->rate = omap_clk_getrate(timer->clk);
+}
+
+static uint32_t omap_mpu_timer_read(void *opaque, target_phys_addr_t addr)
+{
+    struct omap_mpu_timer_s *s = (struct omap_mpu_timer_s *) opaque;
+    int offset = addr - s->base;
+
+    switch (offset) {
+    case 0x00: /* CNTL_TIMER */
+        return (s->enable << 5) | (s->ptv << 2) | (s->ar << 1) | s->st;
+
+    case 0x04: /* LOAD_TIM */
+        break;
+
+    case 0x08: /* READ_TIM */
+        return omap_timer_read(s);
+    }
+
+    OMAP_BAD_REG(addr);
+    return 0;
+}
+
+static void omap_mpu_timer_write(void *opaque, target_phys_addr_t addr,
+                uint32_t value)
+{
+    struct omap_mpu_timer_s *s = (struct omap_mpu_timer_s *) opaque;
+    int offset = addr - s->base;
+
+    switch (offset) {
+    case 0x00: /* CNTL_TIMER */
+        omap_timer_sync(s);
+        s->enable = (value >> 5) & 1;
+        s->ptv = (value >> 2) & 7;
+        s->ar = (value >> 1) & 1;
+        s->st = value & 1;
+        omap_timer_update(s);
+        return;
+
+    case 0x04: /* LOAD_TIM */
+        s->reset_val = value;
+        return;
+
+    case 0x08: /* READ_TIM */
+        OMAP_RO_REG(addr);
+        break;
+
+    default:
+        OMAP_BAD_REG(addr);
+    }
+}
+
+static CPUReadMemoryFunc *omap_mpu_timer_readfn[] = {
+    omap_badwidth_read32,
+    omap_badwidth_read32,
+    omap_mpu_timer_read,
+};
+
+static CPUWriteMemoryFunc *omap_mpu_timer_writefn[] = {
+    omap_badwidth_write32,
+    omap_badwidth_write32,
+    omap_mpu_timer_write,
+};
+
+static void omap_mpu_timer_reset(struct omap_mpu_timer_s *s)
+{
+    qemu_del_timer(s->timer);
+    s->enable = 0;
+    s->reset_val = 31337;
+    s->val = 0;
+    s->ptv = 0;
+    s->ar = 0;
+    s->st = 0;
+    s->it_ena = 1;
+}
+
+struct omap_mpu_timer_s *omap_mpu_timer_init(target_phys_addr_t base,
+                qemu_irq irq, omap_clk clk)
+{
+    int iomemtype;
+    struct omap_mpu_timer_s *s = (struct omap_mpu_timer_s *)
+            qemu_mallocz(sizeof(struct omap_mpu_timer_s));
+
+    s->irq = irq;
+    s->clk = clk;
+    s->base = base;
+    s->timer = qemu_new_timer(vm_clock, omap_timer_tick, s);
+    omap_mpu_timer_reset(s);
+    omap_timer_clk_setup(s);
+
+    iomemtype = cpu_register_io_memory(0, omap_mpu_timer_readfn,
+                    omap_mpu_timer_writefn, s);
+    cpu_register_physical_memory(s->base, 0x100, iomemtype);
+
+    return s;
+}
+
+/* Watchdog timer */
+struct omap_watchdog_timer_s {
+    struct omap_mpu_timer_s timer;
+    uint8_t last_wr;
+    int mode;
+    int free;
+    int reset;
+};
+
+static uint32_t omap_wd_timer_read(void *opaque, target_phys_addr_t addr)
+{
+    struct omap_watchdog_timer_s *s = (struct omap_watchdog_timer_s *) opaque;
+    int offset = addr - s->timer.base;
+
+    switch (offset) {
+    case 0x00: /* CNTL_TIMER */
+        return (s->timer.ptv << 9) | (s->timer.ar << 8) |
+                (s->timer.st << 7) | (s->free << 1);
+
+    case 0x04: /* READ_TIMER */
+        return omap_timer_read(&s->timer);
+
+    case 0x08: /* TIMER_MODE */
+        return s->mode << 15;
+    }
+
+    OMAP_BAD_REG(addr);
+    return 0;
+}
+
+static void omap_wd_timer_write(void *opaque, target_phys_addr_t addr,
+                uint32_t value)
+{
+    struct omap_watchdog_timer_s *s = (struct omap_watchdog_timer_s *) opaque;
+    int offset = addr - s->timer.base;
+
+    switch (offset) {
+    case 0x00: /* CNTL_TIMER */
+        omap_timer_sync(&s->timer);
+        s->timer.ptv = (value >> 9) & 7;
+        s->timer.ar = (value >> 8) & 1;
+        s->timer.st = (value >> 7) & 1;
+        s->free = (value >> 1) & 1;
+        omap_timer_update(&s->timer);
+        break;
+
+    case 0x04: /* LOAD_TIMER */
+        s->timer.reset_val = value & 0xffff;
+        break;
+
+    case 0x08: /* TIMER_MODE */
+        if (!s->mode && ((value >> 15) & 1))
+            omap_clk_get(s->timer.clk);
+        s->mode |= (value >> 15) & 1;
+        if (s->last_wr == 0xf5) {
+            if ((value & 0xff) == 0xa0) {
+                s->mode = 0;
+                omap_clk_put(s->timer.clk);
+            } else {
+                /* XXX: on T|E hardware somehow this has no effect,
+                 * on Zire 71 it works as specified.  */
+                s->reset = 1;
+                qemu_system_reset_request();
+            }
+        }
+        s->last_wr = value & 0xff;
+        break;
+
+    default:
+        OMAP_BAD_REG(addr);
+    }
+}
+
+static CPUReadMemoryFunc *omap_wd_timer_readfn[] = {
+    omap_badwidth_read16,
+    omap_wd_timer_read,
+    omap_badwidth_read16,
+};
+
+static CPUWriteMemoryFunc *omap_wd_timer_writefn[] = {
+    omap_badwidth_write16,
+    omap_wd_timer_write,
+    omap_badwidth_write16,
+};
+
+static void omap_wd_timer_reset(struct omap_watchdog_timer_s *s)
+{
+    qemu_del_timer(s->timer.timer);
+    if (!s->mode)
+        omap_clk_get(s->timer.clk);
+    s->mode = 1;
+    s->free = 1;
+    s->reset = 0;
+    s->timer.enable = 1;
+    s->timer.it_ena = 1;
+    s->timer.reset_val = 0xffff;
+    s->timer.val = 0;
+    s->timer.st = 0;
+    s->timer.ptv = 0;
+    s->timer.ar = 0;
+    omap_timer_update(&s->timer);
+}
+
+struct omap_watchdog_timer_s *omap_wd_timer_init(target_phys_addr_t base,
+                qemu_irq irq, omap_clk clk)
+{
+    int iomemtype;
+    struct omap_watchdog_timer_s *s = (struct omap_watchdog_timer_s *)
+            qemu_mallocz(sizeof(struct omap_watchdog_timer_s));
+
+    s->timer.irq = irq;
+    s->timer.clk = clk;
+    s->timer.base = base;
+    s->timer.timer = qemu_new_timer(vm_clock, omap_timer_tick, &s->timer);
+    omap_wd_timer_reset(s);
+    omap_timer_clk_setup(&s->timer);
+
+    iomemtype = cpu_register_io_memory(0, omap_wd_timer_readfn,
+                    omap_wd_timer_writefn, s);
+    cpu_register_physical_memory(s->timer.base, 0x100, iomemtype);
+
+    return s;
+}
+
+/* 32-kHz timer */
+struct omap_32khz_timer_s {
+    struct omap_mpu_timer_s timer;
+};
+
+static uint32_t omap_os_timer_read(void *opaque, target_phys_addr_t addr)
+{
+    struct omap_32khz_timer_s *s = (struct omap_32khz_timer_s *) opaque;
+    int offset = addr - s->timer.base;
+
+    switch (offset) {
+    case 0x00: /* TVR */
+        return s->timer.reset_val;
+
+    case 0x04: /* TCR */
+        return omap_timer_read(&s->timer);
+
+    case 0x08: /* CR */
+        return (s->timer.ar << 3) | (s->timer.it_ena << 2) | s->timer.st;
+
+    default:
+        break;
+    }
+    OMAP_BAD_REG(addr);
+    return 0;
+}
+
+static void omap_os_timer_write(void *opaque, target_phys_addr_t addr,
+                uint32_t value)
+{
+    struct omap_32khz_timer_s *s = (struct omap_32khz_timer_s *) opaque;
+    int offset = addr - s->timer.base;
+
+    switch (offset) {
+    case 0x00: /* TVR */
+        s->timer.reset_val = value & 0x00ffffff;
+        break;
+
+    case 0x04: /* TCR */
+        OMAP_RO_REG(addr);
+        break;
+
+    case 0x08: /* CR */
+        s->timer.ar = (value >> 3) & 1;
+        s->timer.it_ena = (value >> 2) & 1;
+        if (s->timer.st != (value & 1) || (value & 2)) {
+            omap_timer_sync(&s->timer);
+            s->timer.enable = value & 1;
+            s->timer.st = value & 1;
+            omap_timer_update(&s->timer);
+        }
+        break;
+
+    default:
+        OMAP_BAD_REG(addr);
+    }
+}
+
+static CPUReadMemoryFunc *omap_os_timer_readfn[] = {
+    omap_badwidth_read32,
+    omap_badwidth_read32,
+    omap_os_timer_read,
+};
+
+static CPUWriteMemoryFunc *omap_os_timer_writefn[] = {
+    omap_badwidth_write32,
+    omap_badwidth_write32,
+    omap_os_timer_write,
+};
+
+static void omap_os_timer_reset(struct omap_32khz_timer_s *s)
+{
+    qemu_del_timer(s->timer.timer);
+    s->timer.enable = 0;
+    s->timer.it_ena = 0;
+    s->timer.reset_val = 0x00ffffff;
+    s->timer.val = 0;
+    s->timer.st = 0;
+    s->timer.ptv = 0;
+    s->timer.ar = 1;
+}
+
+struct omap_32khz_timer_s *omap_os_timer_init(target_phys_addr_t base,
+                qemu_irq irq, omap_clk clk)
+{
+    int iomemtype;
+    struct omap_32khz_timer_s *s = (struct omap_32khz_timer_s *)
+            qemu_mallocz(sizeof(struct omap_32khz_timer_s));
+
+    s->timer.irq = irq;
+    s->timer.clk = clk;
+    s->timer.base = base;
+    s->timer.timer = qemu_new_timer(vm_clock, omap_timer_tick, &s->timer);
+    omap_os_timer_reset(s);
+    omap_timer_clk_setup(&s->timer);
+
+    iomemtype = cpu_register_io_memory(0, omap_os_timer_readfn,
+                    omap_os_timer_writefn, s);
+    cpu_register_physical_memory(s->timer.base, 0x800, iomemtype);
+
+    return s;
+}
+
+/* Ultra Low-Power Device Module */
+static uint32_t omap_ulpd_pm_read(void *opaque, target_phys_addr_t addr)
+{
+    struct omap_mpu_state_s *s = (struct omap_mpu_state_s *) opaque;
+    int offset = addr - s->ulpd_pm_base;
+    uint16_t ret;
+
+    switch (offset) {
+    case 0x14: /* IT_STATUS */
+        ret = s->ulpd_pm_regs[offset >> 2];
+        s->ulpd_pm_regs[offset >> 2] = 0;
+        qemu_irq_lower(s->irq[1][OMAP_INT_GAUGE_32K]);
+        return ret;
+
+    case 0x18: /* Reserved */
+    case 0x1c: /* Reserved */
+    case 0x20: /* Reserved */
+    case 0x28: /* Reserved */
+    case 0x2c: /* Reserved */
+        OMAP_BAD_REG(addr);
+    case 0x00: /* COUNTER_32_LSB */
+    case 0x04: /* COUNTER_32_MSB */
+    case 0x08: /* COUNTER_HIGH_FREQ_LSB */
+    case 0x0c: /* COUNTER_HIGH_FREQ_MSB */
+    case 0x10: /* GAUGING_CTRL */
+    case 0x24: /* SETUP_ANALOG_CELL3_ULPD1 */
+    case 0x30: /* CLOCK_CTRL */
+    case 0x34: /* SOFT_REQ */
+    case 0x38: /* COUNTER_32_FIQ */
+    case 0x3c: /* DPLL_CTRL */
+    case 0x40: /* STATUS_REQ */
+        /* XXX: check clk::usecount state for every clock */
+    case 0x48: /* LOCL_TIME */
+    case 0x4c: /* APLL_CTRL */
+    case 0x50: /* POWER_CTRL */
+        return s->ulpd_pm_regs[offset >> 2];
+    }
+
+    OMAP_BAD_REG(addr);
+    return 0;
+}
+
+static inline void omap_ulpd_clk_update(struct omap_mpu_state_s *s,
+                uint16_t diff, uint16_t value)
+{
+    if (diff & (1 << 4))                               /* USB_MCLK_EN */
+        omap_clk_onoff(omap_findclk(s, "usb_clk0"), (value >> 4) & 1);
+    if (diff & (1 << 5))                               /* DIS_USB_PVCI_CLK */
+        omap_clk_onoff(omap_findclk(s, "usb_w2fc_ck"), (~value >> 5) & 1);
+}
+
+static inline void omap_ulpd_req_update(struct omap_mpu_state_s *s,
+                uint16_t diff, uint16_t value)
+{
+    if (diff & (1 << 0))                               /* SOFT_DPLL_REQ */
+        omap_clk_canidle(omap_findclk(s, "dpll4"), (~value >> 0) & 1);
+    if (diff & (1 << 1))                               /* SOFT_COM_REQ */
+        omap_clk_canidle(omap_findclk(s, "com_mclk_out"), (~value >> 1) & 1);
+    if (diff & (1 << 2))                               /* SOFT_SDW_REQ */
+        omap_clk_canidle(omap_findclk(s, "bt_mclk_out"), (~value >> 2) & 1);
+    if (diff & (1 << 3))                               /* SOFT_USB_REQ */
+        omap_clk_canidle(omap_findclk(s, "usb_clk0"), (~value >> 3) & 1);
+}
+
+static void omap_ulpd_pm_write(void *opaque, target_phys_addr_t addr,
+                uint32_t value)
+{
+    struct omap_mpu_state_s *s = (struct omap_mpu_state_s *) opaque;
+    int offset = addr - s->ulpd_pm_base;
+    int64_t now, ticks;
+    int div, mult;
+    static const int bypass_div[4] = { 1, 2, 4, 4 };
+    uint16_t diff;
+
+    switch (offset) {
+    case 0x00: /* COUNTER_32_LSB */
+    case 0x04: /* COUNTER_32_MSB */
+    case 0x08: /* COUNTER_HIGH_FREQ_LSB */
+    case 0x0c: /* COUNTER_HIGH_FREQ_MSB */
+    case 0x14: /* IT_STATUS */
+    case 0x40: /* STATUS_REQ */
+        OMAP_RO_REG(addr);
+        break;
+
+    case 0x10: /* GAUGING_CTRL */
+        /* Bits 0 and 1 seem to be confused in the OMAP 310 TRM */
+        if ((s->ulpd_pm_regs[offset >> 2] ^ value) & 1) {
+            now = qemu_get_clock(vm_clock);
+
+            if (value & 1)
+                s->ulpd_gauge_start = now;
+            else {
+                now -= s->ulpd_gauge_start;
+
+                /* 32-kHz ticks */
+                ticks = muldiv64(now, 32768, ticks_per_sec);
+                s->ulpd_pm_regs[0x00 >> 2] = (ticks >>  0) & 0xffff;
+                s->ulpd_pm_regs[0x04 >> 2] = (ticks >> 16) & 0xffff;
+                if (ticks >> 32)       /* OVERFLOW_32K */
+                    s->ulpd_pm_regs[0x14 >> 2] |= 1 << 2;
+
+                /* High frequency ticks */
+                ticks = muldiv64(now, 12000000, ticks_per_sec);
+                s->ulpd_pm_regs[0x08 >> 2] = (ticks >>  0) & 0xffff;
+                s->ulpd_pm_regs[0x0c >> 2] = (ticks >> 16) & 0xffff;
+                if (ticks >> 32)       /* OVERFLOW_HI_FREQ */
+                    s->ulpd_pm_regs[0x14 >> 2] |= 1 << 1;
+
+                s->ulpd_pm_regs[0x14 >> 2] |= 1 << 0;  /* IT_GAUGING */
+                qemu_irq_raise(s->irq[1][OMAP_INT_GAUGE_32K]);
+            }
+        }
+        s->ulpd_pm_regs[offset >> 2] = value;
+        break;
+
+    case 0x18: /* Reserved */
+    case 0x1c: /* Reserved */
+    case 0x20: /* Reserved */
+    case 0x28: /* Reserved */
+    case 0x2c: /* Reserved */
+        OMAP_BAD_REG(addr);
+    case 0x24: /* SETUP_ANALOG_CELL3_ULPD1 */
+    case 0x38: /* COUNTER_32_FIQ */
+    case 0x48: /* LOCL_TIME */
+    case 0x50: /* POWER_CTRL */
+        s->ulpd_pm_regs[offset >> 2] = value;
+        break;
+
+    case 0x30: /* CLOCK_CTRL */
+        diff = s->ulpd_pm_regs[offset >> 2] ^ value;
+        s->ulpd_pm_regs[offset >> 2] = value & 0x3f;
+        omap_ulpd_clk_update(s, diff, value);
+        break;
+
+    case 0x34: /* SOFT_REQ */
+        diff = s->ulpd_pm_regs[offset >> 2] ^ value;
+        s->ulpd_pm_regs[offset >> 2] = value & 0x1f;
+        omap_ulpd_req_update(s, diff, value);
+        break;
+
+    case 0x3c: /* DPLL_CTRL */
+        /* XXX: OMAP310 TRM claims bit 3 is PLL_ENABLE, and bit 4 is
+         * omitted altogether, probably a typo.  */
+        /* This register has identical semantics with DPLL(1:3) control
+         * registers, see omap_dpll_write() */
+        diff = s->ulpd_pm_regs[offset >> 2] & value;
+        s->ulpd_pm_regs[offset >> 2] = value & 0x2fff;
+        if (diff & (0x3ff << 2)) {
+            if (value & (1 << 4)) {                    /* PLL_ENABLE */
+                div = ((value >> 5) & 3) + 1;          /* PLL_DIV */
+                mult = MIN((value >> 7) & 0x1f, 1);    /* PLL_MULT */
+            } else {
+                div = bypass_div[((value >> 2) & 3)];  /* BYPASS_DIV */
+                mult = 1;
+            }
+            omap_clk_setrate(omap_findclk(s, "dpll4"), div, mult);
+        }
+
+        /* Enter the desired mode.  */
+        s->ulpd_pm_regs[offset >> 2] =
+                (s->ulpd_pm_regs[offset >> 2] & 0xfffe) |
+                ((s->ulpd_pm_regs[offset >> 2] >> 4) & 1);
+
+        /* Act as if the lock is restored.  */
+        s->ulpd_pm_regs[offset >> 2] |= 2;
+        break;
+
+    case 0x4c: /* APLL_CTRL */
+        diff = s->ulpd_pm_regs[offset >> 2] & value;
+        s->ulpd_pm_regs[offset >> 2] = value & 0xf;
+        if (diff & (1 << 0))                           /* APLL_NDPLL_SWITCH */
+            omap_clk_reparent(omap_findclk(s, "ck_48m"), omap_findclk(s,
+                                    (value & (1 << 0)) ? "apll" : "dpll4"));
+        break;
+
+    default:
+        OMAP_BAD_REG(addr);
+    }
+}
+
+static CPUReadMemoryFunc *omap_ulpd_pm_readfn[] = {
+    omap_badwidth_read16,
+    omap_ulpd_pm_read,
+    omap_badwidth_read16,
+};
+
+static CPUWriteMemoryFunc *omap_ulpd_pm_writefn[] = {
+    omap_badwidth_write16,
+    omap_ulpd_pm_write,
+    omap_badwidth_write16,
+};
+
+static void omap_ulpd_pm_reset(struct omap_mpu_state_s *mpu)
+{
+    mpu->ulpd_pm_regs[0x00 >> 2] = 0x0001;
+    mpu->ulpd_pm_regs[0x04 >> 2] = 0x0000;
+    mpu->ulpd_pm_regs[0x08 >> 2] = 0x0001;
+    mpu->ulpd_pm_regs[0x0c >> 2] = 0x0000;
+    mpu->ulpd_pm_regs[0x10 >> 2] = 0x0000;
+    mpu->ulpd_pm_regs[0x18 >> 2] = 0x01;
+    mpu->ulpd_pm_regs[0x1c >> 2] = 0x01;
+    mpu->ulpd_pm_regs[0x20 >> 2] = 0x01;
+    mpu->ulpd_pm_regs[0x24 >> 2] = 0x03ff;
+    mpu->ulpd_pm_regs[0x28 >> 2] = 0x01;
+    mpu->ulpd_pm_regs[0x2c >> 2] = 0x01;
+    omap_ulpd_clk_update(mpu, mpu->ulpd_pm_regs[0x30 >> 2], 0x0000);
+    mpu->ulpd_pm_regs[0x30 >> 2] = 0x0000;
+    omap_ulpd_req_update(mpu, mpu->ulpd_pm_regs[0x34 >> 2], 0x0000);
+    mpu->ulpd_pm_regs[0x34 >> 2] = 0x0000;
+    mpu->ulpd_pm_regs[0x38 >> 2] = 0x0001;
+    mpu->ulpd_pm_regs[0x3c >> 2] = 0x2211;
+    mpu->ulpd_pm_regs[0x40 >> 2] = 0x0000; /* FIXME: dump a real STATUS_REQ */
+    mpu->ulpd_pm_regs[0x48 >> 2] = 0x960;
+    mpu->ulpd_pm_regs[0x4c >> 2] = 0x08;
+    mpu->ulpd_pm_regs[0x50 >> 2] = 0x08;
+    omap_clk_setrate(omap_findclk(mpu, "dpll4"), 1, 4);
+    omap_clk_reparent(omap_findclk(mpu, "ck_48m"), omap_findclk(mpu, "dpll4"));
+}
+
+static void omap_ulpd_pm_init(target_phys_addr_t base,
+                struct omap_mpu_state_s *mpu)
+{
+    int iomemtype = cpu_register_io_memory(0, omap_ulpd_pm_readfn,
+                    omap_ulpd_pm_writefn, mpu);
+
+    mpu->ulpd_pm_base = base;
+    cpu_register_physical_memory(mpu->ulpd_pm_base, 0x800, iomemtype);
+    omap_ulpd_pm_reset(mpu);
+}
+
+/* OMAP Pin Configuration */
+static uint32_t omap_pin_cfg_read(void *opaque, target_phys_addr_t addr)
+{
+    struct omap_mpu_state_s *s = (struct omap_mpu_state_s *) opaque;
+    int offset = addr - s->pin_cfg_base;
+
+    switch (offset) {
+    case 0x00: /* FUNC_MUX_CTRL_0 */
+    case 0x04: /* FUNC_MUX_CTRL_1 */
+    case 0x08: /* FUNC_MUX_CTRL_2 */
+        return s->func_mux_ctrl[offset >> 2];
+
+    case 0x0c: /* COMP_MODE_CTRL_0 */
+        return s->comp_mode_ctrl[0];
+
+    case 0x10: /* FUNC_MUX_CTRL_3 */
+    case 0x14: /* FUNC_MUX_CTRL_4 */
+    case 0x18: /* FUNC_MUX_CTRL_5 */
+    case 0x1c: /* FUNC_MUX_CTRL_6 */
+    case 0x20: /* FUNC_MUX_CTRL_7 */
+    case 0x24: /* FUNC_MUX_CTRL_8 */
+    case 0x28: /* FUNC_MUX_CTRL_9 */
+    case 0x2c: /* FUNC_MUX_CTRL_A */
+    case 0x30: /* FUNC_MUX_CTRL_B */
+    case 0x34: /* FUNC_MUX_CTRL_C */
+    case 0x38: /* FUNC_MUX_CTRL_D */
+        return s->func_mux_ctrl[(offset >> 2) - 1];
+
+    case 0x40: /* PULL_DWN_CTRL_0 */
+    case 0x44: /* PULL_DWN_CTRL_1 */
+    case 0x48: /* PULL_DWN_CTRL_2 */
+    case 0x4c: /* PULL_DWN_CTRL_3 */
+        return s->pull_dwn_ctrl[(offset & 0xf) >> 2];
+
+    case 0x50: /* GATE_INH_CTRL_0 */
+        return s->gate_inh_ctrl[0];
+
+    case 0x60: /* VOLTAGE_CTRL_0 */
+        return s->voltage_ctrl[0];
+
+    case 0x70: /* TEST_DBG_CTRL_0 */
+        return s->test_dbg_ctrl[0];
+
+    case 0x80: /* MOD_CONF_CTRL_0 */
+        return s->mod_conf_ctrl[0];
+    }
+
+    OMAP_BAD_REG(addr);
+    return 0;
+}
+
+static inline void omap_pin_funcmux0_update(struct omap_mpu_state_s *s,
+                uint32_t diff, uint32_t value)
+{
+    if (s->compat1509) {
+        if (diff & (1 << 9))                   /* BLUETOOTH */
+            omap_clk_onoff(omap_findclk(s, "bt_mclk_out"),
+                            (~value >> 9) & 1);
+        if (diff & (1 << 7))                   /* USB.CLKO */
+            omap_clk_onoff(omap_findclk(s, "usb.clko"),
+                            (value >> 7) & 1);
+    }
+}
+
+static inline void omap_pin_funcmux1_update(struct omap_mpu_state_s *s,
+                uint32_t diff, uint32_t value)
+{
+    if (s->compat1509) {
+        if (diff & (1 << 31))                  /* MCBSP3_CLK_HIZ_DI */
+            omap_clk_onoff(omap_findclk(s, "mcbsp3.clkx"),
+                            (value >> 31) & 1);
+        if (diff & (1 << 1))                   /* CLK32K */
+            omap_clk_onoff(omap_findclk(s, "clk32k_out"),
+                            (~value >> 1) & 1);
+    }
+}
+
+static inline void omap_pin_modconf1_update(struct omap_mpu_state_s *s,
+                uint32_t diff, uint32_t value)
+{
+    if (diff & (1 << 31))                      /* CONF_MOD_UART3_CLK_MODE_R */
+         omap_clk_reparent(omap_findclk(s, "uart3_ck"),
+                         omap_findclk(s, ((value >> 31) & 1) ?
+                                 "ck_48m" : "armper_ck"));
+    if (diff & (1 << 30))                      /* CONF_MOD_UART2_CLK_MODE_R */
+         omap_clk_reparent(omap_findclk(s, "uart2_ck"),
+                         omap_findclk(s, ((value >> 30) & 1) ?
+                                 "ck_48m" : "armper_ck"));
+    if (diff & (1 << 29))                      /* CONF_MOD_UART1_CLK_MODE_R */
+         omap_clk_reparent(omap_findclk(s, "uart1_ck"),
+                         omap_findclk(s, ((value >> 29) & 1) ?
+                                 "ck_48m" : "armper_ck"));
+    if (diff & (1 << 23))                      /* CONF_MOD_MMC_SD_CLK_REQ_R */
+         omap_clk_reparent(omap_findclk(s, "mmc_ck"),
+                         omap_findclk(s, ((value >> 23) & 1) ?
+                                 "ck_48m" : "armper_ck"));
+    if (diff & (1 << 12))                      /* CONF_MOD_COM_MCLK_12_48_S */
+         omap_clk_reparent(omap_findclk(s, "com_mclk_out"),
+                         omap_findclk(s, ((value >> 12) & 1) ?
+                                 "ck_48m" : "armper_ck"));
+    if (diff & (1 << 9))                       /* CONF_MOD_USB_HOST_HHC_UHO */
+         omap_clk_onoff(omap_findclk(s, "usb_hhc_ck"), (value >> 9) & 1);
+}
+
+static void omap_pin_cfg_write(void *opaque, target_phys_addr_t addr,
+                uint32_t value)
+{
+    struct omap_mpu_state_s *s = (struct omap_mpu_state_s *) opaque;
+    int offset = addr - s->pin_cfg_base;
+    uint32_t diff;
+
+    switch (offset) {
+    case 0x00: /* FUNC_MUX_CTRL_0 */
+        diff = s->func_mux_ctrl[offset >> 2] ^ value;
+        s->func_mux_ctrl[offset >> 2] = value;
+        omap_pin_funcmux0_update(s, diff, value);
+        return;
+
+    case 0x04: /* FUNC_MUX_CTRL_1 */
+        diff = s->func_mux_ctrl[offset >> 2] ^ value;
+        s->func_mux_ctrl[offset >> 2] = value;
+        omap_pin_funcmux1_update(s, diff, value);
+        return;
+
+    case 0x08: /* FUNC_MUX_CTRL_2 */
+        s->func_mux_ctrl[offset >> 2] = value;
+        return;
+
+    case 0x0c: /* COMP_MODE_CTRL_0 */
+        s->comp_mode_ctrl[0] = value;
+        s->compat1509 = (value != 0x0000eaef);
+        omap_pin_funcmux0_update(s, ~0, s->func_mux_ctrl[0]);
+        omap_pin_funcmux1_update(s, ~0, s->func_mux_ctrl[1]);
+        return;
+
+    case 0x10: /* FUNC_MUX_CTRL_3 */
+    case 0x14: /* FUNC_MUX_CTRL_4 */
+    case 0x18: /* FUNC_MUX_CTRL_5 */
+    case 0x1c: /* FUNC_MUX_CTRL_6 */
+    case 0x20: /* FUNC_MUX_CTRL_7 */
+    case 0x24: /* FUNC_MUX_CTRL_8 */
+    case 0x28: /* FUNC_MUX_CTRL_9 */
+    case 0x2c: /* FUNC_MUX_CTRL_A */
+    case 0x30: /* FUNC_MUX_CTRL_B */
+    case 0x34: /* FUNC_MUX_CTRL_C */
+    case 0x38: /* FUNC_MUX_CTRL_D */
+        s->func_mux_ctrl[(offset >> 2) - 1] = value;
+        return;
+
+    case 0x40: /* PULL_DWN_CTRL_0 */
+    case 0x44: /* PULL_DWN_CTRL_1 */
+    case 0x48: /* PULL_DWN_CTRL_2 */
+    case 0x4c: /* PULL_DWN_CTRL_3 */
+        s->pull_dwn_ctrl[(offset & 0xf) >> 2] = value;
+        return;
+
+    case 0x50: /* GATE_INH_CTRL_0 */
+        s->gate_inh_ctrl[0] = value;
+        return;
+
+    case 0x60: /* VOLTAGE_CTRL_0 */
+        s->voltage_ctrl[0] = value;
+        return;
+
+    case 0x70: /* TEST_DBG_CTRL_0 */
+        s->test_dbg_ctrl[0] = value;
+        return;
+
+    case 0x80: /* MOD_CONF_CTRL_0 */
+        diff = s->mod_conf_ctrl[0] ^ value;
+        s->mod_conf_ctrl[0] = value;
+        omap_pin_modconf1_update(s, diff, value);
+        return;
+
+    default:
+        OMAP_BAD_REG(addr);
+    }
+}
+
+static CPUReadMemoryFunc *omap_pin_cfg_readfn[] = {
+    omap_badwidth_read32,
+    omap_badwidth_read32,
+    omap_pin_cfg_read,
+};
+
+static CPUWriteMemoryFunc *omap_pin_cfg_writefn[] = {
+    omap_badwidth_write32,
+    omap_badwidth_write32,
+    omap_pin_cfg_write,
+};
+
+static void omap_pin_cfg_reset(struct omap_mpu_state_s *mpu)
+{
+    /* Start in Compatibility Mode.  */
+    mpu->compat1509 = 1;
+    omap_pin_funcmux0_update(mpu, mpu->func_mux_ctrl[0], 0);
+    omap_pin_funcmux1_update(mpu, mpu->func_mux_ctrl[1], 0);
+    omap_pin_modconf1_update(mpu, mpu->mod_conf_ctrl[0], 0);
+    memset(mpu->func_mux_ctrl, 0, sizeof(mpu->func_mux_ctrl));
+    memset(mpu->comp_mode_ctrl, 0, sizeof(mpu->comp_mode_ctrl));
+    memset(mpu->pull_dwn_ctrl, 0, sizeof(mpu->pull_dwn_ctrl));
+    memset(mpu->gate_inh_ctrl, 0, sizeof(mpu->gate_inh_ctrl));
+    memset(mpu->voltage_ctrl, 0, sizeof(mpu->voltage_ctrl));
+    memset(mpu->test_dbg_ctrl, 0, sizeof(mpu->test_dbg_ctrl));
+    memset(mpu->mod_conf_ctrl, 0, sizeof(mpu->mod_conf_ctrl));
+}
+
+static void omap_pin_cfg_init(target_phys_addr_t base,
+                struct omap_mpu_state_s *mpu)
+{
+    int iomemtype = cpu_register_io_memory(0, omap_pin_cfg_readfn,
+                    omap_pin_cfg_writefn, mpu);
+
+    mpu->pin_cfg_base = base;
+    cpu_register_physical_memory(mpu->pin_cfg_base, 0x800, iomemtype);
+    omap_pin_cfg_reset(mpu);
+}
+
+/* Device Identification, Die Identification */
+static uint32_t omap_id_read(void *opaque, target_phys_addr_t addr)
+{
+    struct omap_mpu_state_s *s = (struct omap_mpu_state_s *) opaque;
+
+    switch (addr) {
+    case 0xfffe1800:   /* DIE_ID_LSB */
+        return 0xc9581f0e;
+    case 0xfffe1804:   /* DIE_ID_MSB */
+        return 0xa8858bfa;
+
+    case 0xfffe2000:   /* PRODUCT_ID_LSB */
+        return 0x00aaaafc;
+    case 0xfffe2004:   /* PRODUCT_ID_MSB */
+        return 0xcafeb574;
+
+    case 0xfffed400:   /* JTAG_ID_LSB */
+        switch (s->mpu_model) {
+        case omap310:
+            return 0x03310315;
+        case omap1510:
+            return 0x03310115;
+        }
+        break;
+
+    case 0xfffed404:   /* JTAG_ID_MSB */
+        switch (s->mpu_model) {
+        case omap310:
+            return 0xfb57402f;
+        case omap1510:
+            return 0xfb47002f;
+        }
+        break;
+    }
+
+    OMAP_BAD_REG(addr);
+    return 0;
+}
+
+static void omap_id_write(void *opaque, target_phys_addr_t addr,
+                uint32_t value)
+{
+    OMAP_BAD_REG(addr);
+}
+
+static CPUReadMemoryFunc *omap_id_readfn[] = {
+    omap_badwidth_read32,
+    omap_badwidth_read32,
+    omap_id_read,
+};
+
+static CPUWriteMemoryFunc *omap_id_writefn[] = {
+    omap_badwidth_write32,
+    omap_badwidth_write32,
+    omap_id_write,
+};
+
+static void omap_id_init(struct omap_mpu_state_s *mpu)
+{
+    int iomemtype = cpu_register_io_memory(0, omap_id_readfn,
+                    omap_id_writefn, mpu);
+    cpu_register_physical_memory(0xfffe1800, 0x800, iomemtype);
+    cpu_register_physical_memory(0xfffed400, 0x100, iomemtype);
+    if (!cpu_is_omap15xx(mpu))
+        cpu_register_physical_memory(0xfffe2000, 0x800, iomemtype);
+}
+
+/* MPUI Control (Dummy) */
+static uint32_t omap_mpui_read(void *opaque, target_phys_addr_t addr)
+{
+    struct omap_mpu_state_s *s = (struct omap_mpu_state_s *) opaque;
+    int offset = addr - s->mpui_base;
+
+    switch (offset) {
+    case 0x00: /* CTRL */
+        return s->mpui_ctrl;
+    case 0x04: /* DEBUG_ADDR */
+        return 0x01ffffff;
+    case 0x08: /* DEBUG_DATA */
+        return 0xffffffff;
+    case 0x0c: /* DEBUG_FLAG */
+        return 0x00000800;
+    case 0x10: /* STATUS */
+        return 0x00000000;
+
+    /* Not in OMAP310 */
+    case 0x14: /* DSP_STATUS */
+    case 0x18: /* DSP_BOOT_CONFIG */
+        return 0x00000000;
+    case 0x1c: /* DSP_MPUI_CONFIG */
+        return 0x0000ffff;
+    }
+
+    OMAP_BAD_REG(addr);
+    return 0;
+}
+
+static void omap_mpui_write(void *opaque, target_phys_addr_t addr,
+                uint32_t value)
+{
+    struct omap_mpu_state_s *s = (struct omap_mpu_state_s *) opaque;
+    int offset = addr - s->mpui_base;
+
+    switch (offset) {
+    case 0x00: /* CTRL */
+        s->mpui_ctrl = value & 0x007fffff;
+        break;
+
+    case 0x04: /* DEBUG_ADDR */
+    case 0x08: /* DEBUG_DATA */
+    case 0x0c: /* DEBUG_FLAG */
+    case 0x10: /* STATUS */
+    /* Not in OMAP310 */
+    case 0x14: /* DSP_STATUS */
+        OMAP_RO_REG(addr);
+    case 0x18: /* DSP_BOOT_CONFIG */
+    case 0x1c: /* DSP_MPUI_CONFIG */
+        break;
+
+    default:
+        OMAP_BAD_REG(addr);
+    }
+}
+
+static CPUReadMemoryFunc *omap_mpui_readfn[] = {
+    omap_badwidth_read32,
+    omap_badwidth_read32,
+    omap_mpui_read,
+};
+
+static CPUWriteMemoryFunc *omap_mpui_writefn[] = {
+    omap_badwidth_write32,
+    omap_badwidth_write32,
+    omap_mpui_write,
+};
+
+static void omap_mpui_reset(struct omap_mpu_state_s *s)
+{
+    s->mpui_ctrl = 0x0003ff1b;
+}
+
+static void omap_mpui_init(target_phys_addr_t base,
+                struct omap_mpu_state_s *mpu)
+{
+    int iomemtype = cpu_register_io_memory(0, omap_mpui_readfn,
+                    omap_mpui_writefn, mpu);
+
+    mpu->mpui_base = base;
+    cpu_register_physical_memory(mpu->mpui_base, 0x100, iomemtype);
+
+    omap_mpui_reset(mpu);
+}
+
+/* TIPB Bridges */
+struct omap_tipb_bridge_s {
+    target_phys_addr_t base;
+    qemu_irq abort;
+
+    int width_intr;
+    uint16_t control;
+    uint16_t alloc;
+    uint16_t buffer;
+    uint16_t enh_control;
+};
+
+static uint32_t omap_tipb_bridge_read(void *opaque, target_phys_addr_t addr)
+{
+    struct omap_tipb_bridge_s *s = (struct omap_tipb_bridge_s *) opaque;
+    int offset = addr - s->base;
+
+    switch (offset) {
+    case 0x00: /* TIPB_CNTL */
+        return s->control;
+    case 0x04: /* TIPB_BUS_ALLOC */
+        return s->alloc;
+    case 0x08: /* MPU_TIPB_CNTL */
+        return s->buffer;
+    case 0x0c: /* ENHANCED_TIPB_CNTL */
+        return s->enh_control;
+    case 0x10: /* ADDRESS_DBG */
+    case 0x14: /* DATA_DEBUG_LOW */
+    case 0x18: /* DATA_DEBUG_HIGH */
+        return 0xffff;
+    case 0x1c: /* DEBUG_CNTR_SIG */
+        return 0x00f8;
+    }
+
+    OMAP_BAD_REG(addr);
+    return 0;
+}
+
+static void omap_tipb_bridge_write(void *opaque, target_phys_addr_t addr,
+                uint32_t value)
+{
+    struct omap_tipb_bridge_s *s = (struct omap_tipb_bridge_s *) opaque;
+    int offset = addr - s->base;
+
+    switch (offset) {
+    case 0x00: /* TIPB_CNTL */
+        s->control = value & 0xffff;
+        break;
+
+    case 0x04: /* TIPB_BUS_ALLOC */
+        s->alloc = value & 0x003f;
+        break;
+
+    case 0x08: /* MPU_TIPB_CNTL */
+        s->buffer = value & 0x0003;
+        break;
+
+    case 0x0c: /* ENHANCED_TIPB_CNTL */
+        s->width_intr = !(value & 2);
+        s->enh_control = value & 0x000f;
+        break;
+
+    case 0x10: /* ADDRESS_DBG */
+    case 0x14: /* DATA_DEBUG_LOW */
+    case 0x18: /* DATA_DEBUG_HIGH */
+    case 0x1c: /* DEBUG_CNTR_SIG */
+        OMAP_RO_REG(addr);
+        break;
+
+    default:
+        OMAP_BAD_REG(addr);
+    }
+}
+
+static CPUReadMemoryFunc *omap_tipb_bridge_readfn[] = {
+    omap_badwidth_read16,
+    omap_tipb_bridge_read,
+    omap_tipb_bridge_read,
+};
+
+static CPUWriteMemoryFunc *omap_tipb_bridge_writefn[] = {
+    omap_badwidth_write16,
+    omap_tipb_bridge_write,
+    omap_tipb_bridge_write,
+};
+
+static void omap_tipb_bridge_reset(struct omap_tipb_bridge_s *s)
+{
+    s->control = 0xffff;
+    s->alloc = 0x0009;
+    s->buffer = 0x0000;
+    s->enh_control = 0x000f;
+}
+
+struct omap_tipb_bridge_s *omap_tipb_bridge_init(target_phys_addr_t base,
+                qemu_irq abort_irq, omap_clk clk)
+{
+    int iomemtype;
+    struct omap_tipb_bridge_s *s = (struct omap_tipb_bridge_s *)
+            qemu_mallocz(sizeof(struct omap_tipb_bridge_s));
+
+    s->abort = abort_irq;
+    s->base = base;
+    omap_tipb_bridge_reset(s);
+
+    iomemtype = cpu_register_io_memory(0, omap_tipb_bridge_readfn,
+                    omap_tipb_bridge_writefn, s);
+    cpu_register_physical_memory(s->base, 0x100, iomemtype);
+
+    return s;
+}
+
+/* Dummy Traffic Controller's Memory Interface */
+static uint32_t omap_tcmi_read(void *opaque, target_phys_addr_t addr)
+{
+    struct omap_mpu_state_s *s = (struct omap_mpu_state_s *) opaque;
+    int offset = addr - s->tcmi_base;
+    uint32_t ret;
+
+    switch (offset) {
+    case 0xfffecc00:   /* IMIF_PRIO */
+    case 0xfffecc04:   /* EMIFS_PRIO */
+    case 0xfffecc08:   /* EMIFF_PRIO */
+    case 0xfffecc0c:   /* EMIFS_CONFIG */
+    case 0xfffecc10:   /* EMIFS_CS0_CONFIG */
+    case 0xfffecc14:   /* EMIFS_CS1_CONFIG */
+    case 0xfffecc18:   /* EMIFS_CS2_CONFIG */
+    case 0xfffecc1c:   /* EMIFS_CS3_CONFIG */
+    case 0xfffecc24:   /* EMIFF_MRS */
+    case 0xfffecc28:   /* TIMEOUT1 */
+    case 0xfffecc2c:   /* TIMEOUT2 */
+    case 0xfffecc30:   /* TIMEOUT3 */
+    case 0xfffecc3c:   /* EMIFF_SDRAM_CONFIG_2 */
+    case 0xfffecc40:   /* EMIFS_CFG_DYN_WAIT */
+        return s->tcmi_regs[offset >> 2];
+
+    case 0xfffecc20:   /* EMIFF_SDRAM_CONFIG */
+        ret = s->tcmi_regs[offset >> 2];
+        s->tcmi_regs[offset >> 2] &= ~1; /* XXX: Clear SLRF on SDRAM access */
+        /* XXX: We can try using the VGA_DIRTY flag for this */
+        return ret;
+    }
+
+    OMAP_BAD_REG(addr);
+    return 0;
+}
+
+static void omap_tcmi_write(void *opaque, target_phys_addr_t addr,
+                uint32_t value)
+{
+    struct omap_mpu_state_s *s = (struct omap_mpu_state_s *) opaque;
+    int offset = addr - s->tcmi_base;
+
+    switch (offset) {
+    case 0xfffecc00:   /* IMIF_PRIO */
+    case 0xfffecc04:   /* EMIFS_PRIO */
+    case 0xfffecc08:   /* EMIFF_PRIO */
+    case 0xfffecc10:   /* EMIFS_CS0_CONFIG */
+    case 0xfffecc14:   /* EMIFS_CS1_CONFIG */
+    case 0xfffecc18:   /* EMIFS_CS2_CONFIG */
+    case 0xfffecc1c:   /* EMIFS_CS3_CONFIG */
+    case 0xfffecc20:   /* EMIFF_SDRAM_CONFIG */
+    case 0xfffecc24:   /* EMIFF_MRS */
+    case 0xfffecc28:   /* TIMEOUT1 */
+    case 0xfffecc2c:   /* TIMEOUT2 */
+    case 0xfffecc30:   /* TIMEOUT3 */
+    case 0xfffecc3c:   /* EMIFF_SDRAM_CONFIG_2 */
+    case 0xfffecc40:   /* EMIFS_CFG_DYN_WAIT */
+        s->tcmi_regs[offset >> 2] = value;
+        break;
+    case 0xfffecc0c:   /* EMIFS_CONFIG */
+        s->tcmi_regs[offset >> 2] = (value & 0xf) | (1 << 4);
+        break;
+
+    default:
+        OMAP_BAD_REG(addr);
+    }
+}
+
+static CPUReadMemoryFunc *omap_tcmi_readfn[] = {
+    omap_badwidth_read32,
+    omap_badwidth_read32,
+    omap_tcmi_read,
+};
+
+static CPUWriteMemoryFunc *omap_tcmi_writefn[] = {
+    omap_badwidth_write32,
+    omap_badwidth_write32,
+    omap_tcmi_write,
+};
+
+static void omap_tcmi_reset(struct omap_mpu_state_s *mpu)
+{
+    mpu->tcmi_regs[0x00 >> 2] = 0x00000000;
+    mpu->tcmi_regs[0x04 >> 2] = 0x00000000;
+    mpu->tcmi_regs[0x08 >> 2] = 0x00000000;
+    mpu->tcmi_regs[0x0c >> 2] = 0x00000010;
+    mpu->tcmi_regs[0x10 >> 2] = 0x0010fffb;
+    mpu->tcmi_regs[0x14 >> 2] = 0x0010fffb;
+    mpu->tcmi_regs[0x18 >> 2] = 0x0010fffb;
+    mpu->tcmi_regs[0x1c >> 2] = 0x0010fffb;
+    mpu->tcmi_regs[0x20 >> 2] = 0x00618800;
+    mpu->tcmi_regs[0x24 >> 2] = 0x00000037;
+    mpu->tcmi_regs[0x28 >> 2] = 0x00000000;
+    mpu->tcmi_regs[0x2c >> 2] = 0x00000000;
+    mpu->tcmi_regs[0x30 >> 2] = 0x00000000;
+    mpu->tcmi_regs[0x3c >> 2] = 0x00000003;
+    mpu->tcmi_regs[0x40 >> 2] = 0x00000000;
+}
+
+static void omap_tcmi_init(target_phys_addr_t base,
+                struct omap_mpu_state_s *mpu)
+{
+    int iomemtype = cpu_register_io_memory(0, omap_tcmi_readfn,
+                    omap_tcmi_writefn, mpu);
+
+    mpu->tcmi_base = base;
+    cpu_register_physical_memory(mpu->tcmi_base, 0x100, iomemtype);
+    omap_tcmi_reset(mpu);
+}
+
+/* Digital phase-locked loops control */
+static uint32_t omap_dpll_read(void *opaque, target_phys_addr_t addr)
+{
+    struct dpll_ctl_s *s = (struct dpll_ctl_s *) opaque;
+    int offset = addr - s->base;
+
+    if (offset == 0x00)        /* CTL_REG */
+        return s->mode;
+
+    OMAP_BAD_REG(addr);
+    return 0;
+}
+
+static void omap_dpll_write(void *opaque, target_phys_addr_t addr,
+                uint32_t value)
+{
+    struct dpll_ctl_s *s = (struct dpll_ctl_s *) opaque;
+    uint16_t diff;
+    int offset = addr - s->base;
+    static const int bypass_div[4] = { 1, 2, 4, 4 };
+    int div, mult;
+
+    if (offset == 0x00) {      /* CTL_REG */
+        /* See omap_ulpd_pm_write() too */
+        diff = s->mode & value;
+        s->mode = value & 0x2fff;
+        if (diff & (0x3ff << 2)) {
+            if (value & (1 << 4)) {                    /* PLL_ENABLE */
+                div = ((value >> 5) & 3) + 1;          /* PLL_DIV */
+                mult = MIN((value >> 7) & 0x1f, 1);    /* PLL_MULT */
+            } else {
+                div = bypass_div[((value >> 2) & 3)];  /* BYPASS_DIV */
+                mult = 1;
+            }
+            omap_clk_setrate(s->dpll, div, mult);
+        }
+
+        /* Enter the desired mode.  */
+        s->mode = (s->mode & 0xfffe) | ((s->mode >> 4) & 1);
+
+        /* Act as if the lock is restored.  */
+        s->mode |= 2;
+    } else {
+        OMAP_BAD_REG(addr);
+    }
+}
+
+static CPUReadMemoryFunc *omap_dpll_readfn[] = {
+    omap_badwidth_read16,
+    omap_dpll_read,
+    omap_badwidth_read16,
+};
+
+static CPUWriteMemoryFunc *omap_dpll_writefn[] = {
+    omap_badwidth_write16,
+    omap_dpll_write,
+    omap_badwidth_write16,
+};
+
+static void omap_dpll_reset(struct dpll_ctl_s *s)
+{
+    s->mode = 0x2002;
+    omap_clk_setrate(s->dpll, 1, 1);
+}
+
+static void omap_dpll_init(struct dpll_ctl_s *s, target_phys_addr_t base,
+                omap_clk clk)
+{
+    int iomemtype = cpu_register_io_memory(0, omap_dpll_readfn,
+                    omap_dpll_writefn, s);
+
+    s->base = base;
+    s->dpll = clk;
+    omap_dpll_reset(s);
+
+    cpu_register_physical_memory(s->base, 0x100, iomemtype);
+}
+
+/* UARTs */
+struct omap_uart_s {
+    SerialState *serial; /* TODO */
+};
+
+static void omap_uart_reset(struct omap_uart_s *s)
+{
+}
+
+struct omap_uart_s *omap_uart_init(target_phys_addr_t base,
+                qemu_irq irq, omap_clk clk, CharDriverState *chr)
+{
+    struct omap_uart_s *s = (struct omap_uart_s *)
+            qemu_mallocz(sizeof(struct omap_uart_s));
+    if (chr)
+        s->serial = serial_mm_init(base, 2, irq, chr, 1);
+    return s;
+}
+
+/* MPU Clock/Reset/Power Mode Control */
+static uint32_t omap_clkm_read(void *opaque, target_phys_addr_t addr)
+{
+    struct omap_mpu_state_s *s = (struct omap_mpu_state_s *) opaque;
+    int offset = addr - s->clkm.mpu_base;
+
+    switch (offset) {
+    case 0x00: /* ARM_CKCTL */
+        return s->clkm.arm_ckctl;
+
+    case 0x04: /* ARM_IDLECT1 */
+        return s->clkm.arm_idlect1;
+
+    case 0x08: /* ARM_IDLECT2 */
+        return s->clkm.arm_idlect2;
+
+    case 0x0c: /* ARM_EWUPCT */
+        return s->clkm.arm_ewupct;
+
+    case 0x10: /* ARM_RSTCT1 */
+        return s->clkm.arm_rstct1;
+
+    case 0x14: /* ARM_RSTCT2 */
+        return s->clkm.arm_rstct2;
+
+    case 0x18: /* ARM_SYSST */
+        return (s->clkm.clocking_scheme < 11) | s->clkm.cold_start;
+
+    case 0x1c: /* ARM_CKOUT1 */
+        return s->clkm.arm_ckout1;
+
+    case 0x20: /* ARM_CKOUT2 */
+        break;
+    }
+
+    OMAP_BAD_REG(addr);
+    return 0;
+}
+
+static inline void omap_clkm_ckctl_update(struct omap_mpu_state_s *s,
+                uint16_t diff, uint16_t value)
+{
+    omap_clk clk;
+
+    if (diff & (1 << 14)) {                            /* ARM_INTHCK_SEL */
+        if (value & (1 << 14))
+            /* Reserved */;
+        else {
+            clk = omap_findclk(s, "arminth_ck");
+            omap_clk_reparent(clk, omap_findclk(s, "tc_ck"));
+        }
+    }
+    if (diff & (1 << 12)) {                            /* ARM_TIMXO */
+        clk = omap_findclk(s, "armtim_ck");
+        if (value & (1 << 12))
+            omap_clk_reparent(clk, omap_findclk(s, "clkin"));
+        else
+            omap_clk_reparent(clk, omap_findclk(s, "ck_gen1"));
+    }
+    /* XXX: en_dspck */
+    if (diff & (3 << 10)) {                            /* DSPMMUDIV */
+        clk = omap_findclk(s, "dspmmu_ck");
+        omap_clk_setrate(clk, 1 << ((value >> 10) & 3), 1);
+    }
+    if (diff & (3 << 8)) {                             /* TCDIV */
+        clk = omap_findclk(s, "tc_ck");
+        omap_clk_setrate(clk, 1 << ((value >> 8) & 3), 1);
+    }
+    if (diff & (3 << 6)) {                             /* DSPDIV */
+        clk = omap_findclk(s, "dsp_ck");
+        omap_clk_setrate(clk, 1 << ((value >> 6) & 3), 1);
+    }
+    if (diff & (3 << 4)) {                             /* ARMDIV */
+        clk = omap_findclk(s, "arm_ck");
+        omap_clk_setrate(clk, 1 << ((value >> 4) & 3), 1);
+    }
+    if (diff & (3 << 2)) {                             /* LCDDIV */
+        clk = omap_findclk(s, "lcd_ck");
+        omap_clk_setrate(clk, 1 << ((value >> 2) & 3), 1);
+    }
+    if (diff & (3 << 0)) {                             /* PERDIV */
+        clk = omap_findclk(s, "armper_ck");
+        omap_clk_setrate(clk, 1 << ((value >> 0) & 3), 1);
+    }
+}
+
+static inline void omap_clkm_idlect1_update(struct omap_mpu_state_s *s,
+                uint16_t diff, uint16_t value)
+{
+    omap_clk clk;
+
+    if (value & (1 << 11))                             /* SETARM_IDLE */
+        cpu_interrupt(s->env, CPU_INTERRUPT_HALT);
+    if (!(value & (1 << 10)))                          /* WKUP_MODE */
+        qemu_system_shutdown_request();        /* XXX: disable wakeup from IRQ */
+
+#define SET_CANIDLE(clock, bit)                                \
+    if (diff & (1 << bit)) {                           \
+        clk = omap_findclk(s, clock);                  \
+        omap_clk_canidle(clk, (value >> bit) & 1);     \
+    }
+    SET_CANIDLE("mpuwd_ck", 0)                         /* IDLWDT_ARM */
+    SET_CANIDLE("armxor_ck", 1)                                /* IDLXORP_ARM */
+    SET_CANIDLE("mpuper_ck", 2)                                /* IDLPER_ARM */
+    SET_CANIDLE("lcd_ck", 3)                           /* IDLLCD_ARM */
+    SET_CANIDLE("lb_ck", 4)                            /* IDLLB_ARM */
+    SET_CANIDLE("hsab_ck", 5)                          /* IDLHSAB_ARM */
+    SET_CANIDLE("tipb_ck", 6)                          /* IDLIF_ARM */
+    SET_CANIDLE("dma_ck", 6)                           /* IDLIF_ARM */
+    SET_CANIDLE("tc_ck", 6)                            /* IDLIF_ARM */
+    SET_CANIDLE("dpll1", 7)                            /* IDLDPLL_ARM */
+    SET_CANIDLE("dpll2", 7)                            /* IDLDPLL_ARM */
+    SET_CANIDLE("dpll3", 7)                            /* IDLDPLL_ARM */
+    SET_CANIDLE("mpui_ck", 8)                          /* IDLAPI_ARM */
+    SET_CANIDLE("armtim_ck", 9)                                /* IDLTIM_ARM */
+}
+
+static inline void omap_clkm_idlect2_update(struct omap_mpu_state_s *s,
+                uint16_t diff, uint16_t value)
+{
+    omap_clk clk;
+
+#define SET_ONOFF(clock, bit)                          \
+    if (diff & (1 << bit)) {                           \
+        clk = omap_findclk(s, clock);                  \
+        omap_clk_onoff(clk, (value >> bit) & 1);       \
+    }
+    SET_ONOFF("mpuwd_ck", 0)                           /* EN_WDTCK */
+    SET_ONOFF("armxor_ck", 1)                          /* EN_XORPCK */
+    SET_ONOFF("mpuper_ck", 2)                          /* EN_PERCK */
+    SET_ONOFF("lcd_ck", 3)                             /* EN_LCDCK */
+    SET_ONOFF("lb_ck", 4)                              /* EN_LBCK */
+    SET_ONOFF("hsab_ck", 5)                            /* EN_HSABCK */
+    SET_ONOFF("mpui_ck", 6)                            /* EN_APICK */
+    SET_ONOFF("armtim_ck", 7)                          /* EN_TIMCK */
+    SET_CANIDLE("dma_ck", 8)                           /* DMACK_REQ */
+    SET_ONOFF("arm_gpio_ck", 9)                                /* EN_GPIOCK */
+    SET_ONOFF("lbfree_ck", 10)                         /* EN_LBFREECK */
+}
+
+static inline void omap_clkm_ckout1_update(struct omap_mpu_state_s *s,
+                uint16_t diff, uint16_t value)
+{
+    omap_clk clk;
+
+    if (diff & (3 << 4)) {                             /* TCLKOUT */
+        clk = omap_findclk(s, "tclk_out");
+        switch ((value >> 4) & 3) {
+        case 1:
+            omap_clk_reparent(clk, omap_findclk(s, "ck_gen3"));
+            omap_clk_onoff(clk, 1);
+            break;
+        case 2:
+            omap_clk_reparent(clk, omap_findclk(s, "tc_ck"));
+            omap_clk_onoff(clk, 1);
+            break;
+        default:
+            omap_clk_onoff(clk, 0);
+        }
+    }
+    if (diff & (3 << 2)) {                             /* DCLKOUT */
+        clk = omap_findclk(s, "dclk_out");
+        switch ((value >> 2) & 3) {
+        case 0:
+            omap_clk_reparent(clk, omap_findclk(s, "dspmmu_ck"));
+            break;
+        case 1:
+            omap_clk_reparent(clk, omap_findclk(s, "ck_gen2"));
+            break;
+        case 2:
+            omap_clk_reparent(clk, omap_findclk(s, "dsp_ck"));
+            break;
+        case 3:
+            omap_clk_reparent(clk, omap_findclk(s, "ck_ref14"));
+            break;
+        }
+    }
+    if (diff & (3 << 0)) {                             /* ACLKOUT */
+        clk = omap_findclk(s, "aclk_out");
+        switch ((value >> 0) & 3) {
+        case 1:
+            omap_clk_reparent(clk, omap_findclk(s, "ck_gen1"));
+            omap_clk_onoff(clk, 1);
+            break;
+        case 2:
+            omap_clk_reparent(clk, omap_findclk(s, "arm_ck"));
+            omap_clk_onoff(clk, 1);
+            break;
+        case 3:
+            omap_clk_reparent(clk, omap_findclk(s, "ck_ref14"));
+            omap_clk_onoff(clk, 1);
+            break;
+        default:
+            omap_clk_onoff(clk, 0);
+        }
+    }
+}
+
+static void omap_clkm_write(void *opaque, target_phys_addr_t addr,
+                uint32_t value)
+{
+    struct omap_mpu_state_s *s = (struct omap_mpu_state_s *) opaque;
+    int offset = addr - s->clkm.mpu_base;
+    uint16_t diff;
+    omap_clk clk;
+    static const char *clkschemename[8] = {
+        "fully synchronous", "fully asynchronous", "synchronous scalable",
+        "mix mode 1", "mix mode 2", "bypass mode", "mix mode 3", "mix mode 4",
+    };
+
+    switch (offset) {
+    case 0x00: /* ARM_CKCTL */
+        diff = s->clkm.arm_ckctl ^ value;
+        s->clkm.arm_ckctl = value & 0x7fff;
+        omap_clkm_ckctl_update(s, diff, value);
+        return;
+
+    case 0x04: /* ARM_IDLECT1 */
+        diff = s->clkm.arm_idlect1 ^ value;
+        s->clkm.arm_idlect1 = value & 0x0fff;
+        omap_clkm_idlect1_update(s, diff, value);
+        return;
+
+    case 0x08: /* ARM_IDLECT2 */
+        diff = s->clkm.arm_idlect2 ^ value;
+        s->clkm.arm_idlect2 = value & 0x07ff;
+        omap_clkm_idlect2_update(s, diff, value);
+        return;
+
+    case 0x0c: /* ARM_EWUPCT */
+        diff = s->clkm.arm_ewupct ^ value;
+        s->clkm.arm_ewupct = value & 0x003f;
+        return;
+
+    case 0x10: /* ARM_RSTCT1 */
+        diff = s->clkm.arm_rstct1 ^ value;
+        s->clkm.arm_rstct1 = value & 0x0007;
+        if (value & 9) {
+            qemu_system_reset_request();
+            s->clkm.cold_start = 0xa;
+        }
+        if (diff & ~value & 4) {                               /* DSP_RST */
+            omap_mpui_reset(s);
+            omap_tipb_bridge_reset(s->private_tipb);
+            omap_tipb_bridge_reset(s->public_tipb);
+        }
+        if (diff & 2) {                                                /* DSP_EN */
+            clk = omap_findclk(s, "dsp_ck");
+            omap_clk_canidle(clk, (~value >> 1) & 1);
+        }
+        return;
+
+    case 0x14: /* ARM_RSTCT2 */
+        s->clkm.arm_rstct2 = value & 0x0001;
+        return;
+
+    case 0x18: /* ARM_SYSST */
+        if ((s->clkm.clocking_scheme ^ (value >> 11)) & 7) {
+            s->clkm.clocking_scheme = (value >> 11) & 7;
+            printf("%s: clocking scheme set to %s\n", __FUNCTION__,
+                            clkschemename[s->clkm.clocking_scheme]);
+        }
+        s->clkm.cold_start &= value & 0x3f;
+        return;
+
+    case 0x1c: /* ARM_CKOUT1 */
+        diff = s->clkm.arm_ckout1 ^ value;
+        s->clkm.arm_ckout1 = value & 0x003f;
+        omap_clkm_ckout1_update(s, diff, value);
+        return;
+
+    case 0x20: /* ARM_CKOUT2 */
+    default:
+        OMAP_BAD_REG(addr);
+    }
+}
+
+static CPUReadMemoryFunc *omap_clkm_readfn[] = {
+    omap_badwidth_read16,
+    omap_clkm_read,
+    omap_badwidth_read16,
+};
+
+static CPUWriteMemoryFunc *omap_clkm_writefn[] = {
+    omap_badwidth_write16,
+    omap_clkm_write,
+    omap_badwidth_write16,
+};
+
+static uint32_t omap_clkdsp_read(void *opaque, target_phys_addr_t addr)
+{
+    struct omap_mpu_state_s *s = (struct omap_mpu_state_s *) opaque;
+    int offset = addr - s->clkm.dsp_base;
+
+    switch (offset) {
+    case 0x04: /* DSP_IDLECT1 */
+        return s->clkm.dsp_idlect1;
+
+    case 0x08: /* DSP_IDLECT2 */
+        return s->clkm.dsp_idlect2;
+
+    case 0x14: /* DSP_RSTCT2 */
+        return s->clkm.dsp_rstct2;
+
+    case 0x18: /* DSP_SYSST */
+        return (s->clkm.clocking_scheme < 11) | s->clkm.cold_start |
+                (s->env->halted << 6); /* Quite useless... */
+    }
+
+    OMAP_BAD_REG(addr);
+    return 0;
+}
+
+static inline void omap_clkdsp_idlect1_update(struct omap_mpu_state_s *s,
+                uint16_t diff, uint16_t value)
+{
+    omap_clk clk;
+
+    SET_CANIDLE("dspxor_ck", 1);                       /* IDLXORP_DSP */
+}
+
+static inline void omap_clkdsp_idlect2_update(struct omap_mpu_state_s *s,
+                uint16_t diff, uint16_t value)
+{
+    omap_clk clk;
+
+    SET_ONOFF("dspxor_ck", 1);                         /* EN_XORPCK */
+}
+
+static void omap_clkdsp_write(void *opaque, target_phys_addr_t addr,
+                uint32_t value)
+{
+    struct omap_mpu_state_s *s = (struct omap_mpu_state_s *) opaque;
+    int offset = addr - s->clkm.dsp_base;
+    uint16_t diff;
+
+    switch (offset) {
+    case 0x04: /* DSP_IDLECT1 */
+        diff = s->clkm.dsp_idlect1 ^ value;
+        s->clkm.dsp_idlect1 = value & 0x01f7;
+        omap_clkdsp_idlect1_update(s, diff, value);
+        break;
+
+    case 0x08: /* DSP_IDLECT2 */
+        s->clkm.dsp_idlect2 = value & 0x0037;
+        diff = s->clkm.dsp_idlect1 ^ value;
+        omap_clkdsp_idlect2_update(s, diff, value);
+        break;
+
+    case 0x14: /* DSP_RSTCT2 */
+        s->clkm.dsp_rstct2 = value & 0x0001;
+        break;
+
+    case 0x18: /* DSP_SYSST */
+        s->clkm.cold_start &= value & 0x3f;
+        break;
+
+    default:
+        OMAP_BAD_REG(addr);
+    }
+}
+
+static CPUReadMemoryFunc *omap_clkdsp_readfn[] = {
+    omap_badwidth_read16,
+    omap_clkdsp_read,
+    omap_badwidth_read16,
+};
+
+static CPUWriteMemoryFunc *omap_clkdsp_writefn[] = {
+    omap_badwidth_write16,
+    omap_clkdsp_write,
+    omap_badwidth_write16,
+};
+
+static void omap_clkm_reset(struct omap_mpu_state_s *s)
+{
+    if (s->wdt && s->wdt->reset)
+        s->clkm.cold_start = 0x6;
+    s->clkm.clocking_scheme = 0;
+    omap_clkm_ckctl_update(s, ~0, 0x3000);
+    s->clkm.arm_ckctl = 0x3000;
+    omap_clkm_idlect1_update(s, s->clkm.arm_idlect1 & 0x0400, 0x0400);
+    s->clkm.arm_idlect1 = 0x0400;
+    omap_clkm_idlect2_update(s, s->clkm.arm_idlect2 & 0x0100, 0x0100);
+    s->clkm.arm_idlect2 = 0x0100;
+    s->clkm.arm_ewupct = 0x003f;
+    s->clkm.arm_rstct1 = 0x0000;
+    s->clkm.arm_rstct2 = 0x0000;
+    s->clkm.arm_ckout1 = 0x0015;
+    s->clkm.dpll1_mode = 0x2002;
+    omap_clkdsp_idlect1_update(s, s->clkm.dsp_idlect1 ^ 0x0040, 0x0040);
+    s->clkm.dsp_idlect1 = 0x0040;
+    omap_clkdsp_idlect2_update(s, ~0, 0x0000);
+    s->clkm.dsp_idlect2 = 0x0000;
+    s->clkm.dsp_rstct2 = 0x0000;
+}
+
+static void omap_clkm_init(target_phys_addr_t mpu_base,
+                target_phys_addr_t dsp_base, struct omap_mpu_state_s *s)
+{
+    int iomemtype[2] = {
+        cpu_register_io_memory(0, omap_clkm_readfn, omap_clkm_writefn, s),
+        cpu_register_io_memory(0, omap_clkdsp_readfn, omap_clkdsp_writefn, s),
+    };
+
+    s->clkm.mpu_base = mpu_base;
+    s->clkm.dsp_base = dsp_base;
+    s->clkm.cold_start = 0x3a;
+    omap_clkm_reset(s);
+
+    cpu_register_physical_memory(s->clkm.mpu_base, 0x100, iomemtype[0]);
+    cpu_register_physical_memory(s->clkm.dsp_base, 0x1000, iomemtype[1]);
+}
+
+/* General chip reset */
+static void omap_mpu_reset(void *opaque)
+{
+    struct omap_mpu_state_s *mpu = (struct omap_mpu_state_s *) opaque;
+
+    omap_clkm_reset(mpu);
+    omap_inth_reset(mpu->ih[0]);
+    omap_inth_reset(mpu->ih[1]);
+    omap_dma_reset(mpu->dma);
+    omap_mpu_timer_reset(mpu->timer[0]);
+    omap_mpu_timer_reset(mpu->timer[1]);
+    omap_mpu_timer_reset(mpu->timer[2]);
+    omap_wd_timer_reset(mpu->wdt);
+    omap_os_timer_reset(mpu->os_timer);
+    omap_lcdc_reset(mpu->lcd);
+    omap_ulpd_pm_reset(mpu);
+    omap_pin_cfg_reset(mpu);
+    omap_mpui_reset(mpu);
+    omap_tipb_bridge_reset(mpu->private_tipb);
+    omap_tipb_bridge_reset(mpu->public_tipb);
+    omap_dpll_reset(&mpu->dpll[0]);
+    omap_dpll_reset(&mpu->dpll[1]);
+    omap_dpll_reset(&mpu->dpll[2]);
+    omap_uart_reset(mpu->uart1);
+    omap_uart_reset(mpu->uart2);
+    omap_uart_reset(mpu->uart3);
+    cpu_reset(mpu->env);
+}
+
+static void omap_mpu_wakeup(void *opaque, int irq, int req)
+{
+    struct omap_mpu_state_s *mpu = (struct omap_mpu_state_s *) opaque;
+
+    cpu_interrupt(mpu->env, CPU_INTERRUPT_EXITTB);
+}
+
+struct omap_mpu_state_s *omap310_mpu_init(unsigned long sdram_size,
+                DisplayState *ds, const char *core)
+{
+    struct omap_mpu_state_s *s = (struct omap_mpu_state_s *)
+            qemu_mallocz(sizeof(struct omap_mpu_state_s));
+    ram_addr_t imif_base, emiff_base;
+
+    /* Core */
+    s->mpu_model = omap310;
+    s->env = cpu_init();
+    s->sdram_size = sdram_size;
+    s->sram_size = OMAP15XX_SRAM_SIZE;
+
+    cpu_arm_set_model(s->env, core ?: "ti925t");
+
+    /* Clocks */
+    omap_clk_init(s);
+
+    /* Memory-mapped stuff */
+    cpu_register_physical_memory(OMAP_EMIFF_BASE, s->sdram_size,
+                    (emiff_base = qemu_ram_alloc(s->sdram_size)) | IO_MEM_RAM);
+    cpu_register_physical_memory(OMAP_IMIF_BASE, s->sram_size,
+                    (imif_base = qemu_ram_alloc(s->sram_size)) | IO_MEM_RAM);
+
+    omap_clkm_init(0xfffece00, 0xe1008000, s);
+
+    s->ih[0] = omap_inth_init(0xfffecb00, 0x100,
+                    arm_pic_init_cpu(s->env),
+                    omap_findclk(s, "arminth_ck"));
+    s->ih[1] = omap_inth_init(0xfffe0000, 0x800,
+                    &s->ih[0]->pins[OMAP_INT_15XX_IH2_IRQ],
+                    omap_findclk(s, "arminth_ck"));
+    s->irq[0] = s->ih[0]->pins;
+    s->irq[1] = s->ih[1]->pins;
+
+    s->dma = omap_dma_init(0xfffed800, s->irq[0], s,
+                    omap_findclk(s, "dma_ck"));
+    s->port[emiff    ].addr_valid = omap_validate_emiff_addr;
+    s->port[emifs    ].addr_valid = omap_validate_emifs_addr;
+    s->port[imif     ].addr_valid = omap_validate_imif_addr;
+    s->port[tipb     ].addr_valid = omap_validate_tipb_addr;
+    s->port[local    ].addr_valid = omap_validate_local_addr;
+    s->port[tipb_mpui].addr_valid = omap_validate_tipb_mpui_addr;
+
+    s->timer[0] = omap_mpu_timer_init(0xfffec500,
+                    s->irq[0][OMAP_INT_TIMER1],
+                    omap_findclk(s, "mputim_ck"));
+    s->timer[1] = omap_mpu_timer_init(0xfffec600,
+                    s->irq[0][OMAP_INT_TIMER2],
+                    omap_findclk(s, "mputim_ck"));
+    s->timer[2] = omap_mpu_timer_init(0xfffec700,
+                    s->irq[0][OMAP_INT_TIMER3],
+                    omap_findclk(s, "mputim_ck"));
+
+    s->wdt = omap_wd_timer_init(0xfffec800,
+                    s->irq[0][OMAP_INT_WD_TIMER],
+                    omap_findclk(s, "armwdt_ck"));
+
+    s->os_timer = omap_os_timer_init(0xfffb9000,
+                    s->irq[1][OMAP_INT_OS_TIMER],
+                    omap_findclk(s, "clk32-kHz"));
+
+    s->lcd = omap_lcdc_init(0xfffec000, s->irq[0][OMAP_INT_LCD_CTRL],
+                    &s->dma->lcd_ch, ds, imif_base, emiff_base,
+                    omap_findclk(s, "lcd_ck"));
+
+    omap_ulpd_pm_init(0xfffe0800, s);
+    omap_pin_cfg_init(0xfffe1000, s);
+    omap_id_init(s);
+
+    omap_mpui_init(0xfffec900, s);
+
+    s->private_tipb = omap_tipb_bridge_init(0xfffeca00,
+                    s->irq[0][OMAP_INT_BRIDGE_PRIV],
+                    omap_findclk(s, "tipb_ck"));
+    s->public_tipb = omap_tipb_bridge_init(0xfffed300,
+                    s->irq[0][OMAP_INT_BRIDGE_PUB],
+                    omap_findclk(s, "tipb_ck"));
+
+    omap_tcmi_init(0xfffecc00, s);
+
+    s->uart1 = omap_uart_init(0xfffb0000, s->irq[1][OMAP_INT_UART1],
+                    omap_findclk(s, "uart1_ck"),
+                    serial_hds[0]);
+    s->uart2 = omap_uart_init(0xfffb0800, s->irq[1][OMAP_INT_UART2],
+                    omap_findclk(s, "uart2_ck"),
+                    serial_hds[0] ? serial_hds[1] : 0);
+    s->uart3 = omap_uart_init(0xe1019800, s->irq[0][OMAP_INT_UART3],
+                    omap_findclk(s, "uart3_ck"),
+                    serial_hds[0] && serial_hds[1] ? serial_hds[2] : 0);
+
+    omap_dpll_init(&s->dpll[0], 0xfffecf00, omap_findclk(s, "dpll1"));
+    omap_dpll_init(&s->dpll[1], 0xfffed000, omap_findclk(s, "dpll2"));
+    omap_dpll_init(&s->dpll[2], 0xfffed100, omap_findclk(s, "dpll3"));
+
+    qemu_register_reset(omap_mpu_reset, s);
+    s->wakeup = qemu_allocate_irqs(omap_mpu_wakeup, s, 1)[0];
+
+    return s;
+}
diff --git a/hw/omap.h b/hw/omap.h
new file mode 100644 (file)
index 0000000..e30d38c
--- /dev/null
+++ b/hw/omap.h
@@ -0,0 +1,581 @@
+/*
+ * Texas Instruments OMAP processors.
+ *
+ * Copyright (C) 2006-2007 Andrzej Zaborowski  <balrog@zabor.org>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
+ * MA 02111-1307 USA
+ */
+#ifndef hw_omap_h
+# define hw_omap_h             "omap.h"
+
+# define OMAP_EMIFS_BASE       0x00000000
+# define OMAP_CS0_BASE         0x00000000
+# define OMAP_CS1_BASE         0x04000000
+# define OMAP_CS2_BASE         0x08000000
+# define OMAP_CS3_BASE         0x0c000000
+# define OMAP_EMIFF_BASE       0x10000000
+# define OMAP_IMIF_BASE                0x20000000
+# define OMAP_LOCALBUS_BASE    0x30000000
+# define OMAP_MPUI_BASE                0xe1000000
+
+# define OMAP730_SRAM_SIZE     0x00032000
+# define OMAP15XX_SRAM_SIZE    0x00030000
+# define OMAP16XX_SRAM_SIZE    0x00004000
+# define OMAP1611_SRAM_SIZE    0x0003e800
+# define OMAP_CS0_SIZE         0x04000000
+# define OMAP_CS1_SIZE         0x04000000
+# define OMAP_CS2_SIZE         0x04000000
+# define OMAP_CS3_SIZE         0x04000000
+
+/* omap1_clk.c */
+struct omap_mpu_state_s;
+typedef struct clk *omap_clk;
+omap_clk omap_findclk(struct omap_mpu_state_s *mpu, const char *name);
+void omap_clk_init(struct omap_mpu_state_s *mpu);
+void omap_clk_adduser(struct clk *clk, qemu_irq user);
+void omap_clk_get(omap_clk clk);
+void omap_clk_put(omap_clk clk);
+void omap_clk_onoff(omap_clk clk, int on);
+void omap_clk_canidle(omap_clk clk, int can);
+void omap_clk_setrate(omap_clk clk, int divide, int multiply);
+int64_t omap_clk_getrate(omap_clk clk);
+void omap_clk_reparent(omap_clk clk, omap_clk parent);
+
+/* omap.c */
+struct omap_intr_handler_s;
+struct omap_intr_handler_s *omap_inth_init(target_phys_addr_t base,
+                unsigned long size, qemu_irq parent[2], omap_clk clk);
+
+/*
+ * Common IRQ numbers for level 1 interrupt handler
+ * See /usr/include/asm-arm/arch-omap/irqs.h in Linux.
+ */
+# define OMAP_INT_CAMERA               1
+# define OMAP_INT_FIQ                  3
+# define OMAP_INT_RTDX                 6
+# define OMAP_INT_DSP_MMU_ABORT                7
+# define OMAP_INT_HOST                 8
+# define OMAP_INT_ABORT                        9
+# define OMAP_INT_BRIDGE_PRIV          13
+# define OMAP_INT_GPIO_BANK1           14
+# define OMAP_INT_UART3                        15
+# define OMAP_INT_TIMER3               16
+# define OMAP_INT_DMA_CH0_6            19
+# define OMAP_INT_DMA_CH1_7            20
+# define OMAP_INT_DMA_CH2_8            21
+# define OMAP_INT_DMA_CH3              22
+# define OMAP_INT_DMA_CH4              23
+# define OMAP_INT_DMA_CH5              24
+# define OMAP_INT_DMA_LCD              25
+# define OMAP_INT_TIMER1               26
+# define OMAP_INT_WD_TIMER             27
+# define OMAP_INT_BRIDGE_PUB           28
+# define OMAP_INT_TIMER2               30
+# define OMAP_INT_LCD_CTRL             31
+
+/*
+ * Common OMAP-15xx IRQ numbers for level 1 interrupt handler
+ */
+# define OMAP_INT_15XX_IH2_IRQ         0
+# define OMAP_INT_15XX_LB_MMU          17
+# define OMAP_INT_15XX_LOCAL_BUS       29
+
+/*
+ * OMAP-1510 specific IRQ numbers for level 1 interrupt handler
+ */
+# define OMAP_INT_1510_SPI_TX          4
+# define OMAP_INT_1510_SPI_RX          5
+# define OMAP_INT_1510_DSP_MAILBOX1    10
+# define OMAP_INT_1510_DSP_MAILBOX2    11
+
+/*
+ * OMAP-310 specific IRQ numbers for level 1 interrupt handler
+ */
+# define OMAP_INT_310_McBSP2_TX                4
+# define OMAP_INT_310_McBSP2_RX                5
+# define OMAP_INT_310_HSB_MAILBOX1     12
+# define OMAP_INT_310_HSAB_MMU         18
+
+/*
+ * OMAP-1610 specific IRQ numbers for level 1 interrupt handler
+ */
+# define OMAP_INT_1610_IH2_IRQ         0
+# define OMAP_INT_1610_IH2_FIQ         2
+# define OMAP_INT_1610_McBSP2_TX       4
+# define OMAP_INT_1610_McBSP2_RX       5
+# define OMAP_INT_1610_DSP_MAILBOX1    10
+# define OMAP_INT_1610_DSP_MAILBOX2    11
+# define OMAP_INT_1610_LCD_LINE                12
+# define OMAP_INT_1610_GPTIMER1                17
+# define OMAP_INT_1610_GPTIMER2                18
+# define OMAP_INT_1610_SSR_FIFO_0      29
+
+/*
+ * OMAP-730 specific IRQ numbers for level 1 interrupt handler
+ */
+# define OMAP_INT_730_IH2_FIQ          0
+# define OMAP_INT_730_IH2_IRQ          1
+# define OMAP_INT_730_USB_NON_ISO      2
+# define OMAP_INT_730_USB_ISO          3
+# define OMAP_INT_730_ICR              4
+# define OMAP_INT_730_EAC              5
+# define OMAP_INT_730_GPIO_BANK1       6
+# define OMAP_INT_730_GPIO_BANK2       7
+# define OMAP_INT_730_GPIO_BANK3       8
+# define OMAP_INT_730_McBSP2TX         10
+# define OMAP_INT_730_McBSP2RX         11
+# define OMAP_INT_730_McBSP2RX_OVF     12
+# define OMAP_INT_730_LCD_LINE         14
+# define OMAP_INT_730_GSM_PROTECT      15
+# define OMAP_INT_730_TIMER3           16
+# define OMAP_INT_730_GPIO_BANK5       17
+# define OMAP_INT_730_GPIO_BANK6       18
+# define OMAP_INT_730_SPGIO_WR         29
+
+/*
+ * Common IRQ numbers for level 2 interrupt handler
+ */
+# define OMAP_INT_KEYBOARD             1
+# define OMAP_INT_uWireTX              2
+# define OMAP_INT_uWireRX              3
+# define OMAP_INT_I2C                  4
+# define OMAP_INT_MPUIO                        5
+# define OMAP_INT_USB_HHC_1            6
+# define OMAP_INT_McBSP3TX             10
+# define OMAP_INT_McBSP3RX             11
+# define OMAP_INT_McBSP1TX             12
+# define OMAP_INT_McBSP1RX             13
+# define OMAP_INT_UART1                        14
+# define OMAP_INT_UART2                        15
+# define OMAP_INT_USB_W2FC             20
+# define OMAP_INT_1WIRE                        21
+# define OMAP_INT_OS_TIMER             22
+# define OMAP_INT_MMC                  23
+# define OMAP_INT_GAUGE_32K            24
+# define OMAP_INT_RTC_TIMER            25
+# define OMAP_INT_RTC_ALARM            26
+# define OMAP_INT_DSP_MMU              28
+
+/*
+ * OMAP-1510 specific IRQ numbers for level 2 interrupt handler
+ */
+# define OMAP_INT_1510_BT_MCSI1TX      16
+# define OMAP_INT_1510_BT_MCSI1RX      17
+# define OMAP_INT_1510_SoSSI_MATCH     19
+# define OMAP_INT_1510_MEM_STICK       27
+# define OMAP_INT_1510_COM_SPI_RO      31
+
+/*
+ * OMAP-310 specific IRQ numbers for level 2 interrupt handler
+ */
+# define OMAP_INT_310_FAC              0
+# define OMAP_INT_310_USB_HHC_2                7
+# define OMAP_INT_310_MCSI1_FE         16
+# define OMAP_INT_310_MCSI2_FE         17
+# define OMAP_INT_310_USB_W2FC_ISO     29
+# define OMAP_INT_310_USB_W2FC_NON_ISO 30
+# define OMAP_INT_310_McBSP2RX_OF      31
+
+/*
+ * OMAP-1610 specific IRQ numbers for level 2 interrupt handler
+ */
+# define OMAP_INT_1610_FAC             0
+# define OMAP_INT_1610_USB_HHC_2       7
+# define OMAP_INT_1610_USB_OTG         8
+# define OMAP_INT_1610_SoSSI           9
+# define OMAP_INT_1610_BT_MCSI1TX      16
+# define OMAP_INT_1610_BT_MCSI1RX      17
+# define OMAP_INT_1610_SoSSI_MATCH     19
+# define OMAP_INT_1610_MEM_STICK       27
+# define OMAP_INT_1610_McBSP2RX_OF     31
+# define OMAP_INT_1610_STI             32
+# define OMAP_INT_1610_STI_WAKEUP      33
+# define OMAP_INT_1610_GPTIMER3                34
+# define OMAP_INT_1610_GPTIMER4                35
+# define OMAP_INT_1610_GPTIMER5                36
+# define OMAP_INT_1610_GPTIMER6                37
+# define OMAP_INT_1610_GPTIMER7                38
+# define OMAP_INT_1610_GPTIMER8                39
+# define OMAP_INT_1610_GPIO_BANK2      40
+# define OMAP_INT_1610_GPIO_BANK3      41
+# define OMAP_INT_1610_MMC2            42
+# define OMAP_INT_1610_CF              43
+# define OMAP_INT_1610_WAKE_UP_REQ     46
+# define OMAP_INT_1610_GPIO_BANK4      48
+# define OMAP_INT_1610_SPI             49
+# define OMAP_INT_1610_DMA_CH6         53
+# define OMAP_INT_1610_DMA_CH7         54
+# define OMAP_INT_1610_DMA_CH8         55
+# define OMAP_INT_1610_DMA_CH9         56
+# define OMAP_INT_1610_DMA_CH10                57
+# define OMAP_INT_1610_DMA_CH11                58
+# define OMAP_INT_1610_DMA_CH12                59
+# define OMAP_INT_1610_DMA_CH13                60
+# define OMAP_INT_1610_DMA_CH14                61
+# define OMAP_INT_1610_DMA_CH15                62
+# define OMAP_INT_1610_NAND            63
+
+/*
+ * OMAP-730 specific IRQ numbers for level 2 interrupt handler
+ */
+# define OMAP_INT_730_HW_ERRORS                0
+# define OMAP_INT_730_NFIQ_PWR_FAIL    1
+# define OMAP_INT_730_CFCD             2
+# define OMAP_INT_730_CFIREQ           3
+# define OMAP_INT_730_I2C              4
+# define OMAP_INT_730_PCC              5
+# define OMAP_INT_730_MPU_EXT_NIRQ     6
+# define OMAP_INT_730_SPI_100K_1       7
+# define OMAP_INT_730_SYREN_SPI                8
+# define OMAP_INT_730_VLYNQ            9
+# define OMAP_INT_730_GPIO_BANK4       10
+# define OMAP_INT_730_McBSP1TX         11
+# define OMAP_INT_730_McBSP1RX         12
+# define OMAP_INT_730_McBSP1RX_OF      13
+# define OMAP_INT_730_UART_MODEM_IRDA_2        14
+# define OMAP_INT_730_UART_MODEM_1     15
+# define OMAP_INT_730_MCSI             16
+# define OMAP_INT_730_uWireTX          17
+# define OMAP_INT_730_uWireRX          18
+# define OMAP_INT_730_SMC_CD           19
+# define OMAP_INT_730_SMC_IREQ         20
+# define OMAP_INT_730_HDQ_1WIRE                21
+# define OMAP_INT_730_TIMER32K         22
+# define OMAP_INT_730_MMC_SDIO         23
+# define OMAP_INT_730_UPLD             24
+# define OMAP_INT_730_USB_HHC_1                27
+# define OMAP_INT_730_USB_HHC_2                28
+# define OMAP_INT_730_USB_GENI         29
+# define OMAP_INT_730_USB_OTG          30
+# define OMAP_INT_730_CAMERA_IF                31
+# define OMAP_INT_730_RNG              32
+# define OMAP_INT_730_DUAL_MODE_TIMER  33
+# define OMAP_INT_730_DBB_RF_EN                34
+# define OMAP_INT_730_MPUIO_KEYPAD     35
+# define OMAP_INT_730_SHA1_MD5         36
+# define OMAP_INT_730_SPI_100K_2       37
+# define OMAP_INT_730_RNG_IDLE         38
+# define OMAP_INT_730_MPUIO            39
+# define OMAP_INT_730_LLPC_LCD_CTRL_OFF        40
+# define OMAP_INT_730_LLPC_OE_FALLING  41
+# define OMAP_INT_730_LLPC_OE_RISING   42
+# define OMAP_INT_730_LLPC_VSYNC       43
+# define OMAP_INT_730_WAKE_UP_REQ      46
+# define OMAP_INT_730_DMA_CH6          53
+# define OMAP_INT_730_DMA_CH7          54
+# define OMAP_INT_730_DMA_CH8          55
+# define OMAP_INT_730_DMA_CH9          56
+# define OMAP_INT_730_DMA_CH10         57
+# define OMAP_INT_730_DMA_CH11         58
+# define OMAP_INT_730_DMA_CH12         59
+# define OMAP_INT_730_DMA_CH13         60
+# define OMAP_INT_730_DMA_CH14         61
+# define OMAP_INT_730_DMA_CH15         62
+# define OMAP_INT_730_NAND             63
+
+/*
+ * OMAP-24xx common IRQ numbers
+ */
+# define OMAP_INT_24XX_SYS_NIRQ                7
+# define OMAP_INT_24XX_SDMA_IRQ0       12
+# define OMAP_INT_24XX_SDMA_IRQ1       13
+# define OMAP_INT_24XX_SDMA_IRQ2       14
+# define OMAP_INT_24XX_SDMA_IRQ3       15
+# define OMAP_INT_24XX_CAM_IRQ         24
+# define OMAP_INT_24XX_DSS_IRQ         25
+# define OMAP_INT_24XX_MAIL_U0_MPU     26
+# define OMAP_INT_24XX_DSP_UMA         27
+# define OMAP_INT_24XX_DSP_MMU         28
+# define OMAP_INT_24XX_GPIO_BANK1      29
+# define OMAP_INT_24XX_GPIO_BANK2      30
+# define OMAP_INT_24XX_GPIO_BANK3      31
+# define OMAP_INT_24XX_GPIO_BANK4      32
+# define OMAP_INT_24XX_GPIO_BANK5      33
+# define OMAP_INT_24XX_MAIL_U3_MPU     34
+# define OMAP_INT_24XX_GPTIMER1                37
+# define OMAP_INT_24XX_GPTIMER2                38
+# define OMAP_INT_24XX_GPTIMER3                39
+# define OMAP_INT_24XX_GPTIMER4                40
+# define OMAP_INT_24XX_GPTIMER5                41
+# define OMAP_INT_24XX_GPTIMER6                42
+# define OMAP_INT_24XX_GPTIMER7                43
+# define OMAP_INT_24XX_GPTIMER8                44
+# define OMAP_INT_24XX_GPTIMER9                45
+# define OMAP_INT_24XX_GPTIMER10       46
+# define OMAP_INT_24XX_GPTIMER11       47
+# define OMAP_INT_24XX_GPTIMER12       48
+# define OMAP_INT_24XX_MCBSP1_IRQ_TX   59
+# define OMAP_INT_24XX_MCBSP1_IRQ_RX   60
+# define OMAP_INT_24XX_MCBSP2_IRQ_TX   62
+# define OMAP_INT_24XX_MCBSP2_IRQ_RX   63
+# define OMAP_INT_24XX_UART1_IRQ       72
+# define OMAP_INT_24XX_UART2_IRQ       73
+# define OMAP_INT_24XX_UART3_IRQ       74
+# define OMAP_INT_24XX_USB_IRQ_GEN     75
+# define OMAP_INT_24XX_USB_IRQ_NISO    76
+# define OMAP_INT_24XX_USB_IRQ_ISO     77
+# define OMAP_INT_24XX_USB_IRQ_HGEN    78
+# define OMAP_INT_24XX_USB_IRQ_HSOF    79
+# define OMAP_INT_24XX_USB_IRQ_OTG     80
+# define OMAP_INT_24XX_MMC_IRQ         83
+# define OMAP_INT_243X_HS_USB_MC       92
+# define OMAP_INT_243X_HS_USB_DMA      93
+# define OMAP_INT_243X_CARKIT          94
+
+struct omap_dma_s;
+struct omap_dma_s *omap_dma_init(target_phys_addr_t base,
+                qemu_irq pic[], struct omap_mpu_state_s *mpu, omap_clk clk);
+
+enum omap_dma_port {
+    emiff = 0,
+    emifs,
+    imif,
+    tipb,
+    local,
+    tipb_mpui,
+    omap_dma_port_last,
+};
+
+struct omap_dma_lcd_channel_s {
+    enum omap_dma_port src;
+    target_phys_addr_t src_f1_top;
+    target_phys_addr_t src_f1_bottom;
+    target_phys_addr_t src_f2_top;
+    target_phys_addr_t src_f2_bottom;
+    /* Destination port is fixed.  */
+    int interrupts;
+    int condition;
+    int dual;
+
+    int current_frame;
+    ram_addr_t phys_framebuffer[2];
+    qemu_irq irq;
+    struct omap_mpu_state_s *mpu;
+};
+
+/*
+ * DMA request numbers for OMAP1
+ * See /usr/include/asm-arm/arch-omap/dma.h in Linux.
+ */
+# define OMAP_DMA_NO_DEVICE            0
+# define OMAP_DMA_MCSI1_TX             1
+# define OMAP_DMA_MCSI1_RX             2
+# define OMAP_DMA_I2C_RX               3
+# define OMAP_DMA_I2C_TX               4
+# define OMAP_DMA_EXT_NDMA_REQ0                5
+# define OMAP_DMA_EXT_NDMA_REQ1                6
+# define OMAP_DMA_UWIRE_TX             7
+# define OMAP_DMA_MCBSP1_TX            8
+# define OMAP_DMA_MCBSP1_RX            9
+# define OMAP_DMA_MCBSP3_TX            10
+# define OMAP_DMA_MCBSP3_RX            11
+# define OMAP_DMA_UART1_TX             12
+# define OMAP_DMA_UART1_RX             13
+# define OMAP_DMA_UART2_TX             14
+# define OMAP_DMA_UART2_RX             15
+# define OMAP_DMA_MCBSP2_TX            16
+# define OMAP_DMA_MCBSP2_RX            17
+# define OMAP_DMA_UART3_TX             18
+# define OMAP_DMA_UART3_RX             19
+# define OMAP_DMA_CAMERA_IF_RX         20
+# define OMAP_DMA_MMC_TX               21
+# define OMAP_DMA_MMC_RX               22
+# define OMAP_DMA_NAND                 23      /* Not in OMAP310 */
+# define OMAP_DMA_IRQ_LCD_LINE         24      /* Not in OMAP310 */
+# define OMAP_DMA_MEMORY_STICK         25      /* Not in OMAP310 */
+# define OMAP_DMA_USB_W2FC_RX0         26
+# define OMAP_DMA_USB_W2FC_RX1         27
+# define OMAP_DMA_USB_W2FC_RX2         28
+# define OMAP_DMA_USB_W2FC_TX0         29
+# define OMAP_DMA_USB_W2FC_TX1         30
+# define OMAP_DMA_USB_W2FC_TX2         31
+
+/* These are only for 1610 */
+# define OMAP_DMA_CRYPTO_DES_IN                32
+# define OMAP_DMA_SPI_TX               33
+# define OMAP_DMA_SPI_RX               34
+# define OMAP_DMA_CRYPTO_HASH          35
+# define OMAP_DMA_CCP_ATTN             36
+# define OMAP_DMA_CCP_FIFO_NOT_EMPTY   37
+# define OMAP_DMA_CMT_APE_TX_CHAN_0    38
+# define OMAP_DMA_CMT_APE_RV_CHAN_0    39
+# define OMAP_DMA_CMT_APE_TX_CHAN_1    40
+# define OMAP_DMA_CMT_APE_RV_CHAN_1    41
+# define OMAP_DMA_CMT_APE_TX_CHAN_2    42
+# define OMAP_DMA_CMT_APE_RV_CHAN_2    43
+# define OMAP_DMA_CMT_APE_TX_CHAN_3    44
+# define OMAP_DMA_CMT_APE_RV_CHAN_3    45
+# define OMAP_DMA_CMT_APE_TX_CHAN_4    46
+# define OMAP_DMA_CMT_APE_RV_CHAN_4    47
+# define OMAP_DMA_CMT_APE_TX_CHAN_5    48
+# define OMAP_DMA_CMT_APE_RV_CHAN_5    49
+# define OMAP_DMA_CMT_APE_TX_CHAN_6    50
+# define OMAP_DMA_CMT_APE_RV_CHAN_6    51
+# define OMAP_DMA_CMT_APE_TX_CHAN_7    52
+# define OMAP_DMA_CMT_APE_RV_CHAN_7    53
+# define OMAP_DMA_MMC2_TX              54
+# define OMAP_DMA_MMC2_RX              55
+# define OMAP_DMA_CRYPTO_DES_OUT       56
+
+struct omap_mpu_timer_s;
+struct omap_mpu_timer_s *omap_mpu_timer_init(target_phys_addr_t base,
+                qemu_irq irq, omap_clk clk);
+
+struct omap_watchdog_timer_s;
+struct omap_watchdog_timer_s *omap_wd_timer_init(target_phys_addr_t base,
+                qemu_irq irq, omap_clk clk);
+
+struct omap_32khz_timer_s;
+struct omap_32khz_timer_s *omap_os_timer_init(target_phys_addr_t base,
+                qemu_irq irq, omap_clk clk);
+
+struct omap_tipb_bridge_s;
+struct omap_tipb_bridge_s *omap_tipb_bridge_init(target_phys_addr_t base,
+                qemu_irq abort_irq, omap_clk clk);
+
+struct omap_uart_s;
+struct omap_uart_s *omap_uart_init(target_phys_addr_t base,
+                qemu_irq irq, omap_clk clk, CharDriverState *chr);
+
+/* omap_lcdc.c */
+struct omap_lcd_panel_s;
+void omap_lcdc_reset(struct omap_lcd_panel_s *s);
+struct omap_lcd_panel_s *omap_lcdc_init(target_phys_addr_t base, qemu_irq irq,
+                struct omap_dma_lcd_channel_s *dma, DisplayState *ds,
+                ram_addr_t imif_base, ram_addr_t emiff_base, omap_clk clk);
+
+# define cpu_is_omap310(cpu)           (cpu->mpu_model == omap310)
+# define cpu_is_omap1510(cpu)          (cpu->mpu_model == omap1510)
+# define cpu_is_omap15xx(cpu)          \
+        (cpu_is_omap310(cpu) || cpu_is_omap1510(cpu))
+# define cpu_class_omap1(cpu)          1
+
+struct omap_mpu_state_s {
+    enum omap1_mpu_model {
+        omap310,
+        omap1510,
+    } mpu_model;
+
+    CPUState *env;
+
+    qemu_irq *irq[2];
+    qemu_irq *drq;
+
+    qemu_irq wakeup;
+
+    struct omap_dma_port_if_s {
+        uint32_t (*read[3])(struct omap_mpu_state_s *s, 
+                        target_phys_addr_t offset);
+        void (*write[3])(struct omap_mpu_state_s *s,
+                        target_phys_addr_t offset, uint32_t value);
+        int (*addr_valid)(struct omap_mpu_state_s *s,
+                        target_phys_addr_t addr);
+    } port[omap_dma_port_last];
+
+    unsigned long sdram_size;
+    unsigned long sram_size;
+
+    /* MPUI-TIPB peripherals */
+    struct omap_uart_s *uart3;
+
+    /* MPU public TIPB peripherals */
+    struct omap_32khz_timer_s *os_timer;
+
+    struct omap_uart_s *uart1;
+    struct omap_uart_s *uart2;
+
+    /* MPU private TIPB peripherals */
+    struct omap_intr_handler_s *ih[2];
+
+    struct omap_dma_s *dma;
+
+    struct omap_mpu_timer_s *timer[3];
+    struct omap_watchdog_timer_s *wdt;
+
+    struct omap_lcd_panel_s *lcd;
+
+    target_phys_addr_t ulpd_pm_base;
+    uint32_t ulpd_pm_regs[21];
+    int64_t ulpd_gauge_start;
+
+    target_phys_addr_t pin_cfg_base;
+    uint32_t func_mux_ctrl[14];
+    uint32_t comp_mode_ctrl[1];
+    uint32_t pull_dwn_ctrl[4];
+    uint32_t gate_inh_ctrl[1];
+    uint32_t voltage_ctrl[1];
+    uint32_t test_dbg_ctrl[1];
+    uint32_t mod_conf_ctrl[1];
+    int compat1509;
+
+    uint32_t mpui_ctrl;
+    target_phys_addr_t mpui_base;
+
+    struct omap_tipb_bridge_s *private_tipb;
+    struct omap_tipb_bridge_s *public_tipb;
+
+    target_phys_addr_t tcmi_base;
+    uint32_t tcmi_regs[17];
+
+    struct dpll_ctl_s {
+        target_phys_addr_t base;
+        uint16_t mode;
+        omap_clk dpll;
+    } dpll[3];
+
+    omap_clk clks;
+    struct {
+        target_phys_addr_t mpu_base;
+        target_phys_addr_t dsp_base;
+
+        int cold_start;
+        int clocking_scheme;
+        uint16_t arm_ckctl;
+        uint16_t arm_idlect1;
+        uint16_t arm_idlect2;
+        uint16_t arm_ewupct;
+        uint16_t arm_rstct1;
+        uint16_t arm_rstct2;
+        uint16_t arm_ckout1;
+        int dpll1_mode;
+        uint16_t dsp_idlect1;
+        uint16_t dsp_idlect2;
+        uint16_t dsp_rstct2;
+    } clkm;
+} *omap310_mpu_init(unsigned long sdram_size,
+                DisplayState *ds, const char *core);
+
+# if TARGET_PHYS_ADDR_BITS == 32
+#  define OMAP_FMT_plx "%#08x"
+# elif TARGET_PHYS_ADDR_BITS == 64
+#  define OMAP_FMT_plx "%#08" PRIx64
+# else
+#  error TARGET_PHYS_ADDR_BITS undefined
+# endif
+
+# define OMAP_BAD_REG(paddr)           \
+        printf("%s: Bad register " OMAP_FMT_plx "\n", __FUNCTION__, paddr)
+# define OMAP_RO_REG(paddr)            \
+        printf("%s: Read-only register " OMAP_FMT_plx "\n",    \
+                        __FUNCTION__, paddr)
+# define OMAP_16B_REG(paddr)           \
+        printf("%s: 16-bit register " OMAP_FMT_plx "\n",       \
+                        __FUNCTION__, paddr)
+# define OMAP_32B_REG(paddr)           \
+        printf("%s: 32-bit register " OMAP_FMT_plx "\n",       \
+                        __FUNCTION__, paddr)
+
+#endif /* hw_omap_h */
diff --git a/hw/omap1_clk.c b/hw/omap1_clk.c
new file mode 100644 (file)
index 0000000..7888e41
--- /dev/null
@@ -0,0 +1,745 @@
+/*
+ * OMAP clocks.
+ *
+ * Copyright (C) 2006-2007 Andrzej Zaborowski  <balrog@zabor.org>
+ *
+ * Clocks data comes in part from arch/arm/mach-omap1/clock.h in Linux.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
+ * MA 02111-1307 USA
+ */
+#include "vl.h"
+
+struct clk {
+    const char *name;
+    const char *alias;
+    struct clk *parent;
+    struct clk *child1;
+    struct clk *sibling;
+#define ALWAYS_ENABLED         (1 << 0)
+#define CLOCK_IN_OMAP310       (1 << 10)
+#define CLOCK_IN_OMAP730       (1 << 11)
+#define CLOCK_IN_OMAP1510      (1 << 12)
+#define CLOCK_IN_OMAP16XX      (1 << 13)
+    uint32_t flags;
+    int id;
+
+    int running;               /* Is currently ticking */
+    int enabled;               /* Is enabled, regardless of its input clk */
+    unsigned long rate;                /* Current rate (if .running) */
+    unsigned int divisor;      /* Rate relative to input (if .enabled) */
+    unsigned int multiplier;   /* Rate relative to input (if .enabled) */
+    qemu_irq users[16];                /* Who to notify on change */
+    int usecount;              /* Automatically idle when unused */
+};
+
+static struct clk xtal_osc12m = {
+    .name      = "xtal_osc_12m",
+    .rate      = 12000000,
+    .flags     = CLOCK_IN_OMAP1510 | CLOCK_IN_OMAP16XX | CLOCK_IN_OMAP310,
+};
+
+static struct clk xtal_osc32k = {
+    .name      = "xtal_osc_32k",
+    .rate      = 32768,
+    .flags     = CLOCK_IN_OMAP1510 | CLOCK_IN_OMAP16XX | CLOCK_IN_OMAP310,
+};
+
+static struct clk ck_ref = {
+    .name      = "ck_ref",
+    .alias     = "clkin",
+    .parent    = &xtal_osc12m,
+    .flags     = CLOCK_IN_OMAP1510 | CLOCK_IN_OMAP16XX | CLOCK_IN_OMAP310 |
+            ALWAYS_ENABLED,
+};
+
+/* If a dpll is disabled it becomes a bypass, child clocks don't stop */
+static struct clk dpll1 = {
+    .name      = "dpll1",
+    .parent    = &ck_ref,
+    .flags     = CLOCK_IN_OMAP1510 | CLOCK_IN_OMAP16XX | CLOCK_IN_OMAP310 |
+            ALWAYS_ENABLED,
+};
+
+static struct clk dpll2 = {
+    .name      = "dpll2",
+    .parent    = &ck_ref,
+    .flags     = CLOCK_IN_OMAP310 | ALWAYS_ENABLED,
+};
+
+static struct clk dpll3 = {
+    .name      = "dpll3",
+    .parent    = &ck_ref,
+    .flags     = CLOCK_IN_OMAP310 | ALWAYS_ENABLED,
+};
+
+static struct clk dpll4 = {
+    .name      = "dpll4",
+    .parent    = &ck_ref,
+    .multiplier        = 4,
+    .flags     = CLOCK_IN_OMAP1510 | CLOCK_IN_OMAP16XX | CLOCK_IN_OMAP310,
+};
+
+static struct clk apll = {
+    .name      = "apll",
+    .parent    = &ck_ref,
+    .multiplier        = 48,
+    .divisor   = 12,
+    .flags     = CLOCK_IN_OMAP1510 | CLOCK_IN_OMAP16XX | CLOCK_IN_OMAP310,
+};
+
+static struct clk ck_48m = {
+    .name      = "ck_48m",
+    .parent    = &dpll4,       /* either dpll4 or apll */
+    .flags     = CLOCK_IN_OMAP1510 | CLOCK_IN_OMAP16XX | CLOCK_IN_OMAP310,
+};
+
+static struct clk ck_dpll1out = {
+    .name      = "ck_dpll1out",
+    .parent    = &dpll1,
+    .flags     = CLOCK_IN_OMAP16XX,
+};
+
+static struct clk sossi_ck = {
+    .name      = "ck_sossi",
+    .parent    = &ck_dpll1out,
+    .flags     = CLOCK_IN_OMAP16XX,
+};
+
+static struct clk clkm1 = {
+    .name      = "clkm1",
+    .alias     = "ck_gen1",
+    .parent    = &dpll1,
+    .flags     = CLOCK_IN_OMAP1510 | CLOCK_IN_OMAP16XX | CLOCK_IN_OMAP310 |
+            ALWAYS_ENABLED,
+};
+
+static struct clk clkm2 = {
+    .name      = "clkm2",
+    .alias     = "ck_gen2",
+    .parent    = &dpll1,
+    .flags     = CLOCK_IN_OMAP1510 | CLOCK_IN_OMAP16XX | CLOCK_IN_OMAP310 |
+            ALWAYS_ENABLED,
+};
+
+static struct clk clkm3 = {
+    .name      = "clkm3",
+    .alias     = "ck_gen3",
+    .parent    = &dpll1,       /* either dpll1 or ck_ref */
+    .flags     = CLOCK_IN_OMAP1510 | CLOCK_IN_OMAP16XX | CLOCK_IN_OMAP310 |
+            ALWAYS_ENABLED,
+};
+
+static struct clk arm_ck = {
+    .name      = "arm_ck",
+    .alias     = "mpu_ck",
+    .parent    = &clkm1,
+    .flags     = CLOCK_IN_OMAP1510 | CLOCK_IN_OMAP16XX | CLOCK_IN_OMAP310 |
+            ALWAYS_ENABLED,
+};
+
+static struct clk armper_ck = {
+    .name      = "armper_ck",
+    .alias     = "mpuper_ck",
+    .parent    = &clkm1,
+    .flags     = CLOCK_IN_OMAP1510 | CLOCK_IN_OMAP16XX | CLOCK_IN_OMAP310,
+};
+
+static struct clk arm_gpio_ck = {
+    .name      = "arm_gpio_ck",
+    .alias     = "mpu_gpio_ck",
+    .parent    = &clkm1,
+    .divisor   = 1,
+    .flags     = CLOCK_IN_OMAP1510 | CLOCK_IN_OMAP310,
+};
+
+static struct clk armxor_ck = {
+    .name      = "armxor_ck",
+    .alias     = "mpuxor_ck",
+    .parent    = &ck_ref,
+    .flags     = CLOCK_IN_OMAP1510 | CLOCK_IN_OMAP16XX | CLOCK_IN_OMAP310,
+};
+
+static struct clk armtim_ck = {
+    .name      = "armtim_ck",
+    .alias     = "mputim_ck",
+    .parent    = &ck_ref,      /* either CLKIN or DPLL1 */
+    .flags     = CLOCK_IN_OMAP1510 | CLOCK_IN_OMAP16XX | CLOCK_IN_OMAP310,
+};
+
+static struct clk armwdt_ck = {
+    .name      = "armwdt_ck",
+    .alias     = "mpuwd_ck",
+    .parent    = &clkm1,
+    .divisor   = 14,
+    .flags     = CLOCK_IN_OMAP1510 | CLOCK_IN_OMAP16XX | CLOCK_IN_OMAP310 |
+            ALWAYS_ENABLED,
+};
+
+static struct clk arminth_ck16xx = {
+    .name      = "arminth_ck",
+    .parent    = &arm_ck,
+    .flags     = CLOCK_IN_OMAP16XX | ALWAYS_ENABLED,
+    /* Note: On 16xx the frequency can be divided by 2 by programming
+     * ARM_CKCTL:ARM_INTHCK_SEL(14) to 1
+     *
+     * 1510 version is in TC clocks.
+     */
+};
+
+static struct clk dsp_ck = {
+    .name      = "dsp_ck",
+    .parent    = &clkm2,
+    .flags     = CLOCK_IN_OMAP310 | CLOCK_IN_OMAP1510 | CLOCK_IN_OMAP16XX,
+};
+
+static struct clk dspmmu_ck = {
+    .name      = "dspmmu_ck",
+    .parent    = &clkm2,
+    .flags     = CLOCK_IN_OMAP310 | CLOCK_IN_OMAP1510 | CLOCK_IN_OMAP16XX |
+            ALWAYS_ENABLED,
+};
+
+static struct clk dspper_ck = {
+    .name      = "dspper_ck",
+    .parent    = &clkm2,
+    .flags     = CLOCK_IN_OMAP310 | CLOCK_IN_OMAP1510 | CLOCK_IN_OMAP16XX,
+};
+
+static struct clk dspxor_ck = {
+    .name      = "dspxor_ck",
+    .parent    = &ck_ref,
+    .flags     = CLOCK_IN_OMAP310 | CLOCK_IN_OMAP1510 | CLOCK_IN_OMAP16XX,
+};
+
+static struct clk dsptim_ck = {
+    .name      = "dsptim_ck",
+    .parent    = &ck_ref,
+    .flags     = CLOCK_IN_OMAP310 | CLOCK_IN_OMAP1510 | CLOCK_IN_OMAP16XX,
+};
+
+static struct clk tc_ck = {
+    .name      = "tc_ck",
+    .parent    = &clkm3,
+    .flags     = CLOCK_IN_OMAP1510 | CLOCK_IN_OMAP16XX |
+            CLOCK_IN_OMAP730 | CLOCK_IN_OMAP310 |
+            ALWAYS_ENABLED,
+};
+
+static struct clk arminth_ck15xx = {
+    .name      = "arminth_ck",
+    .parent    = &tc_ck,
+    .flags     = CLOCK_IN_OMAP1510 | CLOCK_IN_OMAP310 | ALWAYS_ENABLED,
+    /* Note: On 1510 the frequency follows TC_CK
+     *
+     * 16xx version is in MPU clocks.
+     */
+};
+
+static struct clk tipb_ck = {
+    /* No-idle controlled by "tc_ck" */
+    .name      = "tipb_ck",
+    .parent    = &tc_ck,
+    .flags     = CLOCK_IN_OMAP1510 | CLOCK_IN_OMAP310 | ALWAYS_ENABLED,
+};
+
+static struct clk l3_ocpi_ck = {
+    /* No-idle controlled by "tc_ck" */
+    .name      = "l3_ocpi_ck",
+    .parent    = &tc_ck,
+    .flags     = CLOCK_IN_OMAP16XX,
+};
+
+static struct clk tc1_ck = {
+    .name      = "tc1_ck",
+    .parent    = &tc_ck,
+    .flags     = CLOCK_IN_OMAP16XX,
+};
+
+static struct clk tc2_ck = {
+    .name      = "tc2_ck",
+    .parent    = &tc_ck,
+    .flags     = CLOCK_IN_OMAP16XX,
+};
+
+static struct clk dma_ck = {
+    /* No-idle controlled by "tc_ck" */
+    .name      = "dma_ck",
+    .parent    = &tc_ck,
+    .flags     = CLOCK_IN_OMAP1510 | CLOCK_IN_OMAP16XX | CLOCK_IN_OMAP310 |
+            ALWAYS_ENABLED,
+};
+
+static struct clk dma_lcdfree_ck = {
+    .name      = "dma_lcdfree_ck",
+    .parent    = &tc_ck,
+    .flags     = CLOCK_IN_OMAP16XX | ALWAYS_ENABLED,
+};
+
+static struct clk api_ck = {
+    .name      = "api_ck",
+    .alias     = "mpui_ck",
+    .parent    = &tc_ck,
+    .flags     = CLOCK_IN_OMAP1510 | CLOCK_IN_OMAP16XX | CLOCK_IN_OMAP310,
+};
+
+static struct clk lb_ck = {
+    .name      = "lb_ck",
+    .parent    = &tc_ck,
+    .flags     = CLOCK_IN_OMAP1510 | CLOCK_IN_OMAP310,
+};
+
+static struct clk lbfree_ck = {
+    .name      = "lbfree_ck",
+    .parent    = &tc_ck,
+    .flags     = CLOCK_IN_OMAP1510 | CLOCK_IN_OMAP310,
+};
+
+static struct clk rhea1_ck = {
+    .name      = "rhea1_ck",
+    .parent    = &tc_ck,
+    .flags     = CLOCK_IN_OMAP16XX | ALWAYS_ENABLED,
+};
+
+static struct clk rhea2_ck = {
+    .name      = "rhea2_ck",
+    .parent    = &tc_ck,
+    .flags     = CLOCK_IN_OMAP16XX | ALWAYS_ENABLED,
+};
+
+static struct clk lcd_ck_16xx = {
+    .name      = "lcd_ck",
+    .parent    = &clkm3,
+    .flags     = CLOCK_IN_OMAP16XX | CLOCK_IN_OMAP730,
+};
+
+static struct clk lcd_ck_1510 = {
+    .name      = "lcd_ck",
+    .parent    = &clkm3,
+    .flags     = CLOCK_IN_OMAP1510 | CLOCK_IN_OMAP310,
+};
+
+static struct clk uart1_1510 = {
+    .name      = "uart1_ck",
+    /* Direct from ULPD, no real parent */
+    .parent    = &armper_ck,   /* either armper_ck or dpll4 */
+    .rate      = 12000000,
+    .flags     = CLOCK_IN_OMAP1510 | CLOCK_IN_OMAP310 | ALWAYS_ENABLED,
+};
+
+static struct clk uart1_16xx = {
+    .name      = "uart1_ck",
+    /* Direct from ULPD, no real parent */
+    .parent    = &armper_ck,
+    .rate      = 48000000,
+    .flags     = CLOCK_IN_OMAP16XX,
+};
+
+static struct clk uart2_ck = {
+    .name      = "uart2_ck",
+    /* Direct from ULPD, no real parent */
+    .parent    = &armper_ck,   /* either armper_ck or dpll4 */
+    .rate      = 12000000,
+    .flags     = CLOCK_IN_OMAP1510 | CLOCK_IN_OMAP16XX | CLOCK_IN_OMAP310 |
+            ALWAYS_ENABLED,
+};
+
+static struct clk uart3_1510 = {
+    .name      = "uart3_ck",
+    /* Direct from ULPD, no real parent */
+    .parent    = &armper_ck,/* either armper_ck or dpll4 */
+    .rate      = 12000000,
+    .flags     = CLOCK_IN_OMAP1510 | CLOCK_IN_OMAP310 | ALWAYS_ENABLED,
+};
+
+static struct clk uart3_16xx = {
+    .name      = "uart3_ck",
+    /* Direct from ULPD, no real parent */
+    .parent    = &armper_ck,
+    .rate      = 48000000,
+    .flags     = CLOCK_IN_OMAP16XX,
+};
+
+static struct clk usb_clk0 = { /* 6 MHz output on W4_USB_CLK0 */
+    .name      = "usb_clk0",
+    .alias     = "usb.clko",
+    /* Direct from ULPD, no parent */
+    .rate      = 6000000,
+    .flags     = CLOCK_IN_OMAP1510 | CLOCK_IN_OMAP16XX | CLOCK_IN_OMAP310,
+};
+
+static struct clk usb_hhc_ck1510 = {
+    .name      = "usb_hhc_ck",
+    /* Direct from ULPD, no parent */
+    .rate      = 48000000, /* Actually 2 clocks, 12MHz and 48MHz */
+    .flags     = CLOCK_IN_OMAP1510 | CLOCK_IN_OMAP310,
+};
+
+static struct clk usb_hhc_ck16xx = {
+    .name      = "usb_hhc_ck",
+    /* Direct from ULPD, no parent */
+    .rate      = 48000000,
+    /* OTG_SYSCON_2.OTG_PADEN == 0 (not 1510-compatible) */
+    .flags     = CLOCK_IN_OMAP16XX,
+};
+
+static struct clk usb_dc_ck = {
+    .name      = "usb_dc_ck",
+    /* Direct from ULPD, no parent */
+    .rate      = 48000000,
+    .flags     = CLOCK_IN_OMAP16XX,
+};
+
+static struct clk mclk_1510 = {
+    .name      = "mclk",
+    /* Direct from ULPD, no parent. May be enabled by ext hardware. */
+    .rate      = 12000000,
+    .flags     = CLOCK_IN_OMAP1510,
+};
+
+static struct clk bclk_310 = {
+    .name      = "bt_mclk_out",        /* Alias midi_mclk_out? */
+    .parent    = &armper_ck,
+    .flags     = CLOCK_IN_OMAP310,
+};
+
+static struct clk mclk_310 = {
+    .name      = "com_mclk_out",
+    .parent    = &armper_ck,
+    .flags     = CLOCK_IN_OMAP310,
+};
+
+static struct clk mclk_16xx = {
+    .name      = "mclk",
+    /* Direct from ULPD, no parent. May be enabled by ext hardware. */
+    .flags     = CLOCK_IN_OMAP16XX,
+};
+
+static struct clk bclk_1510 = {
+    .name      = "bclk",
+    /* Direct from ULPD, no parent. May be enabled by ext hardware. */
+    .rate      = 12000000,
+    .flags     = CLOCK_IN_OMAP1510,
+};
+
+static struct clk bclk_16xx = {
+    .name      = "bclk",
+    /* Direct from ULPD, no parent. May be enabled by ext hardware. */
+    .flags     = CLOCK_IN_OMAP16XX,
+};
+
+static struct clk mmc1_ck = {
+    .name      = "mmc_ck",
+    .id                = 1,
+    /* Functional clock is direct from ULPD, interface clock is ARMPER */
+    .parent    = &armper_ck,   /* either armper_ck or dpll4 */
+    .rate      = 48000000,
+    .flags     = CLOCK_IN_OMAP1510 | CLOCK_IN_OMAP16XX | CLOCK_IN_OMAP310,
+};
+
+static struct clk mmc2_ck = {
+    .name      = "mmc_ck",
+    .id                = 2,
+    /* Functional clock is direct from ULPD, interface clock is ARMPER */
+    .parent    = &armper_ck,
+    .rate      = 48000000,
+    .flags     = CLOCK_IN_OMAP16XX,
+};
+
+static struct clk cam_mclk = {
+    .name      = "cam.mclk",
+    .flags     = CLOCK_IN_OMAP310 | CLOCK_IN_OMAP1510 | CLOCK_IN_OMAP16XX,
+    .rate      = 12000000,
+};
+
+static struct clk cam_exclk = {
+    .name      = "cam.exclk",
+    .flags     = CLOCK_IN_OMAP310 | CLOCK_IN_OMAP1510 | CLOCK_IN_OMAP16XX,
+    /* Either 12M from cam.mclk or 48M from dpll4 */
+    .parent    = &cam_mclk,
+};
+
+static struct clk cam_lclk = {
+    .name      = "cam.lclk",
+    .flags     = CLOCK_IN_OMAP310 | CLOCK_IN_OMAP1510 | CLOCK_IN_OMAP16XX,
+};
+
+static struct clk i2c_fck = {
+    .name      = "i2c_fck",
+    .id                = 1,
+    .flags     = CLOCK_IN_OMAP310 | CLOCK_IN_OMAP1510 | CLOCK_IN_OMAP16XX |
+            ALWAYS_ENABLED,
+    .parent    = &armxor_ck,
+};
+
+static struct clk i2c_ick = {
+    .name      = "i2c_ick",
+    .id                = 1,
+    .flags     = CLOCK_IN_OMAP16XX | ALWAYS_ENABLED,
+    .parent    = &armper_ck,
+};
+
+static struct clk clk32k = {
+    .name      = "clk32-kHz",
+    .flags     = CLOCK_IN_OMAP310 | CLOCK_IN_OMAP1510 | CLOCK_IN_OMAP16XX |
+            ALWAYS_ENABLED,
+    .parent     = &xtal_osc32k,
+};
+
+static struct clk *onchip_clks[] = {
+    /* non-ULPD clocks */
+    &xtal_osc12m,
+    &xtal_osc32k,
+    &ck_ref,
+    &dpll1,
+    &dpll2,
+    &dpll3,
+    &dpll4,
+    &apll,
+    &ck_48m,
+    /* CK_GEN1 clocks */
+    &clkm1,
+    &ck_dpll1out,
+    &sossi_ck,
+    &arm_ck,
+    &armper_ck,
+    &arm_gpio_ck,
+    &armxor_ck,
+    &armtim_ck,
+    &armwdt_ck,
+    &arminth_ck15xx,  &arminth_ck16xx,
+    /* CK_GEN2 clocks */
+    &clkm2,
+    &dsp_ck,
+    &dspmmu_ck,
+    &dspper_ck,
+    &dspxor_ck,
+    &dsptim_ck,
+    /* CK_GEN3 clocks */
+    &clkm3,
+    &tc_ck,
+    &tipb_ck,
+    &l3_ocpi_ck,
+    &tc1_ck,
+    &tc2_ck,
+    &dma_ck,
+    &dma_lcdfree_ck,
+    &api_ck,
+    &lb_ck,
+    &lbfree_ck,
+    &rhea1_ck,
+    &rhea2_ck,
+    &lcd_ck_16xx,
+    &lcd_ck_1510,
+    /* ULPD clocks */
+    &uart1_1510,
+    &uart1_16xx,
+    &uart2_ck,
+    &uart3_1510,
+    &uart3_16xx,
+    &usb_clk0,
+    &usb_hhc_ck1510, &usb_hhc_ck16xx,
+    &usb_dc_ck,
+    &mclk_1510,  &mclk_16xx, &mclk_310,
+    &bclk_1510,  &bclk_16xx, &bclk_310,
+    &mmc1_ck,
+    &mmc2_ck,
+    &cam_mclk,
+    &cam_exclk,
+    &cam_lclk,
+    &clk32k,
+    /* Virtual clocks */
+    &i2c_fck,
+    &i2c_ick,
+    0
+};
+
+void omap_clk_adduser(struct clk *clk, qemu_irq user)
+{
+    qemu_irq *i;
+
+    for (i = clk->users; *i; i ++);
+    *i = user;
+}
+
+/* If a clock is allowed to idle, it is disabled automatically when
+ * all of clock domains using it are disabled.  */
+int omap_clk_is_idle(struct clk *clk)
+{
+    struct clk *chld;
+
+    if (!clk->enabled && (!clk->usecount || !(clk->flags && ALWAYS_ENABLED)))
+        return 1;
+    if (clk->usecount)
+        return 0;
+
+    for (chld = clk->child1; chld; chld = chld->sibling)
+        if (!omap_clk_is_idle(chld))
+            return 0;
+    return 1;
+}
+
+struct clk *omap_findclk(struct omap_mpu_state_s *mpu, const char *name)
+{
+    struct clk *i;
+
+    for (i = mpu->clks; i->name; i ++)
+        if (!strcmp(i->name, name) || (i->alias && !strcmp(i->alias, name)))
+            return i;
+    cpu_abort(mpu->env, "%s: %s not found\n", __FUNCTION__, name);
+}
+
+void omap_clk_get(struct clk *clk)
+{
+    clk->usecount ++;
+}
+
+void omap_clk_put(struct clk *clk)
+{
+    if (!(clk->usecount --))
+        cpu_abort(cpu_single_env, "%s: %s is not in use\n",
+                        __FUNCTION__, clk->name);
+}
+
+static void omap_clk_update(struct clk *clk)
+{
+    int parent, running;
+    qemu_irq *user;
+    struct clk *i;
+
+    if (clk->parent)
+        parent = clk->parent->running;
+    else
+        parent = 1;
+
+    running = parent && (clk->enabled ||
+                    ((clk->flags & ALWAYS_ENABLED) && clk->usecount));
+    if (clk->running != running) {
+        clk->running = running;
+        for (user = clk->users; *user; user ++)
+            qemu_set_irq(*user, running);
+        for (i = clk->child1; i; i = i->sibling)
+            omap_clk_update(i);
+    }
+}
+
+static void omap_clk_rate_update_full(struct clk *clk, unsigned long int rate,
+                unsigned long int div, unsigned long int mult)
+{
+    struct clk *i;
+    qemu_irq *user;
+
+    clk->rate = muldiv64(rate, mult, div);
+    if (clk->running)
+        for (user = clk->users; *user; user ++)
+            qemu_irq_raise(*user);
+    for (i = clk->child1; i; i = i->sibling)
+        omap_clk_rate_update_full(i, rate,
+                        div * i->divisor, mult * i->multiplier);
+}
+
+static void omap_clk_rate_update(struct clk *clk)
+{
+    struct clk *i;
+    unsigned long int div, mult = div = 1;
+
+    for (i = clk; i->parent; i = i->parent) {
+        div *= i->divisor;
+        mult *= i->multiplier;
+    }
+
+    omap_clk_rate_update_full(clk, i->rate, div, mult);
+}
+
+void omap_clk_reparent(struct clk *clk, struct clk *parent)
+{
+    struct clk **p;
+
+    if (clk->parent) {
+        for (p = &clk->parent->child1; *p != clk; p = &(*p)->sibling);
+        *p = clk->sibling;
+    }
+
+    clk->parent = parent;
+    if (parent) {
+        clk->sibling = parent->child1;
+        parent->child1 = clk;
+        omap_clk_update(clk);
+        omap_clk_rate_update(clk);
+    } else
+        clk->sibling = 0;
+}
+
+void omap_clk_onoff(struct clk *clk, int on)
+{
+    clk->enabled = on;
+    omap_clk_update(clk);
+}
+
+void omap_clk_canidle(struct clk *clk, int can)
+{
+    if (can)
+        omap_clk_put(clk);
+    else
+        omap_clk_get(clk);
+}
+
+void omap_clk_setrate(struct clk *clk, int divide, int multiply)
+{
+    clk->divisor = divide;
+    clk->multiplier = multiply;
+    omap_clk_rate_update(clk);
+}
+
+int64_t omap_clk_getrate(omap_clk clk)
+{
+    return clk->rate;
+}
+
+void omap_clk_init(struct omap_mpu_state_s *mpu)
+{
+    struct clk **i, *j, *k;
+    int count;
+    int flag;
+
+    if (cpu_is_omap310(mpu))
+        flag = CLOCK_IN_OMAP310;
+    else if (cpu_is_omap1510(mpu))
+        flag = CLOCK_IN_OMAP1510;
+    else
+        return;
+
+    for (i = onchip_clks, count = 0; *i; i ++)
+        if ((*i)->flags & flag)
+            count ++;
+    mpu->clks = (struct clk *) qemu_mallocz(sizeof(struct clk) * (count + 1));
+    for (i = onchip_clks, j = mpu->clks; *i; i ++)
+        if ((*i)->flags & flag) {
+            memcpy(j, *i, sizeof(struct clk));
+            for (k = mpu->clks; k < j; k ++)
+                if (j->parent && !strcmp(j->parent->name, k->name)) {
+                    j->parent = k;
+                    j->sibling = k->child1;
+                    k->child1 = j;
+                } else if (k->parent && !strcmp(k->parent->name, j->name)) {
+                    k->parent = j;
+                    k->sibling = j->child1;
+                    j->child1 = k;
+                }
+            j->divisor = j->divisor ?: 1;
+            j->multiplier = j->multiplier ?: 1;
+            j ++;
+        }
+}
diff --git a/hw/omap_lcd_template.h b/hw/omap_lcd_template.h
new file mode 100644 (file)
index 0000000..cdfa562
--- /dev/null
@@ -0,0 +1,172 @@
+/*
+ * QEMU OMAP LCD Emulator templates
+ *
+ * Copyright (c) 2006 Andrzej Zaborowski  <balrog@zabor.org>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
+ * PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#if DEPTH == 8
+# define BPP 1
+# define PIXEL_TYPE uint8_t 
+#elif DEPTH == 15 || DEPTH == 16
+# define BPP 2
+# define PIXEL_TYPE uint16_t 
+#elif DEPTH == 32
+# define BPP 4
+# define PIXEL_TYPE uint32_t 
+#else
+# error unsupport depth
+#endif
+
+/* 
+ * 2-bit colour
+ */
+static void glue(draw_line2_, DEPTH)(
+                uint8_t *d, const uint8_t *s, int width, const uint16_t *pal)
+{
+    uint8_t v, r, g, b;
+
+    do {
+        v = ldub_raw((void *) s);
+        r = (pal[v & 3] >> 4) & 0xf0;
+        g = pal[v & 3] & 0xf0;
+        b = (pal[v & 3] << 4) & 0xf0;
+        ((PIXEL_TYPE *) d)[0] = glue(rgb_to_pixel, DEPTH)(r, g, b);
+        d += BPP;
+        v >>= 2;
+        r = (pal[v & 3] >> 4) & 0xf0;
+        g = pal[v & 3] & 0xf0;
+        b = (pal[v & 3] << 4) & 0xf0;
+        ((PIXEL_TYPE *) d)[0] = glue(rgb_to_pixel, DEPTH)(r, g, b);
+        d += BPP;
+        v >>= 2;
+        r = (pal[v & 3] >> 4) & 0xf0;
+        g = pal[v & 3] & 0xf0;
+        b = (pal[v & 3] << 4) & 0xf0;
+        ((PIXEL_TYPE *) d)[0] = glue(rgb_to_pixel, DEPTH)(r, g, b);
+        d += BPP;
+        v >>= 2;
+        r = (pal[v & 3] >> 4) & 0xf0;
+        g = pal[v & 3] & 0xf0;
+        b = (pal[v & 3] << 4) & 0xf0;
+        ((PIXEL_TYPE *) d)[0] = glue(rgb_to_pixel, DEPTH)(r, g, b);
+        d += BPP;
+        s ++;
+        width -= 4;
+    } while (width > 0);
+}
+
+/* 
+ * 4-bit colour
+ */
+static void glue(draw_line4_, DEPTH)(
+                uint8_t *d, const uint8_t *s, int width, const uint16_t *pal)
+{
+    uint8_t v, r, g, b;
+
+    do {
+        v = ldub_raw((void *) s);
+        r = (pal[v & 0xf] >> 4) & 0xf0;
+        g = pal[v & 0xf] & 0xf0;
+        b = (pal[v & 0xf] << 4) & 0xf0;
+        ((PIXEL_TYPE *) d)[0] = glue(rgb_to_pixel, DEPTH)(r, g, b);
+        d += BPP;
+        v >>= 4;
+        r = (pal[v & 0xf] >> 4) & 0xf0;
+        g = pal[v & 0xf] & 0xf0;
+        b = (pal[v & 0xf] << 4) & 0xf0;
+        ((PIXEL_TYPE *) d)[0] = glue(rgb_to_pixel, DEPTH)(r, g, b);
+        d += BPP;
+        s ++;
+        width -= 2;
+    } while (width > 0);
+}
+
+/* 
+ * 8-bit colour
+ */
+static void glue(draw_line8_, DEPTH)(
+                uint8_t *d, const uint8_t *s, int width, const uint16_t *pal)
+{
+    uint8_t v, r, g, b;
+
+    do {
+        v = ldub_raw((void *) s);
+        r = (pal[v] >> 4) & 0xf0;
+        g = pal[v] & 0xf0;
+        b = (pal[v] << 4) & 0xf0;
+        ((PIXEL_TYPE *) d)[0] = glue(rgb_to_pixel, DEPTH)(r, g, b);
+        s ++;
+        d += BPP;
+    } while (-- width != 0);
+}
+
+/* 
+ * 12-bit colour
+ */
+static void glue(draw_line12_, DEPTH)(
+                uint8_t *d, const uint8_t *s, int width, const uint16_t *pal)
+{
+    uint16_t v;
+    uint8_t r, g, b;
+
+    do {
+        v = lduw_raw((void *) s);
+        r = (v >> 4) & 0xf0;
+        g = v & 0xf0;
+        b = (v << 4) & 0xf0;
+        ((PIXEL_TYPE *) d)[0] = glue(rgb_to_pixel, DEPTH)(r, g, b);
+        s += 2;
+        d += BPP;
+    } while (-- width != 0);
+}
+
+/* 
+ * 16-bit colour
+ */
+static void glue(draw_line16_, DEPTH)(
+                uint8_t *d, const uint8_t *s, int width, const uint16_t *pal)
+{
+#if DEPTH == 16 && defined(WORDS_BIGENDIAN) == defined(TARGET_WORDS_BIGENDIAN)
+    memcpy(d, s, width * 2);
+#else
+    uint16_t v;
+    uint8_t r, g, b;
+
+    do {
+        v = lduw_raw((void *) s);
+        r = (v >> 8) & 0xf8;
+        g = (v >> 3) & 0xfc;
+        b = (v << 3) & 0xf8;
+        ((PIXEL_TYPE *) d)[0] = glue(rgb_to_pixel, DEPTH)(r, g, b);
+        s += 2;
+        d += BPP;
+    } while (-- width != 0);
+#endif
+}
+
+#undef DEPTH
+#undef BPP
+#undef PIXEL_TYPE
diff --git a/hw/omap_lcdc.c b/hw/omap_lcdc.c
new file mode 100644 (file)
index 0000000..543d1f2
--- /dev/null
@@ -0,0 +1,499 @@
+/*
+ * OMAP LCD controller.
+ *
+ * Copyright (C) 2006-2007 Andrzej Zaborowski  <balrog@zabor.org>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
+ * MA 02111-1307 USA
+ */
+#include "vl.h"
+
+struct omap_lcd_panel_s {
+    target_phys_addr_t base;
+    qemu_irq irq;
+    DisplayState *state;
+    ram_addr_t imif_base;
+    ram_addr_t emiff_base;
+
+    int plm;
+    int tft;
+    int mono;
+    int enable;
+    int width;
+    int height;
+    int interrupts;
+    uint32_t timing[3];
+    uint32_t subpanel;
+    uint32_t ctrl;
+
+    struct omap_dma_lcd_channel_s *dma;
+    uint16_t palette[256];
+    int palette_done;
+    int frame_done;
+    int invalidate;
+    int sync_error;
+};
+
+static void omap_lcd_interrupts(struct omap_lcd_panel_s *s)
+{
+    if (s->frame_done && (s->interrupts & 1)) {
+        qemu_irq_raise(s->irq);
+        return;
+    }
+
+    if (s->palette_done && (s->interrupts & 2)) {
+        qemu_irq_raise(s->irq);
+        return;
+    }
+
+    if (s->sync_error) {
+        qemu_irq_raise(s->irq);
+        return;
+    }
+
+    qemu_irq_lower(s->irq);
+}
+
+#include "pixel_ops.h"
+
+typedef void draw_line_func(
+                uint8_t *d, const uint8_t *s, int width, const uint16_t *pal);
+
+#define DEPTH 8
+#include "omap_lcd_template.h"
+#define DEPTH 15
+#include "omap_lcd_template.h"
+#define DEPTH 16
+#include "omap_lcd_template.h"
+#define DEPTH 32
+#include "omap_lcd_template.h"
+
+static draw_line_func *draw_line_table2[33] = {
+    [0 ... 32] = 0,
+    [8]                = draw_line2_8,
+    [15]       = draw_line2_15,
+    [16]       = draw_line2_16,
+    [32]       = draw_line2_32,
+}, *draw_line_table4[33] = {
+    [0 ... 32] = 0,
+    [8]                = draw_line4_8,
+    [15]       = draw_line4_15,
+    [16]       = draw_line4_16,
+    [32]       = draw_line4_32,
+}, *draw_line_table8[33] = {
+    [0 ... 32] = 0,
+    [8]                = draw_line8_8,
+    [15]       = draw_line8_15,
+    [16]       = draw_line8_16,
+    [32]       = draw_line8_32,
+}, *draw_line_table12[33] = {
+    [0 ... 32] = 0,
+    [8]                = draw_line12_8,
+    [15]       = draw_line12_15,
+    [16]       = draw_line12_16,
+    [32]       = draw_line12_32,
+}, *draw_line_table16[33] = {
+    [0 ... 32] = 0,
+    [8]                = draw_line16_8,
+    [15]       = draw_line16_15,
+    [16]       = draw_line16_16,
+    [32]       = draw_line16_32,
+};
+
+void omap_update_display(void *opaque)
+{
+    struct omap_lcd_panel_s *omap_lcd = (struct omap_lcd_panel_s *) opaque;
+    draw_line_func *draw_line;
+    int size, dirty[2], minline, maxline, height;
+    int line, width, linesize, step, bpp, frame_offset;
+    ram_addr_t frame_base, scanline, newline, x;
+    uint8_t *s, *d;
+
+    if (!omap_lcd || omap_lcd->plm == 1 ||
+                    !omap_lcd->enable || !omap_lcd->state->depth)
+        return;
+
+    frame_offset = 0;
+    if (omap_lcd->plm != 2) {
+        memcpy(omap_lcd->palette, phys_ram_base +
+                        omap_lcd->dma->phys_framebuffer[
+                        omap_lcd->dma->current_frame], 0x200);
+        switch (omap_lcd->palette[0] >> 12 & 7) {
+        case 3 ... 7:
+            frame_offset += 0x200;
+            break;
+        default:
+            frame_offset += 0x20;
+        }
+    }
+
+    /* Colour depth */
+    switch ((omap_lcd->palette[0] >> 12) & 7) {
+    case 1:
+        draw_line = draw_line_table2[omap_lcd->state->depth];
+        bpp = 2;
+        break;
+
+    case 2:
+        draw_line = draw_line_table4[omap_lcd->state->depth];
+        bpp = 4;
+        break;
+
+    case 3:
+        draw_line = draw_line_table8[omap_lcd->state->depth];
+        bpp = 8;
+        break;
+
+    case 4 ... 7:
+        if (!omap_lcd->tft)
+            draw_line = draw_line_table12[omap_lcd->state->depth];
+        else
+            draw_line = draw_line_table16[omap_lcd->state->depth];
+        bpp = 16;
+        break;
+
+    default:
+        /* Unsupported at the moment.  */
+        return;
+    }
+
+    /* Resolution */
+    width = omap_lcd->width;
+    if (width != omap_lcd->state->width ||
+            omap_lcd->height != omap_lcd->state->height) {
+        dpy_resize(omap_lcd->state,
+                omap_lcd->width, omap_lcd->height);
+        omap_lcd->invalidate = 1;
+    }
+
+    if (omap_lcd->dma->current_frame == 0)
+        size = omap_lcd->dma->src_f1_bottom - omap_lcd->dma->src_f1_top;
+    else
+        size = omap_lcd->dma->src_f2_bottom - omap_lcd->dma->src_f2_top;
+
+    if (frame_offset + ((width * omap_lcd->height * bpp) >> 3) > size + 2) {
+        omap_lcd->sync_error = 1;
+        omap_lcd_interrupts(omap_lcd);
+        omap_lcd->enable = 0;
+        return;
+    }
+
+    /* Content */
+    frame_base = omap_lcd->dma->phys_framebuffer[
+            omap_lcd->dma->current_frame] + frame_offset;
+    omap_lcd->dma->condition |= 1 << omap_lcd->dma->current_frame;
+    if (omap_lcd->dma->interrupts & 1)
+        qemu_irq_raise(omap_lcd->dma->irq);
+    if (omap_lcd->dma->dual)
+        omap_lcd->dma->current_frame ^= 1;
+
+    if (!omap_lcd->state->depth)
+        return;
+
+    line = 0;
+    height = omap_lcd->height;
+    if (omap_lcd->subpanel & (1 << 31)) {
+        if (omap_lcd->subpanel & (1 << 29))
+            line = (omap_lcd->subpanel >> 16) & 0x3ff;
+        else
+            height = (omap_lcd->subpanel >> 16) & 0x3ff;
+        /* TODO: fill the rest of the panel with DPD */
+    }
+    step = width * bpp >> 3;
+    scanline = frame_base + step * line;
+    s = (uint8_t *) (phys_ram_base + scanline);
+    d = omap_lcd->state->data;
+    linesize = omap_lcd->state->linesize;
+
+    dirty[0] = dirty[1] =
+            cpu_physical_memory_get_dirty(scanline, VGA_DIRTY_FLAG);
+    minline = height;
+    maxline = line;
+    for (; line < height; line ++) {
+        newline = scanline + step;
+        for (x = scanline + TARGET_PAGE_SIZE; x < newline;
+                        x += TARGET_PAGE_SIZE) {
+            dirty[1] = cpu_physical_memory_get_dirty(x, VGA_DIRTY_FLAG);
+            dirty[0] |= dirty[1];
+        }
+        if (dirty[0] || omap_lcd->invalidate) {
+            draw_line(d, s, width, omap_lcd->palette);
+            if (line < minline)
+                minline = line;
+            maxline = line + 1;
+        }
+        scanline = newline;
+        dirty[0] = dirty[1];
+        s += step;
+        d += linesize;
+    }
+
+    if (maxline >= minline) {
+        dpy_update(omap_lcd->state, 0, minline, width, maxline);
+        cpu_physical_memory_reset_dirty(frame_base + step * minline,
+                        frame_base + step * maxline, VGA_DIRTY_FLAG);
+    }
+}
+
+static int ppm_save(const char *filename, uint8_t *data,
+                int w, int h, int linesize)
+{
+    FILE *f;
+    uint8_t *d, *d1;
+    unsigned int v;
+    int y, x, bpp;
+
+    f = fopen(filename, "wb");
+    if (!f)
+        return -1;
+    fprintf(f, "P6\n%d %d\n%d\n", w, h, 255);
+    d1 = data;
+    bpp = linesize / w;
+    for (y = 0; y < h; y ++) {
+        d = d1;
+        for (x = 0; x < w; x ++) {
+            v = *(uint32_t *) d;
+            switch (bpp) {
+            case 2:
+                fputc((v >> 8) & 0xf8, f);
+                fputc((v >> 3) & 0xfc, f);
+                fputc((v << 3) & 0xf8, f);
+                break;
+            case 3:
+            case 4:
+            default:
+                fputc((v >> 16) & 0xff, f);
+                fputc((v >> 8) & 0xff, f);
+                fputc((v) & 0xff, f);
+                break;
+            }
+            d += bpp;
+        }
+        d1 += linesize;
+    }
+    fclose(f);
+    return 0;
+}
+
+void omap_screen_dump(void *opaque, const char *filename) {
+    struct omap_lcd_panel_s *omap_lcd = opaque;
+    omap_update_display(opaque);
+    if (omap_lcd && omap_lcd->state->data)
+        ppm_save(filename, omap_lcd->state->data,
+                omap_lcd->width, omap_lcd->height,
+                omap_lcd->state->linesize);
+}
+
+void omap_invalidate_display(void *opaque) {
+    struct omap_lcd_panel_s *omap_lcd = opaque;
+    omap_lcd->invalidate = 1;
+}
+
+void omap_lcd_update(struct omap_lcd_panel_s *s) {
+    if (!s->enable) {
+        s->dma->current_frame = -1;
+        s->sync_error = 0;
+        if (s->plm != 1)
+            s->frame_done = 1;
+        omap_lcd_interrupts(s);
+        return;
+    }
+
+    if (s->dma->current_frame == -1) {
+        s->frame_done = 0;
+        s->palette_done = 0;
+        s->dma->current_frame = 0;
+    }
+
+    if (!s->dma->mpu->port[s->dma->src].addr_valid(s->dma->mpu,
+                            s->dma->src_f1_top) ||
+                    !s->dma->mpu->port[
+                    s->dma->src].addr_valid(s->dma->mpu,
+                            s->dma->src_f1_bottom) ||
+                    (s->dma->dual &&
+                     (!s->dma->mpu->port[
+                      s->dma->src].addr_valid(s->dma->mpu,
+                              s->dma->src_f2_top) ||
+                      !s->dma->mpu->port[
+                      s->dma->src].addr_valid(s->dma->mpu,
+                              s->dma->src_f2_bottom)))) {
+        s->dma->condition |= 1 << 2;
+        if (s->dma->interrupts & (1 << 1))
+            qemu_irq_raise(s->dma->irq);
+        s->enable = 0;
+        return;
+    }
+
+     if (s->dma->src == imif) {
+        /* Framebuffers are in SRAM */
+        s->dma->phys_framebuffer[0] = s->imif_base +
+                s->dma->src_f1_top - OMAP_IMIF_BASE;
+
+        s->dma->phys_framebuffer[1] = s->imif_base +
+                s->dma->src_f2_top - OMAP_IMIF_BASE;
+    } else {
+        /* Framebuffers are in RAM */
+        s->dma->phys_framebuffer[0] = s->emiff_base +
+                s->dma->src_f1_top - OMAP_EMIFF_BASE;
+
+        s->dma->phys_framebuffer[1] = s->emiff_base +
+                s->dma->src_f2_top - OMAP_EMIFF_BASE;
+    }
+
+    if (s->plm != 2 && !s->palette_done) {
+        memcpy(s->palette, phys_ram_base +
+                s->dma->phys_framebuffer[s->dma->current_frame], 0x200);
+        s->palette_done = 1;
+        omap_lcd_interrupts(s);
+    }
+}
+
+static uint32_t omap_lcdc_read(void *opaque, target_phys_addr_t addr)
+{
+    struct omap_lcd_panel_s *s = (struct omap_lcd_panel_s *) opaque;
+    int offset = addr - s->base;
+
+    switch (offset) {
+    case 0x00: /* LCD_CONTROL */
+        return (s->tft << 23) | (s->plm << 20) |
+                (s->tft << 7) | (s->interrupts << 3) |
+                (s->mono << 1) | s->enable | s->ctrl | 0xfe000c34;
+
+    case 0x04: /* LCD_TIMING0 */
+        return (s->timing[0] << 10) | (s->width - 1) | 0x0000000f;
+
+    case 0x08: /* LCD_TIMING1 */
+        return (s->timing[1] << 10) | (s->height - 1);
+
+    case 0x0c: /* LCD_TIMING2 */
+        return s->timing[2] | 0xfc000000;
+
+    case 0x10: /* LCD_STATUS */
+        return (s->palette_done << 6) | (s->sync_error << 2) | s->frame_done;
+
+    case 0x14: /* LCD_SUBPANEL */
+        return s->subpanel;
+
+    default:
+        break;
+    }
+    OMAP_BAD_REG(addr);
+    return 0;
+}
+
+static void omap_lcdc_write(void *opaque, target_phys_addr_t addr,
+                uint32_t value)
+{
+    struct omap_lcd_panel_s *s = (struct omap_lcd_panel_s *) opaque;
+    int offset = addr - s->base;
+
+    switch (offset) {
+    case 0x00: /* LCD_CONTROL */
+        s->plm = (value >> 20) & 3;
+        s->tft = (value >> 7) & 1;
+        s->interrupts = (value >> 3) & 3;
+        s->mono = (value >> 1) & 1;
+        s->ctrl = value & 0x01cff300;
+        if (s->enable != (value & 1)) {
+            s->enable = value & 1;
+            omap_lcd_update(s);
+        }
+        break;
+
+    case 0x04: /* LCD_TIMING0 */
+        s->timing[0] = value >> 10;
+        s->width = (value & 0x3ff) + 1;
+        break;
+
+    case 0x08: /* LCD_TIMING1 */
+        s->timing[1] = value >> 10;
+        s->height = (value & 0x3ff) + 1;
+        break;
+
+    case 0x0c: /* LCD_TIMING2 */
+        s->timing[2] = value;
+        break;
+
+    case 0x10: /* LCD_STATUS */
+        break;
+
+    case 0x14: /* LCD_SUBPANEL */
+        s->subpanel = value & 0xa1ffffff;
+        break;
+
+    default:
+        OMAP_BAD_REG(addr);
+    }
+}
+
+static CPUReadMemoryFunc *omap_lcdc_readfn[] = {
+    omap_lcdc_read,
+    omap_lcdc_read,
+    omap_lcdc_read,
+};
+
+static CPUWriteMemoryFunc *omap_lcdc_writefn[] = {
+    omap_lcdc_write,
+    omap_lcdc_write,
+    omap_lcdc_write,
+};
+
+void omap_lcdc_reset(struct omap_lcd_panel_s *s)
+{
+    s->dma->current_frame = -1;
+    s->plm = 0;
+    s->tft = 0;
+    s->mono = 0;
+    s->enable = 0;
+    s->width = 0;
+    s->height = 0;
+    s->interrupts = 0;
+    s->timing[0] = 0;
+    s->timing[1] = 0;
+    s->timing[2] = 0;
+    s->subpanel = 0;
+    s->palette_done = 0;
+    s->frame_done = 0;
+    s->sync_error = 0;
+    s->invalidate = 1;
+    s->subpanel = 0;
+    s->ctrl = 0;
+}
+
+struct omap_lcd_panel_s *omap_lcdc_init(target_phys_addr_t base, qemu_irq irq,
+                struct omap_dma_lcd_channel_s *dma, DisplayState *ds,
+                ram_addr_t imif_base, ram_addr_t emiff_base, omap_clk clk)
+{
+    int iomemtype;
+    struct omap_lcd_panel_s *s = (struct omap_lcd_panel_s *)
+            qemu_mallocz(sizeof(struct omap_lcd_panel_s));
+
+    s->irq = irq;
+    s->dma = dma;
+    s->base = base;
+    s->state = ds;
+    s->imif_base = imif_base;
+    s->emiff_base = emiff_base;
+    omap_lcdc_reset(s);
+
+    iomemtype = cpu_register_io_memory(0, omap_lcdc_readfn,
+                    omap_lcdc_writefn, s);
+    cpu_register_physical_memory(s->base, 0x100, iomemtype);
+
+    graphic_console_init(ds, omap_update_display,
+                    omap_invalidate_display, omap_screen_dump, s);
+
+    return s;
+}
diff --git a/hw/palm.c b/hw/palm.c
new file mode 100644 (file)
index 0000000..623fcd6
--- /dev/null
+++ b/hw/palm.c
@@ -0,0 +1,140 @@
+/*
+ * PalmOne's (TM) PDAs.
+ *
+ * Copyright (C) 2006-2007 Andrzej Zaborowski  <balrog@zabor.org>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
+ * MA 02111-1307 USA
+ */
+#include "vl.h"
+
+static uint32_t static_readb(void *opaque, target_phys_addr_t offset)
+{
+    uint32_t *val = (uint32_t *) opaque;
+    return *val >> ((offset & 3) << 3);
+}
+
+static uint32_t static_readh(void *opaque, target_phys_addr_t offset) {
+    uint32_t *val = (uint32_t *) opaque;
+    return *val >> ((offset & 1) << 3);
+}
+
+static uint32_t static_readw(void *opaque, target_phys_addr_t offset) {
+    uint32_t *val = (uint32_t *) opaque;
+    return *val >> ((offset & 0) << 3);
+}
+
+static void static_write(void *opaque, target_phys_addr_t offset,
+                uint32_t value) {
+#ifdef SPY
+    printf("%s: value %08lx written at " PA_FMT "\n",
+                    __FUNCTION__, value, offset);
+#endif
+}
+
+static CPUReadMemoryFunc *static_readfn[] = {
+    static_readb,
+    static_readh,
+    static_readw,
+};
+
+static CPUWriteMemoryFunc *static_writefn[] = {
+    static_write,
+    static_write,
+    static_write,
+};
+
+/* Palm Tunsgten|E support */
+static void palmte_microwire_setup(struct omap_mpu_state_s *cpu)
+{
+}
+
+static void palmte_init(int ram_size, int vga_ram_size, int boot_device,
+                DisplayState *ds, const char **fd_filename, int snapshot,
+                const char *kernel_filename, const char *kernel_cmdline,
+                const char *initrd_filename, const char *cpu_model)
+{
+    struct omap_mpu_state_s *cpu;
+    int flash_size = 0x00800000;
+    int sdram_size = 0x02000000;
+    int io;
+    static uint32_t cs0val = 0xffffffff;
+    static uint32_t cs1val = 0x0000e1a0;
+    static uint32_t cs2val = 0x0000e1a0;
+    static uint32_t cs3val = 0xe1a0e1a0;
+    ram_addr_t phys_flash;
+    int rom_size, rom_loaded = 0;
+
+    if (ram_size < flash_size + sdram_size + OMAP15XX_SRAM_SIZE) {
+        fprintf(stderr, "This architecture uses %i bytes of memory\n",
+                        flash_size + sdram_size + OMAP15XX_SRAM_SIZE);
+        exit(1);
+    }
+
+    cpu = omap310_mpu_init(sdram_size, ds, cpu_model);
+
+    /* External Flash (EMIFS) */
+    cpu_register_physical_memory(OMAP_CS0_BASE, flash_size,
+                    (phys_flash = qemu_ram_alloc(flash_size)) | IO_MEM_ROM);
+
+    io = cpu_register_io_memory(0, static_readfn, static_writefn, &cs0val);
+    cpu_register_physical_memory(OMAP_CS0_BASE + flash_size,
+                    OMAP_CS0_SIZE - flash_size, io);
+    io = cpu_register_io_memory(0, static_readfn, static_writefn, &cs1val);
+    cpu_register_physical_memory(OMAP_CS1_BASE, OMAP_CS1_SIZE, io);
+    io = cpu_register_io_memory(0, static_readfn, static_writefn, &cs2val);
+    cpu_register_physical_memory(OMAP_CS2_BASE, OMAP_CS2_SIZE, io);
+    io = cpu_register_io_memory(0, static_readfn, static_writefn, &cs3val);
+    cpu_register_physical_memory(OMAP_CS3_BASE, OMAP_CS3_SIZE, io);
+
+    palmte_microwire_setup(cpu);
+
+    /* Setup initial (reset) machine state */
+    if (nb_option_roms) {
+        rom_size = get_image_size(option_rom[0]);
+        if (rom_size > flash_size)
+            fprintf(stderr, "%s: ROM image too big (%x > %x)\n",
+                            __FUNCTION__, rom_size, flash_size);
+        else if (rom_size > 0 && load_image(option_rom[0],
+                                phys_ram_base + phys_flash) > 0) {
+            rom_loaded = 1;
+            cpu->env->regs[15] = 0x00000000;
+        } else
+            fprintf(stderr, "%s: error loading '%s'\n",
+                            __FUNCTION__, option_rom[0]);
+    }
+
+    if (!rom_loaded && !kernel_filename) {
+        fprintf(stderr, "Kernel or ROM image must be specified\n");
+        exit(1);
+    }
+
+    /* Load the kernel.  */
+    if (kernel_filename) {
+        /* Start at bootloader.  */
+        cpu->env->regs[15] = OMAP_EMIFF_BASE;
+
+        arm_load_kernel(cpu->env, sdram_size, kernel_filename, kernel_cmdline,
+                        initrd_filename, 0x331, OMAP_EMIFF_BASE);
+    }
+
+    dpy_resize(ds, 320, 320);
+}
+
+QEMUMachine palmte_machine = {
+    "cheetah",
+    "Palm Tungsten|E aka. Cheetah PDA (OMAP310)",
+    palmte_init,
+};
index 5bd40ffbc4f75038f00395da8bf390cd6437c8d5..ef203c3315d2b8d18d0ef886888bdc8bd13b64ff 100644 (file)
@@ -99,6 +99,10 @@ typedef struct CPUARMState {
         uint32_t c13_fcse; /* FCSE PID.  */
         uint32_t c13_context; /* Context ID.  */
         uint32_t c15_cpar; /* XScale Coprocessor Access Register */
+        uint32_t c15_ticonfig; /* TI925T configuration byte.  */
+        uint32_t c15_i_max; /* Maximum D-cache dirty line index.  */
+        uint32_t c15_i_min; /* Minimum D-cache dirty line index.  */
+        uint32_t c15_threadid; /* TI debugger thread-ID.  */
     } cp15;
 
     /* Coprocessor IO used by peripherals */
@@ -247,7 +251,8 @@ enum arm_features {
     ARM_FEATURE_AUXCR,  /* ARM1026 Auxiliary control register.  */
     ARM_FEATURE_XSCALE, /* Intel XScale extensions.  */
     ARM_FEATURE_IWMMXT, /* Intel iwMMXt extension.  */
-    ARM_FEATURE_MPU     /* Only has Memory Protection Unit, not full MMU.  */
+    ARM_FEATURE_MPU,    /* Only has Memory Protection Unit, not full MMU.  */
+    ARM_FEATURE_OMAPCP  /* OMAP specific CP15 ops handling.  */
 };
 
 static inline int arm_feature(CPUARMState *env, int feature)
@@ -265,6 +270,8 @@ void cpu_arm_set_cp_io(CPUARMState *env, int cpnum,
 #define ARM_CPUID_ARM1026   0x4106a262
 #define ARM_CPUID_ARM926    0x41069265
 #define ARM_CPUID_ARM946    0x41059461
+#define ARM_CPUID_TI915T    0x54029152
+#define ARM_CPUID_TI925T    0x54029252
 #define ARM_CPUID_PXA250    0x69052100
 #define ARM_CPUID_PXA255    0x69052d00
 #define ARM_CPUID_PXA260    0x69052903
index 61f8109064f866e98fd0ecde4de12f6d167435e2..260a04aa06a51a5fe0bee9471ac903930d29850b 100644 (file)
@@ -32,6 +32,15 @@ static void cpu_reset_model_id(CPUARMState *env, uint32_t id)
         env->cp15.c0_cachetype = 0x1dd20d2;
         env->cp15.c1_sys = 0x00090078;
         break;
+    case ARM_CPUID_TI915T:
+    case ARM_CPUID_TI925T:
+        set_feature(env, ARM_FEATURE_OMAPCP);
+        env->cp15.c0_cpuid = ARM_CPUID_TI925T; /* Depends on wiring.  */
+        env->cp15.c0_cachetype = 0x5109149;
+        env->cp15.c1_sys = 0x00000070;
+        env->cp15.c15_i_max = 0x000;
+        env->cp15.c15_i_min = 0xff0;
+        break;
     case ARM_CPUID_PXA250:
     case ARM_CPUID_PXA255:
     case ARM_CPUID_PXA260:
@@ -101,6 +110,7 @@ static const struct arm_cpu_t arm_cpu_names[] = {
     { ARM_CPUID_ARM926, "arm926"},
     { ARM_CPUID_ARM946, "arm946"},
     { ARM_CPUID_ARM1026, "arm1026"},
+    { ARM_CPUID_TI925T, "ti925t" },
     { ARM_CPUID_PXA250, "pxa250" },
     { ARM_CPUID_PXA255, "pxa255" },
     { ARM_CPUID_PXA260, "pxa260" },
@@ -644,8 +654,12 @@ void helper_set_cp15(CPUState *env, uint32_t insn, uint32_t val)
     case 0: /* ID codes.  */
         if (arm_feature(env, ARM_FEATURE_XSCALE))
             break;
+        if (arm_feature(env, ARM_FEATURE_OMAPCP))
+            break;
         goto bad_reg;
     case 1: /* System configuration.  */
+        if (arm_feature(env, ARM_FEATURE_OMAPCP))
+            op2 = 0;
         switch (op2) {
         case 0:
             if (!arm_feature(env, ARM_FEATURE_XSCALE) || crm == 0)
@@ -693,6 +707,8 @@ void helper_set_cp15(CPUState *env, uint32_t insn, uint32_t val)
     case 4: /* Reserved.  */
         goto bad_reg;
     case 5: /* MMU Fault status / MPU access permission.  */
+        if (arm_feature(env, ARM_FEATURE_OMAPCP))
+            op2 = 0;
         switch (op2) {
         case 0:
             if (arm_feature(env, ARM_FEATURE_MPU))
@@ -724,6 +740,8 @@ void helper_set_cp15(CPUState *env, uint32_t insn, uint32_t val)
                 goto bad_reg;
             env->cp15.c6_region[crm] = val;
         } else {
+            if (arm_feature(env, ARM_FEATURE_OMAPCP))
+                op2 = 0;
             switch (op2) {
             case 0:
                 env->cp15.c6_data = val;
@@ -737,6 +755,8 @@ void helper_set_cp15(CPUState *env, uint32_t insn, uint32_t val)
         }
         break;
     case 7: /* Cache control.  */
+        env->cp15.c15_i_max = 0x000;
+        env->cp15.c15_i_min = 0xff0;
         /* No cache, so nothing to do.  */
         break;
     case 8: /* MMU TLB control.  */
@@ -763,6 +783,8 @@ void helper_set_cp15(CPUState *env, uint32_t insn, uint32_t val)
         }
         break;
     case 9:
+        if (arm_feature(env, ARM_FEATURE_OMAPCP))
+            break;
         switch (crm) {
         case 0: /* Cache lockdown.  */
             switch (op2) {
@@ -823,6 +845,31 @@ void helper_set_cp15(CPUState *env, uint32_t insn, uint32_t val)
             }
             goto bad_reg;
         }
+        if (arm_feature(env, ARM_FEATURE_OMAPCP)) {
+            switch (crm) {
+            case 0:
+                break;
+            case 1: /* Set TI925T configuration.  */
+                env->cp15.c15_ticonfig = val & 0xe7;
+                env->cp15.c0_cpuid = (val & (1 << 5)) ? /* OS_TYPE bit */
+                        ARM_CPUID_TI915T : ARM_CPUID_TI925T;
+                break;
+            case 2: /* Set I_max.  */
+                env->cp15.c15_i_max = val;
+                break;
+            case 3: /* Set I_min.  */
+                env->cp15.c15_i_min = val;
+                break;
+            case 4: /* Set thread-ID.  */
+                env->cp15.c15_threadid = val & 0xffff;
+                break;
+            case 8: /* Wait-for-interrupt (deprecated).  */
+                cpu_interrupt(env, CPU_INTERRUPT_HALT);
+                break;
+            default:
+                goto bad_reg;
+            }
+        }
         break;
     }
     return;
@@ -834,8 +881,10 @@ bad_reg:
 uint32_t helper_get_cp15(CPUState *env, uint32_t insn)
 {
     uint32_t op2;
+    uint32_t crm;
 
     op2 = (insn >> 5) & 7;
+    crm = insn & 0xf;
     switch ((insn >> 16) & 0xf) {
     case 0: /* ID codes.  */
         switch (op2) {
@@ -849,6 +898,8 @@ uint32_t helper_get_cp15(CPUState *env, uint32_t insn)
             return 0;
         }
     case 1: /* System configuration.  */
+        if (arm_feature(env, ARM_FEATURE_OMAPCP))
+            op2 = 0;
         switch (op2) {
         case 0: /* Control register.  */
             return env->cp15.c1_sys;
@@ -885,6 +936,8 @@ uint32_t helper_get_cp15(CPUState *env, uint32_t insn)
     case 4: /* Reserved.  */
         goto bad_reg;
     case 5: /* MMU Fault status / MPU access permission.  */
+        if (arm_feature(env, ARM_FEATURE_OMAPCP))
+            op2 = 0;
         switch (op2) {
         case 0:
             if (arm_feature(env, ARM_FEATURE_MPU))
@@ -913,6 +966,8 @@ uint32_t helper_get_cp15(CPUState *env, uint32_t insn)
                 goto bad_reg;
             return env->cp15.c6_region[n];
         } else {
+            if (arm_feature(env, ARM_FEATURE_OMAPCP))
+                op2 = 0;
             switch (op2) {
             case 0:
                 return env->cp15.c6_data;
@@ -933,6 +988,8 @@ uint32_t helper_get_cp15(CPUState *env, uint32_t insn)
     case 8: /* MMU TLB control.  */
         goto bad_reg;
     case 9: /* Cache lockdown.  */
+        if (arm_feature(env, ARM_FEATURE_OMAPCP))
+            return 0;
         switch (op2) {
         case 0:
             return env->cp15.c9_data;
@@ -960,11 +1017,28 @@ uint32_t helper_get_cp15(CPUState *env, uint32_t insn)
         goto bad_reg;
     case 15: /* Implementation specific.  */
         if (arm_feature(env, ARM_FEATURE_XSCALE)) {
-            if (op2 == 0 && (insn & 0xf) == 1)
+            if (op2 == 0 && crm == 1)
                 return env->cp15.c15_cpar;
 
             goto bad_reg;
         }
+        if (arm_feature(env, ARM_FEATURE_OMAPCP)) {
+            switch (crm) {
+            case 0:
+                return 0;
+            case 1: /* Read TI925T configuration.  */
+                return env->cp15.c15_ticonfig;
+            case 2: /* Read I_max.  */
+                return env->cp15.c15_i_max;
+            case 3: /* Read I_min.  */
+                return env->cp15.c15_i_min;
+            case 4: /* Read thread-ID.  */
+                return env->cp15.c15_threadid;
+            case 8: /* TI925T_status */
+                return 0;
+            }
+            goto bad_reg;
+        }
         return 0;
     }
 bad_reg:
diff --git a/vl.c b/vl.c
index dc2297ecf59b30056a6510784ec372e6a72a5692..4ff23413261c12a1fd1007e3a0ed626a273c599a 100644 (file)
--- a/vl.c
+++ b/vl.c
@@ -6999,6 +6999,7 @@ void register_machines(void)
     qemu_register_machine(&spitzpda_machine);
     qemu_register_machine(&borzoipda_machine);
     qemu_register_machine(&terrierpda_machine);
+    qemu_register_machine(&palmte_machine);
 #elif defined(TARGET_SH4)
     qemu_register_machine(&shix_machine);
 #elif defined(TARGET_ALPHA)
diff --git a/vl.h b/vl.h
index d943e7f4830499645fd7c2d3bdc3e75c89a4d8a3..242b4bc3d2b383eefef8e12de7fa070c80ba5ff9 100644 (file)
--- a/vl.h
+++ b/vl.h
@@ -1417,6 +1417,9 @@ extern QEMUMachine spitzpda_machine;
 extern QEMUMachine borzoipda_machine;
 extern QEMUMachine terrierpda_machine;
 
+/* palm.c */
+extern QEMUMachine palmte_machine;
+
 /* ps2.c */
 void *ps2_kbd_init(void (*update_irq)(void *, int), void *update_arg);
 void *ps2_mouse_init(void (*update_irq)(void *, int), void *update_arg);
@@ -1606,6 +1609,8 @@ void qemu_get_ptimer(QEMUFile *f, ptimer_state *s);
 
 #include "hw/pxa.h"
 
+#include "hw/omap.h"
+
 /* mcf_uart.c */
 uint32_t mcf_uart_read(void *opaque, target_phys_addr_t addr);
 void mcf_uart_write(void *opaque, target_phys_addr_t addr, uint32_t val);