[Top][All Lists]
[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[Qemu-devel] [PATCH v3 05/14] ARM: exynos4210: IRQ subsystem support.
From: |
Evgeny Voevodin |
Subject: |
[Qemu-devel] [PATCH v3 05/14] ARM: exynos4210: IRQ subsystem support. |
Date: |
Mon, 12 Dec 2011 10:43:17 +0400 |
Signed-off-by: Evgeny Voevodin <address@hidden>
---
Makefile.target | 3 +-
hw/exynos4210.c | 175 ++++++++++++++++-
hw/exynos4210.h | 42 ++++
hw/exynos4210_combiner.c | 385 ++++++++++++++++++++++++++++++++++
hw/exynos4210_gic.c | 510 ++++++++++++++++++++++++++++++++++++++++++++++
5 files changed, 1113 insertions(+), 2 deletions(-)
create mode 100644 hw/exynos4210_combiner.c
create mode 100644 hw/exynos4210_gic.c
diff --git a/Makefile.target b/Makefile.target
index 4c706b1..779c9d4 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.o exynos4210_cmu.o exynos4210_uart.o
+obj-arm-y += exynos4210.o exynos4210_cmu.o exynos4210_uart.o exynos4210_gic.o \
+ exynos4210_combiner.o
obj-arm-y += armv7m.o armv7m_nvic.o stellaris.o pl022.o stellaris_enet.o
obj-arm-y += pl061.o
obj-arm-y += arm-semi.o
diff --git a/hw/exynos4210.c b/hw/exynos4210.c
index d5a1fe0..45d427e 100644
--- a/hw/exynos4210.c
+++ b/hw/exynos4210.c
@@ -58,6 +58,8 @@
#define EXYNOS4210_SFR_BASE_ADDR 0x10000000
+#define EXYNOS4210_SMP_PRIVATE_BASE_ADDR 0x10500000
+
#define EXYNOS4210_BASE_BOOT_ADDR EXYNOS4210_DRAM0_BASE_ADDR
/* SFR Base Address for CMUs */
@@ -74,7 +76,16 @@
#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_INTG 26
+
+/* 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 struct arm_boot_info exynos4210_binfo = {
.loader_start = EXYNOS4210_BASE_BOOT_ADDR,
@@ -93,6 +104,87 @@ enum exynos4210_mach_id {
MACH_SMDKC210_ID = 0xB16,
};
+static 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;
+ break;
+
+ /* 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;
+ break;
+
+ /* 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;
+ break;
+
+ /* 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;
+ break;
+
+ /* 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;
+ break;
+
+ /* 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;
+ break;
+ }
+
+ irq[n] = qdev_get_gpio_in(dev, n);
+ }
+}
static void exynos4210_init(ram_addr_t ram_size,
const char *boot_device,
@@ -110,8 +202,13 @@ static void exynos4210_init(ram_addr_t ram_size,
MemoryRegion *irom_alias_mem = g_new(MemoryRegion, 1);
MemoryRegion *dram0_mem = g_new(MemoryRegion, 1);
MemoryRegion *dram1_mem = NULL;
+ Exynos4210Irq *irqs;
+ qemu_irq *irq_table;
qemu_irq *irqp;
qemu_irq cpu_irq[4];
+ qemu_irq gate_irq[EXYNOS4210_IRQ_GATE_NINPUTS];
+ DeviceState *dev;
+ SysBusDevice *busdev;
ram_addr_t mem_size;
int n;
@@ -129,6 +226,12 @@ static void exynos4210_init(ram_addr_t ram_size,
cpu_model = "cortex-a9";
}
+ irqs = g_malloc0(sizeof(Exynos4210Irq));
+ if (!irqs) {
+ fprintf(stderr, "Can't allocate IRQs\n");
+ exit(1);
+ }
+
for (n = 0; n < smp_cpus; n++) {
env = cpu_init(cpu_model);
if (!env) {
@@ -145,6 +248,76 @@ static void exynos4210_init(ram_addr_t ram_size,
cpu_irq[n] = irqp[ARM_PIC_CPU_IRQ];
}
+ /*** IRQs ***/
+
+ irq_table = exynos4210_init_irq(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 < smp_cpus; 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", smp_cpus);
+ qdev_init_nofail(dev);
+ busdev = sysbus_from_qdev(dev);
+ sysbus_mmio_map(busdev, 0, EXYNOS4210_SMP_PRIVATE_BASE_ADDR);
+ for (n = 0; n < smp_cpus; n++) {
+ sysbus_connect_irq(busdev, n, gate_irq[n * 2]);
+ }
+ for (n = 0; n < EXYNOS4210_INT_GIC_NIRQ; n++) {
+ irqs->int_gic_irq[n] = qdev_get_gpio_in(dev, n);
+ }
+
+ /* External GIC */
+ dev = qdev_create(NULL, "exynos4210.gic");
+ qdev_prop_set_uint32(dev, "num-cpu", smp_cpus);
+ 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 < smp_cpus; n++) {
+ sysbus_connect_irq(busdev, n, gate_irq[n * 2 + 1]);
+ }
+ for (n = 0; n < EXYNOS4210_EXT_GIC_NIRQ; n++) {
+ 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, irqs->int_gic_irq[n]);
+ }
+ exynos4210_combiner_get_gpioin(irqs, dev, 0);
+ sysbus_mmio_map(busdev, 0, EXYNOS4210_INT_COMBINER_BASE_ADDR);
+
+ /* External 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, irqs->ext_gic_irq[n]);
+ }
+ exynos4210_combiner_get_gpioin(irqs, dev, 1);
+ sysbus_mmio_map(busdev, 0, EXYNOS4210_EXT_COMBINER_BASE_ADDR);
+ qdev_prop_set_uint32(dev, "external", 1);
+
+ /* Initialize board IRQs. */
+ exynos4210_init_board_irqs(irqs);
+
/*** Memory ***/
/* Chip-ID and OMR */
@@ -224,7 +397,7 @@ static void exynos4210_init(ram_addr_t ram_size,
continue;
}
- uart_irq = NULL;
+ uart_irq = irq_table[exynos4210_get_irq(EXYNOS4210_UART_INTG,
channel)];
exynos4210_uart_create(addr, fifo_size, channel, NULL, uart_irq);
}
diff --git a/hw/exynos4210.h b/hw/exynos4210.h
index 3df7322..f0dfcaf 100644
--- a/hw/exynos4210.h
+++ b/hw/exynos4210.h
@@ -32,6 +32,48 @@
#define EXYNOS4210_MAX_CPUS 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);
+
+/*
* Interface for exynos4210 Clock Management Units (CMUs)
*/
diff --git a/hw/exynos4210_combiner.c b/hw/exynos4210_combiner.c
new file mode 100644
index 0000000..6a080d0
--- /dev/null
+++ b/hw/exynos4210_combiner.c
@@ -0,0 +1,385 @@
+/*
+ * Samsung exynos4210 Interrupt Combiner
+ *
+ * Copyright (c) 2000 - 2011 Samsung Electronics Co., Ltd.
+ * All rights reserved.
+ *
+ * Evgeny Voevodin <address@hidden>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ * See the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc., 51
+ * Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#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
+
+/*
+ * Combiner registers
+ */
+struct CombinerReg {
+ uint32_t iiesr; /* Internal Interrupt Enable Set register */
+ uint32_t iiecr; /* Internal Interrupt Enable Clear register */
+ uint32_t iistr; /* Internal Interrupt Status register.
+ * Shows status of interrupt pending BEFORE masking
+ */
+ uint32_t iimsr; /* Internal Interrupt Mask Status register.
+ * Shows status of interrupt pending AFTER masking
+ */
+};
+
+/*
+ * 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_Exynos4210CombinerGroupState = {
+ .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_Exynos4210Combiner = {
+ .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_Exynos4210CombinerGroupState, CombinerGroupState),
+ VMSTATE_UINT32_ARRAY(reg_set, Exynos4210CombinerState,
+ IIC_REGSET_SIZE),
+ VMSTATE_UINT32_ARRAY(icipsr, Exynos4210CombinerState, 2),
+ VMSTATE_UINT32(external, Exynos4210CombinerState),
+ VMSTATE_END_OF_LIST()
+ }
+};
+
+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);
+
+ exynos4210_combiner_reset((DeviceState *)s);
+ return 0;
+}
+
+static SysBusDeviceInfo exynos4210_combiner_info = {
+ .qdev.name = "exynos4210.combiner",
+ .qdev.size = sizeof(struct Exynos4210CombinerState),
+ .qdev.reset = exynos4210_combiner_reset,
+ .qdev.vmsd = &VMState_Exynos4210Combiner,
+ .init = exynos4210_combiner_init,
+ .qdev.props = (Property[]) {
+ DEFINE_PROP_UINT32("external",
+ struct Exynos4210CombinerState,
+ external,
+ 0),
+ DEFINE_PROP_END_OF_LIST(),
+ }
+};
+
+static void exynos4210_combiner_register_devices(void)
+{
+ sysbus_register_withprop(&exynos4210_combiner_info);
+}
+
+device_init(exynos4210_combiner_register_devices)
diff --git a/hw/exynos4210_gic.c b/hw/exynos4210_gic.c
new file mode 100644
index 0000000..f43e57f
--- /dev/null
+++ b/hw/exynos4210_gic.c
@@ -0,0 +1,510 @@
+/*
+ * Samsung exynos4210 GIC implementation. Based on hw/arm_gic.c
+ *
+ * Copyright (c) 2000 - 2011 Samsung Electronics Co., Ltd.
+ * All rights reserved.
+ *
+ * Evgeny Voevodin <address@hidden>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ * See the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc., 51
+ * Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include "sysbus.h"
+#include "qemu-common.h"
+#include "irq.h"
+#include "exynos4210.h"
+
+//#define DEBUG_EXYNOS4210_IRQ
+//#define DEBUG_EXYNOS4210_GIC
+
+#ifdef DEBUG_EXYNOS4210_IRQ
+#define DPRINTF_EXYNOS4210_IRQ(fmt, ...) \
+ do { fprintf(stdout, "IRQ_GATE: " fmt, ## __VA_ARGS__); } while (0)
+#else
+#define DPRINTF_EXYNOS4210_IRQ(fmt, ...) do {} while (0)
+#endif
+
+#ifdef DEBUG_EXYNOS4210_GIC
+#define DPRINTF_EXYNOS4210_GIC(fmt, ...) \
+ do { fprintf(stdout, "EXT_GIC: " fmt, ## __VA_ARGS__); } while (0)
+#else
+#define DPRINTF_EXYNOS4210_GIC(fmt, ...) do {} while (0)
+#endif
+
+#define EXT_GIC_ID_TVENC 127
+#define EXT_GIC_ID_MFC 126
+#define EXT_GIC_ID_HDMI_I2C 125
+#define EXT_GIC_ID_HDMI 124
+#define EXT_GIC_ID_MIXER 123
+#define EXT_GIC_ID_PCIe 122
+#define EXT_GIC_ID_2D 121
+#define EXT_GIC_ID_JPEG 120
+#define EXT_GIC_ID_FIMC3 119
+#define EXT_GIC_ID_FIMC2 118
+#define EXT_GIC_ID_FIMC1 117
+#define EXT_GIC_ID_FIMC0 116
+#define EXT_GIC_ID_ROTATOR 115
+#define EXT_GIC_ID_ONENAND_AUDI 114
+#define EXT_GIC_ID_MIPI_DSI_2LANE 113
+#define EXT_GIC_ID_MIPI_CSI_2LANE 112
+#define EXT_GIC_ID_MIPI_DSI_4LANE 111
+#define EXT_GIC_ID_MIPI_CSI_4LANE 110
+#define EXT_GIC_ID_SDMMC 109
+#define EXT_GIC_ID_HSMMC3 108
+#define EXT_GIC_ID_HSMMC2 107
+#define EXT_GIC_ID_HSMMC1 106
+#define EXT_GIC_ID_HSMMC0 105
+#define EXT_GIC_ID_MODEMIF 104
+#define EXT_GIC_ID_USB_DEVICE 103
+#define EXT_GIC_ID_USB_HOST 102
+#define EXT_GIC_ID_MCT_G1 101
+#define EXT_GIC_ID_SPI2 100
+#define EXT_GIC_ID_SPI1 99
+#define EXT_GIC_ID_SPI0 98
+#define EXT_GIC_ID_I2C7 97
+#define EXT_GIC_ID_I2C6 96
+#define EXT_GIC_ID_I2C5 95
+#define EXT_GIC_ID_I2C4 94
+#define EXT_GIC_ID_I2C3 93
+#define EXT_GIC_ID_I2C2 92
+#define EXT_GIC_ID_I2C1 91
+#define EXT_GIC_ID_I2C0 90
+#define EXT_GIC_ID_MCT_G0 89
+#define EXT_GIC_ID_UART4 88
+#define EXT_GIC_ID_UART3 87
+#define EXT_GIC_ID_UART2 86
+#define EXT_GIC_ID_UART1 85
+#define EXT_GIC_ID_UART0 84
+#define EXT_GIC_ID_NFC 83
+#define EXT_GIC_ID_IEM_IEC 82
+#define EXT_GIC_ID_IEM_APC 81
+#define EXT_GIC_ID_MCT_L1 80
+#define EXT_GIC_ID_GPIO_XA 79
+#define EXT_GIC_ID_GPIO_XB 78
+#define EXT_GIC_ID_RTC_TIC 77
+#define EXT_GIC_ID_RTC_ALARM 76
+#define EXT_GIC_ID_WDT 75
+#define EXT_GIC_ID_MCT_L0 74
+#define EXT_GIC_ID_TIMER4 73
+#define EXT_GIC_ID_TIMER3 72
+#define EXT_GIC_ID_TIMER2 71
+#define EXT_GIC_ID_TIMER1 70
+#define EXT_GIC_ID_TIMER0 69
+#define EXT_GIC_ID_PDMA1 68
+#define EXT_GIC_ID_PDMA0 67
+#define EXT_GIC_ID_MDMA_LCD0 66
+
+enum ext_int {
+ 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 GIC_NIRQ 160
+#define NCPU EXYNOS4210_MAX_CPUS
+
+#define EXYNOS4210_GIC_CPU_REGION_SIZE 0x8050
+#define EXYNOS4210_GIC_DIST_REGION_SIZE 0x8F04
+
+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;
+ uint32_t num_cpu;
+} Exynos4210GicState;
+
+static uint64_t exynos4210_gic_cpu_read(void *opaque, target_phys_addr_t
offset,
+ unsigned size)
+{
+ Exynos4210GicState *s = (Exynos4210GicState *) opaque;
+ DPRINTF_EXYNOS4210_GIC("CPU%d: read offset 0x%x\n",
+ gic_get_current_cpu(), offset);
+ return gic_cpu_read(&s->gic, gic_get_current_cpu(), offset & ~0x8000);
+}
+
+static void exynos4210_gic_cpu_write(void *opaque, target_phys_addr_t offset,
+ uint64_t value, unsigned size)
+{
+ Exynos4210GicState *s = (Exynos4210GicState *) opaque;
+ DPRINTF_EXYNOS4210_GIC("CPU%d: write offset 0x%x, value 0x%llx\n",
+ gic_get_current_cpu(), offset, value);
+ gic_cpu_write(&s->gic, gic_get_current_cpu(), offset & ~0x8000, value);
+}
+
+static uint32_t
+exynos4210_gic_dist_readb(void *opaque, target_phys_addr_t offset)
+{
+ Exynos4210GicState *s = (Exynos4210GicState *) opaque;
+ DPRINTF_EXYNOS4210_GIC("DIST: readb offset 0x%x\n", offset);
+ return gic_dist_readb(&s->gic, offset & ~0x8000);
+}
+
+static uint32_t
+exynos4210_gic_dist_readw(void *opaque, target_phys_addr_t offset)
+{
+ Exynos4210GicState *s = (Exynos4210GicState *) opaque;
+ DPRINTF_EXYNOS4210_GIC("DIST: readw offset 0x%x\n", offset);
+ return gic_dist_readw(&s->gic, offset & ~0x8000);
+}
+
+static uint32_t
+exynos4210_gic_dist_readl(void *opaque, target_phys_addr_t offset)
+{
+ Exynos4210GicState *s = (Exynos4210GicState *) opaque;
+ DPRINTF_EXYNOS4210_GIC("DIST: readl offset 0x%x\n", offset);
+ return gic_dist_readl(&s->gic, offset & ~0x8000);
+}
+
+static void
+exynos4210_gic_dist_writeb(void *opaque, target_phys_addr_t offset,
+ uint32_t value)
+{
+ Exynos4210GicState *s = (Exynos4210GicState *) opaque;
+ DPRINTF_EXYNOS4210_GIC("DIST: writeb offset 0x%x, value 0x%x\n", offset,
+ value);
+ gic_dist_writeb(&s->gic, offset & ~0x8000, value);
+}
+
+static void exynos4210_gic_dist_writew(void *opaque, target_phys_addr_t offset,
+ uint32_t value)
+{
+ Exynos4210GicState *s = (Exynos4210GicState *) opaque;
+ DPRINTF_EXYNOS4210_GIC("DIST: writew offset 0x%x, value 0x%x\n", offset,
+ value);
+ gic_dist_writew(&s->gic, offset & ~0x8000, value);
+}
+
+static void exynos4210_gic_dist_writel(void *opaque, target_phys_addr_t offset,
+ uint32_t value)
+{
+ Exynos4210GicState *s = (Exynos4210GicState *) opaque;
+ DPRINTF_EXYNOS4210_GIC("DIST: writel offset 0x%x, value 0x%x\n", offset,
+ value);
+ gic_dist_writel(&s->gic, offset & ~0x8000, value);
+}
+
+static const MemoryRegionOps exynos4210_gic_cpu_ops = {
+ .read = exynos4210_gic_cpu_read,
+ .write = exynos4210_gic_cpu_write,
+ .endianness = DEVICE_NATIVE_ENDIAN,
+};
+
+static const MemoryRegionOps exynos4210_gic_dist_ops = {
+ .old_mmio = {
+ .read = { exynos4210_gic_dist_readb,
+ exynos4210_gic_dist_readw,
+ exynos4210_gic_dist_readl, },
+ .write = { exynos4210_gic_dist_writeb,
+ exynos4210_gic_dist_writew,
+ exynos4210_gic_dist_writel, },
+ },
+ .endianness = DEVICE_NATIVE_ENDIAN,
+};
+
+static int exynos4210_gic_init(SysBusDevice *dev)
+{
+ Exynos4210GicState *s = FROM_SYSBUSGIC(Exynos4210GicState, dev);
+ gic_init(&s->gic, s->num_cpu);
+
+ memory_region_init(&s->cpu_container, "exynos4210-gic-cpu_container",
+ EXYNOS4210_GIC_CPU_REGION_SIZE);
+ memory_region_init(&s->cpu_container, "exynos4210-gic-dist_container",
+ EXYNOS4210_GIC_DIST_REGION_SIZE);
+ memory_region_init_io(&s->cpu_container, &exynos4210_gic_cpu_ops, &s->gic,
+ "exynos4210-gic-cpu", EXYNOS4210_GIC_CPU_REGION_SIZE);
+ memory_region_init_io(&s->dist_container, &exynos4210_gic_dist_ops,
&s->gic,
+ "exynos4210-gic-dist", EXYNOS4210_GIC_DIST_REGION_SIZE);
+
+ 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 SysBusDeviceInfo exynos4210_gic_info = {
+ .init = exynos4210_gic_init,
+ .qdev.name = "exynos4210.gic",
+ .qdev.size = sizeof(Exynos4210GicState),
+ .qdev.props = (Property[]) {
+ DEFINE_PROP_UINT32("num-cpu", Exynos4210GicState, num_cpu, 1),
+ DEFINE_PROP_END_OF_LIST(),
+ }
+};
+
+static void exynos4210_gic_register_devices(void)
+{
+ sysbus_register_withprop(&exynos4210_gic_info);
+}
+
+device_init(exynos4210_gic_register_devices)
+
+/*
+ * 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_Exynos4210IRQGate = {
+ .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;
+
+ DPRINTF_EXYNOS4210_IRQ("odd level=0x%x, even level=0x%x\n",
+ s->gpio_level[odd], s->gpio_level[even]);
+
+ 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 SysBusDeviceInfo exynos4210_irq_gate_info = {
+ .qdev.name = "exynos4210.irq_gate",
+ .qdev.size = sizeof(Exynos4210IRQGateState),
+ .qdev.reset = exynos4210_irq_gate_reset,
+ .qdev.vmsd = &VMState_Exynos4210IRQGate,
+ .init = exynos4210_irq_gate_init,
+};
+
+static void exynos4210_irq_gate_register_devices(void)
+{
+ sysbus_register_withprop(&exynos4210_irq_gate_info);
+}
+
+device_init(exynos4210_irq_gate_register_devices)
--
1.7.4.1
- Re: [Qemu-devel] [PATCH v3 03/14] ARM: exynos4210: UART support, (continued)
[Qemu-devel] [PATCH v3 11/14] hw/exynos4210.c: Add LAN support for SMDKC210., Evgeny Voevodin, 2011/12/12
[Qemu-devel] [PATCH v3 05/14] ARM: exynos4210: IRQ subsystem support.,
Evgeny Voevodin <=
[Qemu-devel] [PATCH v3 08/14] ARM: exynos4210: MCT support., Evgeny Voevodin, 2011/12/12
[Qemu-devel] [PATCH v3 12/14] hw/sd.c, hw/sd.h: add receive ready query routine to SD/MMC API, Evgeny Voevodin, 2011/12/12
[Qemu-devel] [PATCH v3 13/14] ARM: exynos4210: added SD/MMC host controller, Evgeny Voevodin, 2011/12/12
[Qemu-devel] [PATCH v3 14/14] ARM: exynos4210: added display controller implementation, Evgeny Voevodin, 2011/12/12
[Qemu-devel] [PATCH v3 01/14] ARM: Samsung exynos 4210-based boards emulation, Evgeny Voevodin, 2011/12/12