All of lore.kernel.org
 help / color / mirror / Atom feed
* [Qemu-devel] [PULL 00/22] arm-devs queue
@ 2012-02-17 11:29 Peter Maydell
  2012-02-17 11:29 ` [Qemu-devel] [PATCH 01/22] ARM: exynos4210: IRQ subsystem support Peter Maydell
                   ` (22 more replies)
  0 siblings, 23 replies; 24+ messages in thread
From: Peter Maydell @ 2012-02-17 11:29 UTC (permalink / raw)
  To: Anthony Liguori; +Cc: qemu-devel, Paul Brook

Hi; this is a pullreq for my arm-devs queue. Changes in
the queue are
 * exynos4 model
 * vexpress-a15 model
 * the addition of a Highbank section to MAINTAINERS
 * a few minor cleanup patches

Please pull (before somebody lands another whole-tree change that
means it needs rebasing again :-))

thanks
-- PMM

The following changes since commit 9de36b1a7cf61aa8be365f13c81668b3e19fbc7f:

  Make -machine/-enable-kvm options merge into a single list (2012-02-17 09:10:13 +0100)

are available in the git repository at:
  git://git.linaro.org/people/pmaydell/qemu-arm.git arm-devs.for-upstream

Evgeny Voevodin (7):
      ARM: exynos4210: IRQ subsystem support.
      ARM: Samsung exynos4210-based boards emulation
      ARM: exynos4210: PWM support.
      ARM: exynos4210: MCT support.
      hw/lan9118: Add basic 16-bit mode support.
      hw/exynos4210.c: Add LAN support for SMDKC210.
      MAINTAINERS: Add maintainers for Exynos SOC.

Maksim Kozlov (2):
      ARM: exynos4210: UART support
      ARM: exynos4210: basic Power Management Unit implementation

Mark Langsdorf (1):
      MAINTAINERS: Add hw/highbank.c maintainer

Mitsyanko Igor (1):
      Exynos4210: added display controller implementation

Peter Maydell (11):
      hw/a15mpcore.c: Add Cortex-A15 private peripheral model
      hw/vexpress.c: Make motherboard peripheral memory map table-driven
      hw/vexpress.c: Move secondary CPU boot code to SRAM
      hw/vexpress.c: Factor out daughterboard-specific initialization
      hw/vexpress.c: Instantiate the motherboard CLCD
      arm_boot: Pass base address of GIC CPU interface, not whole GIC
      hw/vexpress.c: Add vexpress-a15 machine
      hw/arm_sysctl: Drop legacy init function
      hw/primecell.h: Remove obsolete pl080_init() declaration
      Remove unnecessary includes of primecell.h
      hw/pl031: Actually raise interrupt on timer expiry

 MAINTAINERS              |   14 +
 Makefile.target          |    5 +-
 hw/a15mpcore.c           |  103 +++
 hw/arm-misc.h            |    2 +-
 hw/arm_boot.c            |    8 +-
 hw/arm_sysctl.c          |   16 +-
 hw/exynos4210.c          |  270 +++++++
 hw/exynos4210.h          |  131 ++++
 hw/exynos4210_combiner.c |  469 +++++++++++
 hw/exynos4210_fimd.c     | 1928 ++++++++++++++++++++++++++++++++++++++++++++++
 hw/exynos4210_gic.c      |  458 +++++++++++
 hw/exynos4210_mct.c      | 1488 +++++++++++++++++++++++++++++++++++
 hw/exynos4210_pmu.c      |  499 ++++++++++++
 hw/exynos4210_pwm.c      |  422 ++++++++++
 hw/exynos4210_uart.c     |  676 ++++++++++++++++
 hw/exynos4_boards.c      |  177 +++++
 hw/highbank.c            |    1 -
 hw/integratorcp.c        |    1 -
 hw/lan9118.c             |  124 +++-
 hw/pl022.c               |    1 -
 hw/pl031.c               |    2 +-
 hw/primecell.h           |    6 -
 hw/realview.c            |   12 +-
 hw/versatilepb.c         |    1 -
 hw/vexpress.c            |  410 ++++++++--
 25 files changed, 7101 insertions(+), 123 deletions(-)
 create mode 100644 hw/a15mpcore.c
 create mode 100644 hw/exynos4210.c
 create mode 100644 hw/exynos4210.h
 create mode 100644 hw/exynos4210_combiner.c
 create mode 100644 hw/exynos4210_fimd.c
 create mode 100644 hw/exynos4210_gic.c
 create mode 100644 hw/exynos4210_mct.c
 create mode 100644 hw/exynos4210_pmu.c
 create mode 100644 hw/exynos4210_pwm.c
 create mode 100644 hw/exynos4210_uart.c
 create mode 100644 hw/exynos4_boards.c

^ permalink raw reply	[flat|nested] 24+ messages in thread

* [Qemu-devel] [PATCH 01/22] ARM: exynos4210: IRQ subsystem support.
  2012-02-17 11:29 [Qemu-devel] [PULL 00/22] arm-devs queue Peter Maydell
@ 2012-02-17 11:29 ` Peter Maydell
  2012-02-17 11:29 ` [Qemu-devel] [PATCH 02/22] ARM: Samsung exynos4210-based boards emulation Peter Maydell
                   ` (21 subsequent siblings)
  22 siblings, 0 replies; 24+ messages in thread
From: Peter Maydell @ 2012-02-17 11:29 UTC (permalink / raw)
  To: Anthony Liguori; +Cc: qemu-devel, Paul Brook

From: Evgeny Voevodin <e.voevodin@samsung.com>

Signed-off-by: Evgeny Voevodin <e.voevodin@samsung.com>
Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
---
 Makefile.target          |    1 +
 hw/exynos4210.h          |   82 ++++++++
 hw/exynos4210_combiner.c |  469 ++++++++++++++++++++++++++++++++++++++++++++++
 hw/exynos4210_gic.c      |  458 ++++++++++++++++++++++++++++++++++++++++++++
 4 files changed, 1010 insertions(+), 0 deletions(-)
 create mode 100644 hw/exynos4210.h
 create mode 100644 hw/exynos4210_combiner.c
 create mode 100644 hw/exynos4210_gic.c

diff --git a/Makefile.target b/Makefile.target
index 29fde6e..2b16c0f 100644
--- a/Makefile.target
+++ b/Makefile.target
@@ -344,6 +344,7 @@ obj-arm-y = integratorcp.o versatilepb.o arm_pic.o arm_timer.o
 obj-arm-y += arm_boot.o pl011.o pl031.o pl050.o pl080.o pl110.o pl181.o pl190.o
 obj-arm-y += versatile_pci.o
 obj-arm-y += realview_gic.o realview.o arm_sysctl.o arm11mpcore.o a9mpcore.o
+obj-arm-y += exynos4210_gic.o exynos4210_combiner.o
 obj-arm-y += arm_l2x0.o
 obj-arm-y += arm_mptimer.o
 obj-arm-y += armv7m.o armv7m_nvic.o stellaris.o pl022.o stellaris_enet.o
diff --git a/hw/exynos4210.h b/hw/exynos4210.h
new file mode 100644
index 0000000..ef4732f
--- /dev/null
+++ b/hw/exynos4210.h
@@ -0,0 +1,82 @@
+/*
+ *  Samsung exynos4210 SoC emulation
+ *
+ *  Copyright (c) 2011 Samsung Electronics Co., Ltd. All rights reserved.
+ *    Maksim Kozlov <m.kozlov@samsung.com>
+ *    Evgeny Voevodin <e.voevodin@samsung.com>
+ *    Igor Mitsyanko <i.mitsyanko@samsung.com>
+ *
+ *
+ *  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, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+
+#ifndef EXYNOS4210_H_
+#define EXYNOS4210_H_
+
+#include "qemu-common.h"
+#include "memory.h"
+
+#define EXYNOS4210_NCPUS                    2
+
+/*
+ * exynos4210 IRQ subsystem stub definitions.
+ */
+#define EXYNOS4210_IRQ_GATE_NINPUTS 8
+
+#define EXYNOS4210_MAX_INT_COMBINER_OUT_IRQ  64
+#define EXYNOS4210_MAX_EXT_COMBINER_OUT_IRQ  16
+#define EXYNOS4210_MAX_INT_COMBINER_IN_IRQ   \
+    (EXYNOS4210_MAX_INT_COMBINER_OUT_IRQ * 8)
+#define EXYNOS4210_MAX_EXT_COMBINER_IN_IRQ   \
+    (EXYNOS4210_MAX_EXT_COMBINER_OUT_IRQ * 8)
+
+#define EXYNOS4210_COMBINER_GET_IRQ_NUM(grp, bit)  ((grp)*8 + (bit))
+#define EXYNOS4210_COMBINER_GET_GRP_NUM(irq)       ((irq) / 8)
+#define EXYNOS4210_COMBINER_GET_BIT_NUM(irq) \
+    ((irq) - 8 * EXYNOS4210_COMBINER_GET_GRP_NUM(irq))
+
+/* IRQs number for external and internal GIC */
+#define EXYNOS4210_EXT_GIC_NIRQ     (160-32)
+#define EXYNOS4210_INT_GIC_NIRQ     64
+
+typedef struct Exynos4210Irq {
+    qemu_irq int_combiner_irq[EXYNOS4210_MAX_INT_COMBINER_IN_IRQ];
+    qemu_irq ext_combiner_irq[EXYNOS4210_MAX_EXT_COMBINER_IN_IRQ];
+    qemu_irq int_gic_irq[EXYNOS4210_INT_GIC_NIRQ];
+    qemu_irq ext_gic_irq[EXYNOS4210_EXT_GIC_NIRQ];
+    qemu_irq board_irqs[EXYNOS4210_MAX_INT_COMBINER_IN_IRQ];
+} Exynos4210Irq;
+
+/* Initialize exynos4210 IRQ subsystem stub */
+qemu_irq *exynos4210_init_irq(Exynos4210Irq *env);
+
+/* Initialize board IRQs.
+ * These IRQs contain splitted Int/External Combiner and External Gic IRQs */
+void exynos4210_init_board_irqs(Exynos4210Irq *s);
+
+/* Get IRQ number from exynos4210 IRQ subsystem stub.
+ * To identify IRQ source use internal combiner group and bit number
+ *  grp - group number
+ *  bit - bit number inside group */
+uint32_t exynos4210_get_irq(uint32_t grp, uint32_t bit);
+
+/*
+ * Get Combiner input GPIO into irqs structure
+ */
+void exynos4210_combiner_get_gpioin(Exynos4210Irq *irqs, DeviceState *dev,
+        int ext);
+
+#endif /* EXYNOS4210_H_ */
diff --git a/hw/exynos4210_combiner.c b/hw/exynos4210_combiner.c
new file mode 100644
index 0000000..6110c19
--- /dev/null
+++ b/hw/exynos4210_combiner.c
@@ -0,0 +1,469 @@
+/*
+ * Samsung exynos4210 Interrupt Combiner
+ *
+ * Copyright (c) 2000 - 2011 Samsung Electronics Co., Ltd.
+ * All rights reserved.
+ *
+ * Evgeny Voevodin <e.voevodin@samsung.com>
+ *
+ * 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, see <http://www.gnu.org/licenses/>.
+ */
+
+/*
+ * Exynos4210 Combiner represents an OR gate for SOC's IRQ lines. It combines
+ * IRQ sources into groups and provides signal output to GIC from each group. It
+ * is driven by common mask and enable/disable logic. Take a note that not all
+ * IRQs are passed to GIC through Combiner.
+ */
+
+#include "sysbus.h"
+
+#include "exynos4210.h"
+
+//#define DEBUG_COMBINER
+
+#ifdef DEBUG_COMBINER
+#define DPRINTF(fmt, ...) \
+        do { fprintf(stdout, "COMBINER: [%s:%d] " fmt, __func__ , __LINE__, \
+                ## __VA_ARGS__); } while (0)
+#else
+#define DPRINTF(fmt, ...) do {} while (0)
+#endif
+
+#define    IIC_NGRP        64            /* Internal Interrupt Combiner
+                                            Groups number */
+#define    IIC_NIRQ        (IIC_NGRP * 8)/* Internal Interrupt Combiner
+                                            Interrupts number */
+#define IIC_REGION_SIZE    0x108         /* Size of memory mapped region */
+#define IIC_REGSET_SIZE    0x41
+
+/*
+ * State for each output signal of internal combiner
+ */
+typedef struct CombinerGroupState {
+    uint8_t src_mask;            /* 1 - source enabled, 0 - disabled */
+    uint8_t src_pending;        /* Pending source interrupts before masking */
+} CombinerGroupState;
+
+typedef struct Exynos4210CombinerState {
+    SysBusDevice busdev;
+    MemoryRegion iomem;
+
+    struct CombinerGroupState group[IIC_NGRP];
+    uint32_t reg_set[IIC_REGSET_SIZE];
+    uint32_t icipsr[2];
+    uint32_t external;          /* 1 means that this combiner is external */
+
+    qemu_irq output_irq[IIC_NGRP];
+} Exynos4210CombinerState;
+
+static const VMStateDescription vmstate_exynos4210_combiner_group_state = {
+    .name = "exynos4210.combiner.groupstate",
+    .version_id = 1,
+    .minimum_version_id = 1,
+    .minimum_version_id_old = 1,
+    .fields = (VMStateField[]) {
+        VMSTATE_UINT8(src_mask, CombinerGroupState),
+        VMSTATE_UINT8(src_pending, CombinerGroupState),
+        VMSTATE_END_OF_LIST()
+    }
+};
+
+static const VMStateDescription vmstate_exynos4210_combiner = {
+    .name = "exynos4210.combiner",
+    .version_id = 1,
+    .minimum_version_id = 1,
+    .minimum_version_id_old = 1,
+    .fields = (VMStateField[]) {
+        VMSTATE_STRUCT_ARRAY(group, Exynos4210CombinerState, IIC_NGRP, 0,
+                vmstate_exynos4210_combiner_group_state, CombinerGroupState),
+        VMSTATE_UINT32_ARRAY(reg_set, Exynos4210CombinerState,
+                IIC_REGSET_SIZE),
+        VMSTATE_UINT32_ARRAY(icipsr, Exynos4210CombinerState, 2),
+        VMSTATE_UINT32(external, Exynos4210CombinerState),
+        VMSTATE_END_OF_LIST()
+    }
+};
+
+/*
+ * Get Combiner input GPIO into irqs structure
+ */
+void exynos4210_combiner_get_gpioin(Exynos4210Irq *irqs, DeviceState *dev,
+        int ext)
+{
+    int n;
+    int bit;
+    int max;
+    qemu_irq *irq;
+
+    max = ext ? EXYNOS4210_MAX_EXT_COMBINER_IN_IRQ :
+        EXYNOS4210_MAX_INT_COMBINER_IN_IRQ;
+    irq = ext ? irqs->ext_combiner_irq : irqs->int_combiner_irq;
+
+    /*
+     * Some IRQs of Int/External Combiner are going to two Combiners groups,
+     * so let split them.
+     */
+    for (n = 0; n < max; n++) {
+
+        bit = EXYNOS4210_COMBINER_GET_BIT_NUM(n);
+
+        switch (n) {
+        /* MDNIE_LCD1 INTG1 */
+        case EXYNOS4210_COMBINER_GET_IRQ_NUM(1, 0) ...
+             EXYNOS4210_COMBINER_GET_IRQ_NUM(1, 3):
+            irq[n] = qemu_irq_split(qdev_get_gpio_in(dev, n),
+                    irq[EXYNOS4210_COMBINER_GET_IRQ_NUM(0, bit + 4)]);
+            continue;
+
+        /* TMU INTG3 */
+        case EXYNOS4210_COMBINER_GET_IRQ_NUM(3, 4):
+            irq[n] = qemu_irq_split(qdev_get_gpio_in(dev, n),
+                    irq[EXYNOS4210_COMBINER_GET_IRQ_NUM(2, bit)]);
+            continue;
+
+        /* LCD1 INTG12 */
+        case EXYNOS4210_COMBINER_GET_IRQ_NUM(12, 0) ...
+             EXYNOS4210_COMBINER_GET_IRQ_NUM(12, 3):
+            irq[n] = qemu_irq_split(qdev_get_gpio_in(dev, n),
+                    irq[EXYNOS4210_COMBINER_GET_IRQ_NUM(11, bit + 4)]);
+            continue;
+
+        /* Multi-Core Timer INTG12 */
+        case EXYNOS4210_COMBINER_GET_IRQ_NUM(12, 4) ...
+             EXYNOS4210_COMBINER_GET_IRQ_NUM(12, 8):
+               irq[n] = qemu_irq_split(qdev_get_gpio_in(dev, n),
+                       irq[EXYNOS4210_COMBINER_GET_IRQ_NUM(1, bit + 4)]);
+            continue;
+
+        /* Multi-Core Timer INTG35 */
+        case EXYNOS4210_COMBINER_GET_IRQ_NUM(35, 4) ...
+             EXYNOS4210_COMBINER_GET_IRQ_NUM(35, 8):
+            irq[n] = qemu_irq_split(qdev_get_gpio_in(dev, n),
+                    irq[EXYNOS4210_COMBINER_GET_IRQ_NUM(1, bit + 4)]);
+            continue;
+
+        /* Multi-Core Timer INTG51 */
+        case EXYNOS4210_COMBINER_GET_IRQ_NUM(51, 4) ...
+             EXYNOS4210_COMBINER_GET_IRQ_NUM(51, 8):
+            irq[n] = qemu_irq_split(qdev_get_gpio_in(dev, n),
+                    irq[EXYNOS4210_COMBINER_GET_IRQ_NUM(1, bit + 4)]);
+            continue;
+
+        /* Multi-Core Timer INTG53 */
+        case EXYNOS4210_COMBINER_GET_IRQ_NUM(53, 4) ...
+             EXYNOS4210_COMBINER_GET_IRQ_NUM(53, 8):
+            irq[n] = qemu_irq_split(qdev_get_gpio_in(dev, n),
+                    irq[EXYNOS4210_COMBINER_GET_IRQ_NUM(1, bit + 4)]);
+            continue;
+        }
+
+        irq[n] = qdev_get_gpio_in(dev, n);
+    }
+}
+
+static uint64_t
+exynos4210_combiner_read(void *opaque, target_phys_addr_t offset, unsigned size)
+{
+    struct Exynos4210CombinerState *s =
+            (struct Exynos4210CombinerState *)opaque;
+    uint32_t req_quad_base_n;    /* Base of registers quad. Multiply it by 4 and
+                                   get a start of corresponding group quad */
+    uint32_t grp_quad_base_n;    /* Base of group quad */
+    uint32_t reg_n;              /* Register number inside the quad */
+    uint32_t val;
+
+    if (s->external && (offset > 0x3c && offset != 0x100)) {
+        hw_error("exynos4210.combiner: unallowed read access at offset 0x"
+                TARGET_FMT_plx "\n", offset);
+    }
+
+    req_quad_base_n = offset >> 4;
+    grp_quad_base_n = req_quad_base_n << 2;
+    reg_n = (offset - (req_quad_base_n << 4)) >> 2;
+
+    if (req_quad_base_n >= IIC_NGRP) {
+        /* Read of ICIPSR register */
+        return s->icipsr[reg_n];
+    }
+
+    val = 0;
+
+    switch (reg_n) {
+    /* IISTR */
+    case 2:
+        val |= s->group[grp_quad_base_n].src_pending;
+        val |= s->group[grp_quad_base_n + 1].src_pending << 8;
+        val |= s->group[grp_quad_base_n + 2].src_pending << 16;
+        val |= s->group[grp_quad_base_n + 3].src_pending << 24;
+        break;
+    /* IIMSR */
+    case 3:
+        val |= s->group[grp_quad_base_n].src_mask &
+        s->group[grp_quad_base_n].src_pending;
+        val |= (s->group[grp_quad_base_n + 1].src_mask &
+                s->group[grp_quad_base_n + 1].src_pending) << 8;
+        val |= (s->group[grp_quad_base_n + 2].src_mask &
+                s->group[grp_quad_base_n + 2].src_pending) << 16;
+        val |= (s->group[grp_quad_base_n + 3].src_mask &
+                s->group[grp_quad_base_n + 3].src_pending) << 24;
+        break;
+    default:
+        if (offset >> 2 >= IIC_REGSET_SIZE) {
+            hw_error("exynos4210.combiner: overflow of reg_set by 0x"
+                    TARGET_FMT_plx "offset\n", offset);
+        }
+        val = s->reg_set[offset >> 2];
+        return 0;
+    }
+    return val;
+}
+
+static void exynos4210_combiner_update(void *opaque, uint8_t group_n)
+{
+    struct Exynos4210CombinerState *s =
+            (struct Exynos4210CombinerState *)opaque;
+
+    /* Send interrupt if needed */
+    if (s->group[group_n].src_mask & s->group[group_n].src_pending) {
+#ifdef DEBUG_COMBINER
+        if (group_n != 26) {
+            /* skip uart */
+            DPRINTF("%s raise IRQ[%d]\n", s->external ? "EXT" : "INT", group_n);
+        }
+#endif
+
+        /* Set Combiner interrupt pending status after masking */
+        if (group_n >= 32) {
+            s->icipsr[1] |= 1 << (group_n - 32);
+        } else {
+            s->icipsr[0] |= 1 << group_n;
+        }
+
+        qemu_irq_raise(s->output_irq[group_n]);
+    } else {
+#ifdef DEBUG_COMBINER
+        if (group_n != 26) {
+            /* skip uart */
+            DPRINTF("%s lower IRQ[%d]\n", s->external ? "EXT" : "INT", group_n);
+        }
+#endif
+
+        /* Set Combiner interrupt pending status after masking */
+        if (group_n >= 32) {
+            s->icipsr[1] &= ~(1 << (group_n - 32));
+        } else {
+            s->icipsr[0] &= ~(1 << group_n);
+        }
+
+        qemu_irq_lower(s->output_irq[group_n]);
+    }
+}
+
+static void exynos4210_combiner_write(void *opaque, target_phys_addr_t offset,
+        uint64_t val, unsigned size)
+{
+    struct Exynos4210CombinerState *s =
+            (struct Exynos4210CombinerState *)opaque;
+    uint32_t req_quad_base_n;    /* Base of registers quad. Multiply it by 4 and
+                                   get a start of corresponding group quad */
+    uint32_t grp_quad_base_n;    /* Base of group quad */
+    uint32_t reg_n;              /* Register number inside the quad */
+
+    if (s->external && (offset > 0x3c && offset != 0x100)) {
+        hw_error("exynos4210.combiner: unallowed write access at offset 0x"
+                TARGET_FMT_plx "\n", offset);
+    }
+
+    req_quad_base_n = offset >> 4;
+    grp_quad_base_n = req_quad_base_n << 2;
+    reg_n = (offset - (req_quad_base_n << 4)) >> 2;
+
+    if (req_quad_base_n >= IIC_NGRP) {
+        hw_error("exynos4210.combiner: unallowed write access at offset 0x"
+                TARGET_FMT_plx "\n", offset);
+        return;
+    }
+
+    if (reg_n > 1) {
+        hw_error("exynos4210.combiner: unallowed write access at offset 0x"
+                TARGET_FMT_plx "\n", offset);
+        return;
+    }
+
+    if (offset >> 2 >= IIC_REGSET_SIZE) {
+        hw_error("exynos4210.combiner: overflow of reg_set by 0x"
+                TARGET_FMT_plx "offset\n", offset);
+    }
+    s->reg_set[offset >> 2] = val;
+
+    switch (reg_n) {
+    /* IIESR */
+    case 0:
+        /* FIXME: what if irq is pending, allowed by mask, and we allow it
+         * again. Interrupt will rise again! */
+
+        DPRINTF("%s enable IRQ for groups %d, %d, %d, %d\n",
+                s->external ? "EXT" : "INT",
+                grp_quad_base_n,
+                grp_quad_base_n + 1,
+                grp_quad_base_n + 2,
+                grp_quad_base_n + 3);
+
+        /* Enable interrupt sources */
+        s->group[grp_quad_base_n].src_mask |= val & 0xFF;
+        s->group[grp_quad_base_n + 1].src_mask |= (val & 0xFF00) >> 8;
+        s->group[grp_quad_base_n + 2].src_mask |= (val & 0xFF0000) >> 16;
+        s->group[grp_quad_base_n + 3].src_mask |= (val & 0xFF000000) >> 24;
+
+        exynos4210_combiner_update(s, grp_quad_base_n);
+        exynos4210_combiner_update(s, grp_quad_base_n + 1);
+        exynos4210_combiner_update(s, grp_quad_base_n + 2);
+        exynos4210_combiner_update(s, grp_quad_base_n + 3);
+        break;
+        /* IIECR */
+    case 1:
+        DPRINTF("%s disable IRQ for groups %d, %d, %d, %d\n",
+                s->external ? "EXT" : "INT",
+                grp_quad_base_n,
+                grp_quad_base_n + 1,
+                grp_quad_base_n + 2,
+                grp_quad_base_n + 3);
+
+        /* Disable interrupt sources */
+        s->group[grp_quad_base_n].src_mask &= ~(val & 0xFF);
+        s->group[grp_quad_base_n + 1].src_mask &= ~((val & 0xFF00) >> 8);
+        s->group[grp_quad_base_n + 2].src_mask &= ~((val & 0xFF0000) >> 16);
+        s->group[grp_quad_base_n + 3].src_mask &= ~((val & 0xFF000000) >> 24);
+
+        exynos4210_combiner_update(s, grp_quad_base_n);
+        exynos4210_combiner_update(s, grp_quad_base_n + 1);
+        exynos4210_combiner_update(s, grp_quad_base_n + 2);
+        exynos4210_combiner_update(s, grp_quad_base_n + 3);
+        break;
+    default:
+        hw_error("exynos4210.combiner: unallowed write access at offset 0x"
+                TARGET_FMT_plx "\n", offset);
+        break;
+    }
+
+    return;
+}
+
+/* Get combiner group and bit from irq number */
+static uint8_t get_combiner_group_and_bit(int irq, uint8_t *bit)
+{
+    *bit = irq - ((irq >> 3) << 3);
+    return irq >> 3;
+}
+
+/* Process a change in an external IRQ input.  */
+static void exynos4210_combiner_handler(void *opaque, int irq, int level)
+{
+    struct Exynos4210CombinerState *s =
+            (struct Exynos4210CombinerState *)opaque;
+    uint8_t bit_n, group_n;
+
+    group_n = get_combiner_group_and_bit(irq, &bit_n);
+
+    if (s->external && group_n >= EXYNOS4210_MAX_EXT_COMBINER_OUT_IRQ) {
+        DPRINTF("%s unallowed IRQ group 0x%x\n", s->external ? "EXT" : "INT"
+                , group_n);
+        return;
+    }
+
+    if (level) {
+        s->group[group_n].src_pending |= 1 << bit_n;
+    } else {
+        s->group[group_n].src_pending &= ~(1 << bit_n);
+    }
+
+    exynos4210_combiner_update(s, group_n);
+
+    return;
+}
+
+static void exynos4210_combiner_reset(DeviceState *d)
+{
+    struct Exynos4210CombinerState *s = (struct Exynos4210CombinerState *)d;
+
+    memset(&s->group, 0, sizeof(s->group));
+    memset(&s->reg_set, 0, sizeof(s->reg_set));
+
+    s->reg_set[0xC0 >> 2] = 0x01010101;
+    s->reg_set[0xC4 >> 2] = 0x01010101;
+    s->reg_set[0xD0 >> 2] = 0x01010101;
+    s->reg_set[0xD4 >> 2] = 0x01010101;
+}
+
+static const MemoryRegionOps exynos4210_combiner_ops = {
+    .read = exynos4210_combiner_read,
+    .write = exynos4210_combiner_write,
+    .endianness = DEVICE_NATIVE_ENDIAN,
+};
+
+/*
+ * Internal Combiner initialization.
+ */
+static int exynos4210_combiner_init(SysBusDevice *dev)
+{
+    unsigned int i;
+    struct Exynos4210CombinerState *s =
+            FROM_SYSBUS(struct Exynos4210CombinerState, dev);
+
+    /* Allocate general purpose input signals and connect a handler to each of
+     * them */
+    qdev_init_gpio_in(&s->busdev.qdev, exynos4210_combiner_handler, IIC_NIRQ);
+
+    /* Connect SysBusDev irqs to device specific irqs */
+    for (i = 0; i < IIC_NIRQ; i++) {
+        sysbus_init_irq(dev, &s->output_irq[i]);
+    }
+
+    memory_region_init_io(&s->iomem, &exynos4210_combiner_ops, s,
+            "exynos4210-combiner", IIC_REGION_SIZE);
+    sysbus_init_mmio(dev, &s->iomem);
+
+    return 0;
+}
+
+static Property exynos4210_combiner_properties[] = {
+    DEFINE_PROP_UINT32("external", Exynos4210CombinerState, external, 0),
+    DEFINE_PROP_END_OF_LIST(),
+};
+
+static void exynos4210_combiner_class_init(ObjectClass *klass, void *data)
+{
+    DeviceClass *dc = DEVICE_CLASS(klass);
+    SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass);
+
+    k->init = exynos4210_combiner_init;
+    dc->reset = exynos4210_combiner_reset;
+    dc->props = exynos4210_combiner_properties;
+    dc->vmsd = &vmstate_exynos4210_combiner;
+}
+
+static TypeInfo exynos4210_combiner_info = {
+    .name          = "exynos4210.combiner",
+    .parent        = TYPE_SYS_BUS_DEVICE,
+    .instance_size = sizeof(Exynos4210CombinerState),
+    .class_init    = exynos4210_combiner_class_init,
+};
+
+static void exynos4210_combiner_register_types(void)
+{
+    type_register_static(&exynos4210_combiner_info);
+}
+
+type_init(exynos4210_combiner_register_types)
diff --git a/hw/exynos4210_gic.c b/hw/exynos4210_gic.c
new file mode 100644
index 0000000..ec13140
--- /dev/null
+++ b/hw/exynos4210_gic.c
@@ -0,0 +1,458 @@
+/*
+ * Samsung exynos4210 GIC implementation. Based on hw/arm_gic.c
+ *
+ * Copyright (c) 2000 - 2011 Samsung Electronics Co., Ltd.
+ * All rights reserved.
+ *
+ * Evgeny Voevodin <e.voevodin@samsung.com>
+ *
+ * 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, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "sysbus.h"
+#include "qemu-common.h"
+#include "irq.h"
+#include "exynos4210.h"
+
+enum ExtGicId {
+    EXT_GIC_ID_MDMA_LCD0 = 66,
+    EXT_GIC_ID_PDMA0,
+    EXT_GIC_ID_PDMA1,
+    EXT_GIC_ID_TIMER0,
+    EXT_GIC_ID_TIMER1,
+    EXT_GIC_ID_TIMER2,
+    EXT_GIC_ID_TIMER3,
+    EXT_GIC_ID_TIMER4,
+    EXT_GIC_ID_MCT_L0,
+    EXT_GIC_ID_WDT,
+    EXT_GIC_ID_RTC_ALARM,
+    EXT_GIC_ID_RTC_TIC,
+    EXT_GIC_ID_GPIO_XB,
+    EXT_GIC_ID_GPIO_XA,
+    EXT_GIC_ID_MCT_L1,
+    EXT_GIC_ID_IEM_APC,
+    EXT_GIC_ID_IEM_IEC,
+    EXT_GIC_ID_NFC,
+    EXT_GIC_ID_UART0,
+    EXT_GIC_ID_UART1,
+    EXT_GIC_ID_UART2,
+    EXT_GIC_ID_UART3,
+    EXT_GIC_ID_UART4,
+    EXT_GIC_ID_MCT_G0,
+    EXT_GIC_ID_I2C0,
+    EXT_GIC_ID_I2C1,
+    EXT_GIC_ID_I2C2,
+    EXT_GIC_ID_I2C3,
+    EXT_GIC_ID_I2C4,
+    EXT_GIC_ID_I2C5,
+    EXT_GIC_ID_I2C6,
+    EXT_GIC_ID_I2C7,
+    EXT_GIC_ID_SPI0,
+    EXT_GIC_ID_SPI1,
+    EXT_GIC_ID_SPI2,
+    EXT_GIC_ID_MCT_G1,
+    EXT_GIC_ID_USB_HOST,
+    EXT_GIC_ID_USB_DEVICE,
+    EXT_GIC_ID_MODEMIF,
+    EXT_GIC_ID_HSMMC0,
+    EXT_GIC_ID_HSMMC1,
+    EXT_GIC_ID_HSMMC2,
+    EXT_GIC_ID_HSMMC3,
+    EXT_GIC_ID_SDMMC,
+    EXT_GIC_ID_MIPI_CSI_4LANE,
+    EXT_GIC_ID_MIPI_DSI_4LANE,
+    EXT_GIC_ID_MIPI_CSI_2LANE,
+    EXT_GIC_ID_MIPI_DSI_2LANE,
+    EXT_GIC_ID_ONENAND_AUDI,
+    EXT_GIC_ID_ROTATOR,
+    EXT_GIC_ID_FIMC0,
+    EXT_GIC_ID_FIMC1,
+    EXT_GIC_ID_FIMC2,
+    EXT_GIC_ID_FIMC3,
+    EXT_GIC_ID_JPEG,
+    EXT_GIC_ID_2D,
+    EXT_GIC_ID_PCIe,
+    EXT_GIC_ID_MIXER,
+    EXT_GIC_ID_HDMI,
+    EXT_GIC_ID_HDMI_I2C,
+    EXT_GIC_ID_MFC,
+    EXT_GIC_ID_TVENC,
+};
+
+enum ExtInt {
+    EXT_GIC_ID_EXTINT0 = 48,
+    EXT_GIC_ID_EXTINT1,
+    EXT_GIC_ID_EXTINT2,
+    EXT_GIC_ID_EXTINT3,
+    EXT_GIC_ID_EXTINT4,
+    EXT_GIC_ID_EXTINT5,
+    EXT_GIC_ID_EXTINT6,
+    EXT_GIC_ID_EXTINT7,
+    EXT_GIC_ID_EXTINT8,
+    EXT_GIC_ID_EXTINT9,
+    EXT_GIC_ID_EXTINT10,
+    EXT_GIC_ID_EXTINT11,
+    EXT_GIC_ID_EXTINT12,
+    EXT_GIC_ID_EXTINT13,
+    EXT_GIC_ID_EXTINT14,
+    EXT_GIC_ID_EXTINT15
+};
+
+/*
+ * External GIC sources which are not from External Interrupt Combiner or
+ * External Interrupts are starting from EXYNOS4210_MAX_EXT_COMBINER_OUT_IRQ,
+ * which is INTG16 in Internal Interrupt Combiner.
+ */
+
+static uint32_t
+combiner_grp_to_gic_id[64-EXYNOS4210_MAX_EXT_COMBINER_OUT_IRQ][8] = {
+    /* int combiner groups 16-19 */
+    { }, { }, { }, { },
+    /* int combiner group 20 */
+    { 0, EXT_GIC_ID_MDMA_LCD0 },
+    /* int combiner group 21 */
+    { EXT_GIC_ID_PDMA0, EXT_GIC_ID_PDMA1 },
+    /* int combiner group 22 */
+    { EXT_GIC_ID_TIMER0, EXT_GIC_ID_TIMER1, EXT_GIC_ID_TIMER2,
+            EXT_GIC_ID_TIMER3, EXT_GIC_ID_TIMER4 },
+    /* int combiner group 23 */
+    { EXT_GIC_ID_RTC_ALARM, EXT_GIC_ID_RTC_TIC },
+    /* int combiner group 24 */
+    { EXT_GIC_ID_GPIO_XB, EXT_GIC_ID_GPIO_XA },
+    /* int combiner group 25 */
+    { EXT_GIC_ID_IEM_APC, EXT_GIC_ID_IEM_IEC },
+    /* int combiner group 26 */
+    { EXT_GIC_ID_UART0, EXT_GIC_ID_UART1, EXT_GIC_ID_UART2, EXT_GIC_ID_UART3,
+            EXT_GIC_ID_UART4 },
+    /* int combiner group 27 */
+    { EXT_GIC_ID_I2C0, EXT_GIC_ID_I2C1, EXT_GIC_ID_I2C2, EXT_GIC_ID_I2C3,
+            EXT_GIC_ID_I2C4, EXT_GIC_ID_I2C5, EXT_GIC_ID_I2C6,
+            EXT_GIC_ID_I2C7 },
+    /* int combiner group 28 */
+    { EXT_GIC_ID_SPI0, EXT_GIC_ID_SPI1, EXT_GIC_ID_SPI2 },
+    /* int combiner group 29 */
+    { EXT_GIC_ID_HSMMC0, EXT_GIC_ID_HSMMC1, EXT_GIC_ID_HSMMC2,
+     EXT_GIC_ID_HSMMC3, EXT_GIC_ID_SDMMC },
+    /* int combiner group 30 */
+    { EXT_GIC_ID_MIPI_CSI_4LANE, EXT_GIC_ID_MIPI_CSI_2LANE },
+    /* int combiner group 31 */
+    { EXT_GIC_ID_MIPI_DSI_4LANE, EXT_GIC_ID_MIPI_DSI_2LANE },
+    /* int combiner group 32 */
+    { EXT_GIC_ID_FIMC0, EXT_GIC_ID_FIMC1 },
+    /* int combiner group 33 */
+    { EXT_GIC_ID_FIMC2, EXT_GIC_ID_FIMC3 },
+    /* int combiner group 34 */
+    { EXT_GIC_ID_ONENAND_AUDI, EXT_GIC_ID_NFC },
+    /* int combiner group 35 */
+    { 0, 0, 0, EXT_GIC_ID_MCT_L1, EXT_GIC_ID_MCT_G0, EXT_GIC_ID_MCT_G1 },
+    /* int combiner group 36 */
+    { EXT_GIC_ID_MIXER },
+    /* int combiner group 37 */
+    { EXT_GIC_ID_EXTINT4, EXT_GIC_ID_EXTINT5, EXT_GIC_ID_EXTINT6,
+     EXT_GIC_ID_EXTINT7 },
+    /* groups 38-50 */
+    { }, { }, { }, { }, { }, { }, { }, { }, { }, { }, { }, { }, { },
+    /* int combiner group 51 */
+    { EXT_GIC_ID_MCT_L0, 0, 0, 0, EXT_GIC_ID_MCT_G0, EXT_GIC_ID_MCT_G1 },
+    /* group 52 */
+    { },
+    /* int combiner group 53 */
+    { EXT_GIC_ID_WDT, 0, 0, 0, EXT_GIC_ID_MCT_G0, EXT_GIC_ID_MCT_G1 },
+    /* groups 54-63 */
+    { }, { }, { }, { }, { }, { }, { }, { }, { }, { }
+};
+
+#define EXYNOS4210_GIC_NIRQ 160
+#define NCPU                EXYNOS4210_NCPUS
+
+#define EXYNOS4210_EXT_GIC_CPU_REGION_SIZE     0x10000
+#define EXYNOS4210_EXT_GIC_DIST_REGION_SIZE    0x10000
+
+#define EXYNOS4210_EXT_GIC_PER_CPU_OFFSET      0x8000
+#define EXYNOS4210_EXT_GIC_CPU_GET_OFFSET(n) \
+    ((n) * EXYNOS4210_EXT_GIC_PER_CPU_OFFSET)
+#define EXYNOS4210_EXT_GIC_DIST_GET_OFFSET(n) \
+    ((n) * EXYNOS4210_EXT_GIC_PER_CPU_OFFSET)
+
+#define EXYNOS4210_GIC_CPU_REGION_SIZE  0x100
+#define EXYNOS4210_GIC_DIST_REGION_SIZE 0x1000
+
+static void exynos4210_irq_handler(void *opaque, int irq, int level)
+{
+    Exynos4210Irq *s = (Exynos4210Irq *)opaque;
+
+    /* Bypass */
+    qemu_set_irq(s->board_irqs[irq], level);
+
+    return;
+}
+
+/*
+ * Initialize exynos4210 IRQ subsystem stub.
+ */
+qemu_irq *exynos4210_init_irq(Exynos4210Irq *s)
+{
+    return qemu_allocate_irqs(exynos4210_irq_handler, s,
+            EXYNOS4210_MAX_INT_COMBINER_IN_IRQ);
+}
+
+/*
+ * Initialize board IRQs.
+ * These IRQs contain splitted Int/External Combiner and External Gic IRQs.
+ */
+void exynos4210_init_board_irqs(Exynos4210Irq *s)
+{
+    uint32_t grp, bit, irq_id, n;
+
+    for (n = 0; n < EXYNOS4210_MAX_EXT_COMBINER_IN_IRQ; n++) {
+        s->board_irqs[n] = qemu_irq_split(s->int_combiner_irq[n],
+                s->ext_combiner_irq[n]);
+
+        irq_id = 0;
+        if (n == EXYNOS4210_COMBINER_GET_IRQ_NUM(1, 4) ||
+                n == EXYNOS4210_COMBINER_GET_IRQ_NUM(12, 4)) {
+            /* MCT_G0 is passed to External GIC */
+            irq_id = EXT_GIC_ID_MCT_G0;
+        }
+        if (n == EXYNOS4210_COMBINER_GET_IRQ_NUM(1, 5) ||
+                n == EXYNOS4210_COMBINER_GET_IRQ_NUM(12, 5)) {
+            /* MCT_G1 is passed to External and GIC */
+            irq_id = EXT_GIC_ID_MCT_G1;
+        }
+        if (irq_id) {
+            s->board_irqs[n] = qemu_irq_split(s->int_combiner_irq[n],
+                    s->ext_gic_irq[irq_id-32]);
+        }
+
+    }
+    for (; n < EXYNOS4210_MAX_INT_COMBINER_IN_IRQ; n++) {
+        /* these IDs are passed to Internal Combiner and External GIC */
+        grp = EXYNOS4210_COMBINER_GET_GRP_NUM(n);
+        bit = EXYNOS4210_COMBINER_GET_BIT_NUM(n);
+        irq_id = combiner_grp_to_gic_id[grp -
+                     EXYNOS4210_MAX_EXT_COMBINER_OUT_IRQ][bit];
+
+        if (irq_id) {
+            s->board_irqs[n] = qemu_irq_split(s->int_combiner_irq[n],
+                    s->ext_gic_irq[irq_id-32]);
+        }
+    }
+}
+
+/*
+ * Get IRQ number from exynos4210 IRQ subsystem stub.
+ * To identify IRQ source use internal combiner group and bit number
+ *  grp - group number
+ *  bit - bit number inside group
+ */
+uint32_t exynos4210_get_irq(uint32_t grp, uint32_t bit)
+{
+    return EXYNOS4210_COMBINER_GET_IRQ_NUM(grp, bit);
+}
+
+/********* GIC part *********/
+
+static inline int
+gic_get_current_cpu(void)
+{
+    return cpu_single_env->cpu_index;
+}
+
+#include "arm_gic.c"
+
+typedef struct {
+    gic_state gic;
+    MemoryRegion cpu_container;
+    MemoryRegion dist_container;
+    MemoryRegion cpu_alias[NCPU];
+    MemoryRegion dist_alias[NCPU];
+    uint32_t num_cpu;
+} Exynos4210GicState;
+
+static int exynos4210_gic_init(SysBusDevice *dev)
+{
+    Exynos4210GicState *s = FROM_SYSBUSGIC(Exynos4210GicState, dev);
+    uint32_t i;
+    const char cpu_prefix[] = "exynos4210-gic-alias_cpu";
+    const char dist_prefix[] = "exynos4210-gic-alias_dist";
+    char cpu_alias_name[sizeof(cpu_prefix) + 3];
+    char dist_alias_name[sizeof(cpu_prefix) + 3];
+
+    gic_init(&s->gic, s->num_cpu, EXYNOS4210_GIC_NIRQ);
+
+    memory_region_init(&s->cpu_container, "exynos4210-cpu-container",
+            EXYNOS4210_EXT_GIC_CPU_REGION_SIZE);
+    memory_region_init(&s->dist_container, "exynos4210-dist-container",
+            EXYNOS4210_EXT_GIC_DIST_REGION_SIZE);
+
+    for (i = 0; i < s->num_cpu; i++) {
+        /* Map CPU interface per SMP Core */
+        sprintf(cpu_alias_name, "%s%x", cpu_prefix, i);
+        memory_region_init_alias(&s->cpu_alias[i],
+                                 cpu_alias_name,
+                                 &s->gic.cpuiomem[0],
+                                 0,
+                                 EXYNOS4210_GIC_CPU_REGION_SIZE);
+        memory_region_add_subregion(&s->cpu_container,
+                EXYNOS4210_EXT_GIC_CPU_GET_OFFSET(i), &s->cpu_alias[i]);
+
+        /* Map Distributor per SMP Core */
+        sprintf(dist_alias_name, "%s%x", dist_prefix, i);
+        memory_region_init_alias(&s->dist_alias[i],
+                                 dist_alias_name,
+                                 &s->gic.iomem,
+                                 0,
+                                 EXYNOS4210_GIC_DIST_REGION_SIZE);
+        memory_region_add_subregion(&s->dist_container,
+                EXYNOS4210_EXT_GIC_DIST_GET_OFFSET(i), &s->dist_alias[i]);
+    }
+
+    sysbus_init_mmio(dev, &s->cpu_container);
+    sysbus_init_mmio(dev, &s->dist_container);
+
+    gic_cpu_write(&s->gic, 1, 0, 1);
+
+    return 0;
+}
+
+static Property exynos4210_gic_properties[] = {
+    DEFINE_PROP_UINT32("num-cpu", Exynos4210GicState, num_cpu, 1),
+    DEFINE_PROP_END_OF_LIST(),
+};
+
+static void exynos4210_gic_class_init(ObjectClass *klass, void *data)
+{
+    DeviceClass *dc = DEVICE_CLASS(klass);
+    SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass);
+
+    k->init = exynos4210_gic_init;
+    dc->props = exynos4210_gic_properties;
+}
+
+static TypeInfo exynos4210_gic_info = {
+    .name          = "exynos4210.gic",
+    .parent        = TYPE_SYS_BUS_DEVICE,
+    .instance_size = sizeof(Exynos4210GicState),
+    .class_init    = exynos4210_gic_class_init,
+};
+
+static void exynos4210_gic_register_types(void)
+{
+    type_register_static(&exynos4210_gic_info);
+}
+
+type_init(exynos4210_gic_register_types)
+
+/*
+ * IRQGate struct.
+ * IRQ Gate represents OR gate between GICs to pass IRQ to PIC.
+ */
+typedef struct {
+    SysBusDevice busdev;
+
+    qemu_irq pic_irq[NCPU]; /* output IRQs to PICs */
+    uint32_t gpio_level[EXYNOS4210_IRQ_GATE_NINPUTS]; /* Input levels */
+} Exynos4210IRQGateState;
+
+static const VMStateDescription vmstate_exynos4210_irq_gate = {
+    .name = "exynos4210.irq_gate",
+    .version_id = 1,
+    .minimum_version_id = 1,
+    .minimum_version_id_old = 1,
+    .fields = (VMStateField[]) {
+        VMSTATE_UINT32_ARRAY(gpio_level, Exynos4210IRQGateState,
+                EXYNOS4210_IRQ_GATE_NINPUTS),
+        VMSTATE_END_OF_LIST()
+    }
+};
+
+/* Process a change in an external IRQ input.  */
+static void exynos4210_irq_gate_handler(void *opaque, int irq, int level)
+{
+    Exynos4210IRQGateState *s =
+            (Exynos4210IRQGateState *)opaque;
+    uint32_t odd, even;
+
+    if (irq & 1) {
+        odd = irq;
+        even = irq & ~1;
+    } else {
+        even = irq;
+        odd = irq | 1;
+    }
+
+    assert(irq < EXYNOS4210_IRQ_GATE_NINPUTS);
+    s->gpio_level[irq] = level;
+
+    if (s->gpio_level[odd] >= 1 || s->gpio_level[even] >= 1) {
+        qemu_irq_raise(s->pic_irq[even >> 1]);
+    } else {
+        qemu_irq_lower(s->pic_irq[even >> 1]);
+    }
+
+    return;
+}
+
+static void exynos4210_irq_gate_reset(DeviceState *d)
+{
+    Exynos4210IRQGateState *s = (Exynos4210IRQGateState *)d;
+
+    memset(&s->gpio_level, 0, sizeof(s->gpio_level));
+}
+
+/*
+ * IRQ Gate initialization.
+ */
+static int exynos4210_irq_gate_init(SysBusDevice *dev)
+{
+    unsigned int i;
+    Exynos4210IRQGateState *s =
+            FROM_SYSBUS(Exynos4210IRQGateState, dev);
+
+    /* Allocate general purpose input signals and connect a handler to each of
+     * them */
+    qdev_init_gpio_in(&s->busdev.qdev, exynos4210_irq_gate_handler,
+            EXYNOS4210_IRQ_GATE_NINPUTS);
+
+    /* Connect SysBusDev irqs to device specific irqs */
+    for (i = 0; i < NCPU; i++) {
+        sysbus_init_irq(dev, &s->pic_irq[i]);
+    }
+
+    return 0;
+}
+
+static void exynos4210_irq_gate_class_init(ObjectClass *klass, void *data)
+{
+    DeviceClass *dc = DEVICE_CLASS(klass);
+    SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass);
+
+    k->init = exynos4210_irq_gate_init;
+    dc->reset = exynos4210_irq_gate_reset;
+    dc->vmsd = &vmstate_exynos4210_irq_gate;
+}
+
+static TypeInfo exynos4210_irq_gate_info = {
+    .name          = "exynos4210.irq_gate",
+    .parent        = TYPE_SYS_BUS_DEVICE,
+    .instance_size = sizeof(Exynos4210IRQGateState),
+    .class_init    = exynos4210_irq_gate_class_init,
+};
+
+static void exynos4210_irq_gate_register_types(void)
+{
+    type_register_static(&exynos4210_irq_gate_info);
+}
+
+type_init(exynos4210_irq_gate_register_types)
-- 
1.7.1

^ permalink raw reply related	[flat|nested] 24+ messages in thread

* [Qemu-devel] [PATCH 02/22] ARM: Samsung exynos4210-based boards emulation
  2012-02-17 11:29 [Qemu-devel] [PULL 00/22] arm-devs queue Peter Maydell
  2012-02-17 11:29 ` [Qemu-devel] [PATCH 01/22] ARM: exynos4210: IRQ subsystem support Peter Maydell
@ 2012-02-17 11:29 ` Peter Maydell
  2012-02-17 11:29 ` [Qemu-devel] [PATCH 03/22] ARM: exynos4210: UART support Peter Maydell
                   ` (20 subsequent siblings)
  22 siblings, 0 replies; 24+ messages in thread
From: Peter Maydell @ 2012-02-17 11:29 UTC (permalink / raw)
  To: Anthony Liguori; +Cc: qemu-devel, Paul Brook

From: Evgeny Voevodin <e.voevodin@samsung.com>

Add initial support of NURI and SMDKC210 boards

Signed-off-by: Evgeny Voevodin <e.voevodin@samsung.com>
Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
---
 Makefile.target     |    3 +-
 hw/exynos4210.c     |  191 +++++++++++++++++++++++++++++++++++++++++++++++++++
 hw/exynos4210.h     |   40 +++++++++++
 hw/exynos4_boards.c |  153 +++++++++++++++++++++++++++++++++++++++++
 4 files changed, 386 insertions(+), 1 deletions(-)
 create mode 100644 hw/exynos4210.c
 create mode 100644 hw/exynos4_boards.c

diff --git a/Makefile.target b/Makefile.target
index 2b16c0f..c9afbd3 100644
--- a/Makefile.target
+++ b/Makefile.target
@@ -344,7 +344,8 @@ obj-arm-y = integratorcp.o versatilepb.o arm_pic.o arm_timer.o
 obj-arm-y += arm_boot.o pl011.o pl031.o pl050.o pl080.o pl110.o pl181.o pl190.o
 obj-arm-y += versatile_pci.o
 obj-arm-y += realview_gic.o realview.o arm_sysctl.o arm11mpcore.o a9mpcore.o
-obj-arm-y += exynos4210_gic.o exynos4210_combiner.o
+obj-arm-y += exynos4210_gic.o exynos4210_combiner.o exynos4210.o
+obj-arm-y += exynos4_boards.o
 obj-arm-y += arm_l2x0.o
 obj-arm-y += arm_mptimer.o
 obj-arm-y += armv7m.o armv7m_nvic.o stellaris.o pl022.o stellaris_enet.o
diff --git a/hw/exynos4210.c b/hw/exynos4210.c
new file mode 100644
index 0000000..5b1e24c
--- /dev/null
+++ b/hw/exynos4210.c
@@ -0,0 +1,191 @@
+/*
+ *  Samsung exynos4210 SoC emulation
+ *
+ *  Copyright (c) 2011 Samsung Electronics Co., Ltd. All rights reserved.
+ *    Maksim Kozlov <m.kozlov@samsung.com>
+ *    Evgeny Voevodin <e.voevodin@samsung.com>
+ *    Igor Mitsyanko  <i.mitsyanko@samsung.com>
+ *
+ *  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, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "boards.h"
+#include "sysemu.h"
+#include "sysbus.h"
+#include "arm-misc.h"
+#include "exynos4210.h"
+
+#define EXYNOS4210_CHIPID_ADDR         0x10000000
+
+/* External GIC */
+#define EXYNOS4210_EXT_GIC_CPU_BASE_ADDR    0x10480000
+#define EXYNOS4210_EXT_GIC_DIST_BASE_ADDR   0x10490000
+
+/* Combiner */
+#define EXYNOS4210_EXT_COMBINER_BASE_ADDR   0x10440000
+#define EXYNOS4210_INT_COMBINER_BASE_ADDR   0x10448000
+
+static uint8_t chipid_and_omr[] = { 0x11, 0x02, 0x21, 0x43,
+                                    0x09, 0x00, 0x00, 0x00 };
+
+Exynos4210State *exynos4210_init(MemoryRegion *system_mem,
+        unsigned long ram_size)
+{
+    qemu_irq cpu_irq[4];
+    int n;
+    Exynos4210State *s = g_new(Exynos4210State, 1);
+    qemu_irq *irqp;
+    qemu_irq gate_irq[EXYNOS4210_IRQ_GATE_NINPUTS];
+    unsigned long mem_size;
+    DeviceState *dev;
+    SysBusDevice *busdev;
+
+    for (n = 0; n < EXYNOS4210_NCPUS; n++) {
+        s->env[n] = cpu_init("cortex-a9");
+        if (!s->env[n]) {
+            fprintf(stderr, "Unable to find CPU %d definition\n", n);
+            exit(1);
+        }
+        /* Create PIC controller for each processor instance */
+        irqp = arm_pic_init_cpu(s->env[n]);
+
+        /*
+         * Get GICs gpio_in cpu_irq to connect a combiner to them later.
+         * Use only IRQ for a while.
+         */
+        cpu_irq[n] = irqp[ARM_PIC_CPU_IRQ];
+    }
+
+    /*** IRQs ***/
+
+    s->irq_table = exynos4210_init_irq(&s->irqs);
+
+    /* IRQ Gate */
+    dev = qdev_create(NULL, "exynos4210.irq_gate");
+    qdev_init_nofail(dev);
+    /* Get IRQ Gate input in gate_irq */
+    for (n = 0; n < EXYNOS4210_IRQ_GATE_NINPUTS; n++) {
+        gate_irq[n] = qdev_get_gpio_in(dev, n);
+    }
+    busdev = sysbus_from_qdev(dev);
+    /* Connect IRQ Gate output to cpu_irq */
+    for (n = 0; n < EXYNOS4210_NCPUS; n++) {
+        sysbus_connect_irq(busdev, n, cpu_irq[n]);
+    }
+
+    /* Private memory region and Internal GIC */
+    dev = qdev_create(NULL, "a9mpcore_priv");
+    qdev_prop_set_uint32(dev, "num-cpu", EXYNOS4210_NCPUS);
+    qdev_init_nofail(dev);
+    busdev = sysbus_from_qdev(dev);
+    sysbus_mmio_map(busdev, 0, EXYNOS4210_SMP_PRIVATE_BASE_ADDR);
+    for (n = 0; n < EXYNOS4210_NCPUS; n++) {
+        sysbus_connect_irq(busdev, n, gate_irq[n * 2]);
+    }
+    for (n = 0; n < EXYNOS4210_INT_GIC_NIRQ; n++) {
+        s->irqs.int_gic_irq[n] = qdev_get_gpio_in(dev, n);
+    }
+
+    /* Cache controller */
+    sysbus_create_simple("l2x0", EXYNOS4210_L2X0_BASE_ADDR, NULL);
+
+    /* External GIC */
+    dev = qdev_create(NULL, "exynos4210.gic");
+    qdev_prop_set_uint32(dev, "num-cpu", EXYNOS4210_NCPUS);
+    qdev_init_nofail(dev);
+    busdev = sysbus_from_qdev(dev);
+    /* Map CPU interface */
+    sysbus_mmio_map(busdev, 0, EXYNOS4210_EXT_GIC_CPU_BASE_ADDR);
+    /* Map Distributer interface */
+    sysbus_mmio_map(busdev, 1, EXYNOS4210_EXT_GIC_DIST_BASE_ADDR);
+    for (n = 0; n < EXYNOS4210_NCPUS; n++) {
+        sysbus_connect_irq(busdev, n, gate_irq[n * 2 + 1]);
+    }
+    for (n = 0; n < EXYNOS4210_EXT_GIC_NIRQ; n++) {
+        s->irqs.ext_gic_irq[n] = qdev_get_gpio_in(dev, n);
+    }
+
+    /* Internal Interrupt Combiner */
+    dev = qdev_create(NULL, "exynos4210.combiner");
+    qdev_init_nofail(dev);
+    busdev = sysbus_from_qdev(dev);
+    for (n = 0; n < EXYNOS4210_MAX_INT_COMBINER_OUT_IRQ; n++) {
+        sysbus_connect_irq(busdev, n, s->irqs.int_gic_irq[n]);
+    }
+    exynos4210_combiner_get_gpioin(&s->irqs, dev, 0);
+    sysbus_mmio_map(busdev, 0, EXYNOS4210_INT_COMBINER_BASE_ADDR);
+
+    /* External Interrupt Combiner */
+    dev = qdev_create(NULL, "exynos4210.combiner");
+    qdev_prop_set_uint32(dev, "external", 1);
+    qdev_init_nofail(dev);
+    busdev = sysbus_from_qdev(dev);
+    for (n = 0; n < EXYNOS4210_MAX_INT_COMBINER_OUT_IRQ; n++) {
+        sysbus_connect_irq(busdev, n, s->irqs.ext_gic_irq[n]);
+    }
+    exynos4210_combiner_get_gpioin(&s->irqs, dev, 1);
+    sysbus_mmio_map(busdev, 0, EXYNOS4210_EXT_COMBINER_BASE_ADDR);
+
+    /* Initialize board IRQs. */
+    exynos4210_init_board_irqs(&s->irqs);
+
+    /*** Memory ***/
+
+    /* Chip-ID and OMR */
+    memory_region_init_ram_ptr(&s->chipid_mem, "exynos4210.chipid",
+            sizeof(chipid_and_omr), chipid_and_omr);
+    memory_region_set_readonly(&s->chipid_mem, true);
+    memory_region_add_subregion(system_mem, EXYNOS4210_CHIPID_ADDR,
+                                &s->chipid_mem);
+
+    /* Internal ROM */
+    memory_region_init_ram(&s->irom_mem, "exynos4210.irom",
+                           EXYNOS4210_IROM_SIZE);
+    memory_region_set_readonly(&s->irom_mem, true);
+    memory_region_add_subregion(system_mem, EXYNOS4210_IROM_BASE_ADDR,
+                                &s->irom_mem);
+    /* mirror of iROM */
+    memory_region_init_alias(&s->irom_alias_mem, "exynos4210.irom_alias",
+                             &s->irom_mem,
+                             EXYNOS4210_IROM_BASE_ADDR,
+                             EXYNOS4210_IROM_SIZE);
+    memory_region_set_readonly(&s->irom_alias_mem, true);
+    memory_region_add_subregion(system_mem, EXYNOS4210_IROM_MIRROR_BASE_ADDR,
+                                &s->irom_alias_mem);
+
+    /* Internal RAM */
+    memory_region_init_ram(&s->iram_mem, "exynos4210.iram",
+                           EXYNOS4210_IRAM_SIZE);
+    vmstate_register_ram_global(&s->iram_mem);
+    memory_region_add_subregion(system_mem, EXYNOS4210_IRAM_BASE_ADDR,
+                                &s->iram_mem);
+
+    /* DRAM */
+    mem_size = ram_size;
+    if (mem_size > EXYNOS4210_DRAM_MAX_SIZE) {
+        memory_region_init_ram(&s->dram1_mem, "exynos4210.dram1",
+                mem_size - EXYNOS4210_DRAM_MAX_SIZE);
+        vmstate_register_ram_global(&s->dram1_mem);
+        memory_region_add_subregion(system_mem, EXYNOS4210_DRAM1_BASE_ADDR,
+                &s->dram1_mem);
+        mem_size = EXYNOS4210_DRAM_MAX_SIZE;
+    }
+    memory_region_init_ram(&s->dram0_mem, "exynos4210.dram0", mem_size);
+    vmstate_register_ram_global(&s->dram0_mem);
+    memory_region_add_subregion(system_mem, EXYNOS4210_DRAM0_BASE_ADDR,
+            &s->dram0_mem);
+
+    return s;
+}
diff --git a/hw/exynos4210.h b/hw/exynos4210.h
index ef4732f..0026a52 100644
--- a/hw/exynos4210.h
+++ b/hw/exynos4210.h
@@ -31,6 +31,28 @@
 
 #define EXYNOS4210_NCPUS                    2
 
+#define EXYNOS4210_DRAM0_BASE_ADDR          0x40000000
+#define EXYNOS4210_DRAM1_BASE_ADDR          0xa0000000
+#define EXYNOS4210_DRAM_MAX_SIZE            0x60000000  /* 1.5 GB */
+
+#define EXYNOS4210_IROM_BASE_ADDR           0x00000000
+#define EXYNOS4210_IROM_SIZE                0x00010000  /* 64 KB */
+#define EXYNOS4210_IROM_MIRROR_BASE_ADDR    0x02000000
+#define EXYNOS4210_IROM_MIRROR_SIZE         0x00010000  /* 64 KB */
+
+#define EXYNOS4210_IRAM_BASE_ADDR           0x02020000
+#define EXYNOS4210_IRAM_SIZE                0x00020000  /* 128 KB */
+
+/* Secondary CPU startup code is in IROM memory */
+#define EXYNOS4210_SMP_BOOT_ADDR            EXYNOS4210_IROM_BASE_ADDR
+#define EXYNOS4210_SMP_BOOT_SIZE            0x1000
+#define EXYNOS4210_BASE_BOOT_ADDR           EXYNOS4210_DRAM0_BASE_ADDR
+/* Secondary CPU polling address to get loader start from */
+#define EXYNOS4210_SECOND_CPU_BOOTREG       0x10020814
+
+#define EXYNOS4210_SMP_PRIVATE_BASE_ADDR    0x10500000
+#define EXYNOS4210_L2X0_BASE_ADDR           0x10502000
+
 /*
  * exynos4210 IRQ subsystem stub definitions.
  */
@@ -60,6 +82,24 @@ typedef struct Exynos4210Irq {
     qemu_irq board_irqs[EXYNOS4210_MAX_INT_COMBINER_IN_IRQ];
 } Exynos4210Irq;
 
+typedef struct Exynos4210State {
+    CPUState * env[EXYNOS4210_NCPUS];
+    Exynos4210Irq irqs;
+    qemu_irq *irq_table;
+
+    MemoryRegion chipid_mem;
+    MemoryRegion iram_mem;
+    MemoryRegion irom_mem;
+    MemoryRegion irom_alias_mem;
+    MemoryRegion dram0_mem;
+    MemoryRegion dram1_mem;
+    MemoryRegion boot_secondary;
+    MemoryRegion bootreg_mem;
+} Exynos4210State;
+
+Exynos4210State *exynos4210_init(MemoryRegion *system_mem,
+        unsigned long ram_size);
+
 /* Initialize exynos4210 IRQ subsystem stub */
 qemu_irq *exynos4210_init_irq(Exynos4210Irq *env);
 
diff --git a/hw/exynos4_boards.c b/hw/exynos4_boards.c
new file mode 100644
index 0000000..767dc45
--- /dev/null
+++ b/hw/exynos4_boards.c
@@ -0,0 +1,153 @@
+/*
+ *  Samsung exynos4 SoC based boards emulation
+ *
+ *  Copyright (c) 2011 Samsung Electronics Co., Ltd. All rights reserved.
+ *    Maksim Kozlov <m.kozlov@samsung.com>
+ *    Evgeny Voevodin <e.voevodin@samsung.com>
+ *    Igor Mitsyanko  <i.mitsyanko@samsung.com>
+ *
+ *  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, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "sysemu.h"
+#include "sysbus.h"
+#include "arm-misc.h"
+#include "exec-memory.h"
+#include "exynos4210.h"
+#include "boards.h"
+
+#undef DEBUG
+
+//#define DEBUG
+
+#ifdef DEBUG
+    #undef PRINT_DEBUG
+    #define  PRINT_DEBUG(fmt, args...) \
+        do { \
+            fprintf(stderr, "  [%s:%d]   "fmt, __func__, __LINE__, ##args); \
+        } while (0)
+#else
+    #define  PRINT_DEBUG(fmt, args...)  do {} while (0)
+#endif
+
+typedef enum Exynos4BoardType {
+    EXYNOS4_BOARD_NURI,
+    EXYNOS4_BOARD_SMDKC210,
+    EXYNOS4_NUM_OF_BOARDS
+} Exynos4BoardType;
+
+static int exynos4_board_id[EXYNOS4_NUM_OF_BOARDS] = {
+    [EXYNOS4_BOARD_NURI]     = 0xD33,
+    [EXYNOS4_BOARD_SMDKC210] = 0xB16,
+};
+
+static int exynos4_board_smp_bootreg_addr[EXYNOS4_NUM_OF_BOARDS] = {
+    [EXYNOS4_BOARD_NURI]     = EXYNOS4210_SECOND_CPU_BOOTREG,
+    [EXYNOS4_BOARD_SMDKC210] = EXYNOS4210_SECOND_CPU_BOOTREG,
+};
+
+static unsigned long exynos4_board_ram_size[EXYNOS4_NUM_OF_BOARDS] = {
+    [EXYNOS4_BOARD_NURI]     = 0x40000000,
+    [EXYNOS4_BOARD_SMDKC210] = 0x40000000,
+};
+
+static struct arm_boot_info exynos4_board_binfo = {
+    .loader_start     = EXYNOS4210_BASE_BOOT_ADDR,
+    .smp_loader_start = EXYNOS4210_SMP_BOOT_ADDR,
+    .nb_cpus          = EXYNOS4210_NCPUS,
+};
+
+static QEMUMachine exynos4_machines[EXYNOS4_NUM_OF_BOARDS];
+
+static Exynos4210State *exynos4_boards_init_common(
+        const char *kernel_filename,
+        const char *kernel_cmdline,
+        const char *initrd_filename,
+        Exynos4BoardType board_type)
+{
+    if (smp_cpus != EXYNOS4210_NCPUS) {
+        fprintf(stderr, "%s board supports only %d CPU cores. Ignoring smp_cpus"
+                " value.\n",
+                exynos4_machines[board_type].name,
+                exynos4_machines[board_type].max_cpus);
+    }
+
+    exynos4_board_binfo.ram_size = exynos4_board_ram_size[board_type];
+    exynos4_board_binfo.board_id = exynos4_board_id[board_type];
+    exynos4_board_binfo.smp_bootreg_addr =
+            exynos4_board_smp_bootreg_addr[board_type];
+    exynos4_board_binfo.kernel_filename = kernel_filename;
+    exynos4_board_binfo.initrd_filename = initrd_filename;
+    exynos4_board_binfo.kernel_cmdline = kernel_cmdline;
+    exynos4_board_binfo.smp_priv_base = EXYNOS4210_SMP_PRIVATE_BASE_ADDR;
+
+    PRINT_DEBUG("\n ram_size: %luMiB [0x%08lx]\n"
+            " kernel_filename: %s\n"
+            " kernel_cmdline: %s\n"
+            " initrd_filename: %s\n",
+            exynos4_board_ram_size[board_type] / 1048576,
+            exynos4_board_ram_size[board_type],
+            kernel_filename,
+            kernel_cmdline,
+            initrd_filename);
+
+    return exynos4210_init(get_system_memory(),
+            exynos4_board_ram_size[board_type]);
+}
+
+static void nuri_init(ram_addr_t ram_size,
+        const char *boot_device,
+        const char *kernel_filename, const char *kernel_cmdline,
+        const char *initrd_filename, const char *cpu_model)
+{
+    exynos4_boards_init_common(kernel_filename, kernel_cmdline,
+                initrd_filename, EXYNOS4_BOARD_NURI);
+
+    arm_load_kernel(first_cpu, &exynos4_board_binfo);
+}
+
+static void smdkc210_init(ram_addr_t ram_size,
+        const char *boot_device,
+        const char *kernel_filename, const char *kernel_cmdline,
+        const char *initrd_filename, const char *cpu_model)
+{
+    exynos4_boards_init_common(kernel_filename, kernel_cmdline,
+                initrd_filename, EXYNOS4_BOARD_SMDKC210);
+
+    arm_load_kernel(first_cpu, &exynos4_board_binfo);
+}
+
+static QEMUMachine exynos4_machines[EXYNOS4_NUM_OF_BOARDS] = {
+    [EXYNOS4_BOARD_NURI] = {
+        .name = "nuri",
+        .desc = "Samsung NURI board (Exynos4210)",
+        .init = nuri_init,
+        .max_cpus = EXYNOS4210_NCPUS,
+    },
+    [EXYNOS4_BOARD_SMDKC210] = {
+        .name = "smdkc210",
+        .desc = "Samsung SMDKC210 board (Exynos4210)",
+        .init = smdkc210_init,
+        .max_cpus = EXYNOS4210_NCPUS,
+    },
+};
+
+static void exynos4_machine_init(void)
+{
+    qemu_register_machine(&exynos4_machines[EXYNOS4_BOARD_NURI]);
+    qemu_register_machine(&exynos4_machines[EXYNOS4_BOARD_SMDKC210]);
+}
+
+machine_init(exynos4_machine_init);
-- 
1.7.1

^ permalink raw reply related	[flat|nested] 24+ messages in thread

* [Qemu-devel] [PATCH 03/22] ARM: exynos4210: UART support
  2012-02-17 11:29 [Qemu-devel] [PULL 00/22] arm-devs queue Peter Maydell
  2012-02-17 11:29 ` [Qemu-devel] [PATCH 01/22] ARM: exynos4210: IRQ subsystem support Peter Maydell
  2012-02-17 11:29 ` [Qemu-devel] [PATCH 02/22] ARM: Samsung exynos4210-based boards emulation Peter Maydell
@ 2012-02-17 11:29 ` Peter Maydell
  2012-02-17 11:29 ` [Qemu-devel] [PATCH 04/22] ARM: exynos4210: PWM support Peter Maydell
                   ` (19 subsequent siblings)
  22 siblings, 0 replies; 24+ messages in thread
From: Peter Maydell @ 2012-02-17 11:29 UTC (permalink / raw)
  To: Anthony Liguori; +Cc: qemu-devel, Paul Brook

From: Maksim Kozlov <m.kozlov@samsung.com>

Add basic support of exynos4210 UART

Signed-off-by: Maksim Kozlov <m.kozlov@samsung.com>
Signed-off-by: Evgeny Voevodin <e.voevodin@samsung.com>
Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
---
 Makefile.target      |    2 +-
 hw/exynos4210.c      |   29 +++
 hw/exynos4210.h      |    9 +
 hw/exynos4210_uart.c |  676 ++++++++++++++++++++++++++++++++++++++++++++++++++
 4 files changed, 715 insertions(+), 1 deletions(-)
 create mode 100644 hw/exynos4210_uart.c

diff --git a/Makefile.target b/Makefile.target
index c9afbd3..f501d33 100644
--- a/Makefile.target
+++ b/Makefile.target
@@ -345,7 +345,7 @@ obj-arm-y += arm_boot.o pl011.o pl031.o pl050.o pl080.o pl110.o pl181.o pl190.o
 obj-arm-y += versatile_pci.o
 obj-arm-y += realview_gic.o realview.o arm_sysctl.o arm11mpcore.o a9mpcore.o
 obj-arm-y += exynos4210_gic.o exynos4210_combiner.o exynos4210.o
-obj-arm-y += exynos4_boards.o
+obj-arm-y += exynos4_boards.o exynos4210_uart.o
 obj-arm-y += arm_l2x0.o
 obj-arm-y += arm_mptimer.o
 obj-arm-y += armv7m.o armv7m_nvic.o stellaris.o pl022.o stellaris_enet.o
diff --git a/hw/exynos4210.c b/hw/exynos4210.c
index 5b1e24c..555adc0 100644
--- a/hw/exynos4210.c
+++ b/hw/exynos4210.c
@@ -29,6 +29,18 @@
 
 #define EXYNOS4210_CHIPID_ADDR         0x10000000
 
+/* UART's definitions */
+#define EXYNOS4210_UART0_BASE_ADDR     0x13800000
+#define EXYNOS4210_UART1_BASE_ADDR     0x13810000
+#define EXYNOS4210_UART2_BASE_ADDR     0x13820000
+#define EXYNOS4210_UART3_BASE_ADDR     0x13830000
+#define EXYNOS4210_UART0_FIFO_SIZE     256
+#define EXYNOS4210_UART1_FIFO_SIZE     64
+#define EXYNOS4210_UART2_FIFO_SIZE     16
+#define EXYNOS4210_UART3_FIFO_SIZE     16
+/* Interrupt Group of External Interrupt Combiner for UART */
+#define EXYNOS4210_UART_INT_GRP        26
+
 /* External GIC */
 #define EXYNOS4210_EXT_GIC_CPU_BASE_ADDR    0x10480000
 #define EXYNOS4210_EXT_GIC_DIST_BASE_ADDR   0x10490000
@@ -187,5 +199,22 @@ Exynos4210State *exynos4210_init(MemoryRegion *system_mem,
     memory_region_add_subregion(system_mem, EXYNOS4210_DRAM0_BASE_ADDR,
             &s->dram0_mem);
 
+    /*** UARTs ***/
+    exynos4210_uart_create(EXYNOS4210_UART0_BASE_ADDR,
+                           EXYNOS4210_UART0_FIFO_SIZE, 0, NULL,
+                  s->irq_table[exynos4210_get_irq(EXYNOS4210_UART_INT_GRP, 0)]);
+
+    exynos4210_uart_create(EXYNOS4210_UART1_BASE_ADDR,
+                           EXYNOS4210_UART1_FIFO_SIZE, 1, NULL,
+                  s->irq_table[exynos4210_get_irq(EXYNOS4210_UART_INT_GRP, 1)]);
+
+    exynos4210_uart_create(EXYNOS4210_UART2_BASE_ADDR,
+                           EXYNOS4210_UART2_FIFO_SIZE, 2, NULL,
+                  s->irq_table[exynos4210_get_irq(EXYNOS4210_UART_INT_GRP, 2)]);
+
+    exynos4210_uart_create(EXYNOS4210_UART3_BASE_ADDR,
+                           EXYNOS4210_UART3_FIFO_SIZE, 3, NULL,
+                  s->irq_table[exynos4210_get_irq(EXYNOS4210_UART_INT_GRP, 3)]);
+
     return s;
 }
diff --git a/hw/exynos4210.h b/hw/exynos4210.h
index 0026a52..e7522f8 100644
--- a/hw/exynos4210.h
+++ b/hw/exynos4210.h
@@ -119,4 +119,13 @@ uint32_t exynos4210_get_irq(uint32_t grp, uint32_t bit);
 void exynos4210_combiner_get_gpioin(Exynos4210Irq *irqs, DeviceState *dev,
         int ext);
 
+/*
+ * exynos4210 UART
+ */
+DeviceState *exynos4210_uart_create(target_phys_addr_t addr,
+                                    int fifo_size,
+                                    int channel,
+                                    CharDriverState *chr,
+                                    qemu_irq irq);
+
 #endif /* EXYNOS4210_H_ */
diff --git a/hw/exynos4210_uart.c b/hw/exynos4210_uart.c
new file mode 100644
index 0000000..73a9c18
--- /dev/null
+++ b/hw/exynos4210_uart.c
@@ -0,0 +1,676 @@
+/*
+ *  Exynos4210 UART Emulation
+ *
+ *  Copyright (C) 2011 Samsung Electronics Co Ltd.
+ *    Maksim Kozlov, <m.kozlov@samsung.com>
+ *
+ *  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, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "sysbus.h"
+#include "sysemu.h"
+#include "qemu-char.h"
+
+#include "exynos4210.h"
+
+#undef DEBUG_UART
+#undef DEBUG_UART_EXTEND
+#undef DEBUG_IRQ
+#undef DEBUG_Rx_DATA
+#undef DEBUG_Tx_DATA
+
+#define DEBUG_UART            0
+#define DEBUG_UART_EXTEND     0
+#define DEBUG_IRQ             0
+#define DEBUG_Rx_DATA         0
+#define DEBUG_Tx_DATA         0
+
+#if DEBUG_UART
+#define  PRINT_DEBUG(fmt, args...)  \
+        do { \
+            fprintf(stderr, "  [%s:%d]   "fmt, __func__, __LINE__, ##args); \
+        } while (0)
+
+#if DEBUG_UART_EXTEND
+#define  PRINT_DEBUG_EXTEND(fmt, args...) \
+        do { \
+            fprintf(stderr, "  [%s:%d]   "fmt, __func__, __LINE__, ##args); \
+        } while (0)
+#else
+#define  PRINT_DEBUG_EXTEND(fmt, args...) \
+        do {} while (0)
+#endif /* EXTEND */
+
+#else
+#define  PRINT_DEBUG(fmt, args...)  \
+        do {} while (0)
+#define  PRINT_DEBUG_EXTEND(fmt, args...) \
+        do {} while (0)
+#endif
+
+#define  PRINT_ERROR(fmt, args...) \
+        do { \
+            fprintf(stderr, "  [%s:%d]   "fmt, __func__, __LINE__, ##args); \
+        } while (0)
+
+/*
+ *  Offsets for UART registers relative to SFR base address
+ *  for UARTn
+ *
+ */
+#define ULCON      0x0000 /* Line Control             */
+#define UCON       0x0004 /* Control                  */
+#define UFCON      0x0008 /* FIFO Control             */
+#define UMCON      0x000C /* Modem Control            */
+#define UTRSTAT    0x0010 /* Tx/Rx Status             */
+#define UERSTAT    0x0014 /* UART Error Status        */
+#define UFSTAT     0x0018 /* FIFO Status              */
+#define UMSTAT     0x001C /* Modem Status             */
+#define UTXH       0x0020 /* Transmit Buffer          */
+#define URXH       0x0024 /* Receive Buffer           */
+#define UBRDIV     0x0028 /* Baud Rate Divisor        */
+#define UFRACVAL   0x002C /* Divisor Fractional Value */
+#define UINTP      0x0030 /* Interrupt Pending        */
+#define UINTSP     0x0034 /* Interrupt Source Pending */
+#define UINTM      0x0038 /* Interrupt Mask           */
+
+/*
+ * for indexing register in the uint32_t array
+ *
+ * 'reg' - register offset (see offsets definitions above)
+ *
+ */
+#define I_(reg) (reg / sizeof(uint32_t))
+
+typedef struct Exynos4210UartReg {
+    const char         *name; /* the only reason is the debug output */
+    target_phys_addr_t  offset;
+    uint32_t            reset_value;
+} Exynos4210UartReg;
+
+static Exynos4210UartReg exynos4210_uart_regs[] = {
+    {"ULCON",    ULCON,    0x00000000},
+    {"UCON",     UCON,     0x00003000},
+    {"UFCON",    UFCON,    0x00000000},
+    {"UMCON",    UMCON,    0x00000000},
+    {"UTRSTAT",  UTRSTAT,  0x00000006}, /* RO */
+    {"UERSTAT",  UERSTAT,  0x00000000}, /* RO */
+    {"UFSTAT",   UFSTAT,   0x00000000}, /* RO */
+    {"UMSTAT",   UMSTAT,   0x00000000}, /* RO */
+    {"UTXH",     UTXH,     0x5c5c5c5c}, /* WO, undefined reset value*/
+    {"URXH",     URXH,     0x00000000}, /* RO */
+    {"UBRDIV",   UBRDIV,   0x00000000},
+    {"UFRACVAL", UFRACVAL, 0x00000000},
+    {"UINTP",    UINTP,    0x00000000},
+    {"UINTSP",   UINTSP,   0x00000000},
+    {"UINTM",    UINTM,    0x00000000},
+};
+
+#define EXYNOS4210_UART_REGS_MEM_SIZE    0x3C
+
+/* UART FIFO Control */
+#define UFCON_FIFO_ENABLE                    0x1
+#define UFCON_Rx_FIFO_RESET                  0x2
+#define UFCON_Tx_FIFO_RESET                  0x4
+#define UFCON_Tx_FIFO_TRIGGER_LEVEL_SHIFT    8
+#define UFCON_Tx_FIFO_TRIGGER_LEVEL (7 << UFCON_Tx_FIFO_TRIGGER_LEVEL_SHIFT)
+#define UFCON_Rx_FIFO_TRIGGER_LEVEL_SHIFT    4
+#define UFCON_Rx_FIFO_TRIGGER_LEVEL (7 << UFCON_Rx_FIFO_TRIGGER_LEVEL_SHIFT)
+
+/* Uart FIFO Status */
+#define UFSTAT_Rx_FIFO_COUNT        0xff
+#define UFSTAT_Rx_FIFO_FULL         0x100
+#define UFSTAT_Rx_FIFO_ERROR        0x200
+#define UFSTAT_Tx_FIFO_COUNT_SHIFT  16
+#define UFSTAT_Tx_FIFO_COUNT        (0xff << UFSTAT_Tx_FIFO_COUNT_SHIFT)
+#define UFSTAT_Tx_FIFO_FULL_SHIFT   24
+#define UFSTAT_Tx_FIFO_FULL         (1 << UFSTAT_Tx_FIFO_FULL_SHIFT)
+
+/* UART Interrupt Source Pending */
+#define UINTSP_RXD      0x1 /* Receive interrupt  */
+#define UINTSP_ERROR    0x2 /* Error interrupt    */
+#define UINTSP_TXD      0x4 /* Transmit interrupt */
+#define UINTSP_MODEM    0x8 /* Modem interrupt    */
+
+/* UART Line Control */
+#define ULCON_IR_MODE_SHIFT   6
+#define ULCON_PARITY_SHIFT    3
+#define ULCON_STOP_BIT_SHIFT  1
+
+/* UART Tx/Rx Status */
+#define UTRSTAT_TRANSMITTER_EMPTY       0x4
+#define UTRSTAT_Tx_BUFFER_EMPTY         0x2
+#define UTRSTAT_Rx_BUFFER_DATA_READY    0x1
+
+/* UART Error Status */
+#define UERSTAT_OVERRUN  0x1
+#define UERSTAT_PARITY   0x2
+#define UERSTAT_FRAME    0x4
+#define UERSTAT_BREAK    0x8
+
+typedef struct {
+    uint8_t    *data;
+    uint32_t    sp, rp; /* store and retrieve pointers */
+    uint32_t    size;
+} Exynos4210UartFIFO;
+
+typedef struct {
+    SysBusDevice busdev;
+    MemoryRegion iomem;
+
+    uint32_t             reg[EXYNOS4210_UART_REGS_MEM_SIZE / sizeof(uint32_t)];
+    Exynos4210UartFIFO   rx;
+    Exynos4210UartFIFO   tx;
+
+    CharDriverState  *chr;
+    qemu_irq          irq;
+
+    uint32_t channel;
+
+} Exynos4210UartState;
+
+
+#if DEBUG_UART
+/* Used only for debugging inside PRINT_DEBUG_... macros */
+static const char *exynos4210_uart_regname(target_phys_addr_t  offset)
+{
+
+    int regs_number = sizeof(exynos4210_uart_regs) / sizeof(Exynos4210UartReg);
+    int i;
+
+    for (i = 0; i < regs_number; i++) {
+        if (offset == exynos4210_uart_regs[i].offset) {
+            return exynos4210_uart_regs[i].name;
+        }
+    }
+
+    return NULL;
+}
+#endif
+
+
+static void fifo_store(Exynos4210UartFIFO *q, uint8_t ch)
+{
+    q->data[q->sp] = ch;
+    q->sp = (q->sp + 1) % q->size;
+}
+
+static uint8_t fifo_retrieve(Exynos4210UartFIFO *q)
+{
+    uint8_t ret = q->data[q->rp];
+    q->rp = (q->rp + 1) % q->size;
+    return  ret;
+}
+
+static int fifo_elements_number(Exynos4210UartFIFO *q)
+{
+    if (q->sp < q->rp) {
+        return q->size - q->rp + q->sp;
+    }
+
+    return q->sp - q->rp;
+}
+
+static int fifo_empty_elements_number(Exynos4210UartFIFO *q)
+{
+    return q->size - fifo_elements_number(q);
+}
+
+static void fifo_reset(Exynos4210UartFIFO *q)
+{
+    if (q->data != NULL) {
+        g_free(q->data);
+        q->data = NULL;
+    }
+
+    q->data = (uint8_t *)g_malloc0(q->size);
+
+    q->sp = 0;
+    q->rp = 0;
+}
+
+static uint32_t exynos4210_uart_Tx_FIFO_trigger_level(Exynos4210UartState *s)
+{
+    uint32_t level = 0;
+    uint32_t reg;
+
+    reg = (s->reg[I_(UFCON)] && UFCON_Tx_FIFO_TRIGGER_LEVEL) >>
+            UFCON_Tx_FIFO_TRIGGER_LEVEL_SHIFT;
+
+    switch (s->channel) {
+    case 0:
+        level = reg * 32;
+        break;
+    case 1:
+    case 4:
+        level = reg * 8;
+        break;
+    case 2:
+    case 3:
+        level = reg * 2;
+        break;
+    default:
+        level = 0;
+        PRINT_ERROR("Wrong UART channel number: %d\n", s->channel);
+    }
+
+    return level;
+}
+
+static void exynos4210_uart_update_irq(Exynos4210UartState *s)
+{
+    /*
+     * The Tx interrupt is always requested if the number of data in the
+     * transmit FIFO is smaller than the trigger level.
+     */
+    if (s->reg[I_(UFCON)] && UFCON_FIFO_ENABLE) {
+
+        uint32_t count = (s->reg[I_(UFSTAT)] && UFSTAT_Tx_FIFO_COUNT) >>
+                UFSTAT_Tx_FIFO_COUNT_SHIFT;
+
+        if (count <= exynos4210_uart_Tx_FIFO_trigger_level(s)) {
+            s->reg[I_(UINTSP)] |= UINTSP_TXD;
+        }
+    }
+
+    s->reg[I_(UINTP)] = s->reg[I_(UINTSP)] & ~s->reg[I_(UINTM)];
+
+    if (s->reg[I_(UINTP)]) {
+        qemu_irq_raise(s->irq);
+
+#if DEBUG_IRQ
+        fprintf(stderr, "UART%d: IRQ has been raised: %08x\n",
+                s->channel, s->reg[I_(UINTP)]);
+#endif
+
+    } else {
+        qemu_irq_lower(s->irq);
+    }
+}
+
+static void exynos4210_uart_update_parameters(Exynos4210UartState *s)
+{
+    int speed, parity, data_bits, stop_bits, frame_size;
+    QEMUSerialSetParams ssp;
+    uint64_t uclk_rate;
+
+    if (s->reg[I_(UBRDIV)] == 0) {
+        return;
+    }
+
+    frame_size = 1; /* start bit */
+    if (s->reg[I_(ULCON)] & 0x20) {
+        frame_size++; /* parity bit */
+        if (s->reg[I_(ULCON)] & 0x28) {
+            parity = 'E';
+        } else {
+            parity = 'O';
+        }
+    } else {
+        parity = 'N';
+    }
+
+    if (s->reg[I_(ULCON)] & 0x4) {
+        stop_bits = 2;
+    } else {
+        stop_bits = 1;
+    }
+
+    data_bits = (s->reg[I_(ULCON)] & 0x3) + 5;
+
+    frame_size += data_bits + stop_bits;
+
+    uclk_rate = 24000000;
+
+    speed = uclk_rate / ((16 * (s->reg[I_(UBRDIV)]) & 0xffff) +
+            (s->reg[I_(UFRACVAL)] & 0x7) + 16);
+
+    ssp.speed     = speed;
+    ssp.parity    = parity;
+    ssp.data_bits = data_bits;
+    ssp.stop_bits = stop_bits;
+
+    qemu_chr_fe_ioctl(s->chr, CHR_IOCTL_SERIAL_SET_PARAMS, &ssp);
+
+    PRINT_DEBUG("UART%d: speed: %d, parity: %c, data: %d, stop: %d\n",
+                s->channel, speed, parity, data_bits, stop_bits);
+}
+
+static void exynos4210_uart_write(void *opaque, target_phys_addr_t offset,
+                               uint64_t val, unsigned size)
+{
+    Exynos4210UartState *s = (Exynos4210UartState *)opaque;
+    uint8_t ch;
+
+    PRINT_DEBUG_EXTEND("UART%d: <0x%04x> %s <- 0x%08llx\n", s->channel,
+        offset, exynos4210_uart_regname(offset), (long long unsigned int)val);
+
+    switch (offset) {
+    case ULCON:
+    case UBRDIV:
+    case UFRACVAL:
+        s->reg[I_(offset)] = val;
+        exynos4210_uart_update_parameters(s);
+        break;
+    case UFCON:
+        s->reg[I_(UFCON)] = val;
+        if (val & UFCON_Rx_FIFO_RESET) {
+            fifo_reset(&s->rx);
+            s->reg[I_(UFCON)] &= ~UFCON_Rx_FIFO_RESET;
+            PRINT_DEBUG("UART%d: Rx FIFO Reset\n", s->channel);
+        }
+        if (val & UFCON_Tx_FIFO_RESET) {
+            fifo_reset(&s->tx);
+            s->reg[I_(UFCON)] &= ~UFCON_Tx_FIFO_RESET;
+            PRINT_DEBUG("UART%d: Tx FIFO Reset\n", s->channel);
+        }
+        break;
+
+    case UTXH:
+        if (s->chr) {
+            s->reg[I_(UTRSTAT)] &= ~(UTRSTAT_TRANSMITTER_EMPTY |
+                    UTRSTAT_Tx_BUFFER_EMPTY);
+            ch = (uint8_t)val;
+            qemu_chr_fe_write(s->chr, &ch, 1);
+#if DEBUG_Tx_DATA
+            fprintf(stderr, "%c", ch);
+#endif
+            s->reg[I_(UTRSTAT)] |= UTRSTAT_TRANSMITTER_EMPTY |
+                    UTRSTAT_Tx_BUFFER_EMPTY;
+            s->reg[I_(UINTSP)]  |= UINTSP_TXD;
+            exynos4210_uart_update_irq(s);
+        }
+        break;
+
+    case UINTP:
+        s->reg[I_(UINTP)] &= ~val;
+        s->reg[I_(UINTSP)] &= ~val;
+        PRINT_DEBUG("UART%d: UINTP [%04x] have been cleared: %08x\n",
+                    s->channel, offset, s->reg[I_(UINTP)]);
+        exynos4210_uart_update_irq(s);
+        break;
+    case UTRSTAT:
+    case UERSTAT:
+    case UFSTAT:
+    case UMSTAT:
+    case URXH:
+        PRINT_DEBUG("UART%d: Trying to write into RO register: %s [%04x]\n",
+                    s->channel, exynos4210_uart_regname(offset), offset);
+        break;
+    case UINTSP:
+        s->reg[I_(UINTSP)]  &= ~val;
+        break;
+    case UINTM:
+        s->reg[I_(UINTM)] = val;
+        exynos4210_uart_update_irq(s);
+        break;
+    case UCON:
+    case UMCON:
+    default:
+        s->reg[I_(offset)] = val;
+        break;
+    }
+}
+static uint64_t exynos4210_uart_read(void *opaque, target_phys_addr_t offset,
+                                  unsigned size)
+{
+    Exynos4210UartState *s = (Exynos4210UartState *)opaque;
+    uint32_t res;
+
+    switch (offset) {
+    case UERSTAT: /* Read Only */
+        res = s->reg[I_(UERSTAT)];
+        s->reg[I_(UERSTAT)] = 0;
+        return res;
+    case UFSTAT: /* Read Only */
+        s->reg[I_(UFSTAT)] = fifo_elements_number(&s->rx) & 0xff;
+        if (fifo_empty_elements_number(&s->rx) == 0) {
+            s->reg[I_(UFSTAT)] |= UFSTAT_Rx_FIFO_FULL;
+            s->reg[I_(UFSTAT)] &= ~0xff;
+        }
+        return s->reg[I_(UFSTAT)];
+    case URXH:
+        if (s->reg[I_(UFCON)] & UFCON_FIFO_ENABLE) {
+            if (fifo_elements_number(&s->rx)) {
+                res = fifo_retrieve(&s->rx);
+#if DEBUG_Rx_DATA
+                fprintf(stderr, "%c", res);
+#endif
+                if (!fifo_elements_number(&s->rx)) {
+                    s->reg[I_(UTRSTAT)] &= ~UTRSTAT_Rx_BUFFER_DATA_READY;
+                } else {
+                    s->reg[I_(UTRSTAT)] |= UTRSTAT_Rx_BUFFER_DATA_READY;
+                }
+            } else {
+                s->reg[I_(UINTSP)] |= UINTSP_ERROR;
+                exynos4210_uart_update_irq(s);
+                res = 0;
+            }
+        } else {
+            s->reg[I_(UTRSTAT)] &= ~UTRSTAT_Rx_BUFFER_DATA_READY;
+            res = s->reg[I_(URXH)];
+        }
+        return res;
+    case UTXH:
+        PRINT_DEBUG("UART%d: Trying to read from WO register: %s [%04x]\n",
+                    s->channel, exynos4210_uart_regname(offset), offset);
+        break;
+    default:
+        return s->reg[I_(offset)];
+    }
+
+    return 0;
+}
+
+static const MemoryRegionOps exynos4210_uart_ops = {
+    .read = exynos4210_uart_read,
+    .write = exynos4210_uart_write,
+    .endianness = DEVICE_NATIVE_ENDIAN,
+    .valid = {
+        .max_access_size = 4,
+        .unaligned = false
+    },
+};
+
+static int exynos4210_uart_can_receive(void *opaque)
+{
+    Exynos4210UartState *s = (Exynos4210UartState *)opaque;
+
+    return fifo_empty_elements_number(&s->rx);
+}
+
+
+static void exynos4210_uart_receive(void *opaque, const uint8_t *buf, int size)
+{
+    Exynos4210UartState *s = (Exynos4210UartState *)opaque;
+    int i;
+
+    if (s->reg[I_(UFCON)] & UFCON_FIFO_ENABLE) {
+        if (fifo_empty_elements_number(&s->rx) < size) {
+            for (i = 0; i < fifo_empty_elements_number(&s->rx); i++) {
+                fifo_store(&s->rx, buf[i]);
+            }
+            s->reg[I_(UINTSP)] |= UINTSP_ERROR;
+            s->reg[I_(UTRSTAT)] |= UTRSTAT_Rx_BUFFER_DATA_READY;
+        } else {
+            for (i = 0; i < size; i++) {
+                fifo_store(&s->rx, buf[i]);
+            }
+            s->reg[I_(UTRSTAT)] |= UTRSTAT_Rx_BUFFER_DATA_READY;
+        }
+        /* XXX: Around here we maybe should check Rx trigger level */
+        s->reg[I_(UINTSP)] |= UINTSP_RXD;
+    } else {
+        s->reg[I_(URXH)] = buf[0];
+        s->reg[I_(UINTSP)] |= UINTSP_RXD;
+        s->reg[I_(UTRSTAT)] |= UTRSTAT_Rx_BUFFER_DATA_READY;
+    }
+
+    exynos4210_uart_update_irq(s);
+}
+
+
+static void exynos4210_uart_event(void *opaque, int event)
+{
+    Exynos4210UartState *s = (Exynos4210UartState *)opaque;
+
+    if (event == CHR_EVENT_BREAK) {
+        /* When the RxDn is held in logic 0, then a null byte is pushed into the
+         * fifo */
+        fifo_store(&s->rx, '\0');
+        s->reg[I_(UERSTAT)] |= UERSTAT_BREAK;
+        exynos4210_uart_update_irq(s);
+    }
+}
+
+
+static void exynos4210_uart_reset(DeviceState *dev)
+{
+    Exynos4210UartState *s =
+            container_of(dev, Exynos4210UartState, busdev.qdev);
+    int regs_number = sizeof(exynos4210_uart_regs)/sizeof(Exynos4210UartReg);
+    int i;
+
+    for (i = 0; i < regs_number; i++) {
+        s->reg[I_(exynos4210_uart_regs[i].offset)] =
+                exynos4210_uart_regs[i].reset_value;
+    }
+
+    fifo_reset(&s->rx);
+    fifo_reset(&s->tx);
+
+    PRINT_DEBUG("UART%d: Rx FIFO size: %d\n", s->channel, s->rx.size);
+}
+
+static const VMStateDescription vmstate_exynos4210_uart_fifo = {
+    .name = "exynos4210.uart.fifo",
+    .version_id = 1,
+    .minimum_version_id = 1,
+    .minimum_version_id_old = 1,
+    .fields = (VMStateField[]) {
+        VMSTATE_UINT32(sp, Exynos4210UartFIFO),
+        VMSTATE_UINT32(rp, Exynos4210UartFIFO),
+        VMSTATE_VBUFFER_UINT32(data, Exynos4210UartFIFO, 1, NULL, 0, size),
+        VMSTATE_END_OF_LIST()
+    }
+};
+
+static const VMStateDescription vmstate_exynos4210_uart = {
+    .name = "exynos4210.uart",
+    .version_id = 1,
+    .minimum_version_id = 1,
+    .minimum_version_id_old = 1,
+    .fields = (VMStateField[]) {
+        VMSTATE_STRUCT(rx, Exynos4210UartState, 1,
+                       vmstate_exynos4210_uart_fifo, Exynos4210UartFIFO),
+        VMSTATE_UINT32_ARRAY(reg, Exynos4210UartState,
+                             EXYNOS4210_UART_REGS_MEM_SIZE / sizeof(uint32_t)),
+        VMSTATE_END_OF_LIST()
+    }
+};
+
+DeviceState *exynos4210_uart_create(target_phys_addr_t addr,
+                                 int fifo_size,
+                                 int channel,
+                                 CharDriverState *chr,
+                                 qemu_irq irq)
+{
+    DeviceState  *dev;
+    SysBusDevice *bus;
+
+    const char chr_name[] = "serial";
+    char label[ARRAY_SIZE(chr_name) + 1];
+
+    dev = qdev_create(NULL, "exynos4210.uart");
+
+    if (!chr) {
+        if (channel >= MAX_SERIAL_PORTS) {
+            hw_error("Only %d serial ports are supported by QEMU.\n",
+                     MAX_SERIAL_PORTS);
+        }
+        chr = serial_hds[channel];
+        if (!chr) {
+            snprintf(label, ARRAY_SIZE(label), "%s%d", chr_name, channel);
+            chr = qemu_chr_new(label, "null", NULL);
+            if (!(chr)) {
+                hw_error("Can't assign serial port to UART%d.\n", channel);
+            }
+        }
+    }
+
+    qdev_prop_set_chr(dev, "chardev", chr);
+    qdev_prop_set_uint32(dev, "channel", channel);
+    qdev_prop_set_uint32(dev, "rx-size", fifo_size);
+    qdev_prop_set_uint32(dev, "tx-size", fifo_size);
+
+    bus = sysbus_from_qdev(dev);
+    qdev_init_nofail(dev);
+    if (addr != (target_phys_addr_t)-1) {
+        sysbus_mmio_map(bus, 0, addr);
+    }
+    sysbus_connect_irq(bus, 0, irq);
+
+    return dev;
+}
+
+static int exynos4210_uart_init(SysBusDevice *dev)
+{
+    Exynos4210UartState *s = FROM_SYSBUS(Exynos4210UartState, dev);
+
+    /* memory mapping */
+    memory_region_init_io(&s->iomem, &exynos4210_uart_ops, s, "exynos4210.uart",
+                          EXYNOS4210_UART_REGS_MEM_SIZE);
+    sysbus_init_mmio(dev, &s->iomem);
+
+    sysbus_init_irq(dev, &s->irq);
+
+    qemu_chr_add_handlers(s->chr, exynos4210_uart_can_receive,
+                          exynos4210_uart_receive, exynos4210_uart_event, s);
+
+    return 0;
+}
+
+static Property exynos4210_uart_properties[] = {
+    DEFINE_PROP_CHR("chardev", Exynos4210UartState, chr),
+    DEFINE_PROP_UINT32("channel", Exynos4210UartState, channel, 0),
+    DEFINE_PROP_UINT32("rx-size", Exynos4210UartState, rx.size, 16),
+    DEFINE_PROP_UINT32("tx-size", Exynos4210UartState, tx.size, 16),
+    DEFINE_PROP_END_OF_LIST(),
+};
+
+static void exynos4210_uart_class_init(ObjectClass *klass, void *data)
+{
+    DeviceClass *dc = DEVICE_CLASS(klass);
+    SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass);
+
+    k->init = exynos4210_uart_init;
+    dc->reset = exynos4210_uart_reset;
+    dc->props = exynos4210_uart_properties;
+    dc->vmsd = &vmstate_exynos4210_uart;
+}
+
+static TypeInfo exynos4210_uart_info = {
+    .name          = "exynos4210.uart",
+    .parent        = TYPE_SYS_BUS_DEVICE,
+    .instance_size = sizeof(Exynos4210UartState),
+    .class_init    = exynos4210_uart_class_init,
+};
+
+static void exynos4210_uart_register(void)
+{
+    type_register_static(&exynos4210_uart_info);
+}
+
+type_init(exynos4210_uart_register)
-- 
1.7.1

^ permalink raw reply related	[flat|nested] 24+ messages in thread

* [Qemu-devel] [PATCH 04/22] ARM: exynos4210: PWM support.
  2012-02-17 11:29 [Qemu-devel] [PULL 00/22] arm-devs queue Peter Maydell
                   ` (2 preceding siblings ...)
  2012-02-17 11:29 ` [Qemu-devel] [PATCH 03/22] ARM: exynos4210: UART support Peter Maydell
@ 2012-02-17 11:29 ` Peter Maydell
  2012-02-17 11:29 ` [Qemu-devel] [PATCH 05/22] ARM: exynos4210: basic Power Management Unit implementation Peter Maydell
                   ` (18 subsequent siblings)
  22 siblings, 0 replies; 24+ messages in thread
From: Peter Maydell @ 2012-02-17 11:29 UTC (permalink / raw)
  To: Anthony Liguori; +Cc: qemu-devel, Paul Brook

From: Evgeny Voevodin <e.voevodin@samsung.com>

Signed-off-by: Evgeny Voevodin <e.voevodin@samsung.com>
Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
---
 Makefile.target     |    2 +-
 hw/exynos4210.c     |   12 ++
 hw/exynos4210_pwm.c |  422 +++++++++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 435 insertions(+), 1 deletions(-)
 create mode 100644 hw/exynos4210_pwm.c

diff --git a/Makefile.target b/Makefile.target
index f501d33..bbcee06 100644
--- a/Makefile.target
+++ b/Makefile.target
@@ -345,7 +345,7 @@ obj-arm-y += arm_boot.o pl011.o pl031.o pl050.o pl080.o pl110.o pl181.o pl190.o
 obj-arm-y += versatile_pci.o
 obj-arm-y += realview_gic.o realview.o arm_sysctl.o arm11mpcore.o a9mpcore.o
 obj-arm-y += exynos4210_gic.o exynos4210_combiner.o exynos4210.o
-obj-arm-y += exynos4_boards.o exynos4210_uart.o
+obj-arm-y += exynos4_boards.o exynos4210_uart.o exynos4210_pwm.o
 obj-arm-y += arm_l2x0.o
 obj-arm-y += arm_mptimer.o
 obj-arm-y += armv7m.o armv7m_nvic.o stellaris.o pl022.o stellaris_enet.o
diff --git a/hw/exynos4210.c b/hw/exynos4210.c
index 555adc0..8ebe5df 100644
--- a/hw/exynos4210.c
+++ b/hw/exynos4210.c
@@ -29,6 +29,9 @@
 
 #define EXYNOS4210_CHIPID_ADDR         0x10000000
 
+/* PWM */
+#define EXYNOS4210_PWM_BASE_ADDR       0x139D0000
+
 /* UART's definitions */
 #define EXYNOS4210_UART0_BASE_ADDR     0x13800000
 #define EXYNOS4210_UART1_BASE_ADDR     0x13810000
@@ -199,6 +202,15 @@ Exynos4210State *exynos4210_init(MemoryRegion *system_mem,
     memory_region_add_subregion(system_mem, EXYNOS4210_DRAM0_BASE_ADDR,
             &s->dram0_mem);
 
+    /* PWM */
+    sysbus_create_varargs("exynos4210.pwm", EXYNOS4210_PWM_BASE_ADDR,
+                          s->irq_table[exynos4210_get_irq(22, 0)],
+                          s->irq_table[exynos4210_get_irq(22, 1)],
+                          s->irq_table[exynos4210_get_irq(22, 2)],
+                          s->irq_table[exynos4210_get_irq(22, 3)],
+                          s->irq_table[exynos4210_get_irq(22, 4)],
+                          NULL);
+
     /*** UARTs ***/
     exynos4210_uart_create(EXYNOS4210_UART0_BASE_ADDR,
                            EXYNOS4210_UART0_FIFO_SIZE, 0, NULL,
diff --git a/hw/exynos4210_pwm.c b/hw/exynos4210_pwm.c
new file mode 100644
index 0000000..6243e59
--- /dev/null
+++ b/hw/exynos4210_pwm.c
@@ -0,0 +1,422 @@
+/*
+ * Samsung exynos4210 Pulse Width Modulation Timer
+ *
+ * Copyright (c) 2000 - 2011 Samsung Electronics Co., Ltd.
+ * All rights reserved.
+ *
+ * Evgeny Voevodin <e.voevodin@samsung.com>
+ *
+ * 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, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "sysbus.h"
+#include "qemu-timer.h"
+#include "qemu-common.h"
+#include "ptimer.h"
+
+#include "exynos4210.h"
+
+//#define DEBUG_PWM
+
+#ifdef DEBUG_PWM
+#define DPRINTF(fmt, ...) \
+        do { fprintf(stdout, "PWM: [%24s:%5d] " fmt, __func__, __LINE__, \
+                ## __VA_ARGS__); } while (0)
+#else
+#define DPRINTF(fmt, ...) do {} while (0)
+#endif
+
+#define     EXYNOS4210_PWM_TIMERS_NUM      5
+#define     EXYNOS4210_PWM_REG_MEM_SIZE    0x50
+
+#define     TCFG0        0x0000
+#define     TCFG1        0x0004
+#define     TCON         0x0008
+#define     TCNTB0       0x000C
+#define     TCMPB0       0x0010
+#define     TCNTO0       0x0014
+#define     TCNTB1       0x0018
+#define     TCMPB1       0x001C
+#define     TCNTO1       0x0020
+#define     TCNTB2       0x0024
+#define     TCMPB2       0x0028
+#define     TCNTO2       0x002C
+#define     TCNTB3       0x0030
+#define     TCMPB3       0x0034
+#define     TCNTO3       0x0038
+#define     TCNTB4       0x003C
+#define     TCNTO4       0x0040
+#define     TINT_CSTAT   0x0044
+
+#define     TCNTB(x)    (0xC * (x))
+#define     TCMPB(x)    (0xC * (x) + 1)
+#define     TCNTO(x)    (0xC * (x) + 2)
+
+#define GET_PRESCALER(reg, x) (((reg) & (0xFF << (8 * (x)))) >> 8 * (x))
+#define GET_DIVIDER(reg, x) (1 << (((reg) & (0xF << (4 * (x)))) >> (4 * (x))))
+
+/*
+ * Attention! Timer4 doesn't have OUTPUT_INVERTER,
+ * so Auto Reload bit is not accessible by macros!
+ */
+#define     TCON_TIMER_BASE(x)          (((x) ? 1 : 0) * 4 + 4 * (x))
+#define     TCON_TIMER_START(x)         (1 << (TCON_TIMER_BASE(x) + 0))
+#define     TCON_TIMER_MANUAL_UPD(x)    (1 << (TCON_TIMER_BASE(x) + 1))
+#define     TCON_TIMER_OUTPUT_INV(x)    (1 << (TCON_TIMER_BASE(x) + 2))
+#define     TCON_TIMER_AUTO_RELOAD(x)   (1 << (TCON_TIMER_BASE(x) + 3))
+#define     TCON_TIMER4_AUTO_RELOAD     (1 << 22)
+
+#define     TINT_CSTAT_STATUS(x)        (1 << (5 + (x)))
+#define     TINT_CSTAT_ENABLE(x)        (1 << (x))
+
+/* timer struct */
+typedef struct {
+    uint32_t    id;             /* timer id */
+    qemu_irq    irq;            /* local timer irq */
+    uint32_t    freq;           /* timer frequency */
+
+    /* use ptimer.c to represent count down timer */
+    ptimer_state *ptimer;       /* timer  */
+
+    /* registers */
+    uint32_t    reg_tcntb;      /* counter register buffer */
+    uint32_t    reg_tcmpb;      /* compare register buffer */
+
+    struct Exynos4210PWMState *parent;
+
+} Exynos4210PWM;
+
+
+typedef struct Exynos4210PWMState {
+    SysBusDevice busdev;
+    MemoryRegion iomem;
+
+    uint32_t    reg_tcfg[2];
+    uint32_t    reg_tcon;
+    uint32_t    reg_tint_cstat;
+
+    Exynos4210PWM timer[EXYNOS4210_PWM_TIMERS_NUM];
+
+} Exynos4210PWMState;
+
+/*** VMState ***/
+static const VMStateDescription vmstate_exynos4210_pwm = {
+    .name = "exynos4210.pwm.pwm",
+    .version_id = 1,
+    .minimum_version_id = 1,
+    .minimum_version_id_old = 1,
+    .fields = (VMStateField[]) {
+        VMSTATE_UINT32(id, Exynos4210PWM),
+        VMSTATE_UINT32(freq, Exynos4210PWM),
+        VMSTATE_PTIMER(ptimer, Exynos4210PWM),
+        VMSTATE_UINT32(reg_tcntb, Exynos4210PWM),
+        VMSTATE_UINT32(reg_tcmpb, Exynos4210PWM),
+        VMSTATE_END_OF_LIST()
+    }
+};
+
+static const VMStateDescription vmstate_exynos4210_pwm_state = {
+    .name = "exynos4210.pwm",
+    .version_id = 1,
+    .minimum_version_id = 1,
+    .minimum_version_id_old = 1,
+    .fields = (VMStateField[]) {
+        VMSTATE_UINT32_ARRAY(reg_tcfg, Exynos4210PWMState, 2),
+        VMSTATE_UINT32(reg_tcon, Exynos4210PWMState),
+        VMSTATE_UINT32(reg_tint_cstat, Exynos4210PWMState),
+        VMSTATE_STRUCT_ARRAY(timer, Exynos4210PWMState,
+            EXYNOS4210_PWM_TIMERS_NUM, 0,
+        vmstate_exynos4210_pwm, Exynos4210PWM),
+        VMSTATE_END_OF_LIST()
+    }
+};
+
+/*
+ * PWM update frequency
+ */
+static void exynos4210_pwm_update_freq(Exynos4210PWMState *s, uint32_t id)
+{
+    uint32_t freq;
+    freq = s->timer[id].freq;
+    if (id > 1) {
+        s->timer[id].freq = 24000000 /
+        ((GET_PRESCALER(s->reg_tcfg[0], 1) + 1) *
+                (GET_DIVIDER(s->reg_tcfg[1], id)));
+    } else {
+        s->timer[id].freq = 24000000 /
+        ((GET_PRESCALER(s->reg_tcfg[0], 0) + 1) *
+                (GET_DIVIDER(s->reg_tcfg[1], id)));
+    }
+
+    if (freq != s->timer[id].freq) {
+        ptimer_set_freq(s->timer[id].ptimer, s->timer[id].freq);
+        DPRINTF("freq=%dHz\n", s->timer[id].freq);
+    }
+}
+
+/*
+ * Counter tick handler
+ */
+static void exynos4210_pwm_tick(void *opaque)
+{
+    Exynos4210PWM *s = (Exynos4210PWM *)opaque;
+    Exynos4210PWMState *p = (Exynos4210PWMState *)s->parent;
+    uint32_t id = s->id;
+    bool cmp;
+
+    DPRINTF("timer %d tick\n", id);
+
+    /* set irq status */
+    p->reg_tint_cstat |= TINT_CSTAT_STATUS(id);
+
+    /* raise IRQ */
+    if (p->reg_tint_cstat & TINT_CSTAT_ENABLE(id)) {
+        DPRINTF("timer %d IRQ\n", id);
+        qemu_irq_raise(p->timer[id].irq);
+    }
+
+    /* reload timer */
+    if (id != 4) {
+        cmp = p->reg_tcon & TCON_TIMER_AUTO_RELOAD(id);
+    } else {
+        cmp = p->reg_tcon & TCON_TIMER4_AUTO_RELOAD;
+    }
+
+    if (cmp) {
+        DPRINTF("auto reload timer %d count to %x\n", id,
+                p->timer[id].reg_tcntb);
+        ptimer_set_count(p->timer[id].ptimer, p->timer[id].reg_tcntb);
+        ptimer_run(p->timer[id].ptimer, 1);
+    } else {
+        /* stop timer, set status to STOP, see Basic Timer Operation */
+        p->reg_tcon = ~TCON_TIMER_START(id);
+        ptimer_stop(p->timer[id].ptimer);
+    }
+}
+
+/*
+ * PWM Read
+ */
+static uint64_t exynos4210_pwm_read(void *opaque, target_phys_addr_t offset,
+        unsigned size)
+{
+    Exynos4210PWMState *s = (Exynos4210PWMState *)opaque;
+    uint32_t value = 0;
+    int index;
+
+    switch (offset) {
+    case TCFG0: case TCFG1:
+        index = (offset - TCFG0) >> 2;
+        value = s->reg_tcfg[index];
+        break;
+
+    case TCON:
+        value = s->reg_tcon;
+        break;
+
+    case TCNTB0: case TCNTB1:
+    case TCNTB2: case TCNTB3: case TCNTB4:
+        index = (offset - TCNTB0) / 0xC;
+        value = s->timer[index].reg_tcntb;
+        break;
+
+    case TCMPB0: case TCMPB1:
+    case TCMPB2: case TCMPB3:
+        index = (offset - TCMPB0) / 0xC;
+        value = s->timer[index].reg_tcmpb;
+        break;
+
+    case TCNTO0: case TCNTO1:
+    case TCNTO2: case TCNTO3: case TCNTO4:
+        index = (offset == TCNTO4) ? 4 : (offset - TCNTO0) / 0xC;
+        value = ptimer_get_count(s->timer[index].ptimer);
+        break;
+
+    case TINT_CSTAT:
+        value = s->reg_tint_cstat;
+        break;
+
+    default:
+        fprintf(stderr,
+                "[exynos4210.pwm: bad read offset " TARGET_FMT_plx "]\n",
+                offset);
+        break;
+    }
+    return value;
+}
+
+/*
+ * PWM Write
+ */
+static void exynos4210_pwm_write(void *opaque, target_phys_addr_t offset,
+        uint64_t value, unsigned size)
+{
+    Exynos4210PWMState *s = (Exynos4210PWMState *)opaque;
+    int index;
+    uint32_t new_val;
+    int i;
+
+    switch (offset) {
+    case TCFG0: case TCFG1:
+        index = (offset - TCFG0) >> 2;
+        s->reg_tcfg[index] = value;
+
+        /* update timers frequencies */
+        for (i = 0; i < EXYNOS4210_PWM_TIMERS_NUM; i++) {
+            exynos4210_pwm_update_freq(s, s->timer[i].id);
+        }
+        break;
+
+    case TCON:
+        for (i = 0; i < EXYNOS4210_PWM_TIMERS_NUM; i++) {
+            if ((value & TCON_TIMER_MANUAL_UPD(i)) >
+            (s->reg_tcon & TCON_TIMER_MANUAL_UPD(i))) {
+                /*
+                 * TCNTB and TCMPB are loaded into TCNT and TCMP.
+                 * Update timers.
+                 */
+
+                /* this will start timer to run, this ok, because
+                 * during processing start bit timer will be stopped
+                 * if needed */
+                ptimer_set_count(s->timer[i].ptimer, s->timer[i].reg_tcntb);
+                DPRINTF("set timer %d count to %x\n", i,
+                        s->timer[i].reg_tcntb);
+            }
+
+            if ((value & TCON_TIMER_START(i)) >
+            (s->reg_tcon & TCON_TIMER_START(i))) {
+                /* changed to start */
+                ptimer_run(s->timer[i].ptimer, 1);
+                DPRINTF("run timer %d\n", i);
+            }
+
+            if ((value & TCON_TIMER_START(i)) <
+                    (s->reg_tcon & TCON_TIMER_START(i))) {
+                /* changed to stop */
+                ptimer_stop(s->timer[i].ptimer);
+                DPRINTF("stop timer %d\n", i);
+            }
+        }
+        s->reg_tcon = value;
+        break;
+
+    case TCNTB0: case TCNTB1:
+    case TCNTB2: case TCNTB3: case TCNTB4:
+        index = (offset - TCNTB0) / 0xC;
+        s->timer[index].reg_tcntb = value;
+        break;
+
+    case TCMPB0: case TCMPB1:
+    case TCMPB2: case TCMPB3:
+        index = (offset - TCMPB0) / 0xC;
+        s->timer[index].reg_tcmpb = value;
+        break;
+
+    case TINT_CSTAT:
+        new_val = (s->reg_tint_cstat & 0x3E0) + (0x1F & value);
+        new_val &= ~(0x3E0 & value);
+
+        for (i = 0; i < EXYNOS4210_PWM_TIMERS_NUM; i++) {
+            if ((new_val & TINT_CSTAT_STATUS(i)) <
+                    (s->reg_tint_cstat & TINT_CSTAT_STATUS(i))) {
+                qemu_irq_lower(s->timer[i].irq);
+            }
+        }
+
+        s->reg_tint_cstat = new_val;
+        break;
+
+    default:
+        fprintf(stderr,
+                "[exynos4210.pwm: bad write offset " TARGET_FMT_plx "]\n",
+                offset);
+        break;
+
+    }
+}
+
+/*
+ * Set default values to timer fields and registers
+ */
+static void exynos4210_pwm_reset(DeviceState *d)
+{
+    Exynos4210PWMState *s = (Exynos4210PWMState *)d;
+    int i;
+    s->reg_tcfg[0] = 0x0101;
+    s->reg_tcfg[1] = 0x0;
+    s->reg_tcon = 0;
+    s->reg_tint_cstat = 0;
+    for (i = 0; i < EXYNOS4210_PWM_TIMERS_NUM; i++) {
+        s->timer[i].reg_tcmpb = 0;
+        s->timer[i].reg_tcntb = 0;
+
+        exynos4210_pwm_update_freq(s, s->timer[i].id);
+        ptimer_stop(s->timer[i].ptimer);
+    }
+}
+
+static const MemoryRegionOps exynos4210_pwm_ops = {
+    .read = exynos4210_pwm_read,
+    .write = exynos4210_pwm_write,
+    .endianness = DEVICE_NATIVE_ENDIAN,
+};
+
+/*
+ * PWM timer initialization
+ */
+static int exynos4210_pwm_init(SysBusDevice *dev)
+{
+    Exynos4210PWMState *s = FROM_SYSBUS(Exynos4210PWMState, dev);
+    int i;
+    QEMUBH *bh;
+
+    for (i = 0; i < EXYNOS4210_PWM_TIMERS_NUM; i++) {
+        bh = qemu_bh_new(exynos4210_pwm_tick, &s->timer[i]);
+        sysbus_init_irq(dev, &s->timer[i].irq);
+        s->timer[i].ptimer = ptimer_init(bh);
+        s->timer[i].id = i;
+        s->timer[i].parent = s;
+    }
+
+    memory_region_init_io(&s->iomem, &exynos4210_pwm_ops, s, "exynos4210-pwm",
+            EXYNOS4210_PWM_REG_MEM_SIZE);
+    sysbus_init_mmio(dev, &s->iomem);
+
+    return 0;
+}
+
+static void exynos4210_pwm_class_init(ObjectClass *klass, void *data)
+{
+    DeviceClass *dc = DEVICE_CLASS(klass);
+    SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass);
+
+    k->init = exynos4210_pwm_init;
+    dc->reset = exynos4210_pwm_reset;
+    dc->vmsd = &vmstate_exynos4210_pwm_state;
+}
+
+static TypeInfo exynos4210_pwm_info = {
+    .name          = "exynos4210.pwm",
+    .parent        = TYPE_SYS_BUS_DEVICE,
+    .instance_size = sizeof(Exynos4210PWMState),
+    .class_init    = exynos4210_pwm_class_init,
+};
+
+static void exynos4210_pwm_register_types(void)
+{
+    type_register_static(&exynos4210_pwm_info);
+}
+
+type_init(exynos4210_pwm_register_types)
-- 
1.7.1

^ permalink raw reply related	[flat|nested] 24+ messages in thread

* [Qemu-devel] [PATCH 05/22] ARM: exynos4210: basic Power Management Unit implementation
  2012-02-17 11:29 [Qemu-devel] [PULL 00/22] arm-devs queue Peter Maydell
                   ` (3 preceding siblings ...)
  2012-02-17 11:29 ` [Qemu-devel] [PATCH 04/22] ARM: exynos4210: PWM support Peter Maydell
@ 2012-02-17 11:29 ` Peter Maydell
  2012-02-17 11:29 ` [Qemu-devel] [PATCH 06/22] ARM: exynos4210: MCT support Peter Maydell
                   ` (17 subsequent siblings)
  22 siblings, 0 replies; 24+ messages in thread
From: Peter Maydell @ 2012-02-17 11:29 UTC (permalink / raw)
  To: Anthony Liguori; +Cc: qemu-devel, Paul Brook

From: Maksim Kozlov <m.kozlov@samsung.com>

Patch adds basic model for Exynos4210 SoC PMU.
This model implements PMU registers just as a bulk of memory. Currently,
the only reason this device exists is that secondary CPU boot loader
uses PMU INFORM5 register as a holding pen.

Signed-off-by: Maksim Kozlov <m.kozlov@samsung.com>
Signed-off-by: Evgeny Voevodin <e.voevodin@samsung.com>
Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
---
 Makefile.target     |    1 +
 hw/exynos4210.c     |    9 +
 hw/exynos4210_pmu.c |  499 +++++++++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 509 insertions(+), 0 deletions(-)
 create mode 100644 hw/exynos4210_pmu.c

diff --git a/Makefile.target b/Makefile.target
index bbcee06..8a7b4d2 100644
--- a/Makefile.target
+++ b/Makefile.target
@@ -346,6 +346,7 @@ obj-arm-y += versatile_pci.o
 obj-arm-y += realview_gic.o realview.o arm_sysctl.o arm11mpcore.o a9mpcore.o
 obj-arm-y += exynos4210_gic.o exynos4210_combiner.o exynos4210.o
 obj-arm-y += exynos4_boards.o exynos4210_uart.o exynos4210_pwm.o
+obj-arm-y += exynos4210_pmu.o
 obj-arm-y += arm_l2x0.o
 obj-arm-y += arm_mptimer.o
 obj-arm-y += armv7m.o armv7m_nvic.o stellaris.o pl022.o stellaris_enet.o
diff --git a/hw/exynos4210.c b/hw/exynos4210.c
index 8ebe5df..f922f55 100644
--- a/hw/exynos4210.c
+++ b/hw/exynos4210.c
@@ -52,6 +52,9 @@
 #define EXYNOS4210_EXT_COMBINER_BASE_ADDR   0x10440000
 #define EXYNOS4210_INT_COMBINER_BASE_ADDR   0x10448000
 
+/* PMU SFR base address */
+#define EXYNOS4210_PMU_BASE_ADDR            0x10020000
+
 static uint8_t chipid_and_omr[] = { 0x11, 0x02, 0x21, 0x43,
                                     0x09, 0x00, 0x00, 0x00 };
 
@@ -202,6 +205,12 @@ Exynos4210State *exynos4210_init(MemoryRegion *system_mem,
     memory_region_add_subregion(system_mem, EXYNOS4210_DRAM0_BASE_ADDR,
             &s->dram0_mem);
 
+   /* PMU.
+    * The only reason of existence at the moment is that secondary CPU boot
+    * loader uses PMU INFORM5 register as a holding pen.
+    */
+    sysbus_create_simple("exynos4210.pmu", EXYNOS4210_PMU_BASE_ADDR, NULL);
+
     /* PWM */
     sysbus_create_varargs("exynos4210.pwm", EXYNOS4210_PWM_BASE_ADDR,
                           s->irq_table[exynos4210_get_irq(22, 0)],
diff --git a/hw/exynos4210_pmu.c b/hw/exynos4210_pmu.c
new file mode 100644
index 0000000..c12d750
--- /dev/null
+++ b/hw/exynos4210_pmu.c
@@ -0,0 +1,499 @@
+/*
+ *  Exynos4210 Power Management Unit (PMU) Emulation
+ *
+ *  Copyright (C) 2011 Samsung Electronics Co Ltd.
+ *    Maksim Kozlov <m.kozlov@samsung.com>
+ *
+ *  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, see <http://www.gnu.org/licenses/>.
+ */
+
+/*
+ * This model implements PMU registers just as a bulk of memory. Currently,
+ * the only reason this device exists is that secondary CPU boot loader
+ * uses PMU INFORM5 register as a holding pen.
+ */
+
+#include "sysbus.h"
+
+#ifndef DEBUG_PMU
+#define DEBUG_PMU           0
+#endif
+
+#ifndef DEBUG_PMU_EXTEND
+#define DEBUG_PMU_EXTEND    0
+#endif
+
+#if DEBUG_PMU
+#define  PRINT_DEBUG(fmt, args...)  \
+        do { \
+            fprintf(stderr, "  [%s:%d]   "fmt, __func__, __LINE__, ##args); \
+        } while (0)
+
+#if DEBUG_PMU_EXTEND
+#define  PRINT_DEBUG_EXTEND(fmt, args...) \
+        do { \
+            fprintf(stderr, "  [%s:%d]   "fmt, __func__, __LINE__, ##args); \
+        } while (0)
+#else
+#define  PRINT_DEBUG_EXTEND(fmt, args...)  do {} while (0)
+#endif /* EXTEND */
+
+#else
+#define  PRINT_DEBUG(fmt, args...)   do {} while (0)
+#define  PRINT_DEBUG_EXTEND(fmt, args...)  do {} while (0)
+#endif
+
+/*
+ *  Offsets for PMU registers
+ */
+#define OM_STAT                  0x0000 /* OM status register */
+#define RTC_CLKO_SEL             0x000C /* Controls RTCCLKOUT */
+#define GNSS_RTC_OUT_CTRL        0x0010 /* Controls GNSS_RTC_OUT */
+/* Decides whether system-level low-power mode is used. */
+#define SYSTEM_POWER_DOWN_CTRL   0x0200
+/* Sets control options for CENTRAL_SEQ */
+#define SYSTEM_POWER_DOWN_OPTION 0x0208
+#define SWRESET                  0x0400 /* Generate software reset */
+#define RST_STAT                 0x0404 /* Reset status register */
+#define WAKEUP_STAT              0x0600 /* Wakeup status register  */
+#define EINT_WAKEUP_MASK         0x0604 /* Configure External INTerrupt mask */
+#define WAKEUP_MASK              0x0608 /* Configure wakeup source mask */
+#define HDMI_PHY_CONTROL         0x0700 /* HDMI PHY control register */
+#define USBDEVICE_PHY_CONTROL    0x0704 /* USB Device PHY control register */
+#define USBHOST_PHY_CONTROL      0x0708 /* USB HOST PHY control register */
+#define DAC_PHY_CONTROL          0x070C /* DAC control register  */
+#define MIPI_PHY0_CONTROL        0x0710 /* MIPI PHY control register */
+#define MIPI_PHY1_CONTROL        0x0714 /* MIPI PHY control register */
+#define ADC_PHY_CONTROL          0x0718 /* TS-ADC control register */
+#define PCIe_PHY_CONTROL         0x071C /* TS-PCIe control register */
+#define SATA_PHY_CONTROL         0x0720 /* TS-SATA control register */
+#define INFORM0                  0x0800 /* Information register 0  */
+#define INFORM1                  0x0804 /* Information register 1  */
+#define INFORM2                  0x0808 /* Information register 2  */
+#define INFORM3                  0x080C /* Information register 3  */
+#define INFORM4                  0x0810 /* Information register 4  */
+#define INFORM5                  0x0814 /* Information register 5  */
+#define INFORM6                  0x0818 /* Information register 6  */
+#define INFORM7                  0x081C /* Information register 7  */
+#define PMU_DEBUG                0x0A00 /* PMU debug register */
+/* Registers to set system-level low-power option */
+#define ARM_CORE0_SYS_PWR_REG              0x1000
+#define ARM_CORE1_SYS_PWR_REG              0x1010
+#define ARM_COMMON_SYS_PWR_REG             0x1080
+#define ARM_CPU_L2_0_SYS_PWR_REG           0x10C0
+#define ARM_CPU_L2_1_SYS_PWR_REG           0x10C4
+#define CMU_ACLKSTOP_SYS_PWR_REG           0x1100
+#define CMU_SCLKSTOP_SYS_PWR_REG           0x1104
+#define CMU_RESET_SYS_PWR_REG              0x110C
+#define APLL_SYSCLK_SYS_PWR_REG            0x1120
+#define MPLL_SYSCLK_SYS_PWR_REG            0x1124
+#define VPLL_SYSCLK_SYS_PWR_REG            0x1128
+#define EPLL_SYSCLK_SYS_PWR_REG            0x112C
+#define CMU_CLKSTOP_GPS_ALIVE_SYS_PWR_REG  0x1138
+#define CMU_RESET_GPS_ALIVE_SYS_PWR_REG    0x113C
+#define CMU_CLKSTOP_CAM_SYS_PWR_REG        0x1140
+#define CMU_CLKSTOP_TV_SYS_PWR_REG         0x1144
+#define CMU_CLKSTOP_MFC_SYS_PWR_REG        0x1148
+#define CMU_CLKSTOP_G3D_SYS_PWR_REG        0x114C
+#define CMU_CLKSTOP_LCD0_SYS_PWR_REG       0x1150
+#define CMU_CLKSTOP_LCD1_SYS_PWR_REG       0x1154
+#define CMU_CLKSTOP_MAUDIO_SYS_PWR_REG     0x1158
+#define CMU_CLKSTOP_GPS_SYS_PWR_REG        0x115C
+#define CMU_RESET_CAM_SYS_PWR_REG          0x1160
+#define CMU_RESET_TV_SYS_PWR_REG           0x1164
+#define CMU_RESET_MFC_SYS_PWR_REG          0x1168
+#define CMU_RESET_G3D_SYS_PWR_REG          0x116C
+#define CMU_RESET_LCD0_SYS_PWR_REG         0x1170
+#define CMU_RESET_LCD1_SYS_PWR_REG         0x1174
+#define CMU_RESET_MAUDIO_SYS_PWR_REG       0x1178
+#define CMU_RESET_GPS_SYS_PWR_REG          0x117C
+#define TOP_BUS_SYS_PWR_REG                0x1180
+#define TOP_RETENTION_SYS_PWR_REG          0x1184
+#define TOP_PWR_SYS_PWR_REG                0x1188
+#define LOGIC_RESET_SYS_PWR_REG            0x11A0
+#define OneNANDXL_MEM_SYS_PWR_REG          0x11C0
+#define MODEMIF_MEM_SYS_PWR_REG            0x11C4
+#define USBDEVICE_MEM_SYS_PWR_REG          0x11CC
+#define SDMMC_MEM_SYS_PWR_REG              0x11D0
+#define CSSYS_MEM_SYS_PWR_REG              0x11D4
+#define SECSS_MEM_SYS_PWR_REG              0x11D8
+#define PCIe_MEM_SYS_PWR_REG               0x11E0
+#define SATA_MEM_SYS_PWR_REG               0x11E4
+#define PAD_RETENTION_DRAM_SYS_PWR_REG     0x1200
+#define PAD_RETENTION_MAUDIO_SYS_PWR_REG   0x1204
+#define PAD_RETENTION_GPIO_SYS_PWR_REG     0x1220
+#define PAD_RETENTION_UART_SYS_PWR_REG     0x1224
+#define PAD_RETENTION_MMCA_SYS_PWR_REG     0x1228
+#define PAD_RETENTION_MMCB_SYS_PWR_REG     0x122C
+#define PAD_RETENTION_EBIA_SYS_PWR_REG     0x1230
+#define PAD_RETENTION_EBIB_SYS_PWR_REG     0x1234
+#define PAD_ISOLATION_SYS_PWR_REG          0x1240
+#define PAD_ALV_SEL_SYS_PWR_REG            0x1260
+#define XUSBXTI_SYS_PWR_REG                0x1280
+#define XXTI_SYS_PWR_REG                   0x1284
+#define EXT_REGULATOR_SYS_PWR_REG          0x12C0
+#define GPIO_MODE_SYS_PWR_REG              0x1300
+#define GPIO_MODE_MAUDIO_SYS_PWR_REG       0x1340
+#define CAM_SYS_PWR_REG                    0x1380
+#define TV_SYS_PWR_REG                     0x1384
+#define MFC_SYS_PWR_REG                    0x1388
+#define G3D_SYS_PWR_REG                    0x138C
+#define LCD0_SYS_PWR_REG                   0x1390
+#define LCD1_SYS_PWR_REG                   0x1394
+#define MAUDIO_SYS_PWR_REG                 0x1398
+#define GPS_SYS_PWR_REG                    0x139C
+#define GPS_ALIVE_SYS_PWR_REG              0x13A0
+#define ARM_CORE0_CONFIGURATION 0x2000 /* Configure power mode of ARM_CORE0 */
+#define ARM_CORE0_STATUS        0x2004 /* Check power mode of ARM_CORE0 */
+#define ARM_CORE0_OPTION        0x2008 /* Sets control options for ARM_CORE0 */
+#define ARM_CORE1_CONFIGURATION 0x2080 /* Configure power mode of ARM_CORE1 */
+#define ARM_CORE1_STATUS        0x2084 /* Check power mode of ARM_CORE1 */
+#define ARM_CORE1_OPTION        0x2088 /* Sets control options for ARM_CORE0 */
+#define ARM_COMMON_OPTION       0x2408 /* Sets control options for ARM_COMMON */
+/* Configure power mode of ARM_CPU_L2_0 */
+#define ARM_CPU_L2_0_CONFIGURATION 0x2600
+#define ARM_CPU_L2_0_STATUS        0x2604 /* Check power mode of ARM_CPU_L2_0 */
+/* Configure power mode of ARM_CPU_L2_1 */
+#define ARM_CPU_L2_1_CONFIGURATION 0x2620
+#define ARM_CPU_L2_1_STATUS        0x2624 /* Check power mode of ARM_CPU_L2_1 */
+/* Sets control options for PAD_RETENTION_MAUDIO */
+#define PAD_RETENTION_MAUDIO_OPTION 0x3028
+/* Sets control options for PAD_RETENTION_GPIO */
+#define PAD_RETENTION_GPIO_OPTION   0x3108
+/* Sets control options for PAD_RETENTION_UART */
+#define PAD_RETENTION_UART_OPTION   0x3128
+/* Sets control options for PAD_RETENTION_MMCA */
+#define PAD_RETENTION_MMCA_OPTION   0x3148
+/* Sets control options for PAD_RETENTION_MMCB */
+#define PAD_RETENTION_MMCB_OPTION   0x3168
+/* Sets control options for PAD_RETENTION_EBIA */
+#define PAD_RETENTION_EBIA_OPTION   0x3188
+/* Sets control options for PAD_RETENTION_EBIB */
+#define PAD_RETENTION_EBIB_OPTION   0x31A8
+#define PS_HOLD_CONTROL         0x330C /* PS_HOLD control register */
+#define XUSBXTI_CONFIGURATION   0x3400 /* Configure the pad of XUSBXTI */
+#define XUSBXTI_STATUS          0x3404 /* Check the pad of XUSBXTI */
+/* Sets time required for XUSBXTI to be stabilized */
+#define XUSBXTI_DURATION        0x341C
+#define XXTI_CONFIGURATION      0x3420 /* Configure the pad of XXTI */
+#define XXTI_STATUS             0x3424 /* Check the pad of XXTI */
+/* Sets time required for XXTI to be stabilized */
+#define XXTI_DURATION           0x343C
+/* Sets time required for EXT_REGULATOR to be stabilized */
+#define EXT_REGULATOR_DURATION  0x361C
+#define CAM_CONFIGURATION       0x3C00 /* Configure power mode of CAM */
+#define CAM_STATUS              0x3C04 /* Check power mode of CAM */
+#define CAM_OPTION              0x3C08 /* Sets control options for CAM */
+#define TV_CONFIGURATION        0x3C20 /* Configure power mode of TV */
+#define TV_STATUS               0x3C24 /* Check power mode of TV */
+#define TV_OPTION               0x3C28 /* Sets control options for TV */
+#define MFC_CONFIGURATION       0x3C40 /* Configure power mode of MFC */
+#define MFC_STATUS              0x3C44 /* Check power mode of MFC */
+#define MFC_OPTION              0x3C48 /* Sets control options for MFC */
+#define G3D_CONFIGURATION       0x3C60 /* Configure power mode of G3D */
+#define G3D_STATUS              0x3C64 /* Check power mode of G3D */
+#define G3D_OPTION              0x3C68 /* Sets control options for G3D */
+#define LCD0_CONFIGURATION      0x3C80 /* Configure power mode of LCD0 */
+#define LCD0_STATUS             0x3C84 /* Check power mode of LCD0 */
+#define LCD0_OPTION             0x3C88 /* Sets control options for LCD0 */
+#define LCD1_CONFIGURATION      0x3CA0 /* Configure power mode of LCD1 */
+#define LCD1_STATUS             0x3CA4 /* Check power mode of LCD1 */
+#define LCD1_OPTION             0x3CA8 /* Sets control options for LCD1 */
+#define GPS_CONFIGURATION       0x3CE0 /* Configure power mode of GPS */
+#define GPS_STATUS              0x3CE4 /* Check power mode of GPS */
+#define GPS_OPTION              0x3CE8 /* Sets control options for GPS */
+#define GPS_ALIVE_CONFIGURATION 0x3D00 /* Configure power mode of GPS */
+#define GPS_ALIVE_STATUS        0x3D04 /* Check power mode of GPS */
+#define GPS_ALIVE_OPTION        0x3D08 /* Sets control options for GPS */
+
+#define EXYNOS4210_PMU_REGS_MEM_SIZE 0x3d0c
+
+typedef struct Exynos4210PmuReg {
+    const char  *name; /* for debug only */
+    uint32_t     offset;
+    uint32_t     reset_value;
+} Exynos4210PmuReg;
+
+static const Exynos4210PmuReg exynos4210_pmu_regs[] = {
+    {"OM_STAT", OM_STAT, 0x00000000},
+    {"RTC_CLKO_SEL", RTC_CLKO_SEL, 0x00000000},
+    {"GNSS_RTC_OUT_CTRL", GNSS_RTC_OUT_CTRL, 0x00000001},
+    {"SYSTEM_POWER_DOWN_CTRL", SYSTEM_POWER_DOWN_CTRL, 0x00010000},
+    {"SYSTEM_POWER_DOWN_OPTION", SYSTEM_POWER_DOWN_OPTION, 0x03030000},
+    {"SWRESET", SWRESET, 0x00000000},
+    {"RST_STAT", RST_STAT, 0x00000000},
+    {"WAKEUP_STAT", WAKEUP_STAT, 0x00000000},
+    {"EINT_WAKEUP_MASK", EINT_WAKEUP_MASK, 0x00000000},
+    {"WAKEUP_MASK", WAKEUP_MASK, 0x00000000},
+    {"HDMI_PHY_CONTROL", HDMI_PHY_CONTROL, 0x00960000},
+    {"USBDEVICE_PHY_CONTROL", USBDEVICE_PHY_CONTROL, 0x00000000},
+    {"USBHOST_PHY_CONTROL", USBHOST_PHY_CONTROL, 0x00000000},
+    {"DAC_PHY_CONTROL", DAC_PHY_CONTROL, 0x00000000},
+    {"MIPI_PHY0_CONTROL", MIPI_PHY0_CONTROL, 0x00000000},
+    {"MIPI_PHY1_CONTROL", MIPI_PHY1_CONTROL, 0x00000000},
+    {"ADC_PHY_CONTROL", ADC_PHY_CONTROL, 0x00000001},
+    {"PCIe_PHY_CONTROL", PCIe_PHY_CONTROL, 0x00000000},
+    {"SATA_PHY_CONTROL", SATA_PHY_CONTROL, 0x00000000},
+    {"INFORM0", INFORM0, 0x00000000},
+    {"INFORM1", INFORM1, 0x00000000},
+    {"INFORM2", INFORM2, 0x00000000},
+    {"INFORM3", INFORM3, 0x00000000},
+    {"INFORM4", INFORM4, 0x00000000},
+    {"INFORM5", INFORM5, 0x00000000},
+    {"INFORM6", INFORM6, 0x00000000},
+    {"INFORM7", INFORM7, 0x00000000},
+    {"PMU_DEBUG", PMU_DEBUG, 0x00000000},
+    {"ARM_CORE0_SYS_PWR_REG", ARM_CORE0_SYS_PWR_REG, 0xFFFFFFFF},
+    {"ARM_CORE1_SYS_PWR_REG", ARM_CORE1_SYS_PWR_REG, 0xFFFFFFFF},
+    {"ARM_COMMON_SYS_PWR_REG", ARM_COMMON_SYS_PWR_REG, 0xFFFFFFFF},
+    {"ARM_CPU_L2_0_SYS_PWR_REG", ARM_CPU_L2_0_SYS_PWR_REG, 0xFFFFFFFF},
+    {"ARM_CPU_L2_1_SYS_PWR_REG", ARM_CPU_L2_1_SYS_PWR_REG, 0xFFFFFFFF},
+    {"CMU_ACLKSTOP_SYS_PWR_REG", CMU_ACLKSTOP_SYS_PWR_REG, 0xFFFFFFFF},
+    {"CMU_SCLKSTOP_SYS_PWR_REG", CMU_SCLKSTOP_SYS_PWR_REG, 0xFFFFFFFF},
+    {"CMU_RESET_SYS_PWR_REG", CMU_RESET_SYS_PWR_REG, 0xFFFFFFFF},
+    {"APLL_SYSCLK_SYS_PWR_REG", APLL_SYSCLK_SYS_PWR_REG, 0xFFFFFFFF},
+    {"MPLL_SYSCLK_SYS_PWR_REG", MPLL_SYSCLK_SYS_PWR_REG, 0xFFFFFFFF},
+    {"VPLL_SYSCLK_SYS_PWR_REG", VPLL_SYSCLK_SYS_PWR_REG, 0xFFFFFFFF},
+    {"EPLL_SYSCLK_SYS_PWR_REG", EPLL_SYSCLK_SYS_PWR_REG, 0xFFFFFFFF},
+    {"CMU_CLKSTOP_GPS_ALIVE_SYS_PWR_REG", CMU_CLKSTOP_GPS_ALIVE_SYS_PWR_REG,
+            0xFFFFFFFF},
+    {"CMU_RESET_GPS_ALIVE_SYS_PWR_REG", CMU_RESET_GPS_ALIVE_SYS_PWR_REG,
+            0xFFFFFFFF},
+    {"CMU_CLKSTOP_CAM_SYS_PWR_REG", CMU_CLKSTOP_CAM_SYS_PWR_REG, 0xFFFFFFFF},
+    {"CMU_CLKSTOP_TV_SYS_PWR_REG", CMU_CLKSTOP_TV_SYS_PWR_REG, 0xFFFFFFFF},
+    {"CMU_CLKSTOP_MFC_SYS_PWR_REG", CMU_CLKSTOP_MFC_SYS_PWR_REG, 0xFFFFFFFF},
+    {"CMU_CLKSTOP_G3D_SYS_PWR_REG", CMU_CLKSTOP_G3D_SYS_PWR_REG, 0xFFFFFFFF},
+    {"CMU_CLKSTOP_LCD0_SYS_PWR_REG", CMU_CLKSTOP_LCD0_SYS_PWR_REG, 0xFFFFFFFF},
+    {"CMU_CLKSTOP_LCD1_SYS_PWR_REG", CMU_CLKSTOP_LCD1_SYS_PWR_REG, 0xFFFFFFFF},
+    {"CMU_CLKSTOP_MAUDIO_SYS_PWR_REG", CMU_CLKSTOP_MAUDIO_SYS_PWR_REG,
+            0xFFFFFFFF},
+    {"CMU_CLKSTOP_GPS_SYS_PWR_REG", CMU_CLKSTOP_GPS_SYS_PWR_REG, 0xFFFFFFFF},
+    {"CMU_RESET_CAM_SYS_PWR_REG", CMU_RESET_CAM_SYS_PWR_REG, 0xFFFFFFFF},
+    {"CMU_RESET_TV_SYS_PWR_REG", CMU_RESET_TV_SYS_PWR_REG, 0xFFFFFFFF},
+    {"CMU_RESET_MFC_SYS_PWR_REG", CMU_RESET_MFC_SYS_PWR_REG, 0xFFFFFFFF},
+    {"CMU_RESET_G3D_SYS_PWR_REG", CMU_RESET_G3D_SYS_PWR_REG, 0xFFFFFFFF},
+    {"CMU_RESET_LCD0_SYS_PWR_REG", CMU_RESET_LCD0_SYS_PWR_REG, 0xFFFFFFFF},
+    {"CMU_RESET_LCD1_SYS_PWR_REG", CMU_RESET_LCD1_SYS_PWR_REG, 0xFFFFFFFF},
+    {"CMU_RESET_MAUDIO_SYS_PWR_REG", CMU_RESET_MAUDIO_SYS_PWR_REG, 0xFFFFFFFF},
+    {"CMU_RESET_GPS_SYS_PWR_REG", CMU_RESET_GPS_SYS_PWR_REG, 0xFFFFFFFF},
+    {"TOP_BUS_SYS_PWR_REG", TOP_BUS_SYS_PWR_REG, 0xFFFFFFFF},
+    {"TOP_RETENTION_SYS_PWR_REG", TOP_RETENTION_SYS_PWR_REG, 0xFFFFFFFF},
+    {"TOP_PWR_SYS_PWR_REG", TOP_PWR_SYS_PWR_REG, 0xFFFFFFFF},
+    {"LOGIC_RESET_SYS_PWR_REG", LOGIC_RESET_SYS_PWR_REG, 0xFFFFFFFF},
+    {"OneNANDXL_MEM_SYS_PWR_REG", OneNANDXL_MEM_SYS_PWR_REG, 0xFFFFFFFF},
+    {"MODEMIF_MEM_SYS_PWR_REG", MODEMIF_MEM_SYS_PWR_REG, 0xFFFFFFFF},
+    {"USBDEVICE_MEM_SYS_PWR_REG", USBDEVICE_MEM_SYS_PWR_REG, 0xFFFFFFFF},
+    {"SDMMC_MEM_SYS_PWR_REG", SDMMC_MEM_SYS_PWR_REG, 0xFFFFFFFF},
+    {"CSSYS_MEM_SYS_PWR_REG", CSSYS_MEM_SYS_PWR_REG, 0xFFFFFFFF},
+    {"SECSS_MEM_SYS_PWR_REG", SECSS_MEM_SYS_PWR_REG, 0xFFFFFFFF},
+    {"PCIe_MEM_SYS_PWR_REG", PCIe_MEM_SYS_PWR_REG, 0xFFFFFFFF},
+    {"SATA_MEM_SYS_PWR_REG", SATA_MEM_SYS_PWR_REG, 0xFFFFFFFF},
+    {"PAD_RETENTION_DRAM_SYS_PWR_REG", PAD_RETENTION_DRAM_SYS_PWR_REG,
+            0xFFFFFFFF},
+    {"PAD_RETENTION_MAUDIO_SYS_PWR_REG", PAD_RETENTION_MAUDIO_SYS_PWR_REG,
+            0xFFFFFFFF},
+    {"PAD_RETENTION_GPIO_SYS_PWR_REG", PAD_RETENTION_GPIO_SYS_PWR_REG,
+            0xFFFFFFFF},
+    {"PAD_RETENTION_UART_SYS_PWR_REG", PAD_RETENTION_UART_SYS_PWR_REG,
+            0xFFFFFFFF},
+    {"PAD_RETENTION_MMCA_SYS_PWR_REG", PAD_RETENTION_MMCA_SYS_PWR_REG,
+            0xFFFFFFFF},
+    {"PAD_RETENTION_MMCB_SYS_PWR_REG", PAD_RETENTION_MMCB_SYS_PWR_REG,
+            0xFFFFFFFF},
+    {"PAD_RETENTION_EBIA_SYS_PWR_REG", PAD_RETENTION_EBIA_SYS_PWR_REG,
+            0xFFFFFFFF},
+    {"PAD_RETENTION_EBIB_SYS_PWR_REG", PAD_RETENTION_EBIB_SYS_PWR_REG,
+            0xFFFFFFFF},
+    {"PAD_ISOLATION_SYS_PWR_REG", PAD_ISOLATION_SYS_PWR_REG, 0xFFFFFFFF},
+    {"PAD_ALV_SEL_SYS_PWR_REG", PAD_ALV_SEL_SYS_PWR_REG, 0xFFFFFFFF},
+    {"XUSBXTI_SYS_PWR_REG", XUSBXTI_SYS_PWR_REG, 0xFFFFFFFF},
+    {"XXTI_SYS_PWR_REG", XXTI_SYS_PWR_REG, 0xFFFFFFFF},
+    {"EXT_REGULATOR_SYS_PWR_REG", EXT_REGULATOR_SYS_PWR_REG, 0xFFFFFFFF},
+    {"GPIO_MODE_SYS_PWR_REG", GPIO_MODE_SYS_PWR_REG, 0xFFFFFFFF},
+    {"GPIO_MODE_MAUDIO_SYS_PWR_REG", GPIO_MODE_MAUDIO_SYS_PWR_REG, 0xFFFFFFFF},
+    {"CAM_SYS_PWR_REG", CAM_SYS_PWR_REG, 0xFFFFFFFF},
+    {"TV_SYS_PWR_REG", TV_SYS_PWR_REG, 0xFFFFFFFF},
+    {"MFC_SYS_PWR_REG", MFC_SYS_PWR_REG, 0xFFFFFFFF},
+    {"G3D_SYS_PWR_REG", G3D_SYS_PWR_REG, 0xFFFFFFFF},
+    {"LCD0_SYS_PWR_REG", LCD0_SYS_PWR_REG, 0xFFFFFFFF},
+    {"LCD1_SYS_PWR_REG", LCD1_SYS_PWR_REG, 0xFFFFFFFF},
+    {"MAUDIO_SYS_PWR_REG", MAUDIO_SYS_PWR_REG, 0xFFFFFFFF},
+    {"GPS_SYS_PWR_REG", GPS_SYS_PWR_REG, 0xFFFFFFFF},
+    {"GPS_ALIVE_SYS_PWR_REG", GPS_ALIVE_SYS_PWR_REG, 0xFFFFFFFF},
+    {"ARM_CORE0_CONFIGURATION", ARM_CORE0_CONFIGURATION, 0x00000003},
+    {"ARM_CORE0_STATUS", ARM_CORE0_STATUS, 0x00030003},
+    {"ARM_CORE0_OPTION", ARM_CORE0_OPTION, 0x01010001},
+    {"ARM_CORE1_CONFIGURATION", ARM_CORE1_CONFIGURATION, 0x00000003},
+    {"ARM_CORE1_STATUS", ARM_CORE1_STATUS, 0x00030003},
+    {"ARM_CORE1_OPTION", ARM_CORE1_OPTION, 0x01010001},
+    {"ARM_COMMON_OPTION", ARM_COMMON_OPTION, 0x00000001},
+    {"ARM_CPU_L2_0_CONFIGURATION", ARM_CPU_L2_0_CONFIGURATION, 0x00000003},
+    {"ARM_CPU_L2_0_STATUS", ARM_CPU_L2_0_STATUS, 0x00000003},
+    {"ARM_CPU_L2_1_CONFIGURATION", ARM_CPU_L2_1_CONFIGURATION, 0x00000003},
+    {"ARM_CPU_L2_1_STATUS", ARM_CPU_L2_1_STATUS, 0x00000003},
+    {"PAD_RETENTION_MAUDIO_OPTION", PAD_RETENTION_MAUDIO_OPTION, 0x00000000},
+    {"PAD_RETENTION_GPIO_OPTION", PAD_RETENTION_GPIO_OPTION, 0x00000000},
+    {"PAD_RETENTION_UART_OPTION", PAD_RETENTION_UART_OPTION, 0x00000000},
+    {"PAD_RETENTION_MMCA_OPTION", PAD_RETENTION_MMCA_OPTION, 0x00000000},
+    {"PAD_RETENTION_MMCB_OPTION", PAD_RETENTION_MMCB_OPTION, 0x00000000},
+    {"PAD_RETENTION_EBIA_OPTION", PAD_RETENTION_EBIA_OPTION, 0x00000000},
+    {"PAD_RETENTION_EBIB_OPTION", PAD_RETENTION_EBIB_OPTION, 0x00000000},
+    {"PS_HOLD_CONTROL", PS_HOLD_CONTROL, 0x00005200},
+    {"XUSBXTI_CONFIGURATION", XUSBXTI_CONFIGURATION, 0x00000001},
+    {"XUSBXTI_STATUS", XUSBXTI_STATUS, 0x00000001},
+    {"XUSBXTI_DURATION", XUSBXTI_DURATION, 0xFFF00000},
+    {"XXTI_CONFIGURATION", XXTI_CONFIGURATION, 0x00000001},
+    {"XXTI_STATUS", XXTI_STATUS, 0x00000001},
+    {"XXTI_DURATION", XXTI_DURATION, 0xFFF00000},
+    {"EXT_REGULATOR_DURATION", EXT_REGULATOR_DURATION, 0xFFF03FFF},
+    {"CAM_CONFIGURATION", CAM_CONFIGURATION, 0x00000007},
+    {"CAM_STATUS", CAM_STATUS, 0x00060007},
+    {"CAM_OPTION", CAM_OPTION, 0x00000001},
+    {"TV_CONFIGURATION", TV_CONFIGURATION, 0x00000007},
+    {"TV_STATUS", TV_STATUS, 0x00060007},
+    {"TV_OPTION", TV_OPTION, 0x00000001},
+    {"MFC_CONFIGURATION", MFC_CONFIGURATION, 0x00000007},
+    {"MFC_STATUS", MFC_STATUS, 0x00060007},
+    {"MFC_OPTION", MFC_OPTION, 0x00000001},
+    {"G3D_CONFIGURATION", G3D_CONFIGURATION, 0x00000007},
+    {"G3D_STATUS", G3D_STATUS, 0x00060007},
+    {"G3D_OPTION", G3D_OPTION, 0x00000001},
+    {"LCD0_CONFIGURATION", LCD0_CONFIGURATION, 0x00000007},
+    {"LCD0_STATUS", LCD0_STATUS, 0x00060007},
+    {"LCD0_OPTION", LCD0_OPTION, 0x00000001},
+    {"LCD1_CONFIGURATION", LCD1_CONFIGURATION, 0x00000007},
+    {"LCD1_STATUS", LCD1_STATUS, 0x00060007},
+    {"LCD1_OPTION", LCD1_OPTION, 0x00000001},
+    {"GPS_CONFIGURATION", GPS_CONFIGURATION, 0x00000007},
+    {"GPS_STATUS", GPS_STATUS, 0x00060007},
+    {"GPS_OPTION", GPS_OPTION, 0x00000001},
+    {"GPS_ALIVE_CONFIGURATION", GPS_ALIVE_CONFIGURATION, 0x00000007},
+    {"GPS_ALIVE_STATUS", GPS_ALIVE_STATUS, 0x00060007},
+    {"GPS_ALIVE_OPTION", GPS_ALIVE_OPTION, 0x00000001},
+};
+
+#define PMU_NUM_OF_REGISTERS     \
+    (sizeof(exynos4210_pmu_regs) / sizeof(Exynos4210PmuReg))
+
+typedef struct Exynos4210PmuState {
+    SysBusDevice busdev;
+    MemoryRegion iomem;
+    uint32_t reg[PMU_NUM_OF_REGISTERS];
+} Exynos4210PmuState;
+
+static uint64_t exynos4210_pmu_read(void *opaque, target_phys_addr_t offset,
+                                    unsigned size)
+{
+    Exynos4210PmuState *s = (Exynos4210PmuState *)opaque;
+    unsigned i;
+    const Exynos4210PmuReg *reg_p = exynos4210_pmu_regs;
+
+    for (i = 0; i < PMU_NUM_OF_REGISTERS; i++) {
+        if (reg_p->offset == offset) {
+            PRINT_DEBUG_EXTEND("%s [0x%04x] -> 0x%04x\n", reg_p->name,
+                                   (uint32_t)offset, s->reg[i]);
+            return s->reg[i];
+        }
+        reg_p++;
+    }
+    PRINT_DEBUG("QEMU PMU ERROR: bad read offset 0x%04x\n", (uint32_t)offset);
+    return 0;
+}
+
+static void exynos4210_pmu_write(void *opaque, target_phys_addr_t offset,
+                                 uint64_t val, unsigned size)
+{
+    Exynos4210PmuState *s = (Exynos4210PmuState *)opaque;
+    unsigned i;
+    const Exynos4210PmuReg *reg_p = exynos4210_pmu_regs;
+
+    for (i = 0; i < PMU_NUM_OF_REGISTERS; i++) {
+        if (reg_p->offset == offset) {
+            PRINT_DEBUG_EXTEND("%s <0x%04x> <- 0x%04x\n", reg_p->name,
+                    (uint32_t)offset, (uint32_t)val);
+            s->reg[i] = val;
+            return;
+        }
+        reg_p++;
+    }
+    PRINT_DEBUG("QEMU PMU ERROR: bad write offset 0x%04x\n", (uint32_t)offset);
+}
+
+static const MemoryRegionOps exynos4210_pmu_ops = {
+    .read = exynos4210_pmu_read,
+    .write = exynos4210_pmu_write,
+    .endianness = DEVICE_NATIVE_ENDIAN,
+    .valid = {
+        .min_access_size = 4,
+        .max_access_size = 4,
+        .unaligned = false
+    }
+};
+
+static void exynos4210_pmu_reset(DeviceState *dev)
+{
+    Exynos4210PmuState *s =
+            container_of(dev, Exynos4210PmuState, busdev.qdev);
+    unsigned i;
+
+    /* Set default values for registers */
+    for (i = 0; i < PMU_NUM_OF_REGISTERS; i++) {
+        s->reg[i] = exynos4210_pmu_regs[i].reset_value;
+    }
+}
+
+static int exynos4210_pmu_init(SysBusDevice *dev)
+{
+    Exynos4210PmuState *s = FROM_SYSBUS(Exynos4210PmuState, dev);
+
+    /* memory mapping */
+    memory_region_init_io(&s->iomem, &exynos4210_pmu_ops, s, "exynos4210.pmu",
+                          EXYNOS4210_PMU_REGS_MEM_SIZE);
+    sysbus_init_mmio(dev, &s->iomem);
+    return 0;
+}
+
+static const VMStateDescription exynos4210_pmu_vmstate = {
+    .name = "exynos4210.pmu",
+    .version_id = 1,
+    .minimum_version_id = 1,
+    .fields      = (VMStateField[]) {
+        VMSTATE_UINT32_ARRAY(reg, Exynos4210PmuState, PMU_NUM_OF_REGISTERS),
+        VMSTATE_END_OF_LIST()
+    }
+};
+
+static void exynos4210_pmu_class_init(ObjectClass *klass, void *data)
+{
+    DeviceClass *dc = DEVICE_CLASS(klass);
+    SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass);
+
+    k->init = exynos4210_pmu_init;
+    dc->reset = exynos4210_pmu_reset;
+    dc->vmsd = &exynos4210_pmu_vmstate;
+}
+
+static TypeInfo exynos4210_pmu_info = {
+    .name          = "exynos4210.pmu",
+    .parent        = TYPE_SYS_BUS_DEVICE,
+    .instance_size = sizeof(Exynos4210PmuState),
+    .class_init    = exynos4210_pmu_class_init,
+};
+
+static void exynos4210_pmu_register(void)
+{
+    type_register_static(&exynos4210_pmu_info);
+}
+
+type_init(exynos4210_pmu_register)
-- 
1.7.1

^ permalink raw reply related	[flat|nested] 24+ messages in thread

* [Qemu-devel] [PATCH 06/22] ARM: exynos4210: MCT support.
  2012-02-17 11:29 [Qemu-devel] [PULL 00/22] arm-devs queue Peter Maydell
                   ` (4 preceding siblings ...)
  2012-02-17 11:29 ` [Qemu-devel] [PATCH 05/22] ARM: exynos4210: basic Power Management Unit implementation Peter Maydell
@ 2012-02-17 11:29 ` Peter Maydell
  2012-02-17 11:29 ` [Qemu-devel] [PATCH 07/22] hw/lan9118: Add basic 16-bit mode support Peter Maydell
                   ` (16 subsequent siblings)
  22 siblings, 0 replies; 24+ messages in thread
From: Peter Maydell @ 2012-02-17 11:29 UTC (permalink / raw)
  To: Anthony Liguori; +Cc: qemu-devel, Paul Brook

From: Evgeny Voevodin <e.voevodin@samsung.com>

Signed-off-by: Evgeny Voevodin <e.voevodin@samsung.com>
Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
---
 Makefile.target     |    2 +-
 hw/exynos4210.c     |   19 +
 hw/exynos4210_mct.c | 1488 +++++++++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 1508 insertions(+), 1 deletions(-)
 create mode 100644 hw/exynos4210_mct.c

diff --git a/Makefile.target b/Makefile.target
index 8a7b4d2..6ceff54 100644
--- a/Makefile.target
+++ b/Makefile.target
@@ -346,7 +346,7 @@ obj-arm-y += versatile_pci.o
 obj-arm-y += realview_gic.o realview.o arm_sysctl.o arm11mpcore.o a9mpcore.o
 obj-arm-y += exynos4210_gic.o exynos4210_combiner.o exynos4210.o
 obj-arm-y += exynos4_boards.o exynos4210_uart.o exynos4210_pwm.o
-obj-arm-y += exynos4210_pmu.o
+obj-arm-y += exynos4210_pmu.o exynos4210_mct.o
 obj-arm-y += arm_l2x0.o
 obj-arm-y += arm_mptimer.o
 obj-arm-y += armv7m.o armv7m_nvic.o stellaris.o pl022.o stellaris_enet.o
diff --git a/hw/exynos4210.c b/hw/exynos4210.c
index f922f55..558f669 100644
--- a/hw/exynos4210.c
+++ b/hw/exynos4210.c
@@ -32,6 +32,9 @@
 /* PWM */
 #define EXYNOS4210_PWM_BASE_ADDR       0x139D0000
 
+/* MCT */
+#define EXYNOS4210_MCT_BASE_ADDR       0x10050000
+
 /* UART's definitions */
 #define EXYNOS4210_UART0_BASE_ADDR     0x13800000
 #define EXYNOS4210_UART1_BASE_ADDR     0x13810000
@@ -220,6 +223,22 @@ Exynos4210State *exynos4210_init(MemoryRegion *system_mem,
                           s->irq_table[exynos4210_get_irq(22, 4)],
                           NULL);
 
+    /* Multi Core Timer */
+    dev = qdev_create(NULL, "exynos4210.mct");
+    qdev_init_nofail(dev);
+    busdev = sysbus_from_qdev(dev);
+    for (n = 0; n < 4; n++) {
+        /* Connect global timer interrupts to Combiner gpio_in */
+        sysbus_connect_irq(busdev, n,
+                s->irq_table[exynos4210_get_irq(1, 4 + n)]);
+    }
+    /* Connect local timer interrupts to Combiner gpio_in */
+    sysbus_connect_irq(busdev, 4,
+            s->irq_table[exynos4210_get_irq(51, 0)]);
+    sysbus_connect_irq(busdev, 5,
+            s->irq_table[exynos4210_get_irq(35, 3)]);
+    sysbus_mmio_map(busdev, 0, EXYNOS4210_MCT_BASE_ADDR);
+
     /*** UARTs ***/
     exynos4210_uart_create(EXYNOS4210_UART0_BASE_ADDR,
                            EXYNOS4210_UART0_FIFO_SIZE, 0, NULL,
diff --git a/hw/exynos4210_mct.c b/hw/exynos4210_mct.c
new file mode 100644
index 0000000..01e3fb8
--- /dev/null
+++ b/hw/exynos4210_mct.c
@@ -0,0 +1,1488 @@
+/*
+ * Samsung exynos4210 Multi Core timer
+ *
+ * Copyright (c) 2000 - 2011 Samsung Electronics Co., Ltd.
+ * All rights reserved.
+ *
+ * Evgeny Voevodin <e.voevodin@samsung.com>
+ *
+ * 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, see <http://www.gnu.org/licenses/>.
+ */
+
+/*
+ * Global Timer:
+ *
+ * Consists of two timers. First represents Free Running Counter and second
+ * is used to measure interval from FRC to nearest comparator.
+ *
+ *        0                                                           UINT64_MAX
+ *        |                              timer0                             |
+ *        | <-------------------------------------------------------------- |
+ *        | --------------------------------------------frc---------------> |
+ *        |______________________________________________|__________________|
+ *                CMP0          CMP1             CMP2    |           CMP3
+ *                                                     __|            |_
+ *                                                     |     timer1     |
+ *                                                     | -------------> |
+ *                                                    frc              CMPx
+ *
+ * Problem: when implementing global timer as is, overflow arises.
+ * next_time = cur_time + period * count;
+ * period and count are 64 bits width.
+ * Lets arm timer for MCT_GT_COUNTER_STEP count and update internal G_CNT
+ * register during each event.
+ *
+ * Problem: both timers need to be implemented using MCT_XT_COUNTER_STEP because
+ * local timer contains two counters: TCNT and ICNT. TCNT == 0 -> ICNT--.
+ * IRQ is generated when ICNT riches zero. Implementation where TCNT == 0
+ * generates IRQs suffers from too frequently events. Better to have one
+ * uint64_t counter equal to TCNT*ICNT and arm ptimer.c for a minimum(TCNT*ICNT,
+ * MCT_GT_COUNTER_STEP); (yes, if target tunes ICNT * TCNT to be too low values,
+ * there is no way to avoid frequently events).
+ */
+
+#include "sysbus.h"
+#include "qemu-timer.h"
+#include "qemu-common.h"
+#include "ptimer.h"
+
+#include "exynos4210.h"
+
+//#define DEBUG_MCT
+
+#ifdef DEBUG_MCT
+#define DPRINTF(fmt, ...) \
+        do { fprintf(stdout, "MCT: [%24s:%5d] " fmt, __func__, __LINE__, \
+                     ## __VA_ARGS__); } while (0)
+#else
+#define DPRINTF(fmt, ...) do {} while (0)
+#endif
+
+#define    MCT_CFG          0x000
+#define    G_CNT_L          0x100
+#define    G_CNT_U          0x104
+#define    G_CNT_WSTAT      0x110
+#define    G_COMP0_L        0x200
+#define    G_COMP0_U        0x204
+#define    G_COMP0_ADD_INCR 0x208
+#define    G_COMP1_L        0x210
+#define    G_COMP1_U        0x214
+#define    G_COMP1_ADD_INCR 0x218
+#define    G_COMP2_L        0x220
+#define    G_COMP2_U        0x224
+#define    G_COMP2_ADD_INCR 0x228
+#define    G_COMP3_L        0x230
+#define    G_COMP3_U        0x234
+#define    G_COMP3_ADD_INCR 0x238
+#define    G_TCON           0x240
+#define    G_INT_CSTAT      0x244
+#define    G_INT_ENB        0x248
+#define    G_WSTAT          0x24C
+#define    L0_TCNTB         0x300
+#define    L0_TCNTO         0x304
+#define    L0_ICNTB         0x308
+#define    L0_ICNTO         0x30C
+#define    L0_FRCNTB        0x310
+#define    L0_FRCNTO        0x314
+#define    L0_TCON          0x320
+#define    L0_INT_CSTAT     0x330
+#define    L0_INT_ENB       0x334
+#define    L0_WSTAT         0x340
+#define    L1_TCNTB         0x400
+#define    L1_TCNTO         0x404
+#define    L1_ICNTB         0x408
+#define    L1_ICNTO         0x40C
+#define    L1_FRCNTB        0x410
+#define    L1_FRCNTO        0x414
+#define    L1_TCON          0x420
+#define    L1_INT_CSTAT     0x430
+#define    L1_INT_ENB       0x434
+#define    L1_WSTAT         0x440
+
+#define MCT_CFG_GET_PRESCALER(x)    ((x) & 0xFF)
+#define MCT_CFG_GET_DIVIDER(x)      (1 << ((x) >> 8 & 7))
+
+#define GET_G_COMP_IDX(offset)          (((offset) - G_COMP0_L) / 0x10)
+#define GET_G_COMP_ADD_INCR_IDX(offset) (((offset) - G_COMP0_ADD_INCR) / 0x10)
+
+#define G_COMP_L(x) (G_COMP0_L + (x) * 0x10)
+#define G_COMP_U(x) (G_COMP0_U + (x) * 0x10)
+
+#define G_COMP_ADD_INCR(x)  (G_COMP0_ADD_INCR + (x) * 0x10)
+
+/* MCT bits */
+#define G_TCON_COMP_ENABLE(x)   (1 << 2 * (x))
+#define G_TCON_AUTO_ICREMENT(x) (1 << (2 * (x) + 1))
+#define G_TCON_TIMER_ENABLE     (1 << 8)
+
+#define G_INT_ENABLE(x)         (1 << (x))
+#define G_INT_CSTAT_COMP(x)     (1 << (x))
+
+#define G_CNT_WSTAT_L           1
+#define G_CNT_WSTAT_U           2
+
+#define G_WSTAT_COMP_L(x)       (1 << 4 * (x))
+#define G_WSTAT_COMP_U(x)       (1 << ((4 * (x)) + 1))
+#define G_WSTAT_COMP_ADDINCR(x) (1 << ((4 * (x)) + 2))
+#define G_WSTAT_TCON_WRITE      (1 << 16)
+
+#define GET_L_TIMER_IDX(offset) ((((offset) & 0xF00) - L0_TCNTB) / 0x100)
+#define GET_L_TIMER_CNT_REG_IDX(offset, lt_i) \
+        (((offset) - (L0_TCNTB + 0x100 * (lt_i))) >> 2)
+
+#define L_ICNTB_MANUAL_UPDATE   (1 << 31)
+
+#define L_TCON_TICK_START       (1)
+#define L_TCON_INT_START        (1 << 1)
+#define L_TCON_INTERVAL_MODE    (1 << 2)
+#define L_TCON_FRC_START        (1 << 3)
+
+#define L_INT_CSTAT_INTCNT      (1 << 0)
+#define L_INT_CSTAT_FRCCNT      (1 << 1)
+
+#define L_INT_INTENB_ICNTEIE    (1 << 0)
+#define L_INT_INTENB_FRCEIE     (1 << 1)
+
+#define L_WSTAT_TCNTB_WRITE     (1 << 0)
+#define L_WSTAT_ICNTB_WRITE     (1 << 1)
+#define L_WSTAT_FRCCNTB_WRITE   (1 << 2)
+#define L_WSTAT_TCON_WRITE      (1 << 3)
+
+enum LocalTimerRegCntIndexes {
+    L_REG_CNT_TCNTB,
+    L_REG_CNT_TCNTO,
+    L_REG_CNT_ICNTB,
+    L_REG_CNT_ICNTO,
+    L_REG_CNT_FRCCNTB,
+    L_REG_CNT_FRCCNTO,
+
+    L_REG_CNT_AMOUNT
+};
+
+#define MCT_NIRQ                6
+#define MCT_SFR_SIZE            0x444
+
+#define MCT_GT_CMP_NUM          4
+
+#define MCT_GT_MAX_VAL          UINT64_MAX
+
+#define MCT_GT_COUNTER_STEP     0x100000000ULL
+#define MCT_LT_COUNTER_STEP     0x100000000ULL
+#define MCT_LT_CNT_LOW_LIMIT    0x100
+
+/* global timer */
+typedef struct {
+    qemu_irq  irq[MCT_GT_CMP_NUM];
+
+    struct gregs {
+        uint64_t cnt;
+        uint32_t cnt_wstat;
+        uint32_t tcon;
+        uint32_t int_cstat;
+        uint32_t int_enb;
+        uint32_t wstat;
+        uint64_t comp[MCT_GT_CMP_NUM];
+        uint32_t comp_add_incr[MCT_GT_CMP_NUM];
+    } reg;
+
+    uint64_t count;            /* Value FRC was armed with */
+    int32_t curr_comp;             /* Current comparator FRC is running to */
+
+    ptimer_state *ptimer_frc;                   /* FRC timer */
+
+} Exynos4210MCTGT;
+
+/* local timer */
+typedef struct {
+    int         id;             /* timer id */
+    qemu_irq    irq;            /* local timer irq */
+
+    struct tick_timer {
+        uint32_t cnt_run;           /* cnt timer is running */
+        uint32_t int_run;           /* int timer is running */
+
+        uint32_t last_icnto;
+        uint32_t last_tcnto;
+        uint32_t tcntb;             /* initial value for TCNTB */
+        uint32_t icntb;             /* initial value for ICNTB */
+
+        /* for step mode */
+        uint64_t    distance;       /* distance to count to the next event */
+        uint64_t    progress;       /* progress when counting by steps */
+        uint64_t    count;          /* count to arm timer with */
+
+        ptimer_state *ptimer_tick;  /* timer for tick counter */
+    } tick_timer;
+
+    /* use ptimer.c to represent count down timer */
+
+    ptimer_state *ptimer_frc;   /* timer for free running counter */
+
+    /* registers */
+    struct lregs {
+        uint32_t    cnt[L_REG_CNT_AMOUNT];
+        uint32_t    tcon;
+        uint32_t    int_cstat;
+        uint32_t    int_enb;
+        uint32_t    wstat;
+    } reg;
+
+} Exynos4210MCTLT;
+
+typedef struct Exynos4210MCTState {
+    SysBusDevice busdev;
+    MemoryRegion iomem;
+
+    /* Registers */
+    uint32_t    reg_mct_cfg;
+
+    Exynos4210MCTLT l_timer[2];
+    Exynos4210MCTGT g_timer;
+
+    uint32_t    freq;                   /* all timers tick frequency, TCLK */
+} Exynos4210MCTState;
+
+/*** VMState ***/
+static const VMStateDescription vmstate_tick_timer = {
+    .name = "exynos4210.mct.tick_timer",
+    .version_id = 1,
+    .minimum_version_id = 1,
+    .minimum_version_id_old = 1,
+    .fields = (VMStateField[]) {
+        VMSTATE_UINT32(cnt_run, struct tick_timer),
+        VMSTATE_UINT32(int_run, struct tick_timer),
+        VMSTATE_UINT32(last_icnto, struct tick_timer),
+        VMSTATE_UINT32(last_tcnto, struct tick_timer),
+        VMSTATE_UINT32(tcntb, struct tick_timer),
+        VMSTATE_UINT32(icntb, struct tick_timer),
+        VMSTATE_UINT64(distance, struct tick_timer),
+        VMSTATE_UINT64(progress, struct tick_timer),
+        VMSTATE_UINT64(count, struct tick_timer),
+        VMSTATE_PTIMER(ptimer_tick, struct tick_timer),
+        VMSTATE_END_OF_LIST()
+    }
+};
+
+static const VMStateDescription vmstate_lregs = {
+    .name = "exynos4210.mct.lregs",
+    .version_id = 1,
+    .minimum_version_id = 1,
+    .minimum_version_id_old = 1,
+    .fields = (VMStateField[]) {
+        VMSTATE_UINT32_ARRAY(cnt, struct lregs, L_REG_CNT_AMOUNT),
+        VMSTATE_UINT32(tcon, struct lregs),
+        VMSTATE_UINT32(int_cstat, struct lregs),
+        VMSTATE_UINT32(int_enb, struct lregs),
+        VMSTATE_UINT32(wstat, struct lregs),
+        VMSTATE_END_OF_LIST()
+    }
+};
+
+static const VMStateDescription vmstate_exynos4210_mct_lt = {
+    .name = "exynos4210.mct.lt",
+    .version_id = 1,
+    .minimum_version_id = 1,
+    .minimum_version_id_old = 1,
+    .fields = (VMStateField[]) {
+        VMSTATE_INT32(id, Exynos4210MCTLT),
+        VMSTATE_STRUCT(tick_timer, Exynos4210MCTLT, 0,
+                vmstate_tick_timer,
+                struct tick_timer),
+        VMSTATE_PTIMER(ptimer_frc, Exynos4210MCTLT),
+        VMSTATE_STRUCT(reg, Exynos4210MCTLT, 0,
+                vmstate_lregs,
+                struct lregs),
+        VMSTATE_END_OF_LIST()
+    }
+};
+
+static const VMStateDescription vmstate_gregs = {
+    .name = "exynos4210.mct.lregs",
+    .version_id = 1,
+    .minimum_version_id = 1,
+    .minimum_version_id_old = 1,
+    .fields = (VMStateField[]) {
+        VMSTATE_UINT64(cnt, struct gregs),
+        VMSTATE_UINT32(cnt_wstat, struct gregs),
+        VMSTATE_UINT32(tcon, struct gregs),
+        VMSTATE_UINT32(int_cstat, struct gregs),
+        VMSTATE_UINT32(int_enb, struct gregs),
+        VMSTATE_UINT32(wstat, struct gregs),
+        VMSTATE_UINT64_ARRAY(comp, struct gregs, MCT_GT_CMP_NUM),
+        VMSTATE_UINT32_ARRAY(comp_add_incr, struct gregs,
+                MCT_GT_CMP_NUM),
+        VMSTATE_END_OF_LIST()
+    }
+};
+
+static const VMStateDescription vmstate_exynos4210_mct_gt = {
+    .name = "exynos4210.mct.lt",
+    .version_id = 1,
+    .minimum_version_id = 1,
+    .minimum_version_id_old = 1,
+    .fields = (VMStateField[]) {
+        VMSTATE_STRUCT(reg, Exynos4210MCTGT, 0, vmstate_gregs,
+                struct gregs),
+        VMSTATE_UINT64(count, Exynos4210MCTGT),
+        VMSTATE_INT32(curr_comp, Exynos4210MCTGT),
+        VMSTATE_PTIMER(ptimer_frc, Exynos4210MCTGT),
+        VMSTATE_END_OF_LIST()
+    }
+};
+
+static const VMStateDescription vmstate_exynos4210_mct_state = {
+    .name = "exynos4210.mct",
+    .version_id = 1,
+    .minimum_version_id = 1,
+    .minimum_version_id_old = 1,
+    .fields = (VMStateField[]) {
+        VMSTATE_UINT32(reg_mct_cfg, Exynos4210MCTState),
+        VMSTATE_STRUCT_ARRAY(l_timer, Exynos4210MCTState, 2, 0,
+            vmstate_exynos4210_mct_lt, Exynos4210MCTLT),
+        VMSTATE_STRUCT(g_timer, Exynos4210MCTState, 0,
+            vmstate_exynos4210_mct_gt, Exynos4210MCTGT),
+        VMSTATE_UINT32(freq, Exynos4210MCTState),
+        VMSTATE_END_OF_LIST()
+    }
+};
+
+static void exynos4210_mct_update_freq(Exynos4210MCTState *s);
+
+/*
+ * Set counter of FRC global timer.
+ */
+static void exynos4210_gfrc_set_count(Exynos4210MCTGT *s, uint64_t count)
+{
+    s->count = count;
+    DPRINTF("global timer frc set count 0x%llx\n", count);
+    ptimer_set_count(s->ptimer_frc, count);
+}
+
+/*
+ * Get counter of FRC global timer.
+ */
+static uint64_t exynos4210_gfrc_get_count(Exynos4210MCTGT *s)
+{
+    uint64_t count = 0;
+    count = ptimer_get_count(s->ptimer_frc);
+    if (!count) {
+        /* Timer event was generated and s->reg.cnt holds adequate value */
+        return s->reg.cnt;
+    }
+    count = s->count - count;
+    return s->reg.cnt + count;
+}
+
+/*
+ * Stop global FRC timer
+ */
+static void exynos4210_gfrc_stop(Exynos4210MCTGT *s)
+{
+    DPRINTF("global timer frc stop\n");
+
+    ptimer_stop(s->ptimer_frc);
+}
+
+/*
+ * Start global FRC timer
+ */
+static void exynos4210_gfrc_start(Exynos4210MCTGT *s)
+{
+    DPRINTF("global timer frc start\n");
+
+    ptimer_run(s->ptimer_frc, 1);
+}
+
+/*
+ * Find next nearest Comparator. If current Comparator value equals to other
+ * Comparator value, skip them both
+ */
+static int32_t exynos4210_gcomp_find(Exynos4210MCTState *s)
+{
+    int res;
+    int i;
+    int enabled;
+    uint64_t min;
+    int min_comp_i;
+    uint64_t gfrc;
+    uint64_t distance;
+    uint64_t distance_min;
+    int comp_i;
+
+    /* get gfrc count */
+    gfrc = exynos4210_gfrc_get_count(&s->g_timer);
+
+    min = UINT64_MAX;
+    distance_min = UINT64_MAX;
+    comp_i = MCT_GT_CMP_NUM;
+    min_comp_i = MCT_GT_CMP_NUM;
+    enabled = 0;
+
+    /* lookup for nearest comparator */
+    for (i = 0; i < MCT_GT_CMP_NUM; i++) {
+
+        if (s->g_timer.reg.tcon & G_TCON_COMP_ENABLE(i)) {
+
+            enabled = 1;
+
+            if (s->g_timer.reg.comp[i] > gfrc) {
+                /* Comparator is upper then FRC */
+                distance = s->g_timer.reg.comp[i] - gfrc;
+
+                if (distance <= distance_min) {
+                    distance_min = distance;
+                    comp_i = i;
+                }
+            } else {
+                /* Comparator is below FRC, find the smallest */
+
+                if (s->g_timer.reg.comp[i] <= min) {
+                    min = s->g_timer.reg.comp[i];
+                    min_comp_i = i;
+                }
+            }
+        }
+    }
+
+    if (!enabled) {
+        /* All Comparators disabled */
+        res = -1;
+    } else if (comp_i < MCT_GT_CMP_NUM) {
+        /* Found upper Comparator */
+        res = comp_i;
+    } else {
+        /* All Comparators are below or equal to FRC  */
+        res = min_comp_i;
+    }
+
+    DPRINTF("found comparator %d: comp 0x%llx distance 0x%llx, gfrc 0x%llx\n",
+            res,
+            s->g_timer.reg.comp[res],
+            distance_min,
+            gfrc);
+
+    return res;
+}
+
+/*
+ * Get distance to nearest Comparator
+ */
+static uint64_t exynos4210_gcomp_get_distance(Exynos4210MCTState *s, int32_t id)
+{
+    if (id == -1) {
+        /* no enabled Comparators, choose max distance */
+        return MCT_GT_COUNTER_STEP;
+    }
+    if (s->g_timer.reg.comp[id] - s->g_timer.reg.cnt < MCT_GT_COUNTER_STEP) {
+        return s->g_timer.reg.comp[id] - s->g_timer.reg.cnt;
+    } else {
+        return MCT_GT_COUNTER_STEP;
+    }
+}
+
+/*
+ * Restart global FRC timer
+ */
+static void exynos4210_gfrc_restart(Exynos4210MCTState *s)
+{
+    uint64_t distance;
+
+    exynos4210_gfrc_stop(&s->g_timer);
+
+    s->g_timer.curr_comp = exynos4210_gcomp_find(s);
+
+    distance = exynos4210_gcomp_get_distance(s, s->g_timer.curr_comp);
+
+    if (distance > MCT_GT_COUNTER_STEP || !distance) {
+        distance = MCT_GT_COUNTER_STEP;
+    }
+
+    exynos4210_gfrc_set_count(&s->g_timer, distance);
+    exynos4210_gfrc_start(&s->g_timer);
+}
+
+/*
+ * Raise global timer CMP IRQ
+ */
+static void exynos4210_gcomp_raise_irq(void *opaque, uint32_t id)
+{
+    Exynos4210MCTGT *s = opaque;
+
+    /* If CSTAT is pending and IRQ is enabled */
+    if ((s->reg.int_cstat & G_INT_CSTAT_COMP(id)) &&
+            (s->reg.int_enb & G_INT_ENABLE(id))) {
+        DPRINTF("gcmp timer[%d] IRQ\n", id);
+        qemu_irq_raise(s->irq[id]);
+    }
+}
+
+/*
+ * Lower global timer CMP IRQ
+ */
+static void exynos4210_gcomp_lower_irq(void *opaque, uint32_t id)
+{
+    Exynos4210MCTGT *s = opaque;
+    qemu_irq_lower(s->irq[id]);
+}
+
+/*
+ * Global timer FRC event handler.
+ * Each event occurs when internal counter reaches counter + MCT_GT_COUNTER_STEP
+ * Every time we arm global FRC timer to count for MCT_GT_COUNTER_STEP value
+ */
+static void exynos4210_gfrc_event(void *opaque)
+{
+    Exynos4210MCTState *s = (Exynos4210MCTState *)opaque;
+    int i;
+    uint64_t distance;
+
+    DPRINTF("\n");
+
+    s->g_timer.reg.cnt += s->g_timer.count;
+
+    /* Process all comparators */
+    for (i = 0; i < MCT_GT_CMP_NUM; i++) {
+
+        if (s->g_timer.reg.cnt == s->g_timer.reg.comp[i]) {
+            /* reached nearest comparator */
+
+            s->g_timer.reg.int_cstat |= G_INT_CSTAT_COMP(i);
+
+            /* Auto increment */
+            if (s->g_timer.reg.tcon & G_TCON_AUTO_ICREMENT(i)) {
+                s->g_timer.reg.comp[i] += s->g_timer.reg.comp_add_incr[i];
+            }
+
+            /* IRQ */
+            exynos4210_gcomp_raise_irq(&s->g_timer, i);
+        }
+    }
+
+    /* Reload FRC to reach nearest comparator */
+    s->g_timer.curr_comp = exynos4210_gcomp_find(s);
+    distance = exynos4210_gcomp_get_distance(s, s->g_timer.curr_comp);
+    if (distance > MCT_GT_COUNTER_STEP) {
+        distance = MCT_GT_COUNTER_STEP;
+    }
+    exynos4210_gfrc_set_count(&s->g_timer, distance);
+
+    exynos4210_gfrc_start(&s->g_timer);
+
+    return;
+}
+
+/*
+ * Get counter of FRC local timer.
+ */
+static uint64_t exynos4210_lfrc_get_count(Exynos4210MCTLT *s)
+{
+    return ptimer_get_count(s->ptimer_frc);
+}
+
+/*
+ * Set counter of FRC local timer.
+ */
+static void exynos4210_lfrc_update_count(Exynos4210MCTLT *s)
+{
+    if (!s->reg.cnt[L_REG_CNT_FRCCNTB]) {
+        ptimer_set_count(s->ptimer_frc, MCT_LT_COUNTER_STEP);
+    } else {
+        ptimer_set_count(s->ptimer_frc, s->reg.cnt[L_REG_CNT_FRCCNTB]);
+    }
+}
+
+/*
+ * Start local FRC timer
+ */
+static void exynos4210_lfrc_start(Exynos4210MCTLT *s)
+{
+    ptimer_run(s->ptimer_frc, 1);
+}
+
+/*
+ * Stop local FRC timer
+ */
+static void exynos4210_lfrc_stop(Exynos4210MCTLT *s)
+{
+    ptimer_stop(s->ptimer_frc);
+}
+
+/*
+ * Local timer free running counter tick handler
+ */
+static void exynos4210_lfrc_event(void *opaque)
+{
+    Exynos4210MCTLT * s = (Exynos4210MCTLT *)opaque;
+
+    /* local frc expired */
+
+    DPRINTF("\n");
+
+    s->reg.int_cstat |= L_INT_CSTAT_FRCCNT;
+
+    /* update frc counter */
+    exynos4210_lfrc_update_count(s);
+
+    /* raise irq */
+    if (s->reg.int_enb & L_INT_INTENB_FRCEIE) {
+        qemu_irq_raise(s->irq);
+    }
+
+    /*  we reached here, this means that timer is enabled */
+    exynos4210_lfrc_start(s);
+}
+
+static uint32_t exynos4210_ltick_int_get_cnto(struct tick_timer *s);
+static uint32_t exynos4210_ltick_cnt_get_cnto(struct tick_timer *s);
+static void exynos4210_ltick_recalc_count(struct tick_timer *s);
+
+/*
+ * Action on enabling local tick int timer
+ */
+static void exynos4210_ltick_int_start(struct tick_timer *s)
+{
+    if (!s->int_run) {
+        s->int_run = 1;
+    }
+}
+
+/*
+ * Action on disabling local tick int timer
+ */
+static void exynos4210_ltick_int_stop(struct tick_timer *s)
+{
+    if (s->int_run) {
+        s->last_icnto = exynos4210_ltick_int_get_cnto(s);
+        s->int_run = 0;
+    }
+}
+
+/*
+ * Get count for INT timer
+ */
+static uint32_t exynos4210_ltick_int_get_cnto(struct tick_timer *s)
+{
+    uint32_t icnto;
+    uint64_t remain;
+    uint64_t count;
+    uint64_t counted;
+    uint64_t cur_progress;
+
+    count = ptimer_get_count(s->ptimer_tick);
+    if (count) {
+        /* timer is still counting, called not from event */
+        counted = s->count - ptimer_get_count(s->ptimer_tick);
+        cur_progress = s->progress + counted;
+    } else {
+        /* timer expired earlier */
+        cur_progress = s->progress;
+    }
+
+    remain = s->distance - cur_progress;
+
+    if (!s->int_run) {
+        /* INT is stopped. */
+        icnto = s->last_icnto;
+    } else {
+        /* Both are counting */
+        icnto = remain / s->tcntb;
+    }
+
+    return icnto;
+}
+
+/*
+ * Start local tick cnt timer.
+ */
+static void exynos4210_ltick_cnt_start(struct tick_timer *s)
+{
+    if (!s->cnt_run) {
+
+        exynos4210_ltick_recalc_count(s);
+        ptimer_set_count(s->ptimer_tick, s->count);
+        ptimer_run(s->ptimer_tick, 1);
+
+        s->cnt_run = 1;
+    }
+}
+
+/*
+ * Stop local tick cnt timer.
+ */
+static void exynos4210_ltick_cnt_stop(struct tick_timer *s)
+{
+    if (s->cnt_run) {
+
+        s->last_tcnto = exynos4210_ltick_cnt_get_cnto(s);
+
+        if (s->int_run) {
+            exynos4210_ltick_int_stop(s);
+        }
+
+        ptimer_stop(s->ptimer_tick);
+
+        s->cnt_run = 0;
+    }
+}
+
+/*
+ * Get counter for CNT timer
+ */
+static uint32_t exynos4210_ltick_cnt_get_cnto(struct tick_timer *s)
+{
+    uint32_t tcnto;
+    uint32_t icnto;
+    uint64_t remain;
+    uint64_t counted;
+    uint64_t count;
+    uint64_t cur_progress;
+
+    count = ptimer_get_count(s->ptimer_tick);
+    if (count) {
+        /* timer is still counting, called not from event */
+        counted = s->count - ptimer_get_count(s->ptimer_tick);
+        cur_progress = s->progress + counted;
+    } else {
+        /* timer expired earlier */
+        cur_progress = s->progress;
+    }
+
+    remain = s->distance - cur_progress;
+
+    if (!s->cnt_run) {
+        /* Both are stopped. */
+        tcnto = s->last_tcnto;
+    } else if (!s->int_run) {
+        /* INT counter is stopped, progress is by CNT timer */
+        tcnto = remain % s->tcntb;
+    } else {
+        /* Both are counting */
+        icnto = remain / s->tcntb;
+        if (icnto) {
+            tcnto = remain % (icnto * s->tcntb);
+        } else {
+            tcnto = remain % s->tcntb;
+        }
+    }
+
+    return tcnto;
+}
+
+/*
+ * Set new values of counters for CNT and INT timers
+ */
+static void exynos4210_ltick_set_cntb(struct tick_timer *s, uint32_t new_cnt,
+        uint32_t new_int)
+{
+    uint32_t cnt_stopped = 0;
+    uint32_t int_stopped = 0;
+
+    if (s->cnt_run) {
+        exynos4210_ltick_cnt_stop(s);
+        cnt_stopped = 1;
+    }
+
+    if (s->int_run) {
+        exynos4210_ltick_int_stop(s);
+        int_stopped = 1;
+    }
+
+    s->tcntb = new_cnt + 1;
+    s->icntb = new_int + 1;
+
+    if (cnt_stopped) {
+        exynos4210_ltick_cnt_start(s);
+    }
+    if (int_stopped) {
+        exynos4210_ltick_int_start(s);
+    }
+
+}
+
+/*
+ * Calculate new counter value for tick timer
+ */
+static void exynos4210_ltick_recalc_count(struct tick_timer *s)
+{
+    uint64_t to_count;
+
+    if ((s->cnt_run && s->last_tcnto) || (s->int_run && s->last_icnto)) {
+        /*
+         * one or both timers run and not counted to the end;
+         * distance is not passed, recalculate with last_tcnto * last_icnto
+         */
+
+        if (s->last_tcnto) {
+            to_count = s->last_tcnto * s->last_icnto;
+        } else {
+            to_count = s->last_icnto;
+        }
+    } else {
+        /* distance is passed, recalculate with tcnto * icnto */
+        if (s->icntb) {
+            s->distance = s->tcntb * s->icntb;
+        } else {
+            s->distance = s->tcntb;
+        }
+
+        to_count = s->distance;
+        s->progress = 0;
+    }
+
+    if (to_count > MCT_LT_COUNTER_STEP) {
+        /* count by step */
+        s->count = MCT_LT_COUNTER_STEP;
+    } else {
+        s->count = to_count;
+    }
+}
+
+/*
+ * Initialize tick_timer
+ */
+static void exynos4210_ltick_timer_init(struct tick_timer *s)
+{
+    exynos4210_ltick_int_stop(s);
+    exynos4210_ltick_cnt_stop(s);
+
+    s->count = 0;
+    s->distance = 0;
+    s->progress = 0;
+    s->icntb = 0;
+    s->tcntb = 0;
+}
+
+/*
+ * tick_timer event.
+ * Raises when abstract tick_timer expires.
+ */
+static void exynos4210_ltick_timer_event(struct tick_timer *s)
+{
+    s->progress += s->count;
+}
+
+/*
+ * Local timer tick counter handler.
+ * Don't use reloaded timers. If timer counter = zero
+ * then handler called but after handler finished no
+ * timer reload occurs.
+ */
+static void exynos4210_ltick_event(void *opaque)
+{
+    Exynos4210MCTLT * s = (Exynos4210MCTLT *)opaque;
+    uint32_t tcnto;
+    uint32_t icnto;
+#ifdef DEBUG_MCT
+    static uint64_t time1[2] = {0};
+    static uint64_t time2[2] = {0};
+#endif
+
+    /* Call tick_timer event handler, it will update it's tcntb and icntb */
+    exynos4210_ltick_timer_event(&s->tick_timer);
+
+    /* get tick_timer cnt */
+    tcnto = exynos4210_ltick_cnt_get_cnto(&s->tick_timer);
+
+    /* get tick_timer int */
+    icnto = exynos4210_ltick_int_get_cnto(&s->tick_timer);
+
+    /* raise IRQ if needed */
+    if (!icnto && s->reg.tcon & L_TCON_INT_START) {
+        /* INT counter enabled and expired */
+
+        s->reg.int_cstat |= L_INT_CSTAT_INTCNT;
+
+        /* raise interrupt if enabled */
+        if (s->reg.int_enb & L_INT_INTENB_ICNTEIE) {
+#ifdef DEBUG_MCT
+            time2[s->id] = qemu_get_clock_ns(vm_clock);
+            DPRINTF("local timer[%d] IRQ: %llx\n", s->id,
+                    time2[s->id] - time1[s->id]);
+            time1[s->id] = time2[s->id];
+#endif
+            qemu_irq_raise(s->irq);
+        }
+
+        /* reload ICNTB */
+        if (s->reg.tcon & L_TCON_INTERVAL_MODE) {
+            exynos4210_ltick_set_cntb(&s->tick_timer,
+                    s->reg.cnt[L_REG_CNT_TCNTB],
+                    s->reg.cnt[L_REG_CNT_ICNTB]);
+        }
+    } else {
+        /* reload TCNTB */
+        if (!tcnto) {
+            exynos4210_ltick_set_cntb(&s->tick_timer,
+                    s->reg.cnt[L_REG_CNT_TCNTB],
+                    icnto);
+        }
+    }
+
+    /* start tick_timer cnt */
+    exynos4210_ltick_cnt_start(&s->tick_timer);
+
+    /* start tick_timer int */
+    exynos4210_ltick_int_start(&s->tick_timer);
+}
+
+/* update timer frequency */
+static void exynos4210_mct_update_freq(Exynos4210MCTState *s)
+{
+    uint32_t freq = s->freq;
+    s->freq = 24000000 /
+            ((MCT_CFG_GET_PRESCALER(s->reg_mct_cfg)+1) *
+                    MCT_CFG_GET_DIVIDER(s->reg_mct_cfg));
+
+    if (freq != s->freq) {
+        DPRINTF("freq=%dHz\n", s->freq);
+
+        /* global timer */
+        ptimer_set_freq(s->g_timer.ptimer_frc, s->freq);
+
+        /* local timer */
+        ptimer_set_freq(s->l_timer[0].tick_timer.ptimer_tick, s->freq);
+        ptimer_set_freq(s->l_timer[0].ptimer_frc, s->freq);
+        ptimer_set_freq(s->l_timer[1].tick_timer.ptimer_tick, s->freq);
+        ptimer_set_freq(s->l_timer[1].ptimer_frc, s->freq);
+    }
+}
+
+/* set defaul_timer values for all fields */
+static void exynos4210_mct_reset(DeviceState *d)
+{
+    Exynos4210MCTState *s = (Exynos4210MCTState *)d;
+    uint32_t i;
+
+    s->reg_mct_cfg = 0;
+
+    /* global timer */
+    memset(&s->g_timer.reg, 0, sizeof(s->g_timer.reg));
+    exynos4210_gfrc_stop(&s->g_timer);
+
+    /* local timer */
+    memset(s->l_timer[0].reg.cnt, 0, sizeof(s->l_timer[0].reg.cnt));
+    memset(s->l_timer[1].reg.cnt, 0, sizeof(s->l_timer[1].reg.cnt));
+    for (i = 0; i < 2; i++) {
+        s->l_timer[i].reg.int_cstat = 0;
+        s->l_timer[i].reg.int_enb = 0;
+        s->l_timer[i].reg.tcon = 0;
+        s->l_timer[i].reg.wstat = 0;
+        s->l_timer[i].tick_timer.count = 0;
+        s->l_timer[i].tick_timer.distance = 0;
+        s->l_timer[i].tick_timer.progress = 0;
+        ptimer_stop(s->l_timer[i].ptimer_frc);
+
+        exynos4210_ltick_timer_init(&s->l_timer[i].tick_timer);
+    }
+
+    exynos4210_mct_update_freq(s);
+
+}
+
+/* Multi Core Timer read */
+static uint64_t exynos4210_mct_read(void *opaque, target_phys_addr_t offset,
+        unsigned size)
+{
+    Exynos4210MCTState *s = (Exynos4210MCTState *)opaque;
+    int index;
+    int shift;
+    uint64_t count;
+    uint32_t value;
+    int lt_i;
+
+    switch (offset) {
+
+    case MCT_CFG:
+        value = s->reg_mct_cfg;
+        break;
+
+    case G_CNT_L: case G_CNT_U:
+        shift = 8 * (offset & 0x4);
+        count = exynos4210_gfrc_get_count(&s->g_timer);
+        value = UINT32_MAX & (count >> shift);
+        DPRINTF("read FRC=0x%llx\n", count);
+        break;
+
+    case G_CNT_WSTAT:
+        value = s->g_timer.reg.cnt_wstat;
+        break;
+
+    case G_COMP_L(0): case G_COMP_L(1): case G_COMP_L(2): case G_COMP_L(3):
+    case G_COMP_U(0): case G_COMP_U(1): case G_COMP_U(2): case G_COMP_U(3):
+    index = GET_G_COMP_IDX(offset);
+    shift = 8 * (offset & 0x4);
+    value = UINT32_MAX & (s->g_timer.reg.comp[index] >> shift);
+    break;
+
+    case G_TCON:
+        value = s->g_timer.reg.tcon;
+        break;
+
+    case G_INT_CSTAT:
+        value = s->g_timer.reg.int_cstat;
+        break;
+
+    case G_INT_ENB:
+        value = s->g_timer.reg.int_enb;
+        break;
+        break;
+    case G_WSTAT:
+        value = s->g_timer.reg.wstat;
+        break;
+
+    case G_COMP0_ADD_INCR: case G_COMP1_ADD_INCR:
+    case G_COMP2_ADD_INCR: case G_COMP3_ADD_INCR:
+        value = s->g_timer.reg.comp_add_incr[GET_G_COMP_ADD_INCR_IDX(offset)];
+        break;
+
+        /* Local timers */
+    case L0_TCNTB: case L0_ICNTB: case L0_FRCNTB:
+    case L1_TCNTB: case L1_ICNTB: case L1_FRCNTB:
+        lt_i = GET_L_TIMER_IDX(offset);
+        index = GET_L_TIMER_CNT_REG_IDX(offset, lt_i);
+        value = s->l_timer[lt_i].reg.cnt[index];
+        break;
+
+    case L0_TCNTO: case L1_TCNTO:
+        lt_i = GET_L_TIMER_IDX(offset);
+
+        value = exynos4210_ltick_cnt_get_cnto(&s->l_timer[lt_i].tick_timer);
+        DPRINTF("local timer[%d] read TCNTO %x\n", lt_i, value);
+        break;
+
+    case L0_ICNTO: case L1_ICNTO:
+        lt_i = GET_L_TIMER_IDX(offset);
+
+        value = exynos4210_ltick_int_get_cnto(&s->l_timer[lt_i].tick_timer);
+        DPRINTF("local timer[%d] read ICNTO %x\n", lt_i, value);
+        break;
+
+    case L0_FRCNTO: case L1_FRCNTO:
+        lt_i = GET_L_TIMER_IDX(offset);
+
+        value = exynos4210_lfrc_get_count(&s->l_timer[lt_i]);
+
+        break;
+
+    case L0_TCON: case L1_TCON:
+        lt_i = ((offset & 0xF00) - L0_TCNTB) / 0x100;
+        value = s->l_timer[lt_i].reg.tcon;
+        break;
+
+    case L0_INT_CSTAT: case L1_INT_CSTAT:
+        lt_i = ((offset & 0xF00) - L0_TCNTB) / 0x100;
+        value = s->l_timer[lt_i].reg.int_cstat;
+        break;
+
+    case L0_INT_ENB: case L1_INT_ENB:
+        lt_i = ((offset & 0xF00) - L0_TCNTB) / 0x100;
+        value = s->l_timer[lt_i].reg.int_enb;
+        break;
+
+    case L0_WSTAT: case L1_WSTAT:
+        lt_i = ((offset & 0xF00) - L0_TCNTB) / 0x100;
+        value = s->l_timer[lt_i].reg.wstat;
+        break;
+
+    default:
+        hw_error("exynos4210.mct: bad read offset "
+                TARGET_FMT_plx "\n", offset);
+        break;
+    }
+    return value;
+}
+
+/* MCT write */
+static void exynos4210_mct_write(void *opaque, target_phys_addr_t offset,
+        uint64_t value, unsigned size)
+{
+    Exynos4210MCTState *s = (Exynos4210MCTState *)opaque;
+    int index;  /* index in buffer which represents register set */
+    int shift;
+    int lt_i;
+    uint64_t new_frc;
+    uint32_t i;
+    uint32_t old_val;
+#ifdef DEBUG_MCT
+    static uint32_t icntb_max[2] = {0};
+    static uint32_t icntb_min[2] = {UINT32_MAX, UINT32_MAX};
+    static uint32_t tcntb_max[2] = {0};
+    static uint32_t tcntb_min[2] = {UINT32_MAX, UINT32_MAX};
+#endif
+
+    new_frc = s->g_timer.reg.cnt;
+
+    switch (offset) {
+
+    case MCT_CFG:
+        s->reg_mct_cfg = value;
+        exynos4210_mct_update_freq(s);
+        break;
+
+    case G_CNT_L:
+    case G_CNT_U:
+        if (offset == G_CNT_L) {
+
+            DPRINTF("global timer write to reg.cntl %llx\n", value);
+
+            new_frc = (s->g_timer.reg.cnt & (uint64_t)UINT32_MAX << 32) + value;
+            s->g_timer.reg.cnt_wstat |= G_CNT_WSTAT_L;
+        }
+        if (offset == G_CNT_U) {
+
+            DPRINTF("global timer write to reg.cntu %llx\n", value);
+
+            new_frc = (s->g_timer.reg.cnt & UINT32_MAX) +
+                    ((uint64_t)value << 32);
+            s->g_timer.reg.cnt_wstat |= G_CNT_WSTAT_U;
+        }
+
+        s->g_timer.reg.cnt = new_frc;
+        exynos4210_gfrc_restart(s);
+        break;
+
+    case G_CNT_WSTAT:
+        s->g_timer.reg.cnt_wstat &= ~(value);
+        break;
+
+    case G_COMP_L(0): case G_COMP_L(1): case G_COMP_L(2): case G_COMP_L(3):
+    case G_COMP_U(0): case G_COMP_U(1): case G_COMP_U(2): case G_COMP_U(3):
+    index = GET_G_COMP_IDX(offset);
+    shift = 8 * (offset & 0x4);
+    s->g_timer.reg.comp[index] =
+            (s->g_timer.reg.comp[index] &
+            (((uint64_t)UINT32_MAX << 32) >> shift)) +
+            (value << shift);
+
+    DPRINTF("comparator %d write 0x%llx val << %d\n", index, value, shift);
+
+    if (offset&0x4) {
+        s->g_timer.reg.wstat |= G_WSTAT_COMP_U(index);
+    } else {
+        s->g_timer.reg.wstat |= G_WSTAT_COMP_L(index);
+    }
+
+    exynos4210_gfrc_restart(s);
+    break;
+
+    case G_TCON:
+        old_val = s->g_timer.reg.tcon;
+        s->g_timer.reg.tcon = value;
+        s->g_timer.reg.wstat |= G_WSTAT_TCON_WRITE;
+
+        DPRINTF("global timer write to reg.g_tcon %llx\n", value);
+
+        /* Start FRC if transition from disabled to enabled */
+        if ((value & G_TCON_TIMER_ENABLE) > (old_val &
+                G_TCON_TIMER_ENABLE)) {
+            exynos4210_gfrc_start(&s->g_timer);
+        }
+        if ((value & G_TCON_TIMER_ENABLE) < (old_val &
+                G_TCON_TIMER_ENABLE)) {
+            exynos4210_gfrc_stop(&s->g_timer);
+        }
+
+        /* Start CMP if transition from disabled to enabled */
+        for (i = 0; i < MCT_GT_CMP_NUM; i++) {
+            if ((value & G_TCON_COMP_ENABLE(i)) != (old_val &
+                    G_TCON_COMP_ENABLE(i))) {
+                exynos4210_gfrc_restart(s);
+            }
+        }
+        break;
+
+    case G_INT_CSTAT:
+        s->g_timer.reg.int_cstat &= ~(value);
+        for (i = 0; i < MCT_GT_CMP_NUM; i++) {
+            if (value & G_INT_CSTAT_COMP(i)) {
+                exynos4210_gcomp_lower_irq(&s->g_timer, i);
+            }
+        }
+        break;
+
+    case G_INT_ENB:
+
+        /* Raise IRQ if transition from disabled to enabled and CSTAT pending */
+        for (i = 0; i < MCT_GT_CMP_NUM; i++) {
+            if ((value & G_INT_ENABLE(i)) > (s->g_timer.reg.tcon &
+                    G_INT_ENABLE(i))) {
+                if (s->g_timer.reg.int_cstat & G_INT_CSTAT_COMP(i)) {
+                    exynos4210_gcomp_raise_irq(&s->g_timer, i);
+                }
+            }
+
+            if ((value & G_INT_ENABLE(i)) < (s->g_timer.reg.tcon &
+                    G_INT_ENABLE(i))) {
+                exynos4210_gcomp_lower_irq(&s->g_timer, i);
+            }
+        }
+
+        DPRINTF("global timer INT enable %llx\n", value);
+        s->g_timer.reg.int_enb = value;
+        break;
+
+    case G_WSTAT:
+        s->g_timer.reg.wstat &= ~(value);
+        break;
+
+    case G_COMP0_ADD_INCR: case G_COMP1_ADD_INCR:
+    case G_COMP2_ADD_INCR: case G_COMP3_ADD_INCR:
+        index = GET_G_COMP_ADD_INCR_IDX(offset);
+        s->g_timer.reg.comp_add_incr[index] = value;
+        s->g_timer.reg.wstat |= G_WSTAT_COMP_ADDINCR(index);
+        break;
+
+        /* Local timers */
+    case L0_TCON: case L1_TCON:
+        lt_i = GET_L_TIMER_IDX(offset);
+        old_val = s->l_timer[lt_i].reg.tcon;
+
+        s->l_timer[lt_i].reg.wstat |= L_WSTAT_TCON_WRITE;
+        s->l_timer[lt_i].reg.tcon = value;
+
+        /* Stop local CNT */
+        if ((value & L_TCON_TICK_START) <
+                (old_val & L_TCON_TICK_START)) {
+            DPRINTF("local timer[%d] stop cnt\n", lt_i);
+            exynos4210_ltick_cnt_stop(&s->l_timer[lt_i].tick_timer);
+        }
+
+        /* Stop local INT */
+        if ((value & L_TCON_INT_START) <
+                (old_val & L_TCON_INT_START)) {
+            DPRINTF("local timer[%d] stop int\n", lt_i);
+            exynos4210_ltick_int_stop(&s->l_timer[lt_i].tick_timer);
+        }
+
+        /* Start local CNT */
+        if ((value & L_TCON_TICK_START) >
+        (old_val & L_TCON_TICK_START)) {
+            DPRINTF("local timer[%d] start cnt\n", lt_i);
+            exynos4210_ltick_cnt_start(&s->l_timer[lt_i].tick_timer);
+        }
+
+        /* Start local INT */
+        if ((value & L_TCON_INT_START) >
+        (old_val & L_TCON_INT_START)) {
+            DPRINTF("local timer[%d] start int\n", lt_i);
+            exynos4210_ltick_int_start(&s->l_timer[lt_i].tick_timer);
+        }
+
+        /* Start or Stop local FRC if TCON changed */
+        if ((value & L_TCON_FRC_START) >
+        (s->l_timer[lt_i].reg.tcon & L_TCON_FRC_START)) {
+            DPRINTF("local timer[%d] start frc\n", lt_i);
+            exynos4210_lfrc_start(&s->l_timer[lt_i]);
+        }
+        if ((value & L_TCON_FRC_START) <
+                (s->l_timer[lt_i].reg.tcon & L_TCON_FRC_START)) {
+            DPRINTF("local timer[%d] stop frc\n", lt_i);
+            exynos4210_lfrc_stop(&s->l_timer[lt_i]);
+        }
+        break;
+
+    case L0_TCNTB: case L1_TCNTB:
+
+        lt_i = GET_L_TIMER_IDX(offset);
+        index = GET_L_TIMER_CNT_REG_IDX(offset, lt_i);
+
+        /*
+         * TCNTB is updated to internal register only after CNT expired.
+         * Due to this we should reload timer to nearest moment when CNT is
+         * expired and then in event handler update tcntb to new TCNTB value.
+         */
+        exynos4210_ltick_set_cntb(&s->l_timer[lt_i].tick_timer, value,
+                s->l_timer[lt_i].tick_timer.icntb);
+
+        s->l_timer[lt_i].reg.wstat |= L_WSTAT_TCNTB_WRITE;
+        s->l_timer[lt_i].reg.cnt[L_REG_CNT_TCNTB] = value;
+
+#ifdef DEBUG_MCT
+        if (tcntb_min[lt_i] > value) {
+            tcntb_min[lt_i] = value;
+        }
+        if (tcntb_max[lt_i] < value) {
+            tcntb_max[lt_i] = value;
+        }
+        DPRINTF("local timer[%d] TCNTB write %llx; max=%x, min=%x\n",
+                lt_i, value, tcntb_max[lt_i], tcntb_min[lt_i]);
+#endif
+        break;
+
+    case L0_ICNTB: case L1_ICNTB:
+
+        lt_i = GET_L_TIMER_IDX(offset);
+        index = GET_L_TIMER_CNT_REG_IDX(offset, lt_i);
+
+        s->l_timer[lt_i].reg.wstat |= L_WSTAT_ICNTB_WRITE;
+        s->l_timer[lt_i].reg.cnt[L_REG_CNT_ICNTB] = value &
+                ~L_ICNTB_MANUAL_UPDATE;
+
+        /*
+         * We need to avoid too small values for TCNTB*ICNTB. If not, IRQ event
+         * could raise too fast disallowing QEMU to execute target code.
+         */
+        if (s->l_timer[lt_i].reg.cnt[L_REG_CNT_ICNTB] *
+            s->l_timer[lt_i].reg.cnt[L_REG_CNT_TCNTB] < MCT_LT_CNT_LOW_LIMIT) {
+            if (!s->l_timer[lt_i].reg.cnt[L_REG_CNT_TCNTB]) {
+                s->l_timer[lt_i].reg.cnt[L_REG_CNT_ICNTB] =
+                        MCT_LT_CNT_LOW_LIMIT;
+            } else {
+                s->l_timer[lt_i].reg.cnt[L_REG_CNT_ICNTB] =
+                        MCT_LT_CNT_LOW_LIMIT /
+                        s->l_timer[lt_i].reg.cnt[L_REG_CNT_TCNTB];
+            }
+        }
+
+        if (value & L_ICNTB_MANUAL_UPDATE) {
+            exynos4210_ltick_set_cntb(&s->l_timer[lt_i].tick_timer,
+                    s->l_timer[lt_i].tick_timer.tcntb,
+                    s->l_timer[lt_i].reg.cnt[L_REG_CNT_ICNTB]);
+        }
+
+#ifdef DEBUG_MCT
+        if (icntb_min[lt_i] > value) {
+            icntb_min[lt_i] = value;
+        }
+        if (icntb_max[lt_i] < value) {
+            icntb_max[lt_i] = value;
+        }
+DPRINTF("local timer[%d] ICNTB write %llx; max=%x, min=%x\n\n",
+        lt_i, value, icntb_max[lt_i], icntb_min[lt_i]);
+#endif
+break;
+
+    case L0_FRCNTB: case L1_FRCNTB:
+
+        lt_i = GET_L_TIMER_IDX(offset);
+        index = GET_L_TIMER_CNT_REG_IDX(offset, lt_i);
+
+        DPRINTF("local timer[%d] FRCNTB write %llx\n", lt_i, value);
+
+        s->l_timer[lt_i].reg.wstat |= L_WSTAT_FRCCNTB_WRITE;
+        s->l_timer[lt_i].reg.cnt[L_REG_CNT_FRCCNTB] = value;
+
+        break;
+
+    case L0_TCNTO: case L1_TCNTO:
+    case L0_ICNTO: case L1_ICNTO:
+    case L0_FRCNTO: case L1_FRCNTO:
+        fprintf(stderr, "\n[exynos4210.mct: write to RO register "
+                TARGET_FMT_plx "]\n\n", offset);
+        break;
+
+    case L0_INT_CSTAT: case L1_INT_CSTAT:
+        lt_i = GET_L_TIMER_IDX(offset);
+
+        DPRINTF("local timer[%d] CSTAT write %llx\n", lt_i, value);
+
+        s->l_timer[lt_i].reg.int_cstat &= ~value;
+        if (!s->l_timer[lt_i].reg.int_cstat) {
+            qemu_irq_lower(s->l_timer[lt_i].irq);
+        }
+        break;
+
+    case L0_INT_ENB: case L1_INT_ENB:
+        lt_i = GET_L_TIMER_IDX(offset);
+        old_val = s->l_timer[lt_i].reg.int_enb;
+
+        /* Raise Local timer IRQ if cstat is pending */
+        if ((value & L_INT_INTENB_ICNTEIE) > (old_val & L_INT_INTENB_ICNTEIE)) {
+            if (s->l_timer[lt_i].reg.int_cstat & L_INT_CSTAT_INTCNT) {
+                qemu_irq_raise(s->l_timer[lt_i].irq);
+            }
+        }
+
+        s->l_timer[lt_i].reg.int_enb = value;
+
+        break;
+
+    case L0_WSTAT: case L1_WSTAT:
+        lt_i = GET_L_TIMER_IDX(offset);
+
+        s->l_timer[lt_i].reg.wstat &= ~value;
+        break;
+
+    default:
+        hw_error("exynos4210.mct: bad write offset "
+                TARGET_FMT_plx "\n", offset);
+        break;
+    }
+}
+
+static const MemoryRegionOps exynos4210_mct_ops = {
+    .read = exynos4210_mct_read,
+    .write = exynos4210_mct_write,
+    .endianness = DEVICE_NATIVE_ENDIAN,
+};
+
+/* MCT init */
+static int exynos4210_mct_init(SysBusDevice *dev)
+{
+    int i;
+    Exynos4210MCTState *s = FROM_SYSBUS(Exynos4210MCTState, dev);
+    QEMUBH *bh[2];
+
+    /* Global timer */
+    bh[0] = qemu_bh_new(exynos4210_gfrc_event, s);
+    s->g_timer.ptimer_frc = ptimer_init(bh[0]);
+    memset(&s->g_timer.reg, 0, sizeof(struct gregs));
+
+    /* Local timers */
+    for (i = 0; i < 2; i++) {
+        bh[0] = qemu_bh_new(exynos4210_ltick_event, &s->l_timer[i]);
+        bh[1] = qemu_bh_new(exynos4210_lfrc_event, &s->l_timer[i]);
+        s->l_timer[i].tick_timer.ptimer_tick = ptimer_init(bh[0]);
+        s->l_timer[i].ptimer_frc = ptimer_init(bh[1]);
+        s->l_timer[i].id = i;
+    }
+
+    /* IRQs */
+    for (i = 0; i < MCT_GT_CMP_NUM; i++) {
+        sysbus_init_irq(dev, &s->g_timer.irq[i]);
+    }
+    for (i = 0; i < 2; i++) {
+        sysbus_init_irq(dev, &s->l_timer[i].irq);
+    }
+
+    memory_region_init_io(&s->iomem, &exynos4210_mct_ops, s, "exynos4210-mct",
+            MCT_SFR_SIZE);
+    sysbus_init_mmio(dev, &s->iomem);
+
+    return 0;
+}
+
+static void exynos4210_mct_class_init(ObjectClass *klass, void *data)
+{
+    DeviceClass *dc = DEVICE_CLASS(klass);
+    SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass);
+
+    k->init = exynos4210_mct_init;
+    dc->reset = exynos4210_mct_reset;
+    dc->vmsd = &vmstate_exynos4210_mct_state;
+}
+
+static TypeInfo exynos4210_mct_info = {
+    .name          = "exynos4210.mct",
+    .parent        = TYPE_SYS_BUS_DEVICE,
+    .instance_size = sizeof(Exynos4210MCTState),
+    .class_init    = exynos4210_mct_class_init,
+};
+
+static void exynos4210_mct_register_types(void)
+{
+    type_register_static(&exynos4210_mct_info);
+}
+
+type_init(exynos4210_mct_register_types)
-- 
1.7.1

^ permalink raw reply related	[flat|nested] 24+ messages in thread

* [Qemu-devel] [PATCH 07/22] hw/lan9118: Add basic 16-bit mode support.
  2012-02-17 11:29 [Qemu-devel] [PULL 00/22] arm-devs queue Peter Maydell
                   ` (5 preceding siblings ...)
  2012-02-17 11:29 ` [Qemu-devel] [PATCH 06/22] ARM: exynos4210: MCT support Peter Maydell
@ 2012-02-17 11:29 ` Peter Maydell
  2012-02-17 11:29 ` [Qemu-devel] [PATCH 08/22] hw/exynos4210.c: Add LAN support for SMDKC210 Peter Maydell
                   ` (15 subsequent siblings)
  22 siblings, 0 replies; 24+ messages in thread
From: Peter Maydell @ 2012-02-17 11:29 UTC (permalink / raw)
  To: Anthony Liguori; +Cc: qemu-devel, Paul Brook

From: Evgeny Voevodin <e.voevodin@samsung.com>

Signed-off-by: Evgeny Voevodin <e.voevodin@samsung.com>
Reviewed-by: Peter Maydell <peter.maydell@linaro.org>
Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
---
 hw/lan9118.c |  124 +++++++++++++++++++++++++++++++++++++++++++++++++++++++---
 1 files changed, 118 insertions(+), 6 deletions(-)

diff --git a/hw/lan9118.c b/hw/lan9118.c
index aeb0c39..7b4fe87 100644
--- a/hw/lan9118.c
+++ b/hw/lan9118.c
@@ -235,11 +235,21 @@ typedef struct {
     int32_t rxp_offset;
     int32_t rxp_size;
     int32_t rxp_pad;
+
+    uint32_t write_word_prev_offset;
+    uint32_t write_word_n;
+    uint16_t write_word_l;
+    uint16_t write_word_h;
+    uint32_t read_word_prev_offset;
+    uint32_t read_word_n;
+    uint32_t read_long;
+
+    uint32_t mode_16bit;
 } lan9118_state;
 
 static const VMStateDescription vmstate_lan9118 = {
     .name = "lan9118",
-    .version_id = 1,
+    .version_id = 2,
     .minimum_version_id = 1,
     .fields = (VMStateField[]) {
         VMSTATE_PTIMER(timer, lan9118_state),
@@ -294,6 +304,14 @@ static const VMStateDescription vmstate_lan9118 = {
         VMSTATE_INT32(rxp_offset, lan9118_state),
         VMSTATE_INT32(rxp_size, lan9118_state),
         VMSTATE_INT32(rxp_pad, lan9118_state),
+        VMSTATE_UINT32_V(write_word_prev_offset, lan9118_state, 2),
+        VMSTATE_UINT32_V(write_word_n, lan9118_state, 2),
+        VMSTATE_UINT16_V(write_word_l, lan9118_state, 2),
+        VMSTATE_UINT16_V(write_word_h, lan9118_state, 2),
+        VMSTATE_UINT32_V(read_word_prev_offset, lan9118_state, 2),
+        VMSTATE_UINT32_V(read_word_n, lan9118_state, 2),
+        VMSTATE_UINT32_V(read_long, lan9118_state, 2),
+        VMSTATE_UINT32_V(mode_16bit, lan9118_state, 2),
         VMSTATE_END_OF_LIST()
     }
 };
@@ -390,7 +408,7 @@ static void lan9118_reset(DeviceState *d)
     s->fifo_int = 0x48000000;
     s->rx_cfg = 0;
     s->tx_cfg = 0;
-    s->hw_cfg = 0x00050000;
+    s->hw_cfg = s->mode_16bit ? 0x00050000 : 0x00050004;
     s->pmt_ctrl &= 0x45;
     s->gpio_cfg = 0;
     s->txp->fifo_used = 0;
@@ -429,6 +447,9 @@ static void lan9118_reset(DeviceState *d)
     s->mac_mii_data = 0;
     s->mac_flow = 0;
 
+    s->read_word_n = 0;
+    s->write_word_n = 0;
+
     phy_reset(s);
 
     s->eeprom_writable = 0;
@@ -984,7 +1005,7 @@ static void lan9118_writel(void *opaque, target_phys_addr_t offset,
 {
     lan9118_state *s = (lan9118_state *)opaque;
     offset &= 0xff;
-    
+
     //DPRINTF("Write reg 0x%02x = 0x%08x\n", (int)offset, val);
     if (offset >= 0x20 && offset < 0x40) {
         /* TX FIFO */
@@ -1034,7 +1055,7 @@ static void lan9118_writel(void *opaque, target_phys_addr_t offset,
             /* SRST */
             lan9118_reset(&s->busdev.qdev);
         } else {
-            s->hw_cfg = val & 0x003f300;
+            s->hw_cfg = (val & 0x003f300) | (s->hw_cfg & 0x4);
         }
         break;
     case CSR_RX_DP_CTRL:
@@ -1113,6 +1134,46 @@ static void lan9118_writel(void *opaque, target_phys_addr_t offset,
     lan9118_update(s);
 }
 
+static void lan9118_writew(void *opaque, target_phys_addr_t offset,
+                           uint32_t val)
+{
+    lan9118_state *s = (lan9118_state *)opaque;
+    offset &= 0xff;
+
+    if (s->write_word_prev_offset != (offset & ~0x3)) {
+        /* New offset, reset word counter */
+        s->write_word_n = 0;
+        s->write_word_prev_offset = offset & ~0x3;
+    }
+
+    if (offset & 0x2) {
+        s->write_word_h = val;
+    } else {
+        s->write_word_l = val;
+    }
+
+    //DPRINTF("Writew reg 0x%02x = 0x%08x\n", (int)offset, val);
+    s->write_word_n++;
+    if (s->write_word_n == 2) {
+        s->write_word_n = 0;
+        lan9118_writel(s, offset & ~3, s->write_word_l +
+                (s->write_word_h << 16), 4);
+    }
+}
+
+static void lan9118_16bit_mode_write(void *opaque, target_phys_addr_t offset,
+                                     uint64_t val, unsigned size)
+{
+    switch (size) {
+    case 2:
+        return lan9118_writew(opaque, offset, (uint32_t)val);
+    case 4:
+        return lan9118_writel(opaque, offset, val, size);
+    }
+
+    hw_error("lan9118_write: Bad size 0x%x\n", size);
+}
+
 static uint64_t lan9118_readl(void *opaque, target_phys_addr_t offset,
                               unsigned size)
 {
@@ -1149,7 +1210,7 @@ static uint64_t lan9118_readl(void *opaque, target_phys_addr_t offset,
     case CSR_TX_CFG:
         return s->tx_cfg;
     case CSR_HW_CFG:
-        return s->hw_cfg | 0x4;
+        return s->hw_cfg;
     case CSR_RX_DP_CTRL:
         return 0;
     case CSR_RX_FIFO_INF:
@@ -1187,12 +1248,60 @@ static uint64_t lan9118_readl(void *opaque, target_phys_addr_t offset,
     return 0;
 }
 
+static uint32_t lan9118_readw(void *opaque, target_phys_addr_t offset)
+{
+    lan9118_state *s = (lan9118_state *)opaque;
+    uint32_t val;
+
+    if (s->read_word_prev_offset != (offset & ~0x3)) {
+        /* New offset, reset word counter */
+        s->read_word_n = 0;
+        s->read_word_prev_offset = offset & ~0x3;
+    }
+
+    s->read_word_n++;
+    if (s->read_word_n == 1) {
+        s->read_long = lan9118_readl(s, offset & ~3, 4);
+    } else {
+        s->read_word_n = 0;
+    }
+
+    if (offset & 2) {
+        val = s->read_long >> 16;
+    } else {
+        val = s->read_long & 0xFFFF;
+    }
+
+    //DPRINTF("Readw reg 0x%02x, val 0x%x\n", (int)offset, val);
+    return val;
+}
+
+static uint64_t lan9118_16bit_mode_read(void *opaque, target_phys_addr_t offset,
+                                        unsigned size)
+{
+    switch (size) {
+    case 2:
+        return lan9118_readw(opaque, offset);
+    case 4:
+        return lan9118_readl(opaque, offset, size);
+    }
+
+    hw_error("lan9118_read: Bad size 0x%x\n", size);
+    return 0;
+}
+
 static const MemoryRegionOps lan9118_mem_ops = {
     .read = lan9118_readl,
     .write = lan9118_writel,
     .endianness = DEVICE_NATIVE_ENDIAN,
 };
 
+static const MemoryRegionOps lan9118_16bit_mem_ops = {
+    .read = lan9118_16bit_mode_read,
+    .write = lan9118_16bit_mode_write,
+    .endianness = DEVICE_NATIVE_ENDIAN,
+};
+
 static void lan9118_cleanup(VLANClientState *nc)
 {
     lan9118_state *s = DO_UPCAST(NICState, nc, nc)->opaque;
@@ -1214,8 +1323,10 @@ static int lan9118_init1(SysBusDevice *dev)
     lan9118_state *s = FROM_SYSBUS(lan9118_state, dev);
     QEMUBH *bh;
     int i;
+    const MemoryRegionOps *mem_ops =
+            s->mode_16bit ? &lan9118_16bit_mem_ops : &lan9118_mem_ops;
 
-    memory_region_init_io(&s->mmio, &lan9118_mem_ops, s, "lan9118-mmio", 0x100);
+    memory_region_init_io(&s->mmio, mem_ops, s, "lan9118-mmio", 0x100);
     sysbus_init_mmio(dev, &s->mmio);
     sysbus_init_irq(dev, &s->irq);
     qemu_macaddr_default_if_unset(&s->conf.macaddr);
@@ -1240,6 +1351,7 @@ static int lan9118_init1(SysBusDevice *dev)
 
 static Property lan9118_properties[] = {
     DEFINE_NIC_PROPERTIES(lan9118_state, conf),
+    DEFINE_PROP_UINT32("mode_16bit", lan9118_state, mode_16bit, 0),
     DEFINE_PROP_END_OF_LIST(),
 };
 
-- 
1.7.1

^ permalink raw reply related	[flat|nested] 24+ messages in thread

* [Qemu-devel] [PATCH 08/22] hw/exynos4210.c: Add LAN support for SMDKC210.
  2012-02-17 11:29 [Qemu-devel] [PULL 00/22] arm-devs queue Peter Maydell
                   ` (6 preceding siblings ...)
  2012-02-17 11:29 ` [Qemu-devel] [PATCH 07/22] hw/lan9118: Add basic 16-bit mode support Peter Maydell
@ 2012-02-17 11:29 ` Peter Maydell
  2012-02-17 11:29 ` [Qemu-devel] [PATCH 09/22] Exynos4210: added display controller implementation Peter Maydell
                   ` (14 subsequent siblings)
  22 siblings, 0 replies; 24+ messages in thread
From: Peter Maydell @ 2012-02-17 11:29 UTC (permalink / raw)
  To: Anthony Liguori; +Cc: qemu-devel, Paul Brook

From: Evgeny Voevodin <e.voevodin@samsung.com>

SMDKC210 uses lan9215 chip, but lan9118 in 16-bit mode seems to
be enough.

Signed-off-by: Evgeny Voevodin <e.voevodin@samsung.com>
Reviewed-by: Peter Maydell <peter.maydell@linaro.org>
Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
---
 hw/exynos4_boards.c |   27 +++++++++++++++++++++++++--
 1 files changed, 25 insertions(+), 2 deletions(-)

diff --git a/hw/exynos4_boards.c b/hw/exynos4_boards.c
index 767dc45..329efbe 100644
--- a/hw/exynos4_boards.c
+++ b/hw/exynos4_boards.c
@@ -23,6 +23,7 @@
 
 #include "sysemu.h"
 #include "sysbus.h"
+#include "net.h"
 #include "arm-misc.h"
 #include "exec-memory.h"
 #include "exynos4210.h"
@@ -42,6 +43,8 @@
     #define  PRINT_DEBUG(fmt, args...)  do {} while (0)
 #endif
 
+#define SMDK_LAN9118_BASE_ADDR      0x05000000
+
 typedef enum Exynos4BoardType {
     EXYNOS4_BOARD_NURI,
     EXYNOS4_BOARD_SMDKC210,
@@ -71,6 +74,24 @@ static struct arm_boot_info exynos4_board_binfo = {
 
 static QEMUMachine exynos4_machines[EXYNOS4_NUM_OF_BOARDS];
 
+static void lan9215_init(uint32_t base, qemu_irq irq)
+{
+    DeviceState *dev;
+    SysBusDevice *s;
+
+    /* This should be a 9215 but the 9118 is close enough */
+    if (nd_table[0].vlan) {
+        qemu_check_nic_model(&nd_table[0], "lan9118");
+        dev = qdev_create(NULL, "lan9118");
+        qdev_set_nic_properties(dev, &nd_table[0]);
+        qdev_prop_set_uint32(dev, "mode_16bit", 1);
+        qdev_init_nofail(dev);
+        s = sysbus_from_qdev(dev);
+        sysbus_mmio_map(s, 0, base);
+        sysbus_connect_irq(s, 0, irq);
+    }
+}
+
 static Exynos4210State *exynos4_boards_init_common(
         const char *kernel_filename,
         const char *kernel_cmdline,
@@ -123,9 +144,11 @@ static void smdkc210_init(ram_addr_t ram_size,
         const char *kernel_filename, const char *kernel_cmdline,
         const char *initrd_filename, const char *cpu_model)
 {
-    exynos4_boards_init_common(kernel_filename, kernel_cmdline,
-                initrd_filename, EXYNOS4_BOARD_SMDKC210);
+    Exynos4210State *s = exynos4_boards_init_common(kernel_filename,
+            kernel_cmdline, initrd_filename, EXYNOS4_BOARD_SMDKC210);
 
+    lan9215_init(SMDK_LAN9118_BASE_ADDR,
+            qemu_irq_invert(s->irq_table[exynos4210_get_irq(37, 1)]));
     arm_load_kernel(first_cpu, &exynos4_board_binfo);
 }
 
-- 
1.7.1

^ permalink raw reply related	[flat|nested] 24+ messages in thread

* [Qemu-devel] [PATCH 09/22] Exynos4210: added display controller implementation
  2012-02-17 11:29 [Qemu-devel] [PULL 00/22] arm-devs queue Peter Maydell
                   ` (7 preceding siblings ...)
  2012-02-17 11:29 ` [Qemu-devel] [PATCH 08/22] hw/exynos4210.c: Add LAN support for SMDKC210 Peter Maydell
@ 2012-02-17 11:29 ` Peter Maydell
  2012-02-17 11:29 ` [Qemu-devel] [PATCH 10/22] MAINTAINERS: Add maintainers for Exynos SOC Peter Maydell
                   ` (13 subsequent siblings)
  22 siblings, 0 replies; 24+ messages in thread
From: Peter Maydell @ 2012-02-17 11:29 UTC (permalink / raw)
  To: Anthony Liguori; +Cc: qemu-devel, Paul Brook

From: Mitsyanko Igor <i.mitsyanko@samsung.com>

Exynos4210 display controller (FIMD) has 5 hardware windows with alpha and
chroma key blending functions.

Signed-off-by: Mitsyanko Igor <i.mitsyanko@samsung.com>
Signed-off-by: Evgeny Voevodin <e.voevodin@samsung.com>
Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
---
 Makefile.target      |    2 +-
 hw/exynos4210.c      |   10 +
 hw/exynos4210_fimd.c | 1928 ++++++++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 1939 insertions(+), 1 deletions(-)
 create mode 100644 hw/exynos4210_fimd.c

diff --git a/Makefile.target b/Makefile.target
index 6ceff54..2defdc4 100644
--- a/Makefile.target
+++ b/Makefile.target
@@ -346,7 +346,7 @@ obj-arm-y += versatile_pci.o
 obj-arm-y += realview_gic.o realview.o arm_sysctl.o arm11mpcore.o a9mpcore.o
 obj-arm-y += exynos4210_gic.o exynos4210_combiner.o exynos4210.o
 obj-arm-y += exynos4_boards.o exynos4210_uart.o exynos4210_pwm.o
-obj-arm-y += exynos4210_pmu.o exynos4210_mct.o
+obj-arm-y += exynos4210_pmu.o exynos4210_mct.o exynos4210_fimd.o
 obj-arm-y += arm_l2x0.o
 obj-arm-y += arm_mptimer.o
 obj-arm-y += armv7m.o armv7m_nvic.o stellaris.o pl022.o stellaris_enet.o
diff --git a/hw/exynos4210.c b/hw/exynos4210.c
index 558f669..f904370 100644
--- a/hw/exynos4210.c
+++ b/hw/exynos4210.c
@@ -58,6 +58,9 @@
 /* PMU SFR base address */
 #define EXYNOS4210_PMU_BASE_ADDR            0x10020000
 
+/* Display controllers (FIMD) */
+#define EXYNOS4210_FIMD0_BASE_ADDR          0x11C00000
+
 static uint8_t chipid_and_omr[] = { 0x11, 0x02, 0x21, 0x43,
                                     0x09, 0x00, 0x00, 0x00 };
 
@@ -256,5 +259,12 @@ Exynos4210State *exynos4210_init(MemoryRegion *system_mem,
                            EXYNOS4210_UART3_FIFO_SIZE, 3, NULL,
                   s->irq_table[exynos4210_get_irq(EXYNOS4210_UART_INT_GRP, 3)]);
 
+    /*** Display controller (FIMD) ***/
+    sysbus_create_varargs("exynos4210.fimd", EXYNOS4210_FIMD0_BASE_ADDR,
+            s->irq_table[exynos4210_get_irq(11, 0)],
+            s->irq_table[exynos4210_get_irq(11, 1)],
+            s->irq_table[exynos4210_get_irq(11, 2)],
+            NULL);
+
     return s;
 }
diff --git a/hw/exynos4210_fimd.c b/hw/exynos4210_fimd.c
new file mode 100644
index 0000000..3313f00
--- /dev/null
+++ b/hw/exynos4210_fimd.c
@@ -0,0 +1,1928 @@
+/*
+ * Samsung exynos4210 Display Controller (FIMD)
+ *
+ * Copyright (c) 2000 - 2011 Samsung Electronics Co., Ltd.
+ * All rights reserved.
+ * Based on LCD controller for Samsung S5PC1xx-based board emulation
+ * by Kirill Batuzov <batuzovk@ispras.ru>
+ *
+ * Contributed by Mitsyanko Igor <i.mitsyanko@samsung.com>
+ *
+ * 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, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "qemu-common.h"
+#include "cpu-all.h"
+#include "sysbus.h"
+#include "console.h"
+#include "pixel_ops.h"
+#include "bswap.h"
+
+/* Debug messages configuration */
+#define EXYNOS4210_FIMD_DEBUG              0
+#define EXYNOS4210_FIMD_MODE_TRACE         0
+
+#if EXYNOS4210_FIMD_DEBUG == 0
+    #define DPRINT_L1(fmt, args...)       do { } while (0)
+    #define DPRINT_L2(fmt, args...)       do { } while (0)
+    #define DPRINT_ERROR(fmt, args...)    do { } while (0)
+#elif EXYNOS4210_FIMD_DEBUG == 1
+    #define DPRINT_L1(fmt, args...) \
+        do {fprintf(stderr, "QEMU FIMD: "fmt, ## args); } while (0)
+    #define DPRINT_L2(fmt, args...)       do { } while (0)
+    #define DPRINT_ERROR(fmt, args...)  \
+        do {fprintf(stderr, "QEMU FIMD ERROR: "fmt, ## args); } while (0)
+#else
+    #define DPRINT_L1(fmt, args...) \
+        do {fprintf(stderr, "QEMU FIMD: "fmt, ## args); } while (0)
+    #define DPRINT_L2(fmt, args...) \
+        do {fprintf(stderr, "QEMU FIMD: "fmt, ## args); } while (0)
+    #define DPRINT_ERROR(fmt, args...)  \
+        do {fprintf(stderr, "QEMU FIMD ERROR: "fmt, ## args); } while (0)
+#endif
+
+#if EXYNOS4210_FIMD_MODE_TRACE == 0
+    #define DPRINT_TRACE(fmt, args...)        do { } while (0)
+#else
+    #define DPRINT_TRACE(fmt, args...)        \
+        do {fprintf(stderr, "QEMU FIMD: "fmt, ## args); } while (0)
+#endif
+
+#define NUM_OF_WINDOWS              5
+#define FIMD_REGS_SIZE              0x4114
+
+/* Video main control registers */
+#define FIMD_VIDCON0                0x0000
+#define FIMD_VIDCON1                0x0004
+#define FIMD_VIDCON2                0x0008
+#define FIMD_VIDCON3                0x000C
+#define FIMD_VIDCON0_ENVID_F        (1 << 0)
+#define FIMD_VIDCON0_ENVID          (1 << 1)
+#define FIMD_VIDCON0_ENVID_MASK     ((1 << 0) | (1 << 1))
+#define FIMD_VIDCON1_ROMASK         0x07FFE000
+
+/* Video time control registers */
+#define FIMD_VIDTCON_START          0x10
+#define FIMD_VIDTCON_END            0x1C
+#define FIMD_VIDTCON2_SIZE_MASK     0x07FF
+#define FIMD_VIDTCON2_HOR_SHIFT     0
+#define FIMD_VIDTCON2_VER_SHIFT     11
+
+/* Window control registers */
+#define FIMD_WINCON_START           0x0020
+#define FIMD_WINCON_END             0x0030
+#define FIMD_WINCON_ROMASK          0x82200000
+#define FIMD_WINCON_ENWIN           (1 << 0)
+#define FIMD_WINCON_BLD_PIX         (1 << 6)
+#define FIMD_WINCON_ALPHA_MUL       (1 << 7)
+#define FIMD_WINCON_ALPHA_SEL       (1 << 1)
+#define FIMD_WINCON_SWAP            0x078000
+#define FIMD_WINCON_SWAP_SHIFT      15
+#define FIMD_WINCON_SWAP_WORD       0x1
+#define FIMD_WINCON_SWAP_HWORD      0x2
+#define FIMD_WINCON_SWAP_BYTE       0x4
+#define FIMD_WINCON_SWAP_BITS       0x8
+#define FIMD_WINCON_BUFSTAT_L       (1 << 21)
+#define FIMD_WINCON_BUFSTAT_H       (1 << 31)
+#define FIMD_WINCON_BUFSTATUS       ((1 << 21) | (1 << 31))
+#define FIMD_WINCON_BUF0_STAT       ((0 << 21) | (0 << 31))
+#define FIMD_WINCON_BUF1_STAT       ((1 << 21) | (0 << 31))
+#define FIMD_WINCON_BUF2_STAT       ((0 << 21) | (1 << 31))
+#define FIMD_WINCON_BUFSELECT       ((1 << 20) | (1 << 30))
+#define FIMD_WINCON_BUF0_SEL        ((0 << 20) | (0 << 30))
+#define FIMD_WINCON_BUF1_SEL        ((1 << 20) | (0 << 30))
+#define FIMD_WINCON_BUF2_SEL        ((0 << 20) | (1 << 30))
+#define FIMD_WINCON_BUFMODE         (1 << 14)
+#define IS_PALETTIZED_MODE(w)       (w->wincon & 0xC)
+#define PAL_MODE_WITH_ALPHA(x)       ((x) == 7)
+#define WIN_BPP_MODE(w)             ((w->wincon >> 2) & 0xF)
+#define WIN_BPP_MODE_WITH_ALPHA(w)     \
+    (WIN_BPP_MODE(w) == 0xD || WIN_BPP_MODE(w) == 0xE)
+
+/* Shadow control register */
+#define FIMD_SHADOWCON              0x0034
+#define FIMD_WINDOW_PROTECTED(s, w) ((s) & (1 << (10 + (w))))
+/* Channel mapping control register */
+#define FIMD_WINCHMAP               0x003C
+
+/* Window position control registers */
+#define FIMD_VIDOSD_START           0x0040
+#define FIMD_VIDOSD_END             0x0088
+#define FIMD_VIDOSD_COORD_MASK      0x07FF
+#define FIMD_VIDOSD_HOR_SHIFT       11
+#define FIMD_VIDOSD_VER_SHIFT       0
+#define FIMD_VIDOSD_ALPHA_AEN0      0xFFF000
+#define FIMD_VIDOSD_AEN0_SHIFT      12
+#define FIMD_VIDOSD_ALPHA_AEN1      0x000FFF
+
+/* Frame buffer address registers */
+#define FIMD_VIDWADD0_START         0x00A0
+#define FIMD_VIDWADD0_END           0x00C4
+#define FIMD_VIDWADD0_END           0x00C4
+#define FIMD_VIDWADD1_START         0x00D0
+#define FIMD_VIDWADD1_END           0x00F4
+#define FIMD_VIDWADD2_START         0x0100
+#define FIMD_VIDWADD2_END           0x0110
+#define FIMD_VIDWADD2_PAGEWIDTH     0x1FFF
+#define FIMD_VIDWADD2_OFFSIZE       0x1FFF
+#define FIMD_VIDWADD2_OFFSIZE_SHIFT 13
+#define FIMD_VIDW0ADD0_B2           0x20A0
+#define FIMD_VIDW4ADD0_B2           0x20C0
+
+/* Video interrupt control registers */
+#define FIMD_VIDINTCON0             0x130
+#define FIMD_VIDINTCON1             0x134
+
+/* Window color key registers */
+#define FIMD_WKEYCON_START          0x140
+#define FIMD_WKEYCON_END            0x15C
+#define FIMD_WKEYCON0_COMPKEY       0x00FFFFFF
+#define FIMD_WKEYCON0_CTL_SHIFT     24
+#define FIMD_WKEYCON0_DIRCON        (1 << 24)
+#define FIMD_WKEYCON0_KEYEN         (1 << 25)
+#define FIMD_WKEYCON0_KEYBLEN       (1 << 26)
+/* Window color key alpha control register */
+#define FIMD_WKEYALPHA_START        0x160
+#define FIMD_WKEYALPHA_END          0x16C
+
+/* Dithering control register */
+#define FIMD_DITHMODE               0x170
+
+/* Window alpha control registers */
+#define FIMD_VIDALPHA_ALPHA_LOWER   0x000F0F0F
+#define FIMD_VIDALPHA_ALPHA_UPPER   0x00F0F0F0
+#define FIMD_VIDWALPHA_START        0x21C
+#define FIMD_VIDWALPHA_END          0x240
+
+/* Window color map registers */
+#define FIMD_WINMAP_START           0x180
+#define FIMD_WINMAP_END             0x190
+#define FIMD_WINMAP_EN              (1 << 24)
+#define FIMD_WINMAP_COLOR_MASK      0x00FFFFFF
+
+/* Window palette control registers */
+#define FIMD_WPALCON_HIGH           0x019C
+#define FIMD_WPALCON_LOW            0x01A0
+#define FIMD_WPALCON_UPDATEEN       (1 << 9)
+#define FIMD_WPAL_W0PAL_L           0x07
+#define FIMD_WPAL_W0PAL_L_SHT        0
+#define FIMD_WPAL_W1PAL_L           0x07
+#define FIMD_WPAL_W1PAL_L_SHT       3
+#define FIMD_WPAL_W2PAL_L           0x01
+#define FIMD_WPAL_W2PAL_L_SHT       6
+#define FIMD_WPAL_W2PAL_H           0x06
+#define FIMD_WPAL_W2PAL_H_SHT       8
+#define FIMD_WPAL_W3PAL_L           0x01
+#define FIMD_WPAL_W3PAL_L_SHT       7
+#define FIMD_WPAL_W3PAL_H           0x06
+#define FIMD_WPAL_W3PAL_H_SHT       12
+#define FIMD_WPAL_W4PAL_L           0x01
+#define FIMD_WPAL_W4PAL_L_SHT       8
+#define FIMD_WPAL_W4PAL_H           0x06
+#define FIMD_WPAL_W4PAL_H_SHT       16
+
+/* Trigger control registers */
+#define FIMD_TRIGCON                0x01A4
+#define FIMD_TRIGCON_ROMASK         0x00000004
+
+/* LCD I80 Interface Control */
+#define FIMD_I80IFCON_START         0x01B0
+#define FIMD_I80IFCON_END           0x01BC
+/* Color gain control register */
+#define FIMD_COLORGAINCON           0x01C0
+/* LCD i80 Interface Command Control */
+#define FIMD_LDI_CMDCON0            0x01D0
+#define FIMD_LDI_CMDCON1            0x01D4
+/* I80 System Interface Manual Command Control */
+#define FIMD_SIFCCON0               0x01E0
+#define FIMD_SIFCCON2               0x01E8
+
+/* Hue Control Registers */
+#define FIMD_HUECOEFCR_START        0x01EC
+#define FIMD_HUECOEFCR_END          0x01F4
+#define FIMD_HUECOEFCB_START        0x01FC
+#define FIMD_HUECOEFCB_END          0x0208
+#define FIMD_HUEOFFSET              0x020C
+
+/* Video interrupt control registers */
+#define FIMD_VIDINT_INTFIFOPEND     (1 << 0)
+#define FIMD_VIDINT_INTFRMPEND      (1 << 1)
+#define FIMD_VIDINT_INTI80PEND      (1 << 2)
+#define FIMD_VIDINT_INTEN           (1 << 0)
+#define FIMD_VIDINT_INTFIFOEN       (1 << 1)
+#define FIMD_VIDINT_INTFRMEN        (1 << 12)
+#define FIMD_VIDINT_I80IFDONE       (1 << 17)
+
+/* Window blend equation control registers */
+#define FIMD_BLENDEQ_START          0x0244
+#define FIMD_BLENDEQ_END            0x0250
+#define FIMD_BLENDCON               0x0260
+#define FIMD_ALPHA_8BIT             (1 << 0)
+#define FIMD_BLENDEQ_COEF_MASK      0xF
+
+/* Window RTQOS Control Registers */
+#define FIMD_WRTQOSCON_START        0x0264
+#define FIMD_WRTQOSCON_END          0x0274
+
+/* LCD I80 Interface Command */
+#define FIMD_I80IFCMD_START         0x0280
+#define FIMD_I80IFCMD_END           0x02AC
+
+/* Shadow windows control registers */
+#define FIMD_SHD_ADD0_START         0x40A0
+#define FIMD_SHD_ADD0_END           0x40C0
+#define FIMD_SHD_ADD1_START         0x40D0
+#define FIMD_SHD_ADD1_END           0x40F0
+#define FIMD_SHD_ADD2_START         0x4100
+#define FIMD_SHD_ADD2_END           0x4110
+
+/* Palette memory */
+#define FIMD_PAL_MEM_START          0x2400
+#define FIMD_PAL_MEM_END            0x37FC
+/* Palette memory aliases for windows 0 and 1 */
+#define FIMD_PALMEM_AL_START        0x0400
+#define FIMD_PALMEM_AL_END          0x0BFC
+
+typedef struct {
+    uint8_t r, g, b;
+    /* D[31..24]dummy, D[23..16]rAlpha, D[15..8]gAlpha, D[7..0]bAlpha */
+    uint32_t a;
+} rgba;
+#define RGBA_SIZE  7
+
+typedef void pixel_to_rgb_func(uint32_t pixel, rgba *p);
+typedef struct Exynos4210fimdWindow Exynos4210fimdWindow;
+
+struct Exynos4210fimdWindow {
+    uint32_t wincon;        /* Window control register */
+    uint32_t buf_start[3];  /* Start address for video frame buffer */
+    uint32_t buf_end[3];    /* End address for video frame buffer */
+    uint32_t keycon[2];     /* Window color key registers */
+    uint32_t keyalpha;      /* Color key alpha control register */
+    uint32_t winmap;        /* Window color map register */
+    uint32_t blendeq;       /* Window blending equation control register */
+    uint32_t rtqoscon;      /* Window RTQOS Control Registers */
+    uint32_t palette[256];  /* Palette RAM */
+    uint32_t shadow_buf_start;      /* Start address of shadow frame buffer */
+    uint32_t shadow_buf_end;        /* End address of shadow frame buffer */
+    uint32_t shadow_buf_size;       /* Virtual shadow screen width */
+
+    pixel_to_rgb_func *pixel_to_rgb;
+    void (*draw_line)(Exynos4210fimdWindow *w, uint8_t *src, uint8_t *dst,
+            bool blend);
+    uint32_t (*get_alpha)(Exynos4210fimdWindow *w, uint32_t pix_a);
+    uint16_t lefttop_x, lefttop_y;   /* VIDOSD0 register */
+    uint16_t rightbot_x, rightbot_y; /* VIDOSD1 register */
+    uint32_t osdsize;                /* VIDOSD2&3 register */
+    uint32_t alpha_val[2];           /* VIDOSD2&3, VIDWALPHA registers */
+    uint16_t virtpage_width;         /* VIDWADD2 register */
+    uint16_t virtpage_offsize;       /* VIDWADD2 register */
+    MemoryRegionSection mem_section; /* RAM fragment containing framebuffer */
+    uint8_t *host_fb_addr;           /* Host pointer to window's framebuffer */
+    target_phys_addr_t fb_len;       /* Framebuffer length */
+};
+
+typedef struct {
+    SysBusDevice busdev;
+    MemoryRegion iomem;
+    DisplayState *console;
+    qemu_irq irq[3];
+
+    uint32_t vidcon[4];     /* Video main control registers 0-3 */
+    uint32_t vidtcon[4];    /* Video time control registers 0-3 */
+    uint32_t shadowcon;     /* Window shadow control register */
+    uint32_t winchmap;      /* Channel mapping control register */
+    uint32_t vidintcon[2];  /* Video interrupt control registers */
+    uint32_t dithmode;      /* Dithering control register */
+    uint32_t wpalcon[2];    /* Window palette control registers */
+    uint32_t trigcon;       /* Trigger control register */
+    uint32_t i80ifcon[4];   /* I80 interface control registers */
+    uint32_t colorgaincon;  /* Color gain control register */
+    uint32_t ldi_cmdcon[2]; /* LCD I80 interface command control */
+    uint32_t sifccon[3];    /* I80 System Interface Manual Command Control */
+    uint32_t huecoef_cr[4]; /* Hue control registers */
+    uint32_t huecoef_cb[4]; /* Hue control registers */
+    uint32_t hueoffset;     /* Hue offset control register */
+    uint32_t blendcon;      /* Blending control register */
+    uint32_t i80ifcmd[12];  /* LCD I80 Interface Command */
+
+    Exynos4210fimdWindow window[5];    /* Window-specific registers */
+    uint8_t *ifb;           /* Internal frame buffer */
+    bool invalidate;        /* Image needs to be redrawn */
+    bool enabled;           /* Display controller is enabled */
+} Exynos4210fimdState;
+
+/* Perform byte/halfword/word swap of data according to WINCON */
+static inline void fimd_swap_data(unsigned int swap_ctl, uint64_t *data)
+{
+    int i;
+    uint64_t res;
+    uint64_t x = *data;
+
+    if (swap_ctl & FIMD_WINCON_SWAP_BITS) {
+        res = 0;
+        for (i = 0; i < 64; i++) {
+            if (x & (1ULL << (64 - i))) {
+                res |= (1ULL << i);
+            }
+        }
+        x = res;
+    }
+
+    if (swap_ctl & FIMD_WINCON_SWAP_BYTE) {
+        x = bswap64(x);
+    }
+
+    if (swap_ctl & FIMD_WINCON_SWAP_HWORD) {
+        x = ((x & 0x000000000000FFFFULL) << 48) |
+            ((x & 0x00000000FFFF0000ULL) << 16) |
+            ((x & 0x0000FFFF00000000ULL) >> 16) |
+            ((x & 0xFFFF000000000000ULL) >> 48);
+    }
+
+    if (swap_ctl & FIMD_WINCON_SWAP_WORD) {
+        x = ((x & 0x00000000FFFFFFFFULL) << 32) |
+            ((x & 0xFFFFFFFF00000000ULL) >> 32);
+    }
+
+    *data = x;
+}
+
+/* Conversion routines of Pixel data from frame buffer area to internal RGBA
+ * pixel representation.
+ * Every color component internally represented as 8-bit value. If original
+ * data has less than 8 bit for component, data is extended to 8 bit. For
+ * example, if blue component has only two possible values 0 and 1 it will be
+ * extended to 0 and 0xFF */
+
+/* One bit for alpha representation */
+#define DEF_PIXEL_TO_RGB_A1(N, R, G, B) \
+static void N(uint32_t pixel, rgba *p) \
+{ \
+    p->b = ((pixel & ((1 << (B)) - 1)) << (8 - (B))) | \
+           ((pixel >> (2 * (B) - 8)) & ((1 << (8 - (B))) - 1)); \
+    pixel >>= (B); \
+    p->g = (pixel & ((1 << (G)) - 1)) << (8 - (G)) | \
+           ((pixel >> (2 * (G) - 8)) & ((1 << (8 - (G))) - 1)); \
+    pixel >>= (G); \
+    p->r = (pixel & ((1 << (R)) - 1)) << (8 - (R)) | \
+           ((pixel >> (2 * (R) - 8)) & ((1 << (8 - (R))) - 1)); \
+    pixel >>= (R); \
+    p->a = (pixel & 0x1); \
+}
+
+DEF_PIXEL_TO_RGB_A1(pixel_a444_to_rgb, 4, 4, 4)
+DEF_PIXEL_TO_RGB_A1(pixel_a555_to_rgb, 5, 5, 5)
+DEF_PIXEL_TO_RGB_A1(pixel_a666_to_rgb, 6, 6, 6)
+DEF_PIXEL_TO_RGB_A1(pixel_a665_to_rgb, 6, 6, 5)
+DEF_PIXEL_TO_RGB_A1(pixel_a888_to_rgb, 8, 8, 8)
+DEF_PIXEL_TO_RGB_A1(pixel_a887_to_rgb, 8, 8, 7)
+
+/* Alpha component is always zero */
+#define DEF_PIXEL_TO_RGB_A0(N, R, G, B) \
+static void N(uint32_t pixel, rgba *p) \
+{ \
+    p->b = ((pixel & ((1 << (B)) - 1)) << (8 - (B))) | \
+           ((pixel >> (2 * (B) - 8)) & ((1 << (8 - (B))) - 1)); \
+    pixel >>= (B); \
+    p->g = (pixel & ((1 << (G)) - 1)) << (8 - (G)) | \
+           ((pixel >> (2 * (G) - 8)) & ((1 << (8 - (G))) - 1)); \
+    pixel >>= (G); \
+    p->r = (pixel & ((1 << (R)) - 1)) << (8 - (R)) | \
+           ((pixel >> (2 * (R) - 8)) & ((1 << (8 - (R))) - 1)); \
+    p->a = 0x0; \
+}
+
+DEF_PIXEL_TO_RGB_A0(pixel_565_to_rgb,  5, 6, 5)
+DEF_PIXEL_TO_RGB_A0(pixel_555_to_rgb,  5, 5, 5)
+DEF_PIXEL_TO_RGB_A0(pixel_666_to_rgb,  6, 6, 6)
+DEF_PIXEL_TO_RGB_A0(pixel_888_to_rgb,  8, 8, 8)
+
+/* Alpha component has some meaningful value */
+#define DEF_PIXEL_TO_RGB_A(N, R, G, B, A) \
+static void N(uint32_t pixel, rgba *p) \
+{ \
+    p->b = ((pixel & ((1 << (B)) - 1)) << (8 - (B))) | \
+           ((pixel >> (2 * (B) - 8)) & ((1 << (8 - (B))) - 1)); \
+    pixel >>= (B); \
+    p->g = (pixel & ((1 << (G)) - 1)) << (8 - (G)) | \
+           ((pixel >> (2 * (G) - 8)) & ((1 << (8 - (G))) - 1)); \
+    pixel >>= (G); \
+    p->r = (pixel & ((1 << (R)) - 1)) << (8 - (R)) | \
+           ((pixel >> (2 * (R) - 8)) & ((1 << (8 - (R))) - 1)); \
+    pixel >>= (R); \
+    p->a = (pixel & ((1 << (A)) - 1)) << (8 - (A)) | \
+           ((pixel >> (2 * (A) - 8)) & ((1 << (8 - (A))) - 1)); \
+    p->a = p->a | (p->a << 8) | (p->a << 16); \
+}
+
+DEF_PIXEL_TO_RGB_A(pixel_4444_to_rgb, 4, 4, 4, 4)
+DEF_PIXEL_TO_RGB_A(pixel_8888_to_rgb, 8, 8, 8, 8)
+
+/* Lookup table to extent 2-bit color component to 8 bit */
+static const uint8_t pixel_lutable_2b[4] = {
+     0x0, 0x55, 0xAA, 0xFF
+};
+/* Lookup table to extent 3-bit color component to 8 bit */
+static const uint8_t pixel_lutable_3b[8] = {
+     0x0, 0x24, 0x49, 0x6D, 0x92, 0xB6, 0xDB, 0xFF
+};
+/* Special case for a232 bpp mode */
+static void pixel_a232_to_rgb(uint32_t pixel, rgba *p)
+{
+    p->b = pixel_lutable_2b[(pixel & 0x3)];
+    pixel >>= 2;
+    p->g = pixel_lutable_3b[(pixel & 0x7)];
+    pixel >>= 3;
+    p->r = pixel_lutable_2b[(pixel & 0x3)];
+    pixel >>= 2;
+    p->a = (pixel & 0x1);
+}
+
+/* Special case for (5+1, 5+1, 5+1) mode. Data bit 15 is common LSB
+ * for all three color components */
+static void pixel_1555_to_rgb(uint32_t pixel, rgba *p)
+{
+    uint8_t comm = (pixel >> 15) & 1;
+    p->b = ((((pixel & 0x1F) << 1) | comm) << 2) | ((pixel >> 3) & 0x3);
+    pixel >>= 5;
+    p->g = ((((pixel & 0x1F) << 1) | comm) << 2) | ((pixel >> 3) & 0x3);
+    pixel >>= 5;
+    p->r = ((((pixel & 0x1F) << 1) | comm) << 2) | ((pixel >> 3) & 0x3);
+    p->a = 0x0;
+}
+
+/* Put/get pixel to/from internal LCD Controller framebuffer */
+
+static int put_pixel_ifb(const rgba p, uint8_t *d)
+{
+    *(uint8_t *)d++ = p.r;
+    *(uint8_t *)d++ = p.g;
+    *(uint8_t *)d++ = p.b;
+    *(uint32_t *)d = p.a;
+    return RGBA_SIZE;
+}
+
+static int get_pixel_ifb(const uint8_t *s, rgba *p)
+{
+    p->r = *(uint8_t *)s++;
+    p->g = *(uint8_t *)s++;
+    p->b = *(uint8_t *)s++;
+    p->a = (*(uint32_t *)s) & 0x00FFFFFF;
+    return RGBA_SIZE;
+}
+
+static pixel_to_rgb_func *palette_data_format[8] = {
+    [0] = pixel_565_to_rgb,
+    [1] = pixel_a555_to_rgb,
+    [2] = pixel_666_to_rgb,
+    [3] = pixel_a665_to_rgb,
+    [4] = pixel_a666_to_rgb,
+    [5] = pixel_888_to_rgb,
+    [6] = pixel_a888_to_rgb,
+    [7] = pixel_8888_to_rgb
+};
+
+/* Returns Index in palette data formats table for given window number WINDOW */
+static uint32_t
+exynos4210_fimd_palette_format(Exynos4210fimdState *s, int window)
+{
+    uint32_t ret;
+
+    switch (window) {
+    case 0:
+        ret = (s->wpalcon[1] >> FIMD_WPAL_W0PAL_L_SHT) & FIMD_WPAL_W0PAL_L;
+        if (ret != 7) {
+            ret = 6 - ret;
+        }
+        break;
+    case 1:
+        ret = (s->wpalcon[1] >> FIMD_WPAL_W1PAL_L_SHT) & FIMD_WPAL_W1PAL_L;
+        if (ret != 7) {
+            ret = 6 - ret;
+        }
+        break;
+    case 2:
+        ret = ((s->wpalcon[0] >> FIMD_WPAL_W2PAL_H_SHT) & FIMD_WPAL_W2PAL_H) |
+            ((s->wpalcon[1] >> FIMD_WPAL_W2PAL_L_SHT) & FIMD_WPAL_W2PAL_L);
+        break;
+    case 3:
+        ret = ((s->wpalcon[0] >> FIMD_WPAL_W3PAL_H_SHT) & FIMD_WPAL_W3PAL_H) |
+            ((s->wpalcon[1] >> FIMD_WPAL_W3PAL_L_SHT) & FIMD_WPAL_W3PAL_L);
+        break;
+    case 4:
+        ret = ((s->wpalcon[0] >> FIMD_WPAL_W4PAL_H_SHT) & FIMD_WPAL_W4PAL_H) |
+            ((s->wpalcon[1] >> FIMD_WPAL_W4PAL_L_SHT) & FIMD_WPAL_W4PAL_L);
+        break;
+    default:
+        hw_error("exynos4210.fimd: incorrect window number %d\n", window);
+        ret = 0;
+        break;
+    }
+    return ret;
+}
+
+#define FIMD_1_MINUS_COLOR(x)    \
+            ((0xFF - ((x) & 0xFF)) | (0xFF00 - ((x) & 0xFF00)) | \
+                                  (0xFF0000 - ((x) & 0xFF0000)))
+#define EXTEND_LOWER_HALFBYTE(x) (((x) & 0xF0F0F) | (((x) << 4) & 0xF0F0F0))
+#define EXTEND_UPPER_HALFBYTE(x) (((x) & 0xF0F0F0) | (((x) >> 4) & 0xF0F0F))
+
+/* Multiply three lower bytes of two 32-bit words with each other.
+ * Each byte with values 0-255 is considered as a number with possible values
+ * in a range [0 - 1] */
+static inline uint32_t fimd_mult_each_byte(uint32_t a, uint32_t b)
+{
+    uint32_t tmp;
+    uint32_t ret;
+
+    ret = ((tmp = (((a & 0xFF) * (b & 0xFF)) / 0xFF)) > 0xFF) ? 0xFF : tmp;
+    ret |= ((tmp = ((((a >> 8) & 0xFF) * ((b >> 8) & 0xFF)) / 0xFF)) > 0xFF) ?
+            0xFF00 : tmp << 8;
+    ret |= ((tmp = ((((a >> 16) & 0xFF) * ((b >> 16) & 0xFF)) / 0xFF)) > 0xFF) ?
+            0xFF0000 : tmp << 16;
+    return ret;
+}
+
+/* For each corresponding bytes of two 32-bit words: (a*b + c*d)
+ * Byte values 0-255 are mapped to a range [0 .. 1] */
+static inline uint32_t
+fimd_mult_and_sum_each_byte(uint32_t a, uint32_t b, uint32_t c, uint32_t d)
+{
+    uint32_t tmp;
+    uint32_t ret;
+
+    ret = ((tmp = (((a & 0xFF) * (b & 0xFF) + (c & 0xFF) * (d & 0xFF)) / 0xFF))
+            > 0xFF) ? 0xFF : tmp;
+    ret |= ((tmp = ((((a >> 8) & 0xFF) * ((b >> 8) & 0xFF) + ((c >> 8) & 0xFF) *
+            ((d >> 8) & 0xFF)) / 0xFF)) > 0xFF) ? 0xFF00 : tmp << 8;
+    ret |= ((tmp = ((((a >> 16) & 0xFF) * ((b >> 16) & 0xFF) +
+            ((c >> 16) & 0xFF) * ((d >> 16) & 0xFF)) / 0xFF)) > 0xFF) ?
+                    0xFF0000 : tmp << 16;
+    return ret;
+}
+
+/* These routines cover all possible sources of window's transparent factor
+ * used in blending equation. Choice of routine is affected by WPALCON
+ * registers, BLENDCON register and window's WINCON register */
+
+static uint32_t fimd_get_alpha_pix(Exynos4210fimdWindow *w, uint32_t pix_a)
+{
+    return pix_a;
+}
+
+static uint32_t
+fimd_get_alpha_pix_extlow(Exynos4210fimdWindow *w, uint32_t pix_a)
+{
+    return EXTEND_LOWER_HALFBYTE(pix_a);
+}
+
+static uint32_t
+fimd_get_alpha_pix_exthigh(Exynos4210fimdWindow *w, uint32_t pix_a)
+{
+    return EXTEND_UPPER_HALFBYTE(pix_a);
+}
+
+static uint32_t fimd_get_alpha_mult(Exynos4210fimdWindow *w, uint32_t pix_a)
+{
+    return fimd_mult_each_byte(pix_a, w->alpha_val[0]);
+}
+
+static uint32_t fimd_get_alpha_mult_ext(Exynos4210fimdWindow *w, uint32_t pix_a)
+{
+    return fimd_mult_each_byte(EXTEND_LOWER_HALFBYTE(pix_a),
+            EXTEND_UPPER_HALFBYTE(w->alpha_val[0]));
+}
+
+static uint32_t fimd_get_alpha_aen(Exynos4210fimdWindow *w, uint32_t pix_a)
+{
+    return w->alpha_val[pix_a];
+}
+
+static uint32_t fimd_get_alpha_aen_ext(Exynos4210fimdWindow *w, uint32_t pix_a)
+{
+    return EXTEND_UPPER_HALFBYTE(w->alpha_val[pix_a]);
+}
+
+static uint32_t fimd_get_alpha_sel(Exynos4210fimdWindow *w, uint32_t pix_a)
+{
+    return w->alpha_val[(w->wincon & FIMD_WINCON_ALPHA_SEL) ? 1 : 0];
+}
+
+static uint32_t fimd_get_alpha_sel_ext(Exynos4210fimdWindow *w, uint32_t pix_a)
+{
+    return EXTEND_UPPER_HALFBYTE(w->alpha_val[(w->wincon &
+            FIMD_WINCON_ALPHA_SEL) ? 1 : 0]);
+}
+
+/* Updates currently active alpha value get function for specified window */
+static void fimd_update_get_alpha(Exynos4210fimdState *s, int win)
+{
+    Exynos4210fimdWindow *w = &s->window[win];
+    const bool alpha_is_8bit = s->blendcon & FIMD_ALPHA_8BIT;
+
+    if (w->wincon & FIMD_WINCON_BLD_PIX) {
+        if ((w->wincon & FIMD_WINCON_ALPHA_SEL) && WIN_BPP_MODE_WITH_ALPHA(w)) {
+            /* In this case, alpha component contains meaningful value */
+            if (w->wincon & FIMD_WINCON_ALPHA_MUL) {
+                w->get_alpha = alpha_is_8bit ?
+                        fimd_get_alpha_mult : fimd_get_alpha_mult_ext;
+            } else {
+                w->get_alpha = alpha_is_8bit ?
+                        fimd_get_alpha_pix : fimd_get_alpha_pix_extlow;
+            }
+        } else {
+            if (IS_PALETTIZED_MODE(w) &&
+                  PAL_MODE_WITH_ALPHA(exynos4210_fimd_palette_format(s, win))) {
+                /* Alpha component has 8-bit numeric value */
+                w->get_alpha = alpha_is_8bit ?
+                        fimd_get_alpha_pix : fimd_get_alpha_pix_exthigh;
+            } else {
+                /* Alpha has only two possible values (AEN) */
+                w->get_alpha = alpha_is_8bit ?
+                        fimd_get_alpha_aen : fimd_get_alpha_aen_ext;
+            }
+        }
+    } else {
+        w->get_alpha = alpha_is_8bit ? fimd_get_alpha_sel :
+                fimd_get_alpha_sel_ext;
+    }
+}
+
+/* Blends current window's (w) pixel (foreground pixel *ret) with background
+ * window (w_blend) pixel p_bg according to formula:
+ * NEW_COLOR = a_coef x FG_PIXEL_COLOR + b_coef x BG_PIXEL_COLOR
+ * NEW_ALPHA = p_coef x FG_ALPHA + q_coef x BG_ALPHA
+ */
+static void
+exynos4210_fimd_blend_pixel(Exynos4210fimdWindow *w, rgba p_bg, rgba *ret)
+{
+    rgba p_fg = *ret;
+    uint32_t bg_color = ((p_bg.r & 0xFF) << 16) | ((p_bg.g & 0xFF) << 8) |
+            (p_bg.b & 0xFF);
+    uint32_t fg_color = ((p_fg.r & 0xFF) << 16) | ((p_fg.g & 0xFF) << 8) |
+            (p_fg.b & 0xFF);
+    uint32_t alpha_fg = p_fg.a;
+    int i;
+    /* It is possible that blending equation parameters a and b do not
+     * depend on window BLENEQ register. Account for this with first_coef */
+    enum { A_COEF = 0, B_COEF = 1, P_COEF = 2, Q_COEF = 3, COEF_NUM = 4};
+    uint32_t first_coef = A_COEF;
+    uint32_t blend_param[COEF_NUM];
+
+    if (w->keycon[0] & FIMD_WKEYCON0_KEYEN) {
+        uint32_t colorkey = (w->keycon[1] &
+              ~(w->keycon[0] & FIMD_WKEYCON0_COMPKEY)) & FIMD_WKEYCON0_COMPKEY;
+
+        if ((w->keycon[0] & FIMD_WKEYCON0_DIRCON) &&
+            (bg_color & ~(w->keycon[0] & FIMD_WKEYCON0_COMPKEY)) == colorkey) {
+            /* Foreground pixel is displayed */
+            if (w->keycon[0] & FIMD_WKEYCON0_KEYBLEN) {
+                alpha_fg = w->keyalpha;
+                blend_param[A_COEF] = alpha_fg;
+                blend_param[B_COEF] = FIMD_1_MINUS_COLOR(alpha_fg);
+            } else {
+                alpha_fg = 0;
+                blend_param[A_COEF] = 0xFFFFFF;
+                blend_param[B_COEF] = 0x0;
+            }
+            first_coef = P_COEF;
+        } else if ((w->keycon[0] & FIMD_WKEYCON0_DIRCON) == 0 &&
+            (fg_color & ~(w->keycon[0] & FIMD_WKEYCON0_COMPKEY)) == colorkey) {
+            /* Background pixel is displayed */
+            if (w->keycon[0] & FIMD_WKEYCON0_KEYBLEN) {
+                alpha_fg = w->keyalpha;
+                blend_param[A_COEF] = alpha_fg;
+                blend_param[B_COEF] = FIMD_1_MINUS_COLOR(alpha_fg);
+            } else {
+                alpha_fg = 0;
+                blend_param[A_COEF] = 0x0;
+                blend_param[B_COEF] = 0xFFFFFF;
+            }
+            first_coef = P_COEF;
+        }
+    }
+
+    for (i = first_coef; i < COEF_NUM; i++) {
+        switch ((w->blendeq >> i * 6) & FIMD_BLENDEQ_COEF_MASK) {
+        case 0:
+            blend_param[i] = 0;
+            break;
+        case 1:
+            blend_param[i] = 0xFFFFFF;
+            break;
+        case 2:
+            blend_param[i] = alpha_fg;
+            break;
+        case 3:
+            blend_param[i] = FIMD_1_MINUS_COLOR(alpha_fg);
+            break;
+        case 4:
+            blend_param[i] = p_bg.a;
+            break;
+        case 5:
+            blend_param[i] = FIMD_1_MINUS_COLOR(p_bg.a);
+            break;
+        case 6:
+            blend_param[i] = w->alpha_val[0];
+            break;
+        case 10:
+            blend_param[i] = fg_color;
+            break;
+        case 11:
+            blend_param[i] = FIMD_1_MINUS_COLOR(fg_color);
+            break;
+        case 12:
+            blend_param[i] = bg_color;
+            break;
+        case 13:
+            blend_param[i] = FIMD_1_MINUS_COLOR(bg_color);
+            break;
+        default:
+            hw_error("exynos4210.fimd: blend equation coef illegal value\n");
+            break;
+        }
+    }
+
+    fg_color = fimd_mult_and_sum_each_byte(bg_color, blend_param[B_COEF],
+            fg_color, blend_param[A_COEF]);
+    ret->b = fg_color & 0xFF;
+    fg_color >>= 8;
+    ret->g = fg_color & 0xFF;
+    fg_color >>= 8;
+    ret->r = fg_color & 0xFF;
+    ret->a = fimd_mult_and_sum_each_byte(alpha_fg, blend_param[P_COEF],
+            p_bg.a, blend_param[Q_COEF]);
+}
+
+/* These routines read data from video frame buffer in system RAM, convert
+ * this data to display controller internal representation, if necessary,
+ * perform pixel blending with data, currently presented in internal buffer.
+ * Result is stored in display controller internal frame buffer. */
+
+/* Draw line with index in palette table in RAM frame buffer data */
+#define DEF_DRAW_LINE_PALETTE(N) \
+static void glue(draw_line_palette_, N)(Exynos4210fimdWindow *w, uint8_t *src, \
+               uint8_t *dst, bool blend) \
+{ \
+    int width = w->rightbot_x - w->lefttop_x + 1; \
+    uint8_t *ifb = dst; \
+    uint8_t swap = (w->wincon & FIMD_WINCON_SWAP) >> FIMD_WINCON_SWAP_SHIFT; \
+    uint64_t data; \
+    rgba p, p_old; \
+    int i; \
+    do { \
+        data = ldq_raw((void *)src); \
+        src += 8; \
+        fimd_swap_data(swap, &data); \
+        for (i = (64 / (N) - 1); i >= 0; i--) { \
+            w->pixel_to_rgb(w->palette[(data >> ((N) * i)) & \
+                                   ((1ULL << (N)) - 1)], &p); \
+            p.a = w->get_alpha(w, p.a); \
+            if (blend) { \
+                ifb +=  get_pixel_ifb(ifb, &p_old); \
+                exynos4210_fimd_blend_pixel(w, p_old, &p); \
+            } \
+            dst += put_pixel_ifb(p, dst); \
+        } \
+        width -= (64 / (N)); \
+    } while (width > 0); \
+}
+
+/* Draw line with direct color value in RAM frame buffer data */
+#define DEF_DRAW_LINE_NOPALETTE(N) \
+static void glue(draw_line_, N)(Exynos4210fimdWindow *w, uint8_t *src, \
+                    uint8_t *dst, bool blend) \
+{ \
+    int width = w->rightbot_x - w->lefttop_x + 1; \
+    uint8_t *ifb = dst; \
+    uint8_t swap = (w->wincon & FIMD_WINCON_SWAP) >> FIMD_WINCON_SWAP_SHIFT; \
+    uint64_t data; \
+    rgba p, p_old; \
+    int i; \
+    do { \
+        data = ldq_raw((void *)src); \
+        src += 8; \
+        fimd_swap_data(swap, &data); \
+        for (i = (64 / (N) - 1); i >= 0; i--) { \
+            w->pixel_to_rgb((data >> ((N) * i)) & ((1ULL << (N)) - 1), &p); \
+            p.a = w->get_alpha(w, p.a); \
+            if (blend) { \
+                ifb += get_pixel_ifb(ifb, &p_old); \
+                exynos4210_fimd_blend_pixel(w, p_old, &p); \
+            } \
+            dst += put_pixel_ifb(p, dst); \
+        } \
+        width -= (64 / (N)); \
+    } while (width > 0); \
+}
+
+DEF_DRAW_LINE_PALETTE(1)
+DEF_DRAW_LINE_PALETTE(2)
+DEF_DRAW_LINE_PALETTE(4)
+DEF_DRAW_LINE_PALETTE(8)
+DEF_DRAW_LINE_NOPALETTE(8)  /* 8bpp mode has palette and non-palette versions */
+DEF_DRAW_LINE_NOPALETTE(16)
+DEF_DRAW_LINE_NOPALETTE(32)
+
+/* Special draw line routine for window color map case */
+static void draw_line_mapcolor(Exynos4210fimdWindow *w, uint8_t *src,
+                       uint8_t *dst, bool blend)
+{
+    rgba p, p_old;
+    uint8_t *ifb = dst;
+    int width = w->rightbot_x - w->lefttop_x + 1;
+    uint32_t map_color = w->winmap & FIMD_WINMAP_COLOR_MASK;
+
+    do {
+        pixel_888_to_rgb(map_color, &p);
+        p.a = w->get_alpha(w, p.a);
+        if (blend) {
+            ifb += get_pixel_ifb(ifb, &p_old);
+            exynos4210_fimd_blend_pixel(w, p_old, &p);
+        }
+        dst += put_pixel_ifb(p, dst);
+    } while (--width);
+}
+
+/* Write RGB to QEMU's GraphicConsole framebuffer */
+
+static int put_to_qemufb_pixel8(const rgba p, uint8_t *d)
+{
+    uint32_t pixel = rgb_to_pixel8(p.r, p.g, p.b);
+    *(uint8_t *)d = pixel;
+    return 1;
+}
+
+static int put_to_qemufb_pixel15(const rgba p, uint8_t *d)
+{
+    uint32_t pixel = rgb_to_pixel15(p.r, p.g, p.b);
+    *(uint16_t *)d = pixel;
+    return 2;
+}
+
+static int put_to_qemufb_pixel16(const rgba p, uint8_t *d)
+{
+    uint32_t pixel = rgb_to_pixel16(p.r, p.g, p.b);
+    *(uint16_t *)d = pixel;
+    return 2;
+}
+
+static int put_to_qemufb_pixel24(const rgba p, uint8_t *d)
+{
+    uint32_t pixel = rgb_to_pixel24(p.r, p.g, p.b);
+    *(uint8_t *)d++ = (pixel >>  0) & 0xFF;
+    *(uint8_t *)d++ = (pixel >>  8) & 0xFF;
+    *(uint8_t *)d++ = (pixel >> 16) & 0xFF;
+    return 3;
+}
+
+static int put_to_qemufb_pixel32(const rgba p, uint8_t *d)
+{
+    uint32_t pixel = rgb_to_pixel24(p.r, p.g, p.b);
+    *(uint32_t *)d = pixel;
+    return 4;
+}
+
+/* Routine to copy pixel from internal buffer to QEMU buffer */
+static int (*put_pixel_toqemu)(const rgba p, uint8_t *pixel);
+static inline void fimd_update_putpix_qemu(int bpp)
+{
+    switch (bpp) {
+    case 8:
+        put_pixel_toqemu = put_to_qemufb_pixel8;
+        break;
+    case 15:
+        put_pixel_toqemu = put_to_qemufb_pixel15;
+        break;
+    case 16:
+        put_pixel_toqemu = put_to_qemufb_pixel16;
+        break;
+    case 24:
+        put_pixel_toqemu = put_to_qemufb_pixel24;
+        break;
+    case 32:
+        put_pixel_toqemu = put_to_qemufb_pixel32;
+        break;
+    default:
+        hw_error("exynos4210.fimd: unsupported BPP (%d)", bpp);
+        break;
+    }
+}
+
+/* Routine to copy a line from internal frame buffer to QEMU display */
+static void fimd_copy_line_toqemu(int width, uint8_t *src, uint8_t *dst)
+{
+    rgba p;
+
+    do {
+        src += get_pixel_ifb(src, &p);
+        dst += put_pixel_toqemu(p, dst);
+    } while (--width);
+}
+
+/* Parse BPPMODE_F = WINCON1[5:2] bits */
+static void exynos4210_fimd_update_win_bppmode(Exynos4210fimdState *s, int win)
+{
+    Exynos4210fimdWindow *w = &s->window[win];
+
+    if (w->winmap & FIMD_WINMAP_EN) {
+        w->draw_line = draw_line_mapcolor;
+        return;
+    }
+
+    switch (WIN_BPP_MODE(w)) {
+    case 0:
+        w->draw_line = draw_line_palette_1;
+        w->pixel_to_rgb =
+                palette_data_format[exynos4210_fimd_palette_format(s, win)];
+        break;
+    case 1:
+        w->draw_line = draw_line_palette_2;
+        w->pixel_to_rgb =
+                palette_data_format[exynos4210_fimd_palette_format(s, win)];
+        break;
+    case 2:
+        w->draw_line = draw_line_palette_4;
+        w->pixel_to_rgb =
+                palette_data_format[exynos4210_fimd_palette_format(s, win)];
+        break;
+    case 3:
+        w->draw_line = draw_line_palette_8;
+        w->pixel_to_rgb =
+                palette_data_format[exynos4210_fimd_palette_format(s, win)];
+        break;
+    case 4:
+        w->draw_line = draw_line_8;
+        w->pixel_to_rgb = pixel_a232_to_rgb;
+        break;
+    case 5:
+        w->draw_line = draw_line_16;
+        w->pixel_to_rgb = pixel_565_to_rgb;
+        break;
+    case 6:
+        w->draw_line = draw_line_16;
+        w->pixel_to_rgb = pixel_a555_to_rgb;
+        break;
+    case 7:
+        w->draw_line = draw_line_16;
+        w->pixel_to_rgb = pixel_1555_to_rgb;
+        break;
+    case 8:
+        w->draw_line = draw_line_32;
+        w->pixel_to_rgb = pixel_666_to_rgb;
+        break;
+    case 9:
+        w->draw_line = draw_line_32;
+        w->pixel_to_rgb = pixel_a665_to_rgb;
+        break;
+    case 10:
+        w->draw_line = draw_line_32;
+        w->pixel_to_rgb = pixel_a666_to_rgb;
+        break;
+    case 11:
+        w->draw_line = draw_line_32;
+        w->pixel_to_rgb = pixel_888_to_rgb;
+        break;
+    case 12:
+        w->draw_line = draw_line_32;
+        w->pixel_to_rgb = pixel_a887_to_rgb;
+        break;
+    case 13:
+        w->draw_line = draw_line_32;
+        if ((w->wincon & FIMD_WINCON_BLD_PIX) && (w->wincon &
+                FIMD_WINCON_ALPHA_SEL)) {
+            w->pixel_to_rgb = pixel_8888_to_rgb;
+        } else {
+            w->pixel_to_rgb = pixel_a888_to_rgb;
+        }
+        break;
+    case 14:
+        w->draw_line = draw_line_16;
+        if ((w->wincon & FIMD_WINCON_BLD_PIX) && (w->wincon &
+                FIMD_WINCON_ALPHA_SEL)) {
+            w->pixel_to_rgb = pixel_4444_to_rgb;
+        } else {
+            w->pixel_to_rgb = pixel_a444_to_rgb;
+        }
+        break;
+    case 15:
+        w->draw_line = draw_line_16;
+        w->pixel_to_rgb = pixel_555_to_rgb;
+        break;
+    }
+}
+
+#if EXYNOS4210_FIMD_MODE_TRACE > 0
+static const char *exynos4210_fimd_get_bppmode(int mode_code)
+{
+    switch (mode_code) {
+    case 0:
+        return "1 bpp";
+    case 1:
+        return "2 bpp";
+    case 2:
+        return "4 bpp";
+    case 3:
+        return "8 bpp (palettized)";
+    case 4:
+        return "8 bpp (non-palettized, A: 1-R:2-G:3-B:2)";
+    case 5:
+        return "16 bpp (non-palettized, R:5-G:6-B:5)";
+    case 6:
+        return "16 bpp (non-palettized, A:1-R:5-G:5-B:5)";
+    case 7:
+        return "16 bpp (non-palettized, I :1-R:5-G:5-B:5)";
+    case 8:
+        return "Unpacked 18 bpp (non-palettized, R:6-G:6-B:6)";
+    case 9:
+        return "Unpacked 18bpp (non-palettized,A:1-R:6-G:6-B:5)";
+    case 10:
+        return "Unpacked 19bpp (non-palettized,A:1-R:6-G:6-B:6)";
+    case 11:
+        return "Unpacked 24 bpp (non-palettized R:8-G:8-B:8)";
+    case 12:
+        return "Unpacked 24 bpp (non-palettized A:1-R:8-G:8-B:7)";
+    case 13:
+        return "Unpacked 25 bpp (non-palettized A:1-R:8-G:8-B:8)";
+    case 14:
+        return "Unpacked 13 bpp (non-palettized A:1-R:4-G:4-B:4)";
+    case 15:
+        return "Unpacked 15 bpp (non-palettized R:5-G:5-B:5)";
+    default:
+        return "Non-existing bpp mode";
+    }
+}
+
+static inline void exynos4210_fimd_trace_bppmode(Exynos4210fimdState *s,
+                int win_num, uint32_t val)
+{
+    Exynos4210fimdWindow *w = &s->window[win_num];
+
+    if (w->winmap & FIMD_WINMAP_EN) {
+        printf("QEMU FIMD: Window %d is mapped with MAPCOLOR=0x%x\n",
+                win_num, w->winmap & 0xFFFFFF);
+        return;
+    }
+
+    if ((val != 0xFFFFFFFF) && ((w->wincon >> 2) & 0xF) == ((val >> 2) & 0xF)) {
+        return;
+    }
+    printf("QEMU FIMD: Window %d BPP mode set to %s\n", win_num,
+        exynos4210_fimd_get_bppmode((val >> 2) & 0xF));
+}
+#else
+static inline void exynos4210_fimd_trace_bppmode(Exynos4210fimdState *s,
+        int win_num, uint32_t val)
+{
+
+}
+#endif
+
+static inline int fimd_get_buffer_id(Exynos4210fimdWindow *w)
+{
+    switch (w->wincon & FIMD_WINCON_BUFSTATUS) {
+    case FIMD_WINCON_BUF0_STAT:
+        return 0;
+    case FIMD_WINCON_BUF1_STAT:
+        return 1;
+    case FIMD_WINCON_BUF2_STAT:
+        return 2;
+    default:
+        DPRINT_ERROR("Non-existent buffer index\n");
+        return 0;
+    }
+}
+
+/* Updates specified window's MemorySection based on values of WINCON,
+ * VIDOSDA, VIDOSDB, VIDWADDx and SHADOWCON registers */
+static void fimd_update_memory_section(Exynos4210fimdState *s, unsigned win)
+{
+    Exynos4210fimdWindow *w = &s->window[win];
+    target_phys_addr_t fb_start_addr, fb_mapped_len;
+
+    if (!s->enabled || !(w->wincon & FIMD_WINCON_ENWIN) ||
+            FIMD_WINDOW_PROTECTED(s->shadowcon, win)) {
+        return;
+    }
+
+    if (w->host_fb_addr) {
+        cpu_physical_memory_unmap(w->host_fb_addr, w->fb_len, 0, 0);
+        w->host_fb_addr = NULL;
+        w->fb_len = 0;
+    }
+
+    fb_start_addr = w->buf_start[fimd_get_buffer_id(w)];
+    /* Total number of bytes of virtual screen used by current window */
+    w->fb_len = fb_mapped_len = (w->virtpage_width + w->virtpage_offsize) *
+            (w->rightbot_y - w->lefttop_y + 1);
+    w->mem_section = memory_region_find(sysbus_address_space(&s->busdev),
+            fb_start_addr, w->fb_len);
+    assert(w->mem_section.mr);
+    assert(w->mem_section.offset_within_address_space == fb_start_addr);
+    DPRINT_TRACE("Window %u framebuffer changed: address=0x%08x, len=0x%x\n",
+            win, fb_start_addr, w->fb_len);
+
+    if (w->mem_section.size != w->fb_len ||
+            !memory_region_is_ram(w->mem_section.mr)) {
+        DPRINT_ERROR("Failed to find window %u framebuffer region\n", win);
+        goto error_return;
+    }
+
+    w->host_fb_addr = cpu_physical_memory_map(fb_start_addr, &fb_mapped_len, 0);
+    if (!w->host_fb_addr) {
+        DPRINT_ERROR("Failed to map window %u framebuffer\n", win);
+        goto error_return;
+    }
+
+    if (fb_mapped_len != w->fb_len) {
+        DPRINT_ERROR("Window %u mapped framebuffer length is less then "
+                "expected\n", win);
+        cpu_physical_memory_unmap(w->host_fb_addr, fb_mapped_len, 0, 0);
+        goto error_return;
+    }
+    return;
+
+error_return:
+    w->mem_section.mr = NULL;
+    w->mem_section.size = 0;
+    w->host_fb_addr = NULL;
+    w->fb_len = 0;
+}
+
+static void exynos4210_fimd_enable(Exynos4210fimdState *s, bool enabled)
+{
+    if (enabled && !s->enabled) {
+        unsigned w;
+        s->enabled = true;
+        for (w = 0; w < NUM_OF_WINDOWS; w++) {
+            fimd_update_memory_section(s, w);
+        }
+    }
+    s->enabled = enabled;
+    DPRINT_TRACE("display controller %s\n", enabled ? "enabled" : "disabled");
+}
+
+static inline uint32_t unpack_upper_4(uint32_t x)
+{
+    return ((x & 0xF00) << 12) | ((x & 0xF0) << 8) | ((x & 0xF) << 4);
+}
+
+static inline uint32_t pack_upper_4(uint32_t x)
+{
+    return (((x & 0xF00000) >> 12) | ((x & 0xF000) >> 8) |
+            ((x & 0xF0) >> 4)) & 0xFFF;
+}
+
+static void exynos4210_fimd_update_irq(Exynos4210fimdState *s)
+{
+    if (!(s->vidintcon[0] & FIMD_VIDINT_INTEN)) {
+        qemu_irq_lower(s->irq[0]);
+        qemu_irq_lower(s->irq[1]);
+        qemu_irq_lower(s->irq[2]);
+        return;
+    }
+    if ((s->vidintcon[0] & FIMD_VIDINT_INTFIFOEN) &&
+            (s->vidintcon[1] & FIMD_VIDINT_INTFIFOPEND)) {
+        qemu_irq_raise(s->irq[0]);
+    } else {
+        qemu_irq_lower(s->irq[0]);
+    }
+    if ((s->vidintcon[0] & FIMD_VIDINT_INTFRMEN) &&
+            (s->vidintcon[1] & FIMD_VIDINT_INTFRMPEND)) {
+        qemu_irq_raise(s->irq[1]);
+    } else {
+        qemu_irq_lower(s->irq[1]);
+    }
+    if ((s->vidintcon[0] & FIMD_VIDINT_I80IFDONE) &&
+            (s->vidintcon[1] & FIMD_VIDINT_INTI80PEND)) {
+        qemu_irq_raise(s->irq[2]);
+    } else {
+        qemu_irq_lower(s->irq[2]);
+    }
+}
+
+static void exynos4210_fimd_invalidate(void *opaque)
+{
+    Exynos4210fimdState *s = (Exynos4210fimdState *)opaque;
+    s->invalidate = true;
+}
+
+static void exynos4210_update_resolution(Exynos4210fimdState *s)
+{
+    /* LCD resolution is stored in VIDEO TIME CONTROL REGISTER 2 */
+    uint32_t width = ((s->vidtcon[2] >> FIMD_VIDTCON2_HOR_SHIFT) &
+            FIMD_VIDTCON2_SIZE_MASK) + 1;
+    uint32_t height = ((s->vidtcon[2] >> FIMD_VIDTCON2_VER_SHIFT) &
+            FIMD_VIDTCON2_SIZE_MASK) + 1;
+
+    if (s->ifb == NULL || ds_get_width(s->console) != width ||
+            ds_get_height(s->console) != height) {
+        DPRINT_L1("Resolution changed from %ux%u to %ux%u\n",
+           ds_get_width(s->console), ds_get_height(s->console), width, height);
+        qemu_console_resize(s->console, width, height);
+        s->ifb = g_realloc(s->ifb, width * height * RGBA_SIZE + 1);
+        memset(s->ifb, 0, width * height * RGBA_SIZE + 1);
+        exynos4210_fimd_invalidate(s);
+    }
+}
+
+static void exynos4210_fimd_update(void *opaque)
+{
+    Exynos4210fimdState *s = (Exynos4210fimdState *)opaque;
+    Exynos4210fimdWindow *w;
+    int i, line;
+    target_phys_addr_t fb_line_addr, inc_size;
+    int scrn_height;
+    int first_line = -1, last_line = -1, scrn_width;
+    bool blend = false;
+    uint8_t *host_fb_addr;
+    bool is_dirty = false;
+    const int global_width = (s->vidtcon[2] & FIMD_VIDTCON2_SIZE_MASK) + 1;
+    const int global_height = ((s->vidtcon[2] >> FIMD_VIDTCON2_VER_SHIFT) &
+            FIMD_VIDTCON2_SIZE_MASK) + 1;
+
+    if (!s || !s->console || !ds_get_bits_per_pixel(s->console) ||
+            !s->enabled) {
+        return;
+    }
+    exynos4210_update_resolution(s);
+
+    for (i = 0; i < NUM_OF_WINDOWS; i++) {
+        w = &s->window[i];
+        if ((w->wincon & FIMD_WINCON_ENWIN) && w->host_fb_addr) {
+            scrn_height = w->rightbot_y - w->lefttop_y + 1;
+            scrn_width = w->virtpage_width;
+            /* Total width of virtual screen page in bytes */
+            inc_size = scrn_width + w->virtpage_offsize;
+            memory_region_sync_dirty_bitmap(w->mem_section.mr);
+            host_fb_addr = w->host_fb_addr;
+            fb_line_addr = w->mem_section.offset_within_region;
+
+            for (line = 0; line < scrn_height; line++) {
+                is_dirty = memory_region_get_dirty(w->mem_section.mr,
+                            fb_line_addr, scrn_width, DIRTY_MEMORY_VGA);
+
+                if (s->invalidate || is_dirty) {
+                    if (first_line == -1) {
+                        first_line = line;
+                    }
+                    last_line = line;
+                    w->draw_line(w, host_fb_addr, s->ifb +
+                        w->lefttop_x * RGBA_SIZE + (w->lefttop_y + line) *
+                        global_width * RGBA_SIZE, blend);
+                }
+                host_fb_addr += inc_size;
+                fb_line_addr += inc_size;
+                is_dirty = false;
+            }
+            memory_region_reset_dirty(w->mem_section.mr,
+                w->mem_section.offset_within_region,
+                w->fb_len, DIRTY_MEMORY_VGA);
+            blend = true;
+        }
+    }
+
+    /* Copy resulting image to QEMU_CONSOLE. */
+    if (first_line >= 0) {
+        uint8_t *d;
+        int bpp;
+
+        bpp = ds_get_bits_per_pixel(s->console);
+        fimd_update_putpix_qemu(bpp);
+        bpp = (bpp + 1) >> 3;
+        d = ds_get_data(s->console);
+        for (line = first_line; line <= last_line; line++) {
+            fimd_copy_line_toqemu(global_width, s->ifb + global_width * line *
+                    RGBA_SIZE, d + global_width * line * bpp);
+        }
+        dpy_update(s->console, 0, 0, global_width, global_height);
+    }
+    s->invalidate = false;
+    s->vidintcon[1] |= FIMD_VIDINT_INTFRMPEND;
+    if ((s->vidcon[0] & FIMD_VIDCON0_ENVID_F) == 0) {
+        exynos4210_fimd_enable(s, false);
+    }
+    exynos4210_fimd_update_irq(s);
+}
+
+static void exynos4210_fimd_reset(DeviceState *d)
+{
+    Exynos4210fimdState *s = DO_UPCAST(Exynos4210fimdState, busdev.qdev, d);
+    unsigned w;
+
+    DPRINT_TRACE("Display controller reset\n");
+    /* Set all display controller registers to 0 */
+    memset(&s->vidcon, 0, (uint8_t *)&s->window - (uint8_t *)&s->vidcon);
+    for (w = 0; w < NUM_OF_WINDOWS; w++) {
+        memset(&s->window[w], 0, sizeof(Exynos4210fimdWindow));
+        s->window[w].blendeq = 0xC2;
+        exynos4210_fimd_update_win_bppmode(s, w);
+        exynos4210_fimd_trace_bppmode(s, w, 0xFFFFFFFF);
+        fimd_update_get_alpha(s, w);
+    }
+
+    if (s->ifb != NULL) {
+        g_free(s->ifb);
+    }
+    s->ifb = NULL;
+
+    exynos4210_fimd_invalidate(s);
+    exynos4210_fimd_enable(s, false);
+    /* Some registers have non-zero initial values */
+    s->winchmap = 0x7D517D51;
+    s->colorgaincon = 0x10040100;
+    s->huecoef_cr[0] = s->huecoef_cr[3] = 0x01000100;
+    s->huecoef_cb[0] = s->huecoef_cb[3] = 0x01000100;
+    s->hueoffset = 0x01800080;
+}
+
+static void exynos4210_fimd_write(void *opaque, target_phys_addr_t offset,
+                              uint64_t val, unsigned size)
+{
+    Exynos4210fimdState *s = (Exynos4210fimdState *)opaque;
+    unsigned w, i;
+    uint32_t old_value;
+
+    DPRINT_L2("write offset 0x%08x, value=%llu(0x%08llx)\n", offset,
+            (long long unsigned int)val, (long long unsigned int)val);
+
+    switch (offset) {
+    case FIMD_VIDCON0:
+        if ((val & FIMD_VIDCON0_ENVID_MASK) == FIMD_VIDCON0_ENVID_MASK) {
+            exynos4210_fimd_enable(s, true);
+        } else {
+            if ((val & FIMD_VIDCON0_ENVID) == 0) {
+                exynos4210_fimd_enable(s, false);
+            }
+        }
+        s->vidcon[0] = val;
+        break;
+    case FIMD_VIDCON1:
+        /* Leave read-only bits as is */
+        val = (val & (~FIMD_VIDCON1_ROMASK)) |
+                (s->vidcon[1] & FIMD_VIDCON1_ROMASK);
+        s->vidcon[1] = val;
+        break;
+    case FIMD_VIDCON2 ... FIMD_VIDCON3:
+        s->vidcon[(offset) >> 2] = val;
+        break;
+    case FIMD_VIDTCON_START ... FIMD_VIDTCON_END:
+        s->vidtcon[(offset - FIMD_VIDTCON_START) >> 2] = val;
+        break;
+    case FIMD_WINCON_START ... FIMD_WINCON_END:
+        w = (offset - FIMD_WINCON_START) >> 2;
+        /* Window's current buffer ID */
+        i = fimd_get_buffer_id(&s->window[w]);
+        old_value = s->window[w].wincon;
+        val = (val & ~FIMD_WINCON_ROMASK) |
+                (s->window[w].wincon & FIMD_WINCON_ROMASK);
+        if (w == 0) {
+            /* Window 0 wincon ALPHA_MUL bit must always be 0 */
+            val &= ~FIMD_WINCON_ALPHA_MUL;
+        }
+        exynos4210_fimd_trace_bppmode(s, w, val);
+        switch (val & FIMD_WINCON_BUFSELECT) {
+        case FIMD_WINCON_BUF0_SEL:
+            val &= ~FIMD_WINCON_BUFSTATUS;
+            break;
+        case FIMD_WINCON_BUF1_SEL:
+            val = (val & ~FIMD_WINCON_BUFSTAT_H) | FIMD_WINCON_BUFSTAT_L;
+            break;
+        case FIMD_WINCON_BUF2_SEL:
+            if (val & FIMD_WINCON_BUFMODE) {
+                val = (val & ~FIMD_WINCON_BUFSTAT_L) | FIMD_WINCON_BUFSTAT_H;
+            }
+            break;
+        default:
+            break;
+        }
+        s->window[w].wincon = val;
+        exynos4210_fimd_update_win_bppmode(s, w);
+        fimd_update_get_alpha(s, w);
+        if ((i != fimd_get_buffer_id(&s->window[w])) ||
+                (!(old_value & FIMD_WINCON_ENWIN) && (s->window[w].wincon &
+                        FIMD_WINCON_ENWIN))) {
+            fimd_update_memory_section(s, w);
+        }
+        break;
+    case FIMD_SHADOWCON:
+        old_value = s->shadowcon;
+        s->shadowcon = val;
+        for (w = 0; w < NUM_OF_WINDOWS; w++) {
+            if (FIMD_WINDOW_PROTECTED(old_value, w) &&
+                    !FIMD_WINDOW_PROTECTED(s->shadowcon, w)) {
+                fimd_update_memory_section(s, w);
+            }
+        }
+        break;
+    case FIMD_WINCHMAP:
+        s->winchmap = val;
+        break;
+    case FIMD_VIDOSD_START ... FIMD_VIDOSD_END:
+        w = (offset - FIMD_VIDOSD_START) >> 4;
+        i = ((offset - FIMD_VIDOSD_START) & 0xF) >> 2;
+        switch (i) {
+        case 0:
+            old_value = s->window[w].lefttop_y;
+            s->window[w].lefttop_x = (val >> FIMD_VIDOSD_HOR_SHIFT) &
+                                      FIMD_VIDOSD_COORD_MASK;
+            s->window[w].lefttop_y = (val >> FIMD_VIDOSD_VER_SHIFT) &
+                                      FIMD_VIDOSD_COORD_MASK;
+            if (s->window[w].lefttop_y != old_value) {
+                fimd_update_memory_section(s, w);
+            }
+            break;
+        case 1:
+            old_value = s->window[w].rightbot_y;
+            s->window[w].rightbot_x = (val >> FIMD_VIDOSD_HOR_SHIFT) &
+                                       FIMD_VIDOSD_COORD_MASK;
+            s->window[w].rightbot_y = (val >> FIMD_VIDOSD_VER_SHIFT) &
+                                       FIMD_VIDOSD_COORD_MASK;
+            if (s->window[w].rightbot_y != old_value) {
+                fimd_update_memory_section(s, w);
+            }
+            break;
+        case 2:
+            if (w == 0) {
+                s->window[w].osdsize = val;
+            } else {
+                s->window[w].alpha_val[0] =
+                    unpack_upper_4((val & FIMD_VIDOSD_ALPHA_AEN0) >>
+                    FIMD_VIDOSD_AEN0_SHIFT) |
+                    (s->window[w].alpha_val[0] & FIMD_VIDALPHA_ALPHA_LOWER);
+                s->window[w].alpha_val[1] =
+                    unpack_upper_4(val & FIMD_VIDOSD_ALPHA_AEN1) |
+                    (s->window[w].alpha_val[1] & FIMD_VIDALPHA_ALPHA_LOWER);
+            }
+            break;
+        case 3:
+            if (w != 1 && w != 2) {
+                DPRINT_ERROR("Bad write offset 0x%08x\n", offset);
+                return;
+            }
+            s->window[w].osdsize = val;
+            break;
+        }
+        break;
+    case FIMD_VIDWADD0_START ... FIMD_VIDWADD0_END:
+        w = (offset - FIMD_VIDWADD0_START) >> 3;
+        i = ((offset - FIMD_VIDWADD0_START) >> 2) & 1;
+        if (i == fimd_get_buffer_id(&s->window[w]) &&
+                s->window[w].buf_start[i] != val) {
+            s->window[w].buf_start[i] = val;
+            fimd_update_memory_section(s, w);
+            break;
+        }
+        s->window[w].buf_start[i] = val;
+        break;
+    case FIMD_VIDWADD1_START ... FIMD_VIDWADD1_END:
+        w = (offset - FIMD_VIDWADD1_START) >> 3;
+        i = ((offset - FIMD_VIDWADD1_START) >> 2) & 1;
+        s->window[w].buf_end[i] = val;
+        break;
+    case FIMD_VIDWADD2_START ... FIMD_VIDWADD2_END:
+        w = (offset - FIMD_VIDWADD2_START) >> 2;
+        if (((val & FIMD_VIDWADD2_PAGEWIDTH) != s->window[w].virtpage_width) ||
+            (((val >> FIMD_VIDWADD2_OFFSIZE_SHIFT) & FIMD_VIDWADD2_OFFSIZE) !=
+                        s->window[w].virtpage_offsize)) {
+            s->window[w].virtpage_width = val & FIMD_VIDWADD2_PAGEWIDTH;
+            s->window[w].virtpage_offsize =
+                (val >> FIMD_VIDWADD2_OFFSIZE_SHIFT) & FIMD_VIDWADD2_OFFSIZE;
+            fimd_update_memory_section(s, w);
+        }
+        break;
+    case FIMD_VIDINTCON0:
+        s->vidintcon[0] = val;
+        break;
+    case FIMD_VIDINTCON1:
+        s->vidintcon[1] &= ~(val & 7);
+        exynos4210_fimd_update_irq(s);
+        break;
+    case FIMD_WKEYCON_START ... FIMD_WKEYCON_END:
+        w = ((offset - FIMD_WKEYCON_START) >> 3) + 1;
+        i = ((offset - FIMD_WKEYCON_START) >> 2) & 1;
+        s->window[w].keycon[i] = val;
+        break;
+    case FIMD_WKEYALPHA_START ... FIMD_WKEYALPHA_END:
+        w = ((offset - FIMD_WKEYALPHA_START) >> 2) + 1;
+        s->window[w].keyalpha = val;
+        break;
+    case FIMD_DITHMODE:
+        s->dithmode = val;
+        break;
+    case FIMD_WINMAP_START ... FIMD_WINMAP_END:
+        w = (offset - FIMD_WINMAP_START) >> 2;
+        old_value = s->window[w].winmap;
+        s->window[w].winmap = val;
+        if ((val & FIMD_WINMAP_EN) ^ (old_value & FIMD_WINMAP_EN)) {
+            exynos4210_fimd_invalidate(s);
+            exynos4210_fimd_update_win_bppmode(s, w);
+            exynos4210_fimd_trace_bppmode(s, w, 0xFFFFFFFF);
+            exynos4210_fimd_update(s);
+        }
+        break;
+    case FIMD_WPALCON_HIGH ... FIMD_WPALCON_LOW:
+        i = (offset - FIMD_WPALCON_HIGH) >> 2;
+        s->wpalcon[i] = val;
+        if (s->wpalcon[1] & FIMD_WPALCON_UPDATEEN) {
+            for (w = 0; w < NUM_OF_WINDOWS; w++) {
+                exynos4210_fimd_update_win_bppmode(s, w);
+                fimd_update_get_alpha(s, w);
+            }
+        }
+        break;
+    case FIMD_TRIGCON:
+        val = (val & ~FIMD_TRIGCON_ROMASK) | (s->trigcon & FIMD_TRIGCON_ROMASK);
+        s->trigcon = val;
+        break;
+    case FIMD_I80IFCON_START ... FIMD_I80IFCON_END:
+        s->i80ifcon[(offset - FIMD_I80IFCON_START) >> 2] = val;
+        break;
+    case FIMD_COLORGAINCON:
+        s->colorgaincon = val;
+        break;
+    case FIMD_LDI_CMDCON0 ... FIMD_LDI_CMDCON1:
+        s->ldi_cmdcon[(offset - FIMD_LDI_CMDCON0) >> 2] = val;
+        break;
+    case FIMD_SIFCCON0 ... FIMD_SIFCCON2:
+        i = (offset - FIMD_SIFCCON0) >> 2;
+        if (i != 2) {
+            s->sifccon[i] = val;
+        }
+        break;
+    case FIMD_HUECOEFCR_START ... FIMD_HUECOEFCR_END:
+        i = (offset - FIMD_HUECOEFCR_START) >> 2;
+        s->huecoef_cr[i] = val;
+        break;
+    case FIMD_HUECOEFCB_START ... FIMD_HUECOEFCB_END:
+        i = (offset - FIMD_HUECOEFCB_START) >> 2;
+        s->huecoef_cb[i] = val;
+        break;
+    case FIMD_HUEOFFSET:
+        s->hueoffset = val;
+        break;
+    case FIMD_VIDWALPHA_START ... FIMD_VIDWALPHA_END:
+        w = ((offset - FIMD_VIDWALPHA_START) >> 3);
+        i = ((offset - FIMD_VIDWALPHA_START) >> 2) & 1;
+        if (w == 0) {
+            s->window[w].alpha_val[i] = val;
+        } else {
+            s->window[w].alpha_val[i] = (val & FIMD_VIDALPHA_ALPHA_LOWER) |
+                (s->window[w].alpha_val[i] & FIMD_VIDALPHA_ALPHA_UPPER);
+        }
+        break;
+    case FIMD_BLENDEQ_START ... FIMD_BLENDEQ_END:
+        s->window[(offset - FIMD_BLENDEQ_START) >> 2].blendeq = val;
+        break;
+    case FIMD_BLENDCON:
+        old_value = s->blendcon;
+        s->blendcon = val;
+        if ((s->blendcon & FIMD_ALPHA_8BIT) != (old_value & FIMD_ALPHA_8BIT)) {
+            for (w = 0; w < NUM_OF_WINDOWS; w++) {
+                fimd_update_get_alpha(s, w);
+            }
+        }
+        break;
+    case FIMD_WRTQOSCON_START ... FIMD_WRTQOSCON_END:
+        s->window[(offset - FIMD_WRTQOSCON_START) >> 2].rtqoscon = val;
+        break;
+    case FIMD_I80IFCMD_START ... FIMD_I80IFCMD_END:
+        s->i80ifcmd[(offset - FIMD_I80IFCMD_START) >> 2] = val;
+        break;
+    case FIMD_VIDW0ADD0_B2 ... FIMD_VIDW4ADD0_B2:
+        if (offset & 0x0004) {
+            DPRINT_ERROR("bad write offset 0x%08x\n", offset);
+            break;
+        }
+        w = (offset - FIMD_VIDW0ADD0_B2) >> 3;
+        if (fimd_get_buffer_id(&s->window[w]) == 2 &&
+                s->window[w].buf_start[2] != val) {
+            s->window[w].buf_start[2] = val;
+            fimd_update_memory_section(s, w);
+            break;
+        }
+        s->window[w].buf_start[2] = val;
+        break;
+    case FIMD_SHD_ADD0_START ... FIMD_SHD_ADD0_END:
+        if (offset & 0x0004) {
+            DPRINT_ERROR("bad write offset 0x%08x\n", offset);
+            break;
+        }
+        s->window[(offset - FIMD_SHD_ADD0_START) >> 3].shadow_buf_start = val;
+        break;
+    case FIMD_SHD_ADD1_START ... FIMD_SHD_ADD1_END:
+        if (offset & 0x0004) {
+            DPRINT_ERROR("bad write offset 0x%08x\n", offset);
+            break;
+        }
+        s->window[(offset - FIMD_SHD_ADD1_START) >> 3].shadow_buf_end = val;
+        break;
+    case FIMD_SHD_ADD2_START ... FIMD_SHD_ADD2_END:
+        s->window[(offset - FIMD_SHD_ADD2_START) >> 2].shadow_buf_size = val;
+        break;
+    case FIMD_PAL_MEM_START ... FIMD_PAL_MEM_END:
+        w = (offset - FIMD_PAL_MEM_START) >> 10;
+        i = ((offset - FIMD_PAL_MEM_START) >> 2) & 0xFF;
+        s->window[w].palette[i] = val;
+        break;
+    case FIMD_PALMEM_AL_START ... FIMD_PALMEM_AL_END:
+        /* Palette memory aliases for windows 0 and 1 */
+        w = (offset - FIMD_PALMEM_AL_START) >> 10;
+        i = ((offset - FIMD_PALMEM_AL_START) >> 2) & 0xFF;
+        s->window[w].palette[i] = val;
+        break;
+    default:
+        DPRINT_ERROR("bad write offset 0x%08x\n", offset);
+        break;
+    }
+}
+
+static uint64_t exynos4210_fimd_read(void *opaque, target_phys_addr_t offset,
+                                  unsigned size)
+{
+    Exynos4210fimdState *s = (Exynos4210fimdState *)opaque;
+    int w, i;
+    uint32_t ret = 0;
+
+    DPRINT_L2("read offset 0x%08x\n", offset);
+
+    switch (offset) {
+    case FIMD_VIDCON0 ... FIMD_VIDCON3:
+        return s->vidcon[(offset - FIMD_VIDCON0) >> 2];
+    case FIMD_VIDTCON_START ... FIMD_VIDTCON_END:
+        return s->vidtcon[(offset - FIMD_VIDTCON_START) >> 2];
+    case FIMD_WINCON_START ... FIMD_WINCON_END:
+        return s->window[(offset - FIMD_WINCON_START) >> 2].wincon;
+    case FIMD_SHADOWCON:
+        return s->shadowcon;
+    case FIMD_WINCHMAP:
+        return s->winchmap;
+    case FIMD_VIDOSD_START ... FIMD_VIDOSD_END:
+        w = (offset - FIMD_VIDOSD_START) >> 4;
+        i = ((offset - FIMD_VIDOSD_START) & 0xF) >> 2;
+        switch (i) {
+        case 0:
+            ret = ((s->window[w].lefttop_x & FIMD_VIDOSD_COORD_MASK) <<
+            FIMD_VIDOSD_HOR_SHIFT) |
+            (s->window[w].lefttop_y & FIMD_VIDOSD_COORD_MASK);
+            break;
+        case 1:
+            ret = ((s->window[w].rightbot_x & FIMD_VIDOSD_COORD_MASK) <<
+                FIMD_VIDOSD_HOR_SHIFT) |
+                (s->window[w].rightbot_y & FIMD_VIDOSD_COORD_MASK);
+            break;
+        case 2:
+            if (w == 0) {
+                ret = s->window[w].osdsize;
+            } else {
+                ret = (pack_upper_4(s->window[w].alpha_val[0]) <<
+                    FIMD_VIDOSD_AEN0_SHIFT) |
+                    pack_upper_4(s->window[w].alpha_val[1]);
+            }
+            break;
+        case 3:
+            if (w != 1 && w != 2) {
+                DPRINT_ERROR("bad read offset 0x%08x\n", offset);
+                return 0xBAADBAAD;
+            }
+            ret = s->window[w].osdsize;
+            break;
+        }
+        return ret;
+    case FIMD_VIDWADD0_START ... FIMD_VIDWADD0_END:
+        w = (offset - FIMD_VIDWADD0_START) >> 3;
+        i = ((offset - FIMD_VIDWADD0_START) >> 2) & 1;
+        return s->window[w].buf_start[i];
+    case FIMD_VIDWADD1_START ... FIMD_VIDWADD1_END:
+        w = (offset - FIMD_VIDWADD1_START) >> 3;
+        i = ((offset - FIMD_VIDWADD1_START) >> 2) & 1;
+        return s->window[w].buf_end[i];
+    case FIMD_VIDWADD2_START ... FIMD_VIDWADD2_END:
+        w = (offset - FIMD_VIDWADD2_START) >> 2;
+        return s->window[w].virtpage_width | (s->window[w].virtpage_offsize <<
+            FIMD_VIDWADD2_OFFSIZE_SHIFT);
+    case FIMD_VIDINTCON0 ... FIMD_VIDINTCON1:
+        return s->vidintcon[(offset - FIMD_VIDINTCON0) >> 2];
+    case FIMD_WKEYCON_START ... FIMD_WKEYCON_END:
+        w = ((offset - FIMD_WKEYCON_START) >> 3) + 1;
+        i = ((offset - FIMD_WKEYCON_START) >> 2) & 1;
+        return s->window[w].keycon[i];
+    case FIMD_WKEYALPHA_START ... FIMD_WKEYALPHA_END:
+        w = ((offset - FIMD_WKEYALPHA_START) >> 2) + 1;
+        return s->window[w].keyalpha;
+    case FIMD_DITHMODE:
+        return s->dithmode;
+    case FIMD_WINMAP_START ... FIMD_WINMAP_END:
+        return s->window[(offset - FIMD_WINMAP_START) >> 2].winmap;
+    case FIMD_WPALCON_HIGH ... FIMD_WPALCON_LOW:
+        return s->wpalcon[(offset - FIMD_WPALCON_HIGH) >> 2];
+    case FIMD_TRIGCON:
+        return s->trigcon;
+    case FIMD_I80IFCON_START ... FIMD_I80IFCON_END:
+        return s->i80ifcon[(offset - FIMD_I80IFCON_START) >> 2];
+    case FIMD_COLORGAINCON:
+        return s->colorgaincon;
+    case FIMD_LDI_CMDCON0 ... FIMD_LDI_CMDCON1:
+        return s->ldi_cmdcon[(offset - FIMD_LDI_CMDCON0) >> 2];
+    case FIMD_SIFCCON0 ... FIMD_SIFCCON2:
+        i = (offset - FIMD_SIFCCON0) >> 2;
+        return s->sifccon[i];
+    case FIMD_HUECOEFCR_START ... FIMD_HUECOEFCR_END:
+        i = (offset - FIMD_HUECOEFCR_START) >> 2;
+        return s->huecoef_cr[i];
+    case FIMD_HUECOEFCB_START ... FIMD_HUECOEFCB_END:
+        i = (offset - FIMD_HUECOEFCB_START) >> 2;
+        return s->huecoef_cb[i];
+    case FIMD_HUEOFFSET:
+        return s->hueoffset;
+    case FIMD_VIDWALPHA_START ... FIMD_VIDWALPHA_END:
+        w = ((offset - FIMD_VIDWALPHA_START) >> 3);
+        i = ((offset - FIMD_VIDWALPHA_START) >> 2) & 1;
+        return s->window[w].alpha_val[i] &
+                (w == 0 ? 0xFFFFFF : FIMD_VIDALPHA_ALPHA_LOWER);
+    case FIMD_BLENDEQ_START ... FIMD_BLENDEQ_END:
+        return s->window[(offset - FIMD_BLENDEQ_START) >> 2].blendeq;
+    case FIMD_BLENDCON:
+        return s->blendcon;
+    case FIMD_WRTQOSCON_START ... FIMD_WRTQOSCON_END:
+        return s->window[(offset - FIMD_WRTQOSCON_START) >> 2].rtqoscon;
+    case FIMD_I80IFCMD_START ... FIMD_I80IFCMD_END:
+        return s->i80ifcmd[(offset - FIMD_I80IFCMD_START) >> 2];
+    case FIMD_VIDW0ADD0_B2 ... FIMD_VIDW4ADD0_B2:
+        if (offset & 0x0004) {
+            break;
+        }
+        return s->window[(offset - FIMD_VIDW0ADD0_B2) >> 3].buf_start[2];
+    case FIMD_SHD_ADD0_START ... FIMD_SHD_ADD0_END:
+        if (offset & 0x0004) {
+            break;
+        }
+        return s->window[(offset - FIMD_SHD_ADD0_START) >> 3].shadow_buf_start;
+    case FIMD_SHD_ADD1_START ... FIMD_SHD_ADD1_END:
+        if (offset & 0x0004) {
+            break;
+        }
+        return s->window[(offset - FIMD_SHD_ADD1_START) >> 3].shadow_buf_end;
+    case FIMD_SHD_ADD2_START ... FIMD_SHD_ADD2_END:
+        return s->window[(offset - FIMD_SHD_ADD2_START) >> 2].shadow_buf_size;
+    case FIMD_PAL_MEM_START ... FIMD_PAL_MEM_END:
+        w = (offset - FIMD_PAL_MEM_START) >> 10;
+        i = ((offset - FIMD_PAL_MEM_START) >> 2) & 0xFF;
+        return s->window[w].palette[i];
+    case FIMD_PALMEM_AL_START ... FIMD_PALMEM_AL_END:
+        /* Palette aliases for win 0,1 */
+        w = (offset - FIMD_PALMEM_AL_START) >> 10;
+        i = ((offset - FIMD_PALMEM_AL_START) >> 2) & 0xFF;
+        return s->window[w].palette[i];
+    }
+
+    DPRINT_ERROR("bad read offset 0x%08x\n", offset);
+    return 0xBAADBAAD;
+}
+
+static const MemoryRegionOps exynos4210_fimd_mmio_ops = {
+    .read = exynos4210_fimd_read,
+    .write = exynos4210_fimd_write,
+    .valid = {
+        .min_access_size = 4,
+        .max_access_size = 4,
+        .unaligned = false
+    },
+    .endianness = DEVICE_NATIVE_ENDIAN,
+};
+
+static int exynos4210_fimd_load(void *opaque, int version_id)
+{
+    Exynos4210fimdState *s = (Exynos4210fimdState *)opaque;
+    int w;
+
+    if (version_id != 1) {
+        return -EINVAL;
+    }
+
+    for (w = 0; w < NUM_OF_WINDOWS; w++) {
+        exynos4210_fimd_update_win_bppmode(s, w);
+        fimd_update_get_alpha(s, w);
+        fimd_update_memory_section(s, w);
+    }
+
+    /* Redraw the whole screen */
+    exynos4210_update_resolution(s);
+    exynos4210_fimd_invalidate(s);
+    exynos4210_fimd_enable(s, (s->vidcon[0] & FIMD_VIDCON0_ENVID_MASK) ==
+            FIMD_VIDCON0_ENVID_MASK);
+    return 0;
+}
+
+static const VMStateDescription exynos4210_fimd_window_vmstate = {
+    .name = "exynos4210.fimd_window",
+    .version_id = 1,
+    .minimum_version_id = 1,
+    .fields      = (VMStateField[]) {
+        VMSTATE_UINT32(wincon, Exynos4210fimdWindow),
+        VMSTATE_UINT32_ARRAY(buf_start, Exynos4210fimdWindow, 3),
+        VMSTATE_UINT32_ARRAY(buf_end, Exynos4210fimdWindow, 3),
+        VMSTATE_UINT32_ARRAY(keycon, Exynos4210fimdWindow, 2),
+        VMSTATE_UINT32(keyalpha, Exynos4210fimdWindow),
+        VMSTATE_UINT32(winmap, Exynos4210fimdWindow),
+        VMSTATE_UINT32(blendeq, Exynos4210fimdWindow),
+        VMSTATE_UINT32(rtqoscon, Exynos4210fimdWindow),
+        VMSTATE_UINT32_ARRAY(palette, Exynos4210fimdWindow, 256),
+        VMSTATE_UINT32(shadow_buf_start, Exynos4210fimdWindow),
+        VMSTATE_UINT32(shadow_buf_end, Exynos4210fimdWindow),
+        VMSTATE_UINT32(shadow_buf_size, Exynos4210fimdWindow),
+        VMSTATE_UINT16(lefttop_x, Exynos4210fimdWindow),
+        VMSTATE_UINT16(lefttop_y, Exynos4210fimdWindow),
+        VMSTATE_UINT16(rightbot_x, Exynos4210fimdWindow),
+        VMSTATE_UINT16(rightbot_y, Exynos4210fimdWindow),
+        VMSTATE_UINT32(osdsize, Exynos4210fimdWindow),
+        VMSTATE_UINT32_ARRAY(alpha_val, Exynos4210fimdWindow, 2),
+        VMSTATE_UINT16(virtpage_width, Exynos4210fimdWindow),
+        VMSTATE_UINT16(virtpage_offsize, Exynos4210fimdWindow),
+        VMSTATE_END_OF_LIST()
+    }
+};
+
+static const VMStateDescription exynos4210_fimd_vmstate = {
+    .name = "exynos4210.fimd",
+    .version_id = 1,
+    .minimum_version_id = 1,
+    .post_load = exynos4210_fimd_load,
+    .fields      = (VMStateField[]) {
+        VMSTATE_UINT32_ARRAY(vidcon, Exynos4210fimdState, 4),
+        VMSTATE_UINT32_ARRAY(vidtcon, Exynos4210fimdState, 4),
+        VMSTATE_UINT32(shadowcon, Exynos4210fimdState),
+        VMSTATE_UINT32(winchmap, Exynos4210fimdState),
+        VMSTATE_UINT32_ARRAY(vidintcon, Exynos4210fimdState, 2),
+        VMSTATE_UINT32(dithmode, Exynos4210fimdState),
+        VMSTATE_UINT32_ARRAY(wpalcon, Exynos4210fimdState, 2),
+        VMSTATE_UINT32(trigcon, Exynos4210fimdState),
+        VMSTATE_UINT32_ARRAY(i80ifcon, Exynos4210fimdState, 4),
+        VMSTATE_UINT32(colorgaincon, Exynos4210fimdState),
+        VMSTATE_UINT32_ARRAY(ldi_cmdcon, Exynos4210fimdState, 2),
+        VMSTATE_UINT32_ARRAY(sifccon, Exynos4210fimdState, 3),
+        VMSTATE_UINT32_ARRAY(huecoef_cr, Exynos4210fimdState, 4),
+        VMSTATE_UINT32_ARRAY(huecoef_cb, Exynos4210fimdState, 4),
+        VMSTATE_UINT32(hueoffset, Exynos4210fimdState),
+        VMSTATE_UINT32_ARRAY(i80ifcmd, Exynos4210fimdState, 12),
+        VMSTATE_UINT32(blendcon, Exynos4210fimdState),
+        VMSTATE_STRUCT_ARRAY(window, Exynos4210fimdState, 5, 1,
+                exynos4210_fimd_window_vmstate, Exynos4210fimdWindow),
+        VMSTATE_END_OF_LIST()
+    }
+};
+
+static int exynos4210_fimd_init(SysBusDevice *dev)
+{
+    Exynos4210fimdState *s = FROM_SYSBUS(Exynos4210fimdState, dev);
+
+    s->ifb = NULL;
+
+    sysbus_init_irq(dev, &s->irq[0]);
+    sysbus_init_irq(dev, &s->irq[1]);
+    sysbus_init_irq(dev, &s->irq[2]);
+
+    memory_region_init_io(&s->iomem, &exynos4210_fimd_mmio_ops, s,
+            "exynos4210.fimd", FIMD_REGS_SIZE);
+    sysbus_init_mmio(dev, &s->iomem);
+    s->console = graphic_console_init(exynos4210_fimd_update,
+                                  exynos4210_fimd_invalidate, NULL, NULL, s);
+
+    return 0;
+}
+
+static void exynos4210_fimd_class_init(ObjectClass *klass, void *data)
+{
+    DeviceClass *dc = DEVICE_CLASS(klass);
+    SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass);
+
+    dc->vmsd = &exynos4210_fimd_vmstate;
+    dc->reset = exynos4210_fimd_reset;
+    k->init = exynos4210_fimd_init;
+}
+
+static TypeInfo exynos4210_fimd_info = {
+    .name = "exynos4210.fimd",
+    .parent = TYPE_SYS_BUS_DEVICE,
+    .instance_size = sizeof(Exynos4210fimdState),
+    .class_init = exynos4210_fimd_class_init,
+};
+
+static void exynos4210_fimd_register_types(void)
+{
+    type_register_static(&exynos4210_fimd_info);
+}
+
+type_init(exynos4210_fimd_register_types)
-- 
1.7.1

^ permalink raw reply related	[flat|nested] 24+ messages in thread

* [Qemu-devel] [PATCH 10/22] MAINTAINERS: Add maintainers for Exynos SOC.
  2012-02-17 11:29 [Qemu-devel] [PULL 00/22] arm-devs queue Peter Maydell
                   ` (8 preceding siblings ...)
  2012-02-17 11:29 ` [Qemu-devel] [PATCH 09/22] Exynos4210: added display controller implementation Peter Maydell
@ 2012-02-17 11:29 ` Peter Maydell
  2012-02-17 11:29 ` [Qemu-devel] [PATCH 11/22] hw/a15mpcore.c: Add Cortex-A15 private peripheral model Peter Maydell
                   ` (12 subsequent siblings)
  22 siblings, 0 replies; 24+ messages in thread
From: Peter Maydell @ 2012-02-17 11:29 UTC (permalink / raw)
  To: Anthony Liguori; +Cc: qemu-devel, Paul Brook

From: Evgeny Voevodin <e.voevodin@samsung.com>

Signed-off-by: Evgeny Voevodin <e.voevodin@samsung.com>
Acked-by: Andreas Färber <afaerber@suse.de>
Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
---
 MAINTAINERS |    8 ++++++++
 1 files changed, 8 insertions(+), 0 deletions(-)

diff --git a/MAINTAINERS b/MAINTAINERS
index 173e893..e55ea0f 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -183,6 +183,14 @@ F: *win32*
 
 ARM Machines
 ------------
+Exynos
+M: Evgeny Voevodin <e.voevodin@samsung.com>
+M: Maksim Kozlov <m.kozlov@samsung.com>
+M: Igor Mitsyanko <i.mitsyanko@samsung.com>
+M: Dmitry Solodkiy <d.solodkiy@samsung.com>
+S: Maintained
+F: hw/exynos*
+
 Gumstix
 M: qemu-devel@nongnu.org
 S: Orphan
-- 
1.7.1

^ permalink raw reply related	[flat|nested] 24+ messages in thread

* [Qemu-devel] [PATCH 11/22] hw/a15mpcore.c: Add Cortex-A15 private peripheral model
  2012-02-17 11:29 [Qemu-devel] [PULL 00/22] arm-devs queue Peter Maydell
                   ` (9 preceding siblings ...)
  2012-02-17 11:29 ` [Qemu-devel] [PATCH 10/22] MAINTAINERS: Add maintainers for Exynos SOC Peter Maydell
@ 2012-02-17 11:29 ` Peter Maydell
  2012-02-17 11:29 ` [Qemu-devel] [PATCH 12/22] hw/vexpress.c: Make motherboard peripheral memory map table-driven Peter Maydell
                   ` (11 subsequent siblings)
  22 siblings, 0 replies; 24+ messages in thread
From: Peter Maydell @ 2012-02-17 11:29 UTC (permalink / raw)
  To: Anthony Liguori; +Cc: qemu-devel, Paul Brook

Add a model of the Cortex-A15 memory mapped private peripheral
space. This is fairly simple because the only memory mapped
bit of the A15 is the GIC.

Note that we don't currently model a VGIC and therefore don't
map the VGIC related bits of the GIC.

Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
---
 Makefile.target |    2 +-
 hw/a15mpcore.c  |  103 +++++++++++++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 104 insertions(+), 1 deletions(-)
 create mode 100644 hw/a15mpcore.c

diff --git a/Makefile.target b/Makefile.target
index 2defdc4..651500e 100644
--- a/Makefile.target
+++ b/Makefile.target
@@ -348,7 +348,7 @@ obj-arm-y += exynos4210_gic.o exynos4210_combiner.o exynos4210.o
 obj-arm-y += exynos4_boards.o exynos4210_uart.o exynos4210_pwm.o
 obj-arm-y += exynos4210_pmu.o exynos4210_mct.o exynos4210_fimd.o
 obj-arm-y += arm_l2x0.o
-obj-arm-y += arm_mptimer.o
+obj-arm-y += arm_mptimer.o a15mpcore.o
 obj-arm-y += armv7m.o armv7m_nvic.o stellaris.o pl022.o stellaris_enet.o
 obj-arm-y += highbank.o
 obj-arm-y += pl061.o
diff --git a/hw/a15mpcore.c b/hw/a15mpcore.c
new file mode 100644
index 0000000..71142e5
--- /dev/null
+++ b/hw/a15mpcore.c
@@ -0,0 +1,103 @@
+/*
+ * Cortex-A15MPCore internal peripheral emulation.
+ *
+ * Copyright (c) 2012 Linaro Limited.
+ * Written by Peter Maydell.
+ *
+ * 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, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "sysbus.h"
+
+/* Configuration for arm_gic.c:
+ * max number of CPUs, how to ID current CPU
+ */
+#define NCPU 4
+
+static inline int gic_get_current_cpu(void)
+{
+  return cpu_single_env->cpu_index;
+}
+
+#include "arm_gic.c"
+
+/* A15MP private memory region.  */
+
+typedef struct A15MPPrivState {
+    gic_state gic;
+    uint32_t num_cpu;
+    uint32_t num_irq;
+    MemoryRegion container;
+} A15MPPrivState;
+
+static int a15mp_priv_init(SysBusDevice *dev)
+{
+    A15MPPrivState *s = FROM_SYSBUSGIC(A15MPPrivState, dev);
+
+    if (s->num_cpu > NCPU) {
+        hw_error("a15mp_priv_init: num-cpu may not be more than %d\n", NCPU);
+    }
+
+    gic_init(&s->gic, s->num_cpu, s->num_irq);
+
+    /* Memory map (addresses are offsets from PERIPHBASE):
+     *  0x0000-0x0fff -- reserved
+     *  0x1000-0x1fff -- GIC Distributor
+     *  0x2000-0x2fff -- GIC CPU interface
+     *  0x4000-0x4fff -- GIC virtual interface control (not modelled)
+     *  0x5000-0x5fff -- GIC virtual interface control (not modelled)
+     *  0x6000-0x7fff -- GIC virtual CPU interface (not modelled)
+     */
+    memory_region_init(&s->container, "a15mp-priv-container", 0x8000);
+    memory_region_add_subregion(&s->container, 0x1000, &s->gic.iomem);
+    memory_region_add_subregion(&s->container, 0x2000, &s->gic.cpuiomem[0]);
+
+    sysbus_init_mmio(dev, &s->container);
+    return 0;
+}
+
+static Property a15mp_priv_properties[] = {
+    DEFINE_PROP_UINT32("num-cpu", A15MPPrivState, num_cpu, 1),
+    /* The Cortex-A15MP may have anything from 0 to 224 external interrupt
+     * IRQ lines (with another 32 internal). We default to 64+32, which
+     * is the number provided by the Cortex-A15MP test chip in the
+     * Versatile Express A15 development board.
+     * Other boards may differ and should set this property appropriately.
+     */
+    DEFINE_PROP_UINT32("num-irq", A15MPPrivState, num_irq, 96),
+    DEFINE_PROP_END_OF_LIST(),
+};
+
+static void a15mp_priv_class_init(ObjectClass *klass, void *data)
+{
+    DeviceClass *dc = DEVICE_CLASS(klass);
+    SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass);
+    k->init = a15mp_priv_init;
+    dc->props = a15mp_priv_properties;
+    /* We currently have no savable state outside the common GIC state */
+}
+
+static TypeInfo a15mp_priv_info = {
+    .name  = "a15mpcore_priv",
+    .parent = TYPE_SYS_BUS_DEVICE,
+    .instance_size  = sizeof(A15MPPrivState),
+    .class_init = a15mp_priv_class_init,
+};
+
+static void a15mp_register_types(void)
+{
+    type_register_static(&a15mp_priv_info);
+}
+
+type_init(a15mp_register_types)
-- 
1.7.1

^ permalink raw reply related	[flat|nested] 24+ messages in thread

* [Qemu-devel] [PATCH 12/22] hw/vexpress.c: Make motherboard peripheral memory map table-driven
  2012-02-17 11:29 [Qemu-devel] [PULL 00/22] arm-devs queue Peter Maydell
                   ` (10 preceding siblings ...)
  2012-02-17 11:29 ` [Qemu-devel] [PATCH 11/22] hw/a15mpcore.c: Add Cortex-A15 private peripheral model Peter Maydell
@ 2012-02-17 11:29 ` Peter Maydell
  2012-02-17 11:29 ` [Qemu-devel] [PATCH 13/22] hw/vexpress.c: Move secondary CPU boot code to SRAM Peter Maydell
                   ` (10 subsequent siblings)
  22 siblings, 0 replies; 24+ messages in thread
From: Peter Maydell @ 2012-02-17 11:29 UTC (permalink / raw)
  To: Anthony Liguori; +Cc: qemu-devel, Paul Brook

Pull the addresses used for mapping motherboard peripherals into
memory out into a table. This will allow us to simply provide a
second table to implement the "Cortex-A Series" memory map used by
the A15 variant of Versatile Express, as well as the current
"Legacy" map used by A9.

Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
Reviewed-by: Andreas Färber <afaerber@suse.de>
---
 hw/vexpress.c |  137 +++++++++++++++++++++++++++++++++++++++++++--------------
 1 files changed, 103 insertions(+), 34 deletions(-)

diff --git a/hw/vexpress.c b/hw/vexpress.c
index 43f47a6..38ae05f 100644
--- a/hw/vexpress.c
+++ b/hw/vexpress.c
@@ -31,13 +31,80 @@
 #include "exec-memory.h"
 
 #define SMP_BOOT_ADDR 0xe0000000
-#define SMP_BOOTREG_ADDR 0x10000030
 
 #define VEXPRESS_BOARD_ID 0x8e0
 
 static struct arm_boot_info vexpress_binfo = {
     .smp_loader_start = SMP_BOOT_ADDR,
-    .smp_bootreg_addr = SMP_BOOTREG_ADDR,
+};
+
+/* Address maps for peripherals:
+ * the Versatile Express motherboard has two possible maps,
+ * the "legacy" one (used for A9) and the "Cortex-A Series"
+ * map (used for newer cores).
+ * Individual daughterboards can also have different maps for
+ * their peripherals.
+ */
+
+enum {
+    VE_SYSREGS,
+    VE_SP810,
+    VE_SERIALPCI,
+    VE_PL041,
+    VE_MMCI,
+    VE_KMI0,
+    VE_KMI1,
+    VE_UART0,
+    VE_UART1,
+    VE_UART2,
+    VE_UART3,
+    VE_WDT,
+    VE_TIMER01,
+    VE_TIMER23,
+    VE_SERIALDVI,
+    VE_RTC,
+    VE_COMPACTFLASH,
+    VE_CLCD,
+    VE_NORFLASH0,
+    VE_NORFLASH0ALIAS,
+    VE_NORFLASH1,
+    VE_SRAM,
+    VE_VIDEORAM,
+    VE_ETHERNET,
+    VE_USB,
+    VE_DAPROM,
+};
+
+static target_phys_addr_t motherboard_legacy_map[] = {
+    /* CS7: 0x10000000 .. 0x10020000 */
+    [VE_SYSREGS] = 0x10000000,
+    [VE_SP810] = 0x10001000,
+    [VE_SERIALPCI] = 0x10002000,
+    [VE_PL041] = 0x10004000,
+    [VE_MMCI] = 0x10005000,
+    [VE_KMI0] = 0x10006000,
+    [VE_KMI1] = 0x10007000,
+    [VE_UART0] = 0x10009000,
+    [VE_UART1] = 0x1000a000,
+    [VE_UART2] = 0x1000b000,
+    [VE_UART3] = 0x1000c000,
+    [VE_WDT] = 0x1000f000,
+    [VE_TIMER01] = 0x10011000,
+    [VE_TIMER23] = 0x10012000,
+    [VE_SERIALDVI] = 0x10016000,
+    [VE_RTC] = 0x10017000,
+    [VE_COMPACTFLASH] = 0x1001a000,
+    [VE_CLCD] = 0x1001f000,
+    /* CS0: 0x40000000 .. 0x44000000 */
+    [VE_NORFLASH0] = 0x40000000,
+    /* CS1: 0x44000000 .. 0x48000000 */
+    [VE_NORFLASH1] = 0x44000000,
+    /* CS2: 0x48000000 .. 0x4a000000 */
+    [VE_SRAM] = 0x48000000,
+    /* CS3: 0x4c000000 .. 0x50000000 */
+    [VE_VIDEORAM] = 0x4c000000,
+    [VE_ETHERNET] = 0x4e000000,
+    [VE_USB] = 0x4f000000,
 };
 
 static void vexpress_a9_init(ram_addr_t ram_size,
@@ -61,6 +128,7 @@ static void vexpress_a9_init(ram_addr_t ram_size,
     uint32_t proc_id;
     uint32_t sys_id;
     ram_addr_t low_ram_size, vram_size, sram_size;
+    target_phys_addr_t *map = motherboard_legacy_map;
 
     if (!cpu_model) {
         cpu_model = "cortex-a9";
@@ -116,53 +184,53 @@ static void vexpress_a9_init(ram_addr_t ram_size,
         pic[n] = qdev_get_gpio_in(dev, n);
     }
 
-    /* Motherboard peripherals CS7 : 0x10000000 .. 0x10020000 */
+    /* Motherboard peripherals: the wiring is the same but the
+     * addresses vary between the legacy and A-Series memory maps.
+     */
+
     sys_id = 0x1190f500;
     proc_id = 0x0c000191;
 
-    /* 0x10000000 System registers */
     sysctl = qdev_create(NULL, "realview_sysctl");
     qdev_prop_set_uint32(sysctl, "sys_id", sys_id);
     qdev_prop_set_uint32(sysctl, "proc_id", proc_id);
     qdev_init_nofail(sysctl);
-    sysbus_mmio_map(sysbus_from_qdev(sysctl), 0, 0x10000000);
+    sysbus_mmio_map(sysbus_from_qdev(sysctl), 0, map[VE_SYSREGS]);
+
+    /* VE_SP810: not modelled */
+    /* VE_SERIALPCI: not modelled */
 
-    /* 0x10001000 SP810 system control */
-    /* 0x10002000 serial bus PCI */
-    /* 0x10004000 PL041 audio */
     pl041 = qdev_create(NULL, "pl041");
     qdev_prop_set_uint32(pl041, "nc_fifo_depth", 512);
     qdev_init_nofail(pl041);
-    sysbus_mmio_map(sysbus_from_qdev(pl041), 0, 0x10004000);
+    sysbus_mmio_map(sysbus_from_qdev(pl041), 0, map[VE_PL041]);
     sysbus_connect_irq(sysbus_from_qdev(pl041), 0, pic[11]);
 
-    dev = sysbus_create_varargs("pl181", 0x10005000, pic[9], pic[10], NULL);
+    dev = sysbus_create_varargs("pl181", map[VE_MMCI], pic[9], pic[10], NULL);
     /* Wire up MMC card detect and read-only signals */
     qdev_connect_gpio_out(dev, 0,
                           qdev_get_gpio_in(sysctl, ARM_SYSCTL_GPIO_MMC_WPROT));
     qdev_connect_gpio_out(dev, 1,
                           qdev_get_gpio_in(sysctl, ARM_SYSCTL_GPIO_MMC_CARDIN));
 
-    sysbus_create_simple("pl050_keyboard", 0x10006000, pic[12]);
-    sysbus_create_simple("pl050_mouse", 0x10007000, pic[13]);
-
-    sysbus_create_simple("pl011", 0x10009000, pic[5]);
-    sysbus_create_simple("pl011", 0x1000a000, pic[6]);
-    sysbus_create_simple("pl011", 0x1000b000, pic[7]);
-    sysbus_create_simple("pl011", 0x1000c000, pic[8]);
+    sysbus_create_simple("pl050_keyboard", map[VE_KMI0], pic[12]);
+    sysbus_create_simple("pl050_mouse", map[VE_KMI1], pic[13]);
 
-    /* 0x1000f000 SP805 WDT */
+    sysbus_create_simple("pl011", map[VE_UART0], pic[5]);
+    sysbus_create_simple("pl011", map[VE_UART1], pic[6]);
+    sysbus_create_simple("pl011", map[VE_UART2], pic[7]);
+    sysbus_create_simple("pl011", map[VE_UART3], pic[8]);
 
-    sysbus_create_simple("sp804", 0x10011000, pic[2]);
-    sysbus_create_simple("sp804", 0x10012000, pic[3]);
+    sysbus_create_simple("sp804", map[VE_TIMER01], pic[2]);
+    sysbus_create_simple("sp804", map[VE_TIMER23], pic[3]);
 
-    /* 0x10016000 Serial Bus DVI */
+    /* VE_SERIALDVI: not modelled */
 
-    sysbus_create_simple("pl031", 0x10017000, pic[4]); /* RTC */
+    sysbus_create_simple("pl031", map[VE_RTC], pic[4]); /* RTC */
 
-    /* 0x1001a000 Compact Flash */
+    /* VE_COMPACTFLASH: not modelled */
 
-    /* 0x1001f000 PL111 CLCD (motherboard) */
+    /* VE_CLCD: not modelled (we use the daughterboard CLCD only) */
 
     /* Daughterboard peripherals : 0x10020000 .. 0x20000000 */
 
@@ -184,28 +252,28 @@ static void vexpress_a9_init(ram_addr_t ram_size,
     /* 0x1e00a000 PL310 L2 Cache Controller */
     sysbus_create_varargs("l2x0", 0x1e00a000, NULL);
 
-    /* CS0: NOR0 flash          : 0x40000000 .. 0x44000000 */
-    /* CS4: NOR1 flash          : 0x44000000 .. 0x48000000 */
-    /* CS2: SRAM                : 0x48000000 .. 0x4a000000 */
+    /* VE_NORFLASH0: not modelled */
+    /* VE_NORFLASH0ALIAS: not modelled */
+    /* VE_NORFLASH1: not modelled */
+
     sram_size = 0x2000000;
     memory_region_init_ram(sram, "vexpress.sram", sram_size);
     vmstate_register_ram_global(sram);
-    memory_region_add_subregion(sysmem, 0x48000000, sram);
-
-    /* CS3: USB, ethernet, VRAM : 0x4c000000 .. 0x50000000 */
+    memory_region_add_subregion(sysmem, map[VE_SRAM], sram);
 
-    /* 0x4c000000 Video RAM */
     vram_size = 0x800000;
     memory_region_init_ram(vram, "vexpress.vram", vram_size);
     vmstate_register_ram_global(vram);
-    memory_region_add_subregion(sysmem, 0x4c000000, vram);
+    memory_region_add_subregion(sysmem, map[VE_VIDEORAM], vram);
 
     /* 0x4e000000 LAN9118 Ethernet */
     if (nd_table[0].vlan) {
-        lan9118_init(&nd_table[0], 0x4e000000, pic[15]);
+        lan9118_init(&nd_table[0], map[VE_ETHERNET], pic[15]);
     }
 
-    /* 0x4f000000 ISP1761 USB */
+    /* VE_USB: not modelled */
+
+    /* VE_DAPROM: not modelled */
 
     /* ??? Hack to map an additional page of ram for the secondary CPU
        startup code.  I guess this works on real hardware because the
@@ -222,6 +290,7 @@ static void vexpress_a9_init(ram_addr_t ram_size,
     vexpress_binfo.nb_cpus = smp_cpus;
     vexpress_binfo.board_id = VEXPRESS_BOARD_ID;
     vexpress_binfo.loader_start = 0x60000000;
+    vexpress_binfo.smp_bootreg_addr = map[VE_SYSREGS] + 0x30;
     arm_load_kernel(first_cpu, &vexpress_binfo);
 }
 
-- 
1.7.1

^ permalink raw reply related	[flat|nested] 24+ messages in thread

* [Qemu-devel] [PATCH 13/22] hw/vexpress.c: Move secondary CPU boot code to SRAM
  2012-02-17 11:29 [Qemu-devel] [PULL 00/22] arm-devs queue Peter Maydell
                   ` (11 preceding siblings ...)
  2012-02-17 11:29 ` [Qemu-devel] [PATCH 12/22] hw/vexpress.c: Make motherboard peripheral memory map table-driven Peter Maydell
@ 2012-02-17 11:29 ` Peter Maydell
  2012-02-17 11:29 ` [Qemu-devel] [PATCH 14/22] hw/vexpress.c: Factor out daughterboard-specific initialization Peter Maydell
                   ` (9 subsequent siblings)
  22 siblings, 0 replies; 24+ messages in thread
From: Peter Maydell @ 2012-02-17 11:29 UTC (permalink / raw)
  To: Anthony Liguori; +Cc: qemu-devel, Paul Brook

On real Versatile Express hardware, the boot ROM puts the secondary
CPU bootcode/holding pen in SRAM. We can therefore rely on Linux not
trashing this memory until secondary CPUs have booted up, and can
put our QEMU-specific pen code in the same place. This allows us to
drop the odd "hack" RAM page we were using before.

Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
---
 hw/vexpress.c |   16 ++--------------
 1 files changed, 2 insertions(+), 14 deletions(-)

diff --git a/hw/vexpress.c b/hw/vexpress.c
index 38ae05f..6d8eee2 100644
--- a/hw/vexpress.c
+++ b/hw/vexpress.c
@@ -30,13 +30,9 @@
 #include "boards.h"
 #include "exec-memory.h"
 
-#define SMP_BOOT_ADDR 0xe0000000
-
 #define VEXPRESS_BOARD_ID 0x8e0
 
-static struct arm_boot_info vexpress_binfo = {
-    .smp_loader_start = SMP_BOOT_ADDR,
-};
+static struct arm_boot_info vexpress_binfo;
 
 /* Address maps for peripherals:
  * the Versatile Express motherboard has two possible maps,
@@ -118,7 +114,6 @@ static void vexpress_a9_init(ram_addr_t ram_size,
     MemoryRegion *lowram = g_new(MemoryRegion, 1);
     MemoryRegion *vram = g_new(MemoryRegion, 1);
     MemoryRegion *sram = g_new(MemoryRegion, 1);
-    MemoryRegion *hackram = g_new(MemoryRegion, 1);
     DeviceState *dev, *sysctl, *pl041;
     SysBusDevice *busdev;
     qemu_irq *irqp;
@@ -275,14 +270,6 @@ static void vexpress_a9_init(ram_addr_t ram_size,
 
     /* VE_DAPROM: not modelled */
 
-    /* ??? Hack to map an additional page of ram for the secondary CPU
-       startup code.  I guess this works on real hardware because the
-       BootROM happens to be in ROM/flash or in memory that isn't clobbered
-       until after Linux boots the secondary CPUs.  */
-    memory_region_init_ram(hackram, "vexpress.hack", 0x1000);
-    vmstate_register_ram_global(hackram);
-    memory_region_add_subregion(sysmem, SMP_BOOT_ADDR, hackram);
-
     vexpress_binfo.ram_size = ram_size;
     vexpress_binfo.kernel_filename = kernel_filename;
     vexpress_binfo.kernel_cmdline = kernel_cmdline;
@@ -290,6 +277,7 @@ static void vexpress_a9_init(ram_addr_t ram_size,
     vexpress_binfo.nb_cpus = smp_cpus;
     vexpress_binfo.board_id = VEXPRESS_BOARD_ID;
     vexpress_binfo.loader_start = 0x60000000;
+    vexpress_binfo.smp_loader_start = map[VE_SRAM];
     vexpress_binfo.smp_bootreg_addr = map[VE_SYSREGS] + 0x30;
     arm_load_kernel(first_cpu, &vexpress_binfo);
 }
-- 
1.7.1

^ permalink raw reply related	[flat|nested] 24+ messages in thread

* [Qemu-devel] [PATCH 14/22] hw/vexpress.c: Factor out daughterboard-specific initialization
  2012-02-17 11:29 [Qemu-devel] [PULL 00/22] arm-devs queue Peter Maydell
                   ` (12 preceding siblings ...)
  2012-02-17 11:29 ` [Qemu-devel] [PATCH 13/22] hw/vexpress.c: Move secondary CPU boot code to SRAM Peter Maydell
@ 2012-02-17 11:29 ` Peter Maydell
  2012-02-17 11:29 ` [Qemu-devel] [PATCH 15/22] hw/vexpress.c: Instantiate the motherboard CLCD Peter Maydell
                   ` (8 subsequent siblings)
  22 siblings, 0 replies; 24+ messages in thread
From: Peter Maydell @ 2012-02-17 11:29 UTC (permalink / raw)
  To: Anthony Liguori; +Cc: qemu-devel, Paul Brook

Factor out daughterboard specifics into a data structure and
daughterboard initialization function, in preparation for adding
vexpress-a15 support.

Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
---
 hw/vexpress.c |  118 ++++++++++++++++++++++++++++++++++++++++-----------------
 1 files changed, 83 insertions(+), 35 deletions(-)

diff --git a/hw/vexpress.c b/hw/vexpress.c
index 6d8eee2..8239ea1 100644
--- a/hw/vexpress.c
+++ b/hw/vexpress.c
@@ -103,32 +103,43 @@ static target_phys_addr_t motherboard_legacy_map[] = {
     [VE_USB] = 0x4f000000,
 };
 
-static void vexpress_a9_init(ram_addr_t ram_size,
-                     const char *boot_device,
-                     const char *kernel_filename, const char *kernel_cmdline,
-                     const char *initrd_filename, const char *cpu_model)
+/* Structure defining the peculiarities of a specific daughterboard */
+
+typedef struct VEDBoardInfo VEDBoardInfo;
+
+typedef void DBoardInitFn(const VEDBoardInfo *daughterboard,
+                          ram_addr_t ram_size,
+                          const char *cpu_model,
+                          qemu_irq *pic, uint32_t *proc_id);
+
+struct VEDBoardInfo {
+    const target_phys_addr_t *motherboard_map;
+    target_phys_addr_t loader_start;
+    DBoardInitFn *init;
+};
+
+static void a9_daughterboard_init(const VEDBoardInfo *daughterboard,
+                                  ram_addr_t ram_size,
+                                  const char *cpu_model,
+                                  qemu_irq *pic, uint32_t *proc_id)
 {
     CPUState *env = NULL;
     MemoryRegion *sysmem = get_system_memory();
     MemoryRegion *ram = g_new(MemoryRegion, 1);
     MemoryRegion *lowram = g_new(MemoryRegion, 1);
-    MemoryRegion *vram = g_new(MemoryRegion, 1);
-    MemoryRegion *sram = g_new(MemoryRegion, 1);
-    DeviceState *dev, *sysctl, *pl041;
+    DeviceState *dev;
     SysBusDevice *busdev;
     qemu_irq *irqp;
-    qemu_irq pic[64];
     int n;
     qemu_irq cpu_irq[4];
-    uint32_t proc_id;
-    uint32_t sys_id;
-    ram_addr_t low_ram_size, vram_size, sram_size;
-    target_phys_addr_t *map = motherboard_legacy_map;
+    ram_addr_t low_ram_size;
 
     if (!cpu_model) {
         cpu_model = "cortex-a9";
     }
 
+    *proc_id = 0x0c000191;
+
     for (n = 0; n < smp_cpus; n++) {
         env = cpu_init(cpu_model);
         if (!env) {
@@ -141,7 +152,7 @@ static void vexpress_a9_init(ram_addr_t ram_size,
 
     if (ram_size > 0x40000000) {
         /* 1GB is the maximum the address space permits */
-        fprintf(stderr, "vexpress: cannot model more than 1GB RAM\n");
+        fprintf(stderr, "vexpress-a9: cannot model more than 1GB RAM\n");
         exit(1);
     }
 
@@ -179,12 +190,58 @@ static void vexpress_a9_init(ram_addr_t ram_size,
         pic[n] = qdev_get_gpio_in(dev, n);
     }
 
+    /* Daughterboard peripherals : 0x10020000 .. 0x20000000 */
+
+    /* 0x10020000 PL111 CLCD (daughterboard) */
+    sysbus_create_simple("pl111", 0x10020000, pic[44]);
+
+    /* 0x10060000 AXI RAM */
+    /* 0x100e0000 PL341 Dynamic Memory Controller */
+    /* 0x100e1000 PL354 Static Memory Controller */
+    /* 0x100e2000 System Configuration Controller */
+
+    sysbus_create_simple("sp804", 0x100e4000, pic[48]);
+    /* 0x100e5000 SP805 Watchdog module */
+    /* 0x100e6000 BP147 TrustZone Protection Controller */
+    /* 0x100e9000 PL301 'Fast' AXI matrix */
+    /* 0x100ea000 PL301 'Slow' AXI matrix */
+    /* 0x100ec000 TrustZone Address Space Controller */
+    /* 0x10200000 CoreSight debug APB */
+    /* 0x1e00a000 PL310 L2 Cache Controller */
+    sysbus_create_varargs("l2x0", 0x1e00a000, NULL);
+}
+
+static const VEDBoardInfo a9_daughterboard = {
+    .motherboard_map = motherboard_legacy_map,
+    .loader_start = 0x60000000,
+    .init = a9_daughterboard_init,
+};
+
+static void vexpress_common_init(const VEDBoardInfo *daughterboard,
+                                 ram_addr_t ram_size,
+                                 const char *boot_device,
+                                 const char *kernel_filename,
+                                 const char *kernel_cmdline,
+                                 const char *initrd_filename,
+                                 const char *cpu_model)
+{
+    DeviceState *dev, *sysctl, *pl041;
+    qemu_irq pic[64];
+    uint32_t proc_id;
+    uint32_t sys_id;
+    ram_addr_t vram_size, sram_size;
+    MemoryRegion *sysmem = get_system_memory();
+    MemoryRegion *vram = g_new(MemoryRegion, 1);
+    MemoryRegion *sram = g_new(MemoryRegion, 1);
+    const target_phys_addr_t *map = daughterboard->motherboard_map;
+
+    daughterboard->init(daughterboard, ram_size, cpu_model, pic, &proc_id);
+
     /* Motherboard peripherals: the wiring is the same but the
      * addresses vary between the legacy and A-Series memory maps.
      */
 
     sys_id = 0x1190f500;
-    proc_id = 0x0c000191;
 
     sysctl = qdev_create(NULL, "realview_sysctl");
     qdev_prop_set_uint32(sysctl, "sys_id", sys_id);
@@ -227,26 +284,6 @@ static void vexpress_a9_init(ram_addr_t ram_size,
 
     /* VE_CLCD: not modelled (we use the daughterboard CLCD only) */
 
-    /* Daughterboard peripherals : 0x10020000 .. 0x20000000 */
-
-    /* 0x10020000 PL111 CLCD (daughterboard) */
-    sysbus_create_simple("pl111", 0x10020000, pic[44]);
-
-    /* 0x10060000 AXI RAM */
-    /* 0x100e0000 PL341 Dynamic Memory Controller */
-    /* 0x100e1000 PL354 Static Memory Controller */
-    /* 0x100e2000 System Configuration Controller */
-
-    sysbus_create_simple("sp804", 0x100e4000, pic[48]);
-    /* 0x100e5000 SP805 Watchdog module */
-    /* 0x100e6000 BP147 TrustZone Protection Controller */
-    /* 0x100e9000 PL301 'Fast' AXI matrix */
-    /* 0x100ea000 PL301 'Slow' AXI matrix */
-    /* 0x100ec000 TrustZone Address Space Controller */
-    /* 0x10200000 CoreSight debug APB */
-    /* 0x1e00a000 PL310 L2 Cache Controller */
-    sysbus_create_varargs("l2x0", 0x1e00a000, NULL);
-
     /* VE_NORFLASH0: not modelled */
     /* VE_NORFLASH0ALIAS: not modelled */
     /* VE_NORFLASH1: not modelled */
@@ -276,12 +313,23 @@ static void vexpress_a9_init(ram_addr_t ram_size,
     vexpress_binfo.initrd_filename = initrd_filename;
     vexpress_binfo.nb_cpus = smp_cpus;
     vexpress_binfo.board_id = VEXPRESS_BOARD_ID;
-    vexpress_binfo.loader_start = 0x60000000;
+    vexpress_binfo.loader_start = daughterboard->loader_start;
     vexpress_binfo.smp_loader_start = map[VE_SRAM];
     vexpress_binfo.smp_bootreg_addr = map[VE_SYSREGS] + 0x30;
     arm_load_kernel(first_cpu, &vexpress_binfo);
 }
 
+static void vexpress_a9_init(ram_addr_t ram_size,
+                             const char *boot_device,
+                             const char *kernel_filename,
+                             const char *kernel_cmdline,
+                             const char *initrd_filename,
+                             const char *cpu_model)
+{
+    vexpress_common_init(&a9_daughterboard,
+                         ram_size, boot_device, kernel_filename,
+                         kernel_cmdline, initrd_filename, cpu_model);
+}
 
 static QEMUMachine vexpress_a9_machine = {
     .name = "vexpress-a9",
-- 
1.7.1

^ permalink raw reply related	[flat|nested] 24+ messages in thread

* [Qemu-devel] [PATCH 15/22] hw/vexpress.c: Instantiate the motherboard CLCD
  2012-02-17 11:29 [Qemu-devel] [PULL 00/22] arm-devs queue Peter Maydell
                   ` (13 preceding siblings ...)
  2012-02-17 11:29 ` [Qemu-devel] [PATCH 14/22] hw/vexpress.c: Factor out daughterboard-specific initialization Peter Maydell
@ 2012-02-17 11:29 ` Peter Maydell
  2012-02-17 11:29 ` [Qemu-devel] [PATCH 16/22] arm_boot: Pass base address of GIC CPU interface, not whole GIC Peter Maydell
                   ` (7 subsequent siblings)
  22 siblings, 0 replies; 24+ messages in thread
From: Peter Maydell @ 2012-02-17 11:29 UTC (permalink / raw)
  To: Anthony Liguori; +Cc: qemu-devel, Paul Brook

Instantiate the CLCD on the vexpress motherboard as well as one on
the daughterboard -- the A15 daughterboard does not have a CLCD
and so relies on the motherboard one.

At the moment QEMU doesn't provide infrastructure for selecting
which display device gets to actually show graphics -- the first
one registered is it. Fortunately this works for the major use
case (Linux): if the daughterboard has a CLCD it will come first
and be used, otherwise we fall back to the motherboard CLCD.
So we don't (currently) need to implement the control register
which allows software to tell the mux which video output to pass
through to the outside world.

Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
Reviewed-by: Andreas Färber <afaerber@suse.de>
---
 hw/vexpress.c |    2 +-
 1 files changed, 1 insertions(+), 1 deletions(-)

diff --git a/hw/vexpress.c b/hw/vexpress.c
index 8239ea1..43ad206 100644
--- a/hw/vexpress.c
+++ b/hw/vexpress.c
@@ -282,7 +282,7 @@ static void vexpress_common_init(const VEDBoardInfo *daughterboard,
 
     /* VE_COMPACTFLASH: not modelled */
 
-    /* VE_CLCD: not modelled (we use the daughterboard CLCD only) */
+    sysbus_create_simple("pl111", map[VE_CLCD], pic[14]);
 
     /* VE_NORFLASH0: not modelled */
     /* VE_NORFLASH0ALIAS: not modelled */
-- 
1.7.1

^ permalink raw reply related	[flat|nested] 24+ messages in thread

* [Qemu-devel] [PATCH 16/22] arm_boot: Pass base address of GIC CPU interface, not whole GIC
  2012-02-17 11:29 [Qemu-devel] [PULL 00/22] arm-devs queue Peter Maydell
                   ` (14 preceding siblings ...)
  2012-02-17 11:29 ` [Qemu-devel] [PATCH 15/22] hw/vexpress.c: Instantiate the motherboard CLCD Peter Maydell
@ 2012-02-17 11:29 ` Peter Maydell
  2012-02-17 11:29 ` [Qemu-devel] [PATCH 17/22] hw/vexpress.c: Add vexpress-a15 machine Peter Maydell
                   ` (6 subsequent siblings)
  22 siblings, 0 replies; 24+ messages in thread
From: Peter Maydell @ 2012-02-17 11:29 UTC (permalink / raw)
  To: Anthony Liguori; +Cc: qemu-devel, Paul Brook

The arm_boot secondary boot loader code needs the address of
the GIC CPU interface. Obtaining this from the base address
of the private peripheral region was possible for A9 and 11MPcore,
but the A15 puts the GIC CPU interface in a different place.
So make boards pass in the GIC CPU interface address directly.

Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
---
 hw/arm-misc.h       |    2 +-
 hw/arm_boot.c       |    8 ++++----
 hw/exynos4_boards.c |    3 ++-
 hw/realview.c       |   12 +++++++-----
 hw/vexpress.c       |    6 ++++--
 5 files changed, 18 insertions(+), 13 deletions(-)

diff --git a/hw/arm-misc.h b/hw/arm-misc.h
index 5e5204b..306013a 100644
--- a/hw/arm-misc.h
+++ b/hw/arm-misc.h
@@ -37,7 +37,7 @@ struct arm_boot_info {
      */
     target_phys_addr_t smp_loader_start;
     target_phys_addr_t smp_bootreg_addr;
-    target_phys_addr_t smp_priv_base;
+    target_phys_addr_t gic_cpu_if_addr;
     int nb_cpus;
     int board_id;
     int (*atag_board)(const struct arm_boot_info *info, void *p);
diff --git a/hw/arm_boot.c b/hw/arm_boot.c
index 34a8739..2ef25ca 100644
--- a/hw/arm_boot.c
+++ b/hw/arm_boot.c
@@ -43,16 +43,16 @@ static uint32_t bootloader[] = {
  * location for the kernel secondary CPU entry point.
  */
 static uint32_t smpboot[] = {
-  0xe59f201c, /* ldr r2, privbase */
+  0xe59f201c, /* ldr r2, gic_cpu_if */
   0xe59f001c, /* ldr r0, startaddr */
   0xe3a01001, /* mov r1, #1 */
-  0xe5821100, /* str r1, [r2, #256] */
+  0xe5821000, /* str r1, [r2] */
   0xe320f003, /* wfi */
   0xe5901000, /* ldr     r1, [r0] */
   0xe1110001, /* tst     r1, r1 */
   0x0afffffb, /* beq     <wfi> */
   0xe12fff11, /* bx      r1 */
-  0,          /* privbase: Private memory region base address.  */
+  0,          /* gic_cpu_if: base address of GIC CPU interface */
   0           /* bootreg: Boot register address is held here */
 };
 
@@ -61,7 +61,7 @@ static void default_write_secondary(CPUState *env,
 {
     int n;
     smpboot[ARRAY_SIZE(smpboot) - 1] = info->smp_bootreg_addr;
-    smpboot[ARRAY_SIZE(smpboot) - 2] = info->smp_priv_base;
+    smpboot[ARRAY_SIZE(smpboot) - 2] = info->gic_cpu_if_addr;
     for (n = 0; n < ARRAY_SIZE(smpboot); n++) {
         smpboot[n] = tswap32(smpboot[n]);
     }
diff --git a/hw/exynos4_boards.c b/hw/exynos4_boards.c
index 329efbe..553a02b 100644
--- a/hw/exynos4_boards.c
+++ b/hw/exynos4_boards.c
@@ -112,7 +112,8 @@ static Exynos4210State *exynos4_boards_init_common(
     exynos4_board_binfo.kernel_filename = kernel_filename;
     exynos4_board_binfo.initrd_filename = initrd_filename;
     exynos4_board_binfo.kernel_cmdline = kernel_cmdline;
-    exynos4_board_binfo.smp_priv_base = EXYNOS4210_SMP_PRIVATE_BASE_ADDR;
+    exynos4_board_binfo.gic_cpu_if_addr =
+            EXYNOS4210_SMP_PRIVATE_BASE_ADDR + 0x100;
 
     PRINT_DEBUG("\n ram_size: %luMiB [0x%08lx]\n"
             " kernel_filename: %s\n"
diff --git a/hw/realview.c b/hw/realview.c
index bcf982f..ae1bbcd 100644
--- a/hw/realview.c
+++ b/hw/realview.c
@@ -222,21 +222,23 @@ static void realview_init(ram_addr_t ram_size,
     sysbus_mmio_map(sysbus_from_qdev(sysctl), 0, 0x10000000);
 
     if (is_mpcore) {
+        target_phys_addr_t periphbase;
         dev = qdev_create(NULL, is_pb ? "a9mpcore_priv": "realview_mpcore");
         qdev_prop_set_uint32(dev, "num-cpu", smp_cpus);
         qdev_init_nofail(dev);
         busdev = sysbus_from_qdev(dev);
         if (is_pb) {
-            realview_binfo.smp_priv_base = 0x1f000000;
+            periphbase = 0x1f000000;
         } else {
-            realview_binfo.smp_priv_base = 0x10100000;
+            periphbase = 0x10100000;
         }
-        sysbus_mmio_map(busdev, 0, realview_binfo.smp_priv_base);
+        sysbus_mmio_map(busdev, 0, periphbase);
         for (n = 0; n < smp_cpus; n++) {
             sysbus_connect_irq(busdev, n, cpu_irq[n]);
         }
-        sysbus_create_varargs("l2x0", realview_binfo.smp_priv_base + 0x2000,
-                              NULL);
+        sysbus_create_varargs("l2x0", periphbase + 0x2000, NULL);
+        /* Both A9 and 11MPCore put the GIC CPU i/f at base + 0x100 */
+        realview_binfo.gic_cpu_if_addr = periphbase + 0x100;
     } else {
         uint32_t gic_addr = is_pb ? 0x1e000000 : 0x10040000;
         /* For now just create the nIRQ GIC, and ignore the others.  */
diff --git a/hw/vexpress.c b/hw/vexpress.c
index 43ad206..aae9d81 100644
--- a/hw/vexpress.c
+++ b/hw/vexpress.c
@@ -115,6 +115,7 @@ typedef void DBoardInitFn(const VEDBoardInfo *daughterboard,
 struct VEDBoardInfo {
     const target_phys_addr_t *motherboard_map;
     target_phys_addr_t loader_start;
+    const target_phys_addr_t gic_cpu_if_addr;
     DBoardInitFn *init;
 };
 
@@ -175,8 +176,7 @@ static void a9_daughterboard_init(const VEDBoardInfo *daughterboard,
     qdev_prop_set_uint32(dev, "num-cpu", smp_cpus);
     qdev_init_nofail(dev);
     busdev = sysbus_from_qdev(dev);
-    vexpress_binfo.smp_priv_base = 0x1e000000;
-    sysbus_mmio_map(busdev, 0, vexpress_binfo.smp_priv_base);
+    sysbus_mmio_map(busdev, 0, 0x1e000000);
     for (n = 0; n < smp_cpus; n++) {
         sysbus_connect_irq(busdev, n, cpu_irq[n]);
     }
@@ -214,6 +214,7 @@ static void a9_daughterboard_init(const VEDBoardInfo *daughterboard,
 static const VEDBoardInfo a9_daughterboard = {
     .motherboard_map = motherboard_legacy_map,
     .loader_start = 0x60000000,
+    .gic_cpu_if_addr = 0x1e000100,
     .init = a9_daughterboard_init,
 };
 
@@ -316,6 +317,7 @@ static void vexpress_common_init(const VEDBoardInfo *daughterboard,
     vexpress_binfo.loader_start = daughterboard->loader_start;
     vexpress_binfo.smp_loader_start = map[VE_SRAM];
     vexpress_binfo.smp_bootreg_addr = map[VE_SYSREGS] + 0x30;
+    vexpress_binfo.gic_cpu_if_addr = daughterboard->gic_cpu_if_addr;
     arm_load_kernel(first_cpu, &vexpress_binfo);
 }
 
-- 
1.7.1

^ permalink raw reply related	[flat|nested] 24+ messages in thread

* [Qemu-devel] [PATCH 17/22] hw/vexpress.c: Add vexpress-a15 machine
  2012-02-17 11:29 [Qemu-devel] [PULL 00/22] arm-devs queue Peter Maydell
                   ` (15 preceding siblings ...)
  2012-02-17 11:29 ` [Qemu-devel] [PATCH 16/22] arm_boot: Pass base address of GIC CPU interface, not whole GIC Peter Maydell
@ 2012-02-17 11:29 ` Peter Maydell
  2012-02-17 11:29 ` [Qemu-devel] [PATCH 18/22] hw/arm_sysctl: Drop legacy init function Peter Maydell
                   ` (5 subsequent siblings)
  22 siblings, 0 replies; 24+ messages in thread
From: Peter Maydell @ 2012-02-17 11:29 UTC (permalink / raw)
  To: Anthony Liguori; +Cc: qemu-devel, Paul Brook

Add the vexpress-a15 machine, and the A-Series memory map it uses.

Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
Reviewed-by: Andreas Färber <afaerber@suse.de>
---
 hw/vexpress.c |  141 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 1 files changed, 141 insertions(+), 0 deletions(-)

diff --git a/hw/vexpress.c b/hw/vexpress.c
index aae9d81..b9aafec 100644
--- a/hw/vexpress.c
+++ b/hw/vexpress.c
@@ -103,6 +103,41 @@ static target_phys_addr_t motherboard_legacy_map[] = {
     [VE_USB] = 0x4f000000,
 };
 
+static target_phys_addr_t motherboard_aseries_map[] = {
+    /* CS0: 0x00000000 .. 0x0c000000 */
+    [VE_NORFLASH0] = 0x00000000,
+    [VE_NORFLASH0ALIAS] = 0x08000000,
+    /* CS4: 0x0c000000 .. 0x10000000 */
+    [VE_NORFLASH1] = 0x0c000000,
+    /* CS5: 0x10000000 .. 0x14000000 */
+    /* CS1: 0x14000000 .. 0x18000000 */
+    [VE_SRAM] = 0x14000000,
+    /* CS2: 0x18000000 .. 0x1c000000 */
+    [VE_VIDEORAM] = 0x18000000,
+    [VE_ETHERNET] = 0x1a000000,
+    [VE_USB] = 0x1b000000,
+    /* CS3: 0x1c000000 .. 0x20000000 */
+    [VE_DAPROM] = 0x1c000000,
+    [VE_SYSREGS] = 0x1c010000,
+    [VE_SP810] = 0x1c020000,
+    [VE_SERIALPCI] = 0x1c030000,
+    [VE_PL041] = 0x1c040000,
+    [VE_MMCI] = 0x1c050000,
+    [VE_KMI0] = 0x1c060000,
+    [VE_KMI1] = 0x1c070000,
+    [VE_UART0] = 0x1c090000,
+    [VE_UART1] = 0x1c0a0000,
+    [VE_UART2] = 0x1c0b0000,
+    [VE_UART3] = 0x1c0c0000,
+    [VE_WDT] = 0x1c0f0000,
+    [VE_TIMER01] = 0x1c110000,
+    [VE_TIMER23] = 0x1c120000,
+    [VE_SERIALDVI] = 0x1c160000,
+    [VE_RTC] = 0x1c170000,
+    [VE_COMPACTFLASH] = 0x1c1a0000,
+    [VE_CLCD] = 0x1c1f0000,
+};
+
 /* Structure defining the peculiarities of a specific daughterboard */
 
 typedef struct VEDBoardInfo VEDBoardInfo;
@@ -218,6 +253,91 @@ static const VEDBoardInfo a9_daughterboard = {
     .init = a9_daughterboard_init,
 };
 
+static void a15_daughterboard_init(const VEDBoardInfo *daughterboard,
+                                   ram_addr_t ram_size,
+                                   const char *cpu_model,
+                                   qemu_irq *pic, uint32_t *proc_id)
+{
+    int n;
+    CPUState *env = NULL;
+    MemoryRegion *sysmem = get_system_memory();
+    MemoryRegion *ram = g_new(MemoryRegion, 1);
+    MemoryRegion *sram = g_new(MemoryRegion, 1);
+    qemu_irq cpu_irq[4];
+    DeviceState *dev;
+    SysBusDevice *busdev;
+
+    if (!cpu_model) {
+        cpu_model = "cortex-a15";
+    }
+
+    *proc_id = 0x14000217;
+
+    for (n = 0; n < smp_cpus; n++) {
+        qemu_irq *irqp;
+        env = cpu_init(cpu_model);
+        if (!env) {
+            fprintf(stderr, "Unable to find CPU definition\n");
+            exit(1);
+        }
+        irqp = arm_pic_init_cpu(env);
+        cpu_irq[n] = irqp[ARM_PIC_CPU_IRQ];
+    }
+
+    if (ram_size > 0x80000000) {
+        fprintf(stderr, "vexpress-a15: cannot model more than 2GB RAM\n");
+        exit(1);
+    }
+
+    memory_region_init_ram(ram, "vexpress.highmem", ram_size);
+    vmstate_register_ram_global(ram);
+    /* RAM is from 0x80000000 upwards; there is no low-memory alias for it. */
+    memory_region_add_subregion(sysmem, 0x80000000, ram);
+
+    /* 0x2c000000 A15MPCore private memory region (GIC) */
+    dev = qdev_create(NULL, "a15mpcore_priv");
+    qdev_prop_set_uint32(dev, "num-cpu", smp_cpus);
+    qdev_init_nofail(dev);
+    busdev = sysbus_from_qdev(dev);
+    sysbus_mmio_map(busdev, 0, 0x2c000000);
+    for (n = 0; n < smp_cpus; n++) {
+        sysbus_connect_irq(busdev, n, cpu_irq[n]);
+    }
+    /* Interrupts [42:0] are from the motherboard;
+     * [47:43] are reserved; [63:48] are daughterboard
+     * peripherals. Note that some documentation numbers
+     * external interrupts starting from 32 (because there
+     * are internal interrupts 0..31).
+     */
+    for (n = 0; n < 64; n++) {
+        pic[n] = qdev_get_gpio_in(dev, n);
+    }
+
+    /* A15 daughterboard peripherals: */
+
+    /* 0x20000000: CoreSight interfaces: not modelled */
+    /* 0x2a000000: PL301 AXI interconnect: not modelled */
+    /* 0x2a420000: SCC: not modelled */
+    /* 0x2a430000: system counter: not modelled */
+    /* 0x2b000000: HDLCD controller: not modelled */
+    /* 0x2b060000: SP805 watchdog: not modelled */
+    /* 0x2b0a0000: PL341 dynamic memory controller: not modelled */
+    /* 0x2e000000: system SRAM */
+    memory_region_init_ram(sram, "vexpress.a15sram", 0x10000);
+    vmstate_register_ram_global(sram);
+    memory_region_add_subregion(sysmem, 0x2e000000, sram);
+
+    /* 0x7ffb0000: DMA330 DMA controller: not modelled */
+    /* 0x7ffd0000: PL354 static memory controller: not modelled */
+}
+
+static const VEDBoardInfo a15_daughterboard = {
+    .motherboard_map = motherboard_aseries_map,
+    .loader_start = 0x80000000,
+    .gic_cpu_if_addr = 0x2c002000,
+    .init = a15_daughterboard_init,
+};
+
 static void vexpress_common_init(const VEDBoardInfo *daughterboard,
                                  ram_addr_t ram_size,
                                  const char *boot_device,
@@ -333,6 +453,18 @@ static void vexpress_a9_init(ram_addr_t ram_size,
                          kernel_cmdline, initrd_filename, cpu_model);
 }
 
+static void vexpress_a15_init(ram_addr_t ram_size,
+                              const char *boot_device,
+                              const char *kernel_filename,
+                              const char *kernel_cmdline,
+                              const char *initrd_filename,
+                              const char *cpu_model)
+{
+    vexpress_common_init(&a15_daughterboard,
+                         ram_size, boot_device, kernel_filename,
+                         kernel_cmdline, initrd_filename, cpu_model);
+}
+
 static QEMUMachine vexpress_a9_machine = {
     .name = "vexpress-a9",
     .desc = "ARM Versatile Express for Cortex-A9",
@@ -341,9 +473,18 @@ static QEMUMachine vexpress_a9_machine = {
     .max_cpus = 4,
 };
 
+static QEMUMachine vexpress_a15_machine = {
+    .name = "vexpress-a15",
+    .desc = "ARM Versatile Express for Cortex-A15",
+    .init = vexpress_a15_init,
+    .use_scsi = 1,
+    .max_cpus = 4,
+};
+
 static void vexpress_machine_init(void)
 {
     qemu_register_machine(&vexpress_a9_machine);
+    qemu_register_machine(&vexpress_a15_machine);
 }
 
 machine_init(vexpress_machine_init);
-- 
1.7.1

^ permalink raw reply related	[flat|nested] 24+ messages in thread

* [Qemu-devel] [PATCH 18/22] hw/arm_sysctl: Drop legacy init function
  2012-02-17 11:29 [Qemu-devel] [PULL 00/22] arm-devs queue Peter Maydell
                   ` (16 preceding siblings ...)
  2012-02-17 11:29 ` [Qemu-devel] [PATCH 17/22] hw/vexpress.c: Add vexpress-a15 machine Peter Maydell
@ 2012-02-17 11:29 ` Peter Maydell
  2012-02-17 11:29 ` [Qemu-devel] [PATCH 19/22] hw/primecell.h: Remove obsolete pl080_init() declaration Peter Maydell
                   ` (4 subsequent siblings)
  22 siblings, 0 replies; 24+ messages in thread
From: Peter Maydell @ 2012-02-17 11:29 UTC (permalink / raw)
  To: Anthony Liguori; +Cc: qemu-devel, Paul Brook

Drop the legacy init function arm_sysctl_init(), since it has no
users left any more. This allows us to drop the awkward '1' from
the actual device init function.

Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
Acked-by: Andreas Färber <afaerber@suse.de>
---
 hw/arm_sysctl.c |   16 ++--------------
 hw/primecell.h  |    3 ---
 2 files changed, 2 insertions(+), 17 deletions(-)

diff --git a/hw/arm_sysctl.c b/hw/arm_sysctl.c
index 149c639..5f1237b 100644
--- a/hw/arm_sysctl.c
+++ b/hw/arm_sysctl.c
@@ -378,7 +378,7 @@ static void arm_sysctl_gpio_set(void *opaque, int line, int level)
     }
 }
 
-static int arm_sysctl_init1(SysBusDevice *dev)
+static int arm_sysctl_init(SysBusDevice *dev)
 {
     arm_sysctl_state *s = FROM_SYSBUS(arm_sysctl_state, dev);
 
@@ -389,18 +389,6 @@ static int arm_sysctl_init1(SysBusDevice *dev)
     return 0;
 }
 
-/* Legacy helper function.  */
-void arm_sysctl_init(uint32_t base, uint32_t sys_id, uint32_t proc_id)
-{
-    DeviceState *dev;
-
-    dev = qdev_create(NULL, "realview_sysctl");
-    qdev_prop_set_uint32(dev, "sys_id", sys_id);
-    qdev_init_nofail(dev);
-    qdev_prop_set_uint32(dev, "proc_id", proc_id);
-    sysbus_mmio_map(sysbus_from_qdev(dev), 0, base);
-}
-
 static Property arm_sysctl_properties[] = {
     DEFINE_PROP_UINT32("sys_id", arm_sysctl_state, sys_id, 0),
     DEFINE_PROP_UINT32("proc_id", arm_sysctl_state, proc_id, 0),
@@ -412,7 +400,7 @@ static void arm_sysctl_class_init(ObjectClass *klass, void *data)
     DeviceClass *dc = DEVICE_CLASS(klass);
     SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass);
 
-    k->init = arm_sysctl_init1;
+    k->init = arm_sysctl_init;
     dc->reset = arm_sysctl_reset;
     dc->vmsd = &vmstate_arm_sysctl;
     dc->props = arm_sysctl_properties;
diff --git a/hw/primecell.h b/hw/primecell.h
index de7d6f2..e709ad3 100644
--- a/hw/primecell.h
+++ b/hw/primecell.h
@@ -8,9 +8,6 @@
 /* pl080.c */
 void *pl080_init(uint32_t base, qemu_irq irq, int nchannels);
 
-/* arm_sysctl.c */
-void arm_sysctl_init(uint32_t base, uint32_t sys_id, uint32_t proc_id);
-
 /* arm_sysctl GPIO lines */
 #define ARM_SYSCTL_GPIO_MMC_WPROT 0
 #define ARM_SYSCTL_GPIO_MMC_CARDIN 1
-- 
1.7.1

^ permalink raw reply related	[flat|nested] 24+ messages in thread

* [Qemu-devel] [PATCH 19/22] hw/primecell.h: Remove obsolete pl080_init() declaration
  2012-02-17 11:29 [Qemu-devel] [PULL 00/22] arm-devs queue Peter Maydell
                   ` (17 preceding siblings ...)
  2012-02-17 11:29 ` [Qemu-devel] [PATCH 18/22] hw/arm_sysctl: Drop legacy init function Peter Maydell
@ 2012-02-17 11:29 ` Peter Maydell
  2012-02-17 11:29 ` [Qemu-devel] [PATCH 20/22] Remove unnecessary includes of primecell.h Peter Maydell
                   ` (3 subsequent siblings)
  22 siblings, 0 replies; 24+ messages in thread
From: Peter Maydell @ 2012-02-17 11:29 UTC (permalink / raw)
  To: Anthony Liguori; +Cc: qemu-devel, Paul Brook

Remove an obsolete declaration of pl080_init(), which has been
incorrect since the conversion of pl080 to qdev back in 2009.

Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
---
 hw/primecell.h |    3 ---
 1 files changed, 0 insertions(+), 3 deletions(-)

diff --git a/hw/primecell.h b/hw/primecell.h
index e709ad3..7337c3b 100644
--- a/hw/primecell.h
+++ b/hw/primecell.h
@@ -5,9 +5,6 @@
 /* Also includes some devices that are currently only used by the
    ARM boards.  */
 
-/* pl080.c */
-void *pl080_init(uint32_t base, qemu_irq irq, int nchannels);
-
 /* arm_sysctl GPIO lines */
 #define ARM_SYSCTL_GPIO_MMC_WPROT 0
 #define ARM_SYSCTL_GPIO_MMC_CARDIN 1
-- 
1.7.1

^ permalink raw reply related	[flat|nested] 24+ messages in thread

* [Qemu-devel] [PATCH 20/22] Remove unnecessary includes of primecell.h
  2012-02-17 11:29 [Qemu-devel] [PULL 00/22] arm-devs queue Peter Maydell
                   ` (18 preceding siblings ...)
  2012-02-17 11:29 ` [Qemu-devel] [PATCH 19/22] hw/primecell.h: Remove obsolete pl080_init() declaration Peter Maydell
@ 2012-02-17 11:29 ` Peter Maydell
  2012-02-17 11:29 ` [Qemu-devel] [PATCH 21/22] MAINTAINERS: Add hw/highbank.c maintainer Peter Maydell
                   ` (2 subsequent siblings)
  22 siblings, 0 replies; 24+ messages in thread
From: Peter Maydell @ 2012-02-17 11:29 UTC (permalink / raw)
  To: Anthony Liguori; +Cc: qemu-devel, Paul Brook

The primecell.h header now only has the definitions of constants
indicating the usage of the arm_sysctl GPIO lines; remove obsolete
includes of it from source files which don't care about those GPIO
lines.

Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
---
 hw/highbank.c     |    1 -
 hw/integratorcp.c |    1 -
 hw/pl022.c        |    1 -
 hw/versatilepb.c  |    1 -
 4 files changed, 0 insertions(+), 4 deletions(-)

diff --git a/hw/highbank.c b/hw/highbank.c
index b28b464..489c00e 100644
--- a/hw/highbank.c
+++ b/hw/highbank.c
@@ -19,7 +19,6 @@
 
 #include "sysbus.h"
 #include "arm-misc.h"
-#include "primecell.h"
 #include "devices.h"
 #include "loader.h"
 #include "net.h"
diff --git a/hw/integratorcp.c b/hw/integratorcp.c
index 294d7da..5b06c81 100644
--- a/hw/integratorcp.c
+++ b/hw/integratorcp.c
@@ -8,7 +8,6 @@
  */
 
 #include "sysbus.h"
-#include "primecell.h"
 #include "devices.h"
 #include "boards.h"
 #include "arm-misc.h"
diff --git a/hw/pl022.c b/hw/pl022.c
index 03bf63c..60e35da 100644
--- a/hw/pl022.c
+++ b/hw/pl022.c
@@ -9,7 +9,6 @@
 
 #include "sysbus.h"
 #include "ssi.h"
-#include "primecell.h"
 
 //#define DEBUG_PL022 1
 
diff --git a/hw/versatilepb.c b/hw/versatilepb.c
index 1903db6..b9102f4 100644
--- a/hw/versatilepb.c
+++ b/hw/versatilepb.c
@@ -9,7 +9,6 @@
 
 #include "sysbus.h"
 #include "arm-misc.h"
-#include "primecell.h"
 #include "devices.h"
 #include "net.h"
 #include "sysemu.h"
-- 
1.7.1

^ permalink raw reply related	[flat|nested] 24+ messages in thread

* [Qemu-devel] [PATCH 21/22] MAINTAINERS: Add hw/highbank.c maintainer
  2012-02-17 11:29 [Qemu-devel] [PULL 00/22] arm-devs queue Peter Maydell
                   ` (19 preceding siblings ...)
  2012-02-17 11:29 ` [Qemu-devel] [PATCH 20/22] Remove unnecessary includes of primecell.h Peter Maydell
@ 2012-02-17 11:29 ` Peter Maydell
  2012-02-17 11:29 ` [Qemu-devel] [PATCH 22/22] hw/pl031: Actually raise interrupt on timer expiry Peter Maydell
  2012-02-17 13:53 ` [Qemu-devel] [PULL 00/22] arm-devs queue Anthony Liguori
  22 siblings, 0 replies; 24+ messages in thread
From: Peter Maydell @ 2012-02-17 11:29 UTC (permalink / raw)
  To: Anthony Liguori; +Cc: qemu-devel, Paul Brook

From: Mark Langsdorf <mark.langsdorf@calxeda.com>

Signed-off-by: Mark Langsdorf <mark.langsdorf@calxeda.com>
Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
---
 MAINTAINERS |    6 ++++++
 1 files changed, 6 insertions(+), 0 deletions(-)

diff --git a/MAINTAINERS b/MAINTAINERS
index e55ea0f..8eb80ea 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -191,6 +191,12 @@ M: Dmitry Solodkiy <d.solodkiy@samsung.com>
 S: Maintained
 F: hw/exynos*
 
+Calxeda Highbank
+M: Mark Langsdorf <mark.langsdorf@calxeda.com>
+S: Supported
+F: hw/highbank.c
+F: hw/xgmac.c
+
 Gumstix
 M: qemu-devel@nongnu.org
 S: Orphan
-- 
1.7.1

^ permalink raw reply related	[flat|nested] 24+ messages in thread

* [Qemu-devel] [PATCH 22/22] hw/pl031: Actually raise interrupt on timer expiry
  2012-02-17 11:29 [Qemu-devel] [PULL 00/22] arm-devs queue Peter Maydell
                   ` (20 preceding siblings ...)
  2012-02-17 11:29 ` [Qemu-devel] [PATCH 21/22] MAINTAINERS: Add hw/highbank.c maintainer Peter Maydell
@ 2012-02-17 11:29 ` Peter Maydell
  2012-02-17 13:53 ` [Qemu-devel] [PULL 00/22] arm-devs queue Anthony Liguori
  22 siblings, 0 replies; 24+ messages in thread
From: Peter Maydell @ 2012-02-17 11:29 UTC (permalink / raw)
  To: Anthony Liguori; +Cc: qemu-devel, Paul Brook

Fix a typo in pl031_interrupt() which meant we were setting a bit
in the interrupt mask rather than the interrupt status register
and thus not actually raising an interrupt. This fix allows the
rtctest program from the kernel's Documentation/rtc.txt to pass
rather than hanging.

Reported-by: Daniel Forsgren <daniel.forsgren@enea.com>
Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
Acked-by: Andreas Färber <afaerber@suse.de>
---
 hw/pl031.c |    2 +-
 1 files changed, 1 insertions(+), 1 deletions(-)

diff --git a/hw/pl031.c b/hw/pl031.c
index 05b5b11..69abc4f 100644
--- a/hw/pl031.c
+++ b/hw/pl031.c
@@ -76,7 +76,7 @@ static void pl031_interrupt(void * opaque)
 {
     pl031_state *s = (pl031_state *)opaque;
 
-    s->im = 1;
+    s->is = 1;
     DPRINTF("Alarm raised\n");
     pl031_update(s);
 }
-- 
1.7.1

^ permalink raw reply related	[flat|nested] 24+ messages in thread

* Re: [Qemu-devel] [PULL 00/22] arm-devs queue
  2012-02-17 11:29 [Qemu-devel] [PULL 00/22] arm-devs queue Peter Maydell
                   ` (21 preceding siblings ...)
  2012-02-17 11:29 ` [Qemu-devel] [PATCH 22/22] hw/pl031: Actually raise interrupt on timer expiry Peter Maydell
@ 2012-02-17 13:53 ` Anthony Liguori
  22 siblings, 0 replies; 24+ messages in thread
From: Anthony Liguori @ 2012-02-17 13:53 UTC (permalink / raw)
  To: Peter Maydell; +Cc: qemu-devel, Paul Brook

On 02/17/2012 05:29 AM, Peter Maydell wrote:
> Hi; this is a pullreq for my arm-devs queue. Changes in
> the queue are
>   * exynos4 model
>   * vexpress-a15 model
>   * the addition of a Highbank section to MAINTAINERS
>   * a few minor cleanup patches
>
> Please pull (before somebody lands another whole-tree change that
> means it needs rebasing again :-))

Pulled.  Thanks.

Regards,

Anthony Liguori

>
> thanks
> -- PMM
>
> The following changes since commit 9de36b1a7cf61aa8be365f13c81668b3e19fbc7f:
>
>    Make -machine/-enable-kvm options merge into a single list (2012-02-17 09:10:13 +0100)
>
> are available in the git repository at:
>    git://git.linaro.org/people/pmaydell/qemu-arm.git arm-devs.for-upstream
>
> Evgeny Voevodin (7):
>        ARM: exynos4210: IRQ subsystem support.
>        ARM: Samsung exynos4210-based boards emulation
>        ARM: exynos4210: PWM support.
>        ARM: exynos4210: MCT support.
>        hw/lan9118: Add basic 16-bit mode support.
>        hw/exynos4210.c: Add LAN support for SMDKC210.
>        MAINTAINERS: Add maintainers for Exynos SOC.
>
> Maksim Kozlov (2):
>        ARM: exynos4210: UART support
>        ARM: exynos4210: basic Power Management Unit implementation
>
> Mark Langsdorf (1):
>        MAINTAINERS: Add hw/highbank.c maintainer
>
> Mitsyanko Igor (1):
>        Exynos4210: added display controller implementation
>
> Peter Maydell (11):
>        hw/a15mpcore.c: Add Cortex-A15 private peripheral model
>        hw/vexpress.c: Make motherboard peripheral memory map table-driven
>        hw/vexpress.c: Move secondary CPU boot code to SRAM
>        hw/vexpress.c: Factor out daughterboard-specific initialization
>        hw/vexpress.c: Instantiate the motherboard CLCD
>        arm_boot: Pass base address of GIC CPU interface, not whole GIC
>        hw/vexpress.c: Add vexpress-a15 machine
>        hw/arm_sysctl: Drop legacy init function
>        hw/primecell.h: Remove obsolete pl080_init() declaration
>        Remove unnecessary includes of primecell.h
>        hw/pl031: Actually raise interrupt on timer expiry
>
>   MAINTAINERS              |   14 +
>   Makefile.target          |    5 +-
>   hw/a15mpcore.c           |  103 +++
>   hw/arm-misc.h            |    2 +-
>   hw/arm_boot.c            |    8 +-
>   hw/arm_sysctl.c          |   16 +-
>   hw/exynos4210.c          |  270 +++++++
>   hw/exynos4210.h          |  131 ++++
>   hw/exynos4210_combiner.c |  469 +++++++++++
>   hw/exynos4210_fimd.c     | 1928 ++++++++++++++++++++++++++++++++++++++++++++++
>   hw/exynos4210_gic.c      |  458 +++++++++++
>   hw/exynos4210_mct.c      | 1488 +++++++++++++++++++++++++++++++++++
>   hw/exynos4210_pmu.c      |  499 ++++++++++++
>   hw/exynos4210_pwm.c      |  422 ++++++++++
>   hw/exynos4210_uart.c     |  676 ++++++++++++++++
>   hw/exynos4_boards.c      |  177 +++++
>   hw/highbank.c            |    1 -
>   hw/integratorcp.c        |    1 -
>   hw/lan9118.c             |  124 +++-
>   hw/pl022.c               |    1 -
>   hw/pl031.c               |    2 +-
>   hw/primecell.h           |    6 -
>   hw/realview.c            |   12 +-
>   hw/versatilepb.c         |    1 -
>   hw/vexpress.c            |  410 ++++++++--
>   25 files changed, 7101 insertions(+), 123 deletions(-)
>   create mode 100644 hw/a15mpcore.c
>   create mode 100644 hw/exynos4210.c
>   create mode 100644 hw/exynos4210.h
>   create mode 100644 hw/exynos4210_combiner.c
>   create mode 100644 hw/exynos4210_fimd.c
>   create mode 100644 hw/exynos4210_gic.c
>   create mode 100644 hw/exynos4210_mct.c
>   create mode 100644 hw/exynos4210_pmu.c
>   create mode 100644 hw/exynos4210_pwm.c
>   create mode 100644 hw/exynos4210_uart.c
>   create mode 100644 hw/exynos4_boards.c
>
>

^ permalink raw reply	[flat|nested] 24+ messages in thread

end of thread, other threads:[~2012-02-17 13:53 UTC | newest]

Thread overview: 24+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2012-02-17 11:29 [Qemu-devel] [PULL 00/22] arm-devs queue Peter Maydell
2012-02-17 11:29 ` [Qemu-devel] [PATCH 01/22] ARM: exynos4210: IRQ subsystem support Peter Maydell
2012-02-17 11:29 ` [Qemu-devel] [PATCH 02/22] ARM: Samsung exynos4210-based boards emulation Peter Maydell
2012-02-17 11:29 ` [Qemu-devel] [PATCH 03/22] ARM: exynos4210: UART support Peter Maydell
2012-02-17 11:29 ` [Qemu-devel] [PATCH 04/22] ARM: exynos4210: PWM support Peter Maydell
2012-02-17 11:29 ` [Qemu-devel] [PATCH 05/22] ARM: exynos4210: basic Power Management Unit implementation Peter Maydell
2012-02-17 11:29 ` [Qemu-devel] [PATCH 06/22] ARM: exynos4210: MCT support Peter Maydell
2012-02-17 11:29 ` [Qemu-devel] [PATCH 07/22] hw/lan9118: Add basic 16-bit mode support Peter Maydell
2012-02-17 11:29 ` [Qemu-devel] [PATCH 08/22] hw/exynos4210.c: Add LAN support for SMDKC210 Peter Maydell
2012-02-17 11:29 ` [Qemu-devel] [PATCH 09/22] Exynos4210: added display controller implementation Peter Maydell
2012-02-17 11:29 ` [Qemu-devel] [PATCH 10/22] MAINTAINERS: Add maintainers for Exynos SOC Peter Maydell
2012-02-17 11:29 ` [Qemu-devel] [PATCH 11/22] hw/a15mpcore.c: Add Cortex-A15 private peripheral model Peter Maydell
2012-02-17 11:29 ` [Qemu-devel] [PATCH 12/22] hw/vexpress.c: Make motherboard peripheral memory map table-driven Peter Maydell
2012-02-17 11:29 ` [Qemu-devel] [PATCH 13/22] hw/vexpress.c: Move secondary CPU boot code to SRAM Peter Maydell
2012-02-17 11:29 ` [Qemu-devel] [PATCH 14/22] hw/vexpress.c: Factor out daughterboard-specific initialization Peter Maydell
2012-02-17 11:29 ` [Qemu-devel] [PATCH 15/22] hw/vexpress.c: Instantiate the motherboard CLCD Peter Maydell
2012-02-17 11:29 ` [Qemu-devel] [PATCH 16/22] arm_boot: Pass base address of GIC CPU interface, not whole GIC Peter Maydell
2012-02-17 11:29 ` [Qemu-devel] [PATCH 17/22] hw/vexpress.c: Add vexpress-a15 machine Peter Maydell
2012-02-17 11:29 ` [Qemu-devel] [PATCH 18/22] hw/arm_sysctl: Drop legacy init function Peter Maydell
2012-02-17 11:29 ` [Qemu-devel] [PATCH 19/22] hw/primecell.h: Remove obsolete pl080_init() declaration Peter Maydell
2012-02-17 11:29 ` [Qemu-devel] [PATCH 20/22] Remove unnecessary includes of primecell.h Peter Maydell
2012-02-17 11:29 ` [Qemu-devel] [PATCH 21/22] MAINTAINERS: Add hw/highbank.c maintainer Peter Maydell
2012-02-17 11:29 ` [Qemu-devel] [PATCH 22/22] hw/pl031: Actually raise interrupt on timer expiry Peter Maydell
2012-02-17 13:53 ` [Qemu-devel] [PULL 00/22] arm-devs queue Anthony Liguori

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.