From: Evgeny Voevodin <e.voevodin@samsung.com>
To: qemu-devel@nongnu.org
Cc: m.kozlov@samsung.com, d.solodkiy@samsung.com,
Evgeny Voevodin <e.voevodin@samsung.com>
Subject: [Qemu-devel] [PATCH v4 04/11] ARM: exynos4210: IRQ subsystem support.
Date: Mon, 19 Dec 2011 15:53:30 +0400 [thread overview]
Message-ID: <1324295617-5798-5-git-send-email-e.voevodin@samsung.com> (raw)
In-Reply-To: <1324295617-5798-1-git-send-email-e.voevodin@samsung.com>
Signed-off-by: Evgeny Voevodin <e.voevodin@samsung.com>
---
Makefile.target | 3 +-
hw/exynos4210.c | 179 ++++++++++++++++-
hw/exynos4210.h | 46 ++++
hw/exynos4210_combiner.c | 384 ++++++++++++++++++++++++++++++++++
hw/exynos4210_gic.c | 509 ++++++++++++++++++++++++++++++++++++++++++++++
5 files changed, 1116 insertions(+), 5 deletions(-)
create mode 100644 hw/exynos4210_combiner.c
create mode 100644 hw/exynos4210_gic.c
diff --git a/Makefile.target b/Makefile.target
index ce0e46e..b10cfa2 100644
--- a/Makefile.target
+++ b/Makefile.target
@@ -342,7 +342,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 += exynos4_boards.o exynos4210.o exynos4210_uart.o
+obj-arm-y += exynos4_boards.o exynos4210.o exynos4210_uart.o exynos4210_gic.o \
+ exynos4210_combiner.o
obj-arm-y += arm_mptimer.o
obj-arm-y += armv7m.o armv7m_nvic.o stellaris.o pl022.o stellaris_enet.o
obj-arm-y += pl061.o
diff --git a/hw/exynos4210.c b/hw/exynos4210.c
index afef4cb..a85eb5d 100644
--- a/hw/exynos4210.c
+++ b/hw/exynos4210.c
@@ -23,6 +23,7 @@
#include "boards.h"
#include "sysemu.h"
+#include "sysbus.h"
#include "arm-misc.h"
#include "exynos4210.h"
@@ -38,10 +39,101 @@
#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
+
+/* 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 };
+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);
+ }
+}
Exynos4210State *exynos4210_init(MemoryRegion *system_mem,
unsigned long ram_size)
@@ -49,8 +141,12 @@ Exynos4210State *exynos4210_init(MemoryRegion *system_mem,
qemu_irq cpu_irq[4];
int n;
Exynos4210State *s = g_new(Exynos4210State, 1);
+ qemu_irq *irq_table;
qemu_irq *irqp;
+ qemu_irq gate_irq[EXYNOS4210_IRQ_GATE_NINPUTS];
unsigned long mem_size;
+ DeviceState *dev;
+ SysBusDevice *busdev;
for (n = 0; n < smp_cpus; n++) {
s->env[n] = cpu_init("cortex-a9");
@@ -68,6 +164,77 @@ Exynos4210State *exynos4210_init(MemoryRegion *system_mem,
cpu_irq[n] = irqp[ARM_PIC_CPU_IRQ];
}
+ /*** IRQs ***/
+
+ s->irq_table = exynos4210_init_irq(&s->irqs);
+ irq_table = s->irq_table;
+
+ /* 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++) {
+ s->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++) {
+ 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_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);
+ qdev_prop_set_uint32(dev, "external", 1);
+
+ /* Initialize board IRQs. */
+ exynos4210_init_board_irqs(&s->irqs);
+
/*** Memory ***/
/* Chip-ID and OMR */
@@ -112,16 +279,20 @@ Exynos4210State *exynos4210_init(MemoryRegion *system_mem,
/*** UARTs ***/
exynos4210_uart_create(EXYNOS4210_UART0_BASE_ADDR,
- EXYNOS4210_UART0_FIFO_SIZE, 0, NULL, NULL);
+ EXYNOS4210_UART0_FIFO_SIZE, 0, NULL,
+ irq_table[exynos4210_get_irq(EXYNOS4210_UART_INT_GRP, 0)]);
exynos4210_uart_create(EXYNOS4210_UART1_BASE_ADDR,
- EXYNOS4210_UART1_FIFO_SIZE, 1, NULL, NULL);
+ EXYNOS4210_UART1_FIFO_SIZE, 1, NULL,
+ irq_table[exynos4210_get_irq(EXYNOS4210_UART_INT_GRP, 1)]);
exynos4210_uart_create(EXYNOS4210_UART2_BASE_ADDR,
- EXYNOS4210_UART2_FIFO_SIZE, 2, NULL, NULL);
+ EXYNOS4210_UART2_FIFO_SIZE, 2, NULL,
+ irq_table[exynos4210_get_irq(EXYNOS4210_UART_INT_GRP, 2)]);
exynos4210_uart_create(EXYNOS4210_UART3_BASE_ADDR,
- EXYNOS4210_UART3_FIFO_SIZE, 3, NULL, NULL);
+ EXYNOS4210_UART3_FIFO_SIZE, 3, NULL,
+ irq_table[exynos4210_get_irq(EXYNOS4210_UART_INT_GRP, 3)]);
return s;
}
diff --git a/hw/exynos4210.h b/hw/exynos4210.h
index 4dc119a..b55d159 100644
--- a/hw/exynos4210.h
+++ b/hw/exynos4210.h
@@ -45,8 +45,41 @@
#define EXYNOS4210_BASE_BOOT_ADDR EXYNOS4210_DRAM0_BASE_ADDR
+#define EXYNOS4210_SMP_PRIVATE_BASE_ADDR 0x10500000
+
+/*
+ * 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;
+
typedef struct Exynos4210State {
CPUState * env[EXYNOS4210_MAX_CPUS];
+ Exynos4210Irq irqs;
+ qemu_irq *irq_table;
MemoryRegion chipid_mem;
MemoryRegion iram_mem;
@@ -60,6 +93,19 @@ typedef struct Exynos4210State {
Exynos4210State *exynos4210_init(MemoryRegion *system_mem,
unsigned long ram_size);
+/* 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);
+
/*
* exynos4210 UART
*/
diff --git a/hw/exynos4210_combiner.c b/hw/exynos4210_combiner.c
new file mode 100644
index 0000000..4820f40
--- /dev/null
+++ b/hw/exynos4210_combiner.c
@@ -0,0 +1,384 @@
+/*
+ * 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/>.
+ */
+
+#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..f4ba6b9
--- /dev/null
+++ b/hw/exynos4210_gic.c
@@ -0,0 +1,509 @@
+/*
+ * 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"
+
+//#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->dist_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
next prev parent reply other threads:[~2011-12-19 11:54 UTC|newest]
Thread overview: 21+ messages / expand[flat|nested] mbox.gz Atom feed top
2011-12-19 11:53 [Qemu-devel] [PATCH v4 00/11] ARM: Samsung Exynos4210-based boards support Evgeny Voevodin
2011-12-19 11:53 ` [Qemu-devel] [PATCH v4 01/11] ARM: Samsung exynos4210-based boards emulation Evgeny Voevodin
2011-12-19 11:53 ` [Qemu-devel] [PATCH v4 02/11] ARM: exynos4210: UART support Evgeny Voevodin
2011-12-19 11:53 ` [Qemu-devel] [PATCH v4 03/11] hw/sysbus.h: Increase maximum number of device IRQs Evgeny Voevodin
2011-12-19 11:53 ` Evgeny Voevodin [this message]
2011-12-21 13:50 ` [Qemu-devel] [PATCH v4 04/11] ARM: exynos4210: IRQ subsystem support Peter Maydell
2011-12-21 15:08 ` Evgeny Voevodin
2011-12-21 20:31 ` Peter Maydell
2011-12-22 7:03 ` Evgeny Voevodin
2011-12-22 12:30 ` Peter Maydell
2011-12-22 12:50 ` Evgeny Voevodin
2011-12-22 15:22 ` Peter Maydell
2011-12-19 11:53 ` [Qemu-devel] [PATCH v4 05/11] ARM: exynos4210: PWM support Evgeny Voevodin
2011-12-19 11:53 ` [Qemu-devel] [PATCH v4 06/11] hw/arm_boot.c: Extend secondary CPU bootloader Evgeny Voevodin
2011-12-19 11:53 ` [Qemu-devel] [PATCH v4 07/11] ARM: exynos4210: MCT support Evgeny Voevodin
2011-12-19 11:53 ` [Qemu-devel] [PATCH v4 08/11] hw/exynos4210.c: Boot secondary CPU Evgeny Voevodin
2011-12-19 11:53 ` [Qemu-devel] [PATCH v4 09/11] hw/lan9118: Add basic 16-bit mode support Evgeny Voevodin
2011-12-19 11:53 ` [Qemu-devel] [PATCH v4 10/11] hw/exynos4210.c: Add LAN support for SMDKC210 Evgeny Voevodin
2011-12-19 11:53 ` [Qemu-devel] [PATCH v4 11/11] Exynos4210: added display controller implementation Evgeny Voevodin
2011-12-21 11:49 ` Dmitry Zhurikhin
2011-12-22 8:00 ` Dmitry Solodkiy
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=1324295617-5798-5-git-send-email-e.voevodin@samsung.com \
--to=e.voevodin@samsung.com \
--cc=d.solodkiy@samsung.com \
--cc=m.kozlov@samsung.com \
--cc=qemu-devel@nongnu.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is 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.