* [Qemu-devel] [PATCH 0/4] mips: add Global Interrupt Controller
@ 2015-10-15 23:52 Yongbok Kim
2015-10-15 23:52 ` [Qemu-devel] [PATCH 1/4] target-mips: add CMGCRBase register Yongbok Kim
` (6 more replies)
0 siblings, 7 replies; 14+ messages in thread
From: Yongbok Kim @ 2015-10-15 23:52 UTC (permalink / raw)
To: qemu-devel; +Cc: pbonzini, leon.alrae, afaerber, aurelien
Add support of MIPS GIC.
The patchset implements the Global Interrupt Controller.
With the patchset Linux Kernel detects GIC and utilises it.
# cat /proc/interrupts
CPU0
0: 2 XT-PIC 0 timer
2: 0 XT-PIC 2 cascade
4: 462 XT-PIC 4 serial
8: 0 XT-PIC 8 rtc0
11: 0 XT-PIC 11 uhci_hcd:usb1
14: 1026 XT-PIC 14 ide0
15: 0 XT-PIC 15 ide1
21: 0 MIPS 5 CoreHi
23: 44 MIPS 7 timer
25: 12290 MIPS GIC Local 1 timer
34: 1490 MIPS GIC 10 XT-PIC cascade
98: 1490 MIPS GIC 74
ERR: 0
Limitation:
Level triggering only
No User-Mode Visible Section
GIC CounterHi not implemented (Countbits = 32bits)
DINT not implemented
Local WatchDog, Fast Debug Channel, Perf Counter not implemented
It is based on the earlier un-merged GIC implementation.
https://lists.gnu.org/archive/html/qemu-devel/2013-03/msg00194.html
For more information,
http://imgtec.com/mips/warrior/p-class-p5600-multiprocessor-core/
http://imgtec.com/mips/warrior/i-class-i6400-multiprocessor-core/
Yongbok Kim (4):
target-mips: add CMGCRBase register
mips: add Global Config Register block (part)
mips: add Global Interrupt Controller
mips: add gic support to malta
hw/mips/Makefile.objs | 2 +-
hw/mips/mips_gcr.c | 120 ++++++++
hw/mips/mips_gcr.h | 57 ++++
hw/mips/mips_gic.c | 653 ++++++++++++++++++++++++++++++++++++++++++
hw/mips/mips_gic.h | 298 +++++++++++++++++++
hw/mips/mips_malta.c | 71 +++++-
target-mips/cpu.h | 4 +-
target-mips/translate.c | 17 ++
target-mips/translate_init.c | 3 +-
9 files changed, 1220 insertions(+), 5 deletions(-)
create mode 100644 hw/mips/mips_gcr.c
create mode 100644 hw/mips/mips_gcr.h
create mode 100644 hw/mips/mips_gic.c
create mode 100644 hw/mips/mips_gic.h
^ permalink raw reply [flat|nested] 14+ messages in thread
* [Qemu-devel] [PATCH 1/4] target-mips: add CMGCRBase register
2015-10-15 23:52 [Qemu-devel] [PATCH 0/4] mips: add Global Interrupt Controller Yongbok Kim
@ 2015-10-15 23:52 ` Yongbok Kim
2015-10-19 15:07 ` Leon Alrae
2015-10-15 23:52 ` [Qemu-devel] [PATCH 2/4] mips: add Global Config Register block (part) Yongbok Kim
` (5 subsequent siblings)
6 siblings, 1 reply; 14+ messages in thread
From: Yongbok Kim @ 2015-10-15 23:52 UTC (permalink / raw)
To: qemu-devel; +Cc: pbonzini, leon.alrae, afaerber, aurelien
Physical base address for the memory-mapped Coherency Manager Global
Configuration Register space.
The MIPS default location for the GCR_BASE address is 0x1FBF_8.
This register only exists if Config3 CMGCR is set to one.
Signed-off-by: Yongbok Kim <yongbok.kim@imgtec.com>
---
target-mips/cpu.h | 3 ++-
target-mips/translate.c | 17 +++++++++++++++++
target-mips/translate_init.c | 3 ++-
3 files changed, 21 insertions(+), 2 deletions(-)
diff --git a/target-mips/cpu.h b/target-mips/cpu.h
index f32a0fd..639ef37 100644
--- a/target-mips/cpu.h
+++ b/target-mips/cpu.h
@@ -389,6 +389,7 @@ struct CPUMIPSState {
target_ulong CP0_EPC;
int32_t CP0_PRid;
int32_t CP0_EBase;
+ target_ulong CP0_CMGCRBase;
int32_t CP0_Config0;
#define CP0C0_M 31
#define CP0C0_K23 28
@@ -431,7 +432,7 @@ struct CPUMIPSState {
int32_t CP0_Config3;
#define CP0C3_M 31
#define CP0C3_BPG 30
-#define CP0C3_CMCGR 29
+#define CP0C3_CMGCR 29
#define CP0C3_MSAP 28
#define CP0C3_BP 27
#define CP0C3_BI 26
diff --git a/target-mips/translate.c b/target-mips/translate.c
index 897839c..c74e8e7 100644
--- a/target-mips/translate.c
+++ b/target-mips/translate.c
@@ -1426,6 +1426,7 @@ typedef struct DisasContext {
bool mvh;
int CP0_LLAddr_shift;
bool ps;
+ bool cmgcr;
} DisasContext;
enum {
@@ -5273,6 +5274,12 @@ static void gen_mfc0(DisasContext *ctx, TCGv arg, int reg, int sel)
gen_mfc0_load32(arg, offsetof(CPUMIPSState, CP0_EBase));
rn = "EBase";
break;
+ case 3:
+ check_insn(ctx, ISA_MIPS32R2);
+ CP0_CHECK(ctx->cmgcr);
+ gen_mfc0_load32(arg, offsetof(CPUMIPSState, CP0_CMGCRBase));
+ rn = "CMGCRBase";
+ break;
default:
goto cp0_unimplemented;
}
@@ -6527,6 +6534,12 @@ static void gen_dmfc0(DisasContext *ctx, TCGv arg, int reg, int sel)
gen_mfc0_load32(arg, offsetof(CPUMIPSState, CP0_EBase));
rn = "EBase";
break;
+ case 3:
+ check_insn(ctx, ISA_MIPS32R2);
+ CP0_CHECK(ctx->cmgcr);
+ gen_mfc0_load32(arg, offsetof(CPUMIPSState, CP0_CMGCRBase));
+ rn = "CMGCRBase";
+ break;
default:
goto cp0_unimplemented;
}
@@ -19567,6 +19580,7 @@ void gen_intermediate_code(CPUMIPSState *env, struct TranslationBlock *tb)
ctx.ulri = (env->CP0_Config3 >> CP0C3_ULRI) & 1;
ctx.ps = ((env->active_fpu.fcr0 >> FCR0_PS) & 1) ||
(env->insn_flags & (INSN_LOONGSON2E | INSN_LOONGSON2F));
+ ctx.cmgcr = env->CP0_Config3 & (1 << CP0C3_CMGCR);
restore_cpu_state(env, &ctx);
#ifdef CONFIG_USER_ONLY
ctx.mem_idx = MIPS_HFLAG_UM;
@@ -19955,6 +19969,9 @@ void cpu_state_reset(CPUMIPSState *env)
} else {
env->CP0_EBase |= 0x80000000;
}
+ if (env->CP0_Config3 & (1 << CP0C3_CMGCR)) {
+ env->CP0_CMGCRBase = 0x1fbf8000 >> 4;
+ }
env->CP0_Status = (1 << CP0St_BEV) | (1 << CP0St_ERL);
/* vectored interrupts not implemented, timer on int 7,
no performance counters. */
diff --git a/target-mips/translate_init.c b/target-mips/translate_init.c
index 1b45884..a6b8986 100644
--- a/target-mips/translate_init.c
+++ b/target-mips/translate_init.c
@@ -660,7 +660,8 @@ static const mips_def_t mips_defs[] =
(2 << CP0C1_DS) | (4 << CP0C1_DL) | (3 << CP0C1_DA) |
(0 << CP0C1_PC) | (1 << CP0C1_WR) | (1 << CP0C1_EP),
.CP0_Config2 = MIPS_CONFIG2,
- .CP0_Config3 = MIPS_CONFIG3 | (1U << CP0C3_M) | (1 << CP0C3_MSAP) |
+ .CP0_Config3 = MIPS_CONFIG3 | (1U << CP0C3_M) |
+ (1 << CP0C3_CMGCR) | (1 << CP0C3_MSAP) |
(1 << CP0C3_BP) | (1 << CP0C3_BI) | (1 << CP0C3_ULRI) |
(1 << CP0C3_RXI) | (1 << CP0C3_LPA),
.CP0_Config4 = MIPS_CONFIG4 | (1U << CP0C4_M) | (3 << CP0C4_IE) |
--
1.7.1
^ permalink raw reply related [flat|nested] 14+ messages in thread
* [Qemu-devel] [PATCH 2/4] mips: add Global Config Register block (part)
2015-10-15 23:52 [Qemu-devel] [PATCH 0/4] mips: add Global Interrupt Controller Yongbok Kim
2015-10-15 23:52 ` [Qemu-devel] [PATCH 1/4] target-mips: add CMGCRBase register Yongbok Kim
@ 2015-10-15 23:52 ` Yongbok Kim
2015-10-15 23:52 ` [Qemu-devel] [PATCH 3/4] mips: add Global Interrupt Controller Yongbok Kim
` (4 subsequent siblings)
6 siblings, 0 replies; 14+ messages in thread
From: Yongbok Kim @ 2015-10-15 23:52 UTC (permalink / raw)
To: qemu-devel; +Cc: pbonzini, leon.alrae, afaerber, aurelien
Add part of GCR Block which Linux Kernel utilises and it is enough
to bring the GIC up.
Signed-off-by: Yongbok Kim <yongbok.kim@imgtec.com>
---
hw/mips/Makefile.objs | 2 +-
hw/mips/mips_gcr.c | 120 +++++++++++++++++++++++++++++++++++++++++++++++++
hw/mips/mips_gcr.h | 57 +++++++++++++++++++++++
3 files changed, 178 insertions(+), 1 deletions(-)
create mode 100644 hw/mips/mips_gcr.c
create mode 100644 hw/mips/mips_gcr.h
diff --git a/hw/mips/Makefile.objs b/hw/mips/Makefile.objs
index 9633f3a..d247d95 100644
--- a/hw/mips/Makefile.objs
+++ b/hw/mips/Makefile.objs
@@ -1,5 +1,5 @@
obj-y += mips_r4k.o mips_malta.o mips_mipssim.o
-obj-y += addr.o cputimer.o mips_int.o
+obj-y += addr.o cputimer.o mips_int.o mips_gcr.o
obj-$(CONFIG_JAZZ) += mips_jazz.o
obj-$(CONFIG_FULONG) += mips_fulong2e.o
obj-y += gt64xxx_pci.o
diff --git a/hw/mips/mips_gcr.c b/hw/mips/mips_gcr.c
new file mode 100644
index 0000000..088ddef
--- /dev/null
+++ b/hw/mips/mips_gcr.c
@@ -0,0 +1,120 @@
+/*
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License. See the file "COPYING" in the main directory of this archive
+ * for more details.
+ *
+ * Copyright (C) 2012 MIPS Technologies, Inc. All rights reserved.
+ * Authors: Sanjay Lal <sanjayl@kymasys.com>
+ *
+ * Copyright (C) 2015 Imagination Technologies
+ */
+
+#include "hw/hw.h"
+#include "hw/sysbus.h"
+#include "sysemu/sysemu.h"
+#include "hw/mips/mips_gcr.h"
+
+/* Read GCR registers */
+static uint64_t gcr_read(void *opaque, hwaddr addr, unsigned size)
+{
+ MIPSGCRState *gcr = (MIPSGCRState *) opaque;
+
+ switch (addr) {
+ /* Global Control Block Register */
+ case GCR_CONFIG_OFS:
+ /* Set PCORES to 0 */
+ return 0;
+ case GCR_BASE_OFS:
+ return GCR_BASE_ADDR;
+ case GCR_REV_OFS:
+ return gcr->gcr_rev;
+ case GCR_GIC_BASE_OFS:
+ return gcr->gcr_gic_base;
+ case GCR_GIC_STATUS_OFS:
+ return GCR_GIC_STATUS_GICEX_MSK;
+ case GCR_CPC_STATUS_OFS:
+ return 0;
+ case GCR_L2_CONFIG_OFS:
+ /* L2 BYPASS */
+ return GCR_L2_CONFIG_BYPASS_MSK;
+
+ /* Core-Local and Core-Other Control Blocks */
+ case MIPS_CLCB_OFS + GCR_CL_CONFIG_OFS:
+ case MIPS_COCB_OFS + GCR_CL_CONFIG_OFS:
+ /* Set PVP to # cores - 1 */
+ return smp_cpus - 1;
+ case MIPS_CLCB_OFS + GCR_CL_OTHER_OFS:
+ return 0;
+
+ default:
+ qemu_log_mask(LOG_UNIMP,
+ "Warning *** unimplemented GCR read at offset 0x%" PRIx64 "\n",
+ addr);
+ return 0;
+ }
+ return 0ULL;
+}
+
+/* Write GCR registers */
+static void gcr_write(void *opaque, hwaddr addr, uint64_t data, unsigned size)
+{
+ /*
+ * MIPSGCRState *gcr = (MIPSGCRState *) opaque;
+ * */
+
+ switch (addr) {
+ case GCR_GIC_BASE_OFS:
+ break;
+ default:
+ qemu_log_mask(LOG_UNIMP,
+ "Warning *** unimplemented GCR write at offset 0x%" PRIx64 "\n",
+ addr);
+ break;
+ }
+}
+
+static const MemoryRegionOps gcr_ops = {
+ .read = gcr_read,
+ .write = gcr_write,
+ .endianness = DEVICE_NATIVE_ENDIAN,
+ .impl = {
+ .max_access_size = 8,
+ },
+};
+
+static void mips_gcr_init(Object *obj)
+{
+ SysBusDevice *sbd = SYS_BUS_DEVICE(obj);
+ MIPSGCRState *s = MIPS_GCR(obj);
+
+ memory_region_init_io(&s->gcr_mem, OBJECT(s), &gcr_ops, s,
+ "mips-gcr", GCR_ADDRSPACE_SZ);
+ sysbus_init_mmio(sbd, &s->gcr_mem);
+}
+
+static Property mips_gcr_properties[] = {
+ DEFINE_PROP_INT32("num-cpu", MIPSGCRState, num_cpu, 1),
+ DEFINE_PROP_INT32("gcr-rev", MIPSGCRState, gcr_rev, 0x800),
+ DEFINE_PROP_END_OF_LIST(),
+};
+
+static void mips_gcr_class_init(ObjectClass *klass, void *data)
+{
+ DeviceClass *dc = DEVICE_CLASS(klass);
+ dc->props = mips_gcr_properties;
+}
+
+static const TypeInfo mips_gcr_info = {
+ .name = TYPE_MIPS_GCR,
+ .parent = TYPE_SYS_BUS_DEVICE,
+ .instance_size = sizeof(MIPSGCRState),
+ .instance_init = mips_gcr_init,
+ .class_init = mips_gcr_class_init,
+};
+
+static void mips_gcr_register_types(void)
+{
+ type_register_static(&mips_gcr_info);
+}
+
+type_init(mips_gcr_register_types)
diff --git a/hw/mips/mips_gcr.h b/hw/mips/mips_gcr.h
new file mode 100644
index 0000000..2a9e1c0
--- /dev/null
+++ b/hw/mips/mips_gcr.h
@@ -0,0 +1,57 @@
+/*
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License. See the file "COPYING" in the main directory of this archive
+ * for more details.
+ *
+ * Copyright (C) 2015 Imagination Technologies
+ *
+ */
+
+#ifndef _MIPS_GCR_H
+#define _MIPS_GCR_H
+
+#define TYPE_MIPS_GCR "mips-gcr"
+#define MIPS_GCR(obj) OBJECT_CHECK(MIPSGCRState, (obj), TYPE_MIPS_GCR)
+
+#define GCR_BASE_ADDR 0x1fbf8000ULL
+#define GCR_ADDRSPACE_SZ 0x8000
+
+/* Offsets to register blocks */
+#define MIPS_GCB_OFS 0x0000 /* Global Control Block */
+#define MIPS_CLCB_OFS 0x2000 /* Core Local Control Block */
+#define MIPS_COCB_OFS 0x4000 /* Core Other Control Block */
+#define MIPS_GDB_OFS 0x6000 /* Global Debug Block */
+
+/* Global Control Block Register Map */
+#define GCR_CONFIG_OFS 0x0000
+#define GCR_BASE_OFS 0x0008
+#define GCR_REV_OFS 0x0030
+#define GCR_GIC_BASE_OFS 0x0080
+#define GCR_GIC_STATUS_OFS 0x00D0
+#define GCR_CPC_STATUS_OFS 0x00F0
+#define GCR_L2_CONFIG_OFS 0x0130
+
+/* Core Local and Core Other Block Register Map */
+#define GCR_CL_CONFIG_OFS 0x0010
+#define GCR_CL_OTHER_OFS 0x0018
+
+/* GCR_GIC_STATUS register fields */
+#define GCR_GIC_STATUS_GICEX_SHF 0
+#define GCR_GIC_STATUS_GICEX_MSK ((0x1ULL) << GCR_GIC_STATUS_GICEX_SHF)
+
+/* GCR_L2_CONFIG register fields */
+#define GCR_L2_CONFIG_BYPASS_SHF 20
+#define GCR_L2_CONFIG_BYPASS_MSK ((0x1ULL) << GCR_L2_CONFIG_BYPASS_SHF)
+
+
+typedef struct MIPSGCRState MIPSGCRState;
+struct MIPSGCRState {
+ SysBusDevice parent_obj;
+
+ target_ulong gcr_gic_base;
+ int32_t gcr_rev;
+ int32_t num_cpu;
+ MemoryRegion gcr_mem;
+} ;
+
+#endif /* _MIPS_GCR_H */
--
1.7.1
^ permalink raw reply related [flat|nested] 14+ messages in thread
* [Qemu-devel] [PATCH 3/4] mips: add Global Interrupt Controller
2015-10-15 23:52 [Qemu-devel] [PATCH 0/4] mips: add Global Interrupt Controller Yongbok Kim
2015-10-15 23:52 ` [Qemu-devel] [PATCH 1/4] target-mips: add CMGCRBase register Yongbok Kim
2015-10-15 23:52 ` [Qemu-devel] [PATCH 2/4] mips: add Global Config Register block (part) Yongbok Kim
@ 2015-10-15 23:52 ` Yongbok Kim
2015-10-21 15:49 ` Leon Alrae
2015-10-15 23:52 ` [Qemu-devel] [PATCH 4/4] mips: add gic support to malta Yongbok Kim
` (3 subsequent siblings)
6 siblings, 1 reply; 14+ messages in thread
From: Yongbok Kim @ 2015-10-15 23:52 UTC (permalink / raw)
To: qemu-devel; +Cc: pbonzini, leon.alrae, afaerber, aurelien
The Global Interrupt Controller (GIC) is responsible for mapping each
internal and external interrupt to the correct location for servicing.
Limitations:
Level triggering only
No User-Mode Visible Section
GIC CounterHi not implemented (Countbits = 32bits)
DINT not implemented
Local WatchDog, Fast Debug Channel, Perf Counter not implemented
Signed-off-by: Yongbok Kim <yongbok.kim@imgtec.com>
---
hw/mips/Makefile.objs | 2 +-
hw/mips/mips_gic.c | 653 +++++++++++++++++++++++++++++++++++++++++++++++++
hw/mips/mips_gic.h | 298 ++++++++++++++++++++++
3 files changed, 952 insertions(+), 1 deletions(-)
create mode 100644 hw/mips/mips_gic.c
create mode 100644 hw/mips/mips_gic.h
diff --git a/hw/mips/Makefile.objs b/hw/mips/Makefile.objs
index d247d95..6cd9d67 100644
--- a/hw/mips/Makefile.objs
+++ b/hw/mips/Makefile.objs
@@ -1,5 +1,5 @@
obj-y += mips_r4k.o mips_malta.o mips_mipssim.o
-obj-y += addr.o cputimer.o mips_int.o mips_gcr.o
+obj-y += addr.o cputimer.o mips_int.o mips_gcr.o mips_gic.o
obj-$(CONFIG_JAZZ) += mips_jazz.o
obj-$(CONFIG_FULONG) += mips_fulong2e.o
obj-y += gt64xxx_pci.o
diff --git a/hw/mips/mips_gic.c b/hw/mips/mips_gic.c
new file mode 100644
index 0000000..27ae7ab
--- /dev/null
+++ b/hw/mips/mips_gic.c
@@ -0,0 +1,653 @@
+/*
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License. See the file "COPYING" in the main directory of this archive
+ * for more details.
+ *
+ * Copyright (C) 2012 MIPS Technologies, Inc. All rights reserved.
+ * Authors: Sanjay Lal <sanjayl@kymasys.com>
+ *
+ * Copyright (C) 2015 Imagination Technologies
+ */
+
+#include "hw/hw.h"
+#include "hw/sysbus.h"
+#include "qemu/bitmap.h"
+#include "exec/memory.h"
+#include "sysemu/sysemu.h"
+#include "qom/cpu.h"
+#include "exec/address-spaces.h"
+
+#ifdef CONFIG_KVM
+#include "sysemu/kvm.h"
+#include "kvm_mips.h"
+#endif
+
+#include "hw/mips/mips_gic.h"
+
+#define TIMER_PERIOD 10 /* 10 ns period for 100 Mhz frequency */
+
+static inline int gic_get_current_cpu(MIPSGICState *g)
+{
+ if (g->num_cpu > 1) {
+ return current_cpu->cpu_index;
+ }
+ return 0;
+}
+
+static void gic_set_vp_irq(MIPSGICState *gic, int vpe, int pin, int level)
+{
+ int ored_level = level;
+ int i;
+ /* ORing pending registers sharing same pin */
+ if (!ored_level) {
+ for (i = 0; i < gic->num_irq; i++) {
+ if ((gic->gic_irqs[i].map_pin & GIC_MAP_MSK) == pin &&
+ gic->gic_irqs[i].map_vpe == vpe &&
+ gic->gic_irqs[i].enabled) {
+ ored_level |= gic->gic_irqs[i].pending;
+ }
+ if (ored_level) {
+ /* no need to iterate all interrupts */
+ break;
+ }
+ }
+ if (((gic->vps[vpe].compare_map & GIC_MAP_MSK) == pin) &&
+ (gic->vps[vpe].mask & GIC_VPE_SMASK_CMP_MSK)) {
+ /* ORing with local pending register (count/compare) */
+ ored_level |= ((gic->vps[vpe].pend >> 1) & 1);
+ }
+ }
+
+#ifdef CONFIG_KVM
+ if (kvm_enabled()) {
+ kvm_mips_set_ipi_interrupt(gic->vps[vpe].env, pin + GIC_CPU_PIN_OFFSET,
+ ored_level);
+ }
+#endif
+ qemu_set_irq(gic->vps[vpe].env->irq[pin + GIC_CPU_PIN_OFFSET], ored_level);
+}
+
+/* GIC VPE Local Timer */
+static uint32_t gic_vpe_timer_update(MIPSGICState *gic, uint32_t vp_index)
+{
+ uint64_t now, next;
+ uint32_t wait;
+
+ now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
+ wait = gic->vps[vp_index].comparelo - gic->gic_sh_counterlo -
+ (uint32_t)(now / TIMER_PERIOD);
+ next = now + (uint64_t)wait * TIMER_PERIOD;
+
+ timer_mod(gic->vps[vp_index].gic_timer->qtimer , next);
+ return wait;
+}
+
+static void gic_vpe_timer_expire(MIPSGICState *gic, uint32_t vp_index)
+{
+ uint32_t pin;
+ pin = (gic->vps[vp_index].compare_map & GIC_MAP_MSK);
+ gic_vpe_timer_update(gic, vp_index);
+ gic->vps[vp_index].pend |= (1 << 1);
+
+ if (gic->vps[vp_index].pend &
+ (gic->vps[vp_index].mask & GIC_VPE_SMASK_CMP_MSK)) {
+ if (gic->vps[vp_index].compare_map & 0x80000000) {
+ /* it is safe to set the irq high regardless of other GIC IRQs */
+ qemu_irq_raise(gic->vps[vp_index].env->irq
+ [pin + GIC_CPU_PIN_OFFSET]);
+ }
+ }
+}
+
+static uint32_t gic_get_sh_count(MIPSGICState *gic)
+{
+ int i;
+ if (gic->gic_sh_config & (1 << 28)) {
+ return gic->gic_sh_counterlo;
+ } else {
+ uint64_t now;
+ now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
+ for (i = 0; i < gic->num_cpu; i++) {
+ if (timer_pending(gic->vps[i].gic_timer->qtimer)
+ && timer_expired(gic->vps[i].gic_timer->qtimer , now)) {
+ /* The timer has already expired. */
+ gic_vpe_timer_expire(gic, i);
+ }
+ }
+ return gic->gic_sh_counterlo + (uint32_t)(now / TIMER_PERIOD);
+ }
+}
+
+static void gic_store_sh_count(MIPSGICState *gic, uint64_t count)
+{
+ int i;
+
+ if ((gic->gic_sh_config & 0x10000000) || !gic->vps[0].gic_timer) {
+ gic->gic_sh_counterlo = count;
+ } else {
+ /* Store new count register */
+ gic->gic_sh_counterlo = count -
+ (uint32_t)(qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) / TIMER_PERIOD);
+ /* Update timer timer */
+ for (i = 0; i < gic->num_cpu; i++) {
+ gic_vpe_timer_update(gic, i);
+ }
+ }
+}
+
+static void gic_store_vpe_compare(MIPSGICState *gic, uint32_t vp_index,
+ uint64_t compare)
+{
+ uint32_t wait;
+
+ gic->vps[vp_index].comparelo = (uint32_t) compare;
+ wait = gic_vpe_timer_update(gic, vp_index);
+
+ gic->vps[vp_index].pend &= ~(1 << 1);
+ if (gic->vps[vp_index].compare_map & 0x80000000) {
+ uint32_t pin = (gic->vps[vp_index].compare_map & GIC_MAP_MSK);
+ gic_set_vp_irq(gic, vp_index, pin, 0);
+ }
+}
+
+static void gic_vpe_timer_cb(void *opaque)
+{
+ MIPSGICTimerState *gic_timer = opaque;
+ gic_timer->gic->gic_sh_counterlo++;
+ gic_vpe_timer_expire(gic_timer->gic, gic_timer->vp_index);
+ gic_timer->gic->gic_sh_counterlo--;
+}
+
+static void gic_timer_start_count(MIPSGICState *gic)
+{
+ gic_store_sh_count(gic, gic->gic_sh_counterlo);
+}
+
+static void gic_timer_stop_count(MIPSGICState *gic)
+{
+ int i;
+
+ /* Store the current value */
+ gic->gic_sh_counterlo +=
+ (uint32_t)(qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) / TIMER_PERIOD);
+ for (i = 0; i < gic->num_cpu; i++) {
+ timer_del(gic->vps[i].gic_timer->qtimer);
+ }
+}
+
+static void gic_timer_init(MIPSGICState *gic, uint32_t ncpus)
+{
+ int i;
+ for (i = 0; i < ncpus; i++) {
+ gic->vps[i].gic_timer = g_malloc0(sizeof(MIPSGICTimerState));
+ gic->vps[i].gic_timer->gic = gic;
+ gic->vps[i].gic_timer->vp_index = i;
+ gic->vps[i].gic_timer->qtimer = timer_new_ns(QEMU_CLOCK_VIRTUAL,
+ &gic_vpe_timer_cb,
+ gic->vps[i].gic_timer);
+ }
+ gic_store_sh_count(gic, gic->gic_sh_counterlo);
+}
+
+/* GIC Read VPE Local/Other Registers */
+static uint64_t gic_read_vpe(MIPSGICState *gic, uint32_t vp_index, hwaddr addr,
+ unsigned size)
+{
+ switch (addr) {
+ case GIC_VPE_CTL_OFS:
+ return gic->vps[vp_index].ctl;
+ case GIC_VPE_PEND_OFS:
+ gic_get_sh_count(gic);
+ return gic->vps[vp_index].pend;
+ case GIC_VPE_MASK_OFS:
+ return gic->vps[vp_index].mask;
+ case GIC_VPE_WD_MAP_OFS:
+ return gic->vps[vp_index].wd_map;
+ case GIC_VPE_COMPARE_MAP_OFS:
+ return gic->vps[vp_index].compare_map;
+ case GIC_VPE_TIMER_MAP_OFS:
+ return gic->vps[vp_index].timer_map;
+ case GIC_VPE_OTHER_ADDR_OFS:
+ return gic->vps[vp_index].other_addr;
+ case GIC_VPE_IDENT_OFS:
+ return vp_index;
+ case GIC_VPE_COMPARE_LO_OFS:
+ return gic->vps[vp_index].comparelo;
+ case GIC_VPE_COMPARE_HI_OFS:
+ return gic->vps[vp_index].comparehi;
+ default:
+ qemu_log_mask(LOG_UNIMP,
+ "Warning *** read %d bytes at GIC offset LOCAL/OTHER 0x%"
+ PRIx64 "\n",
+ size, addr);
+ break;
+ }
+ return 0;
+}
+
+static uint64_t gic_read(void *opaque, hwaddr addr, unsigned size)
+{
+ MIPSGICState *gic = (MIPSGICState *) opaque;
+ uint32_t vp_index = gic_get_current_cpu(gic);
+ uint64_t ret = 0;
+ int i, base;
+
+ switch (addr) {
+ case GIC_SH_CONFIG_OFS:
+ return gic->gic_sh_config;
+ case GIC_SH_CONFIG_OFS + 4:
+ /* do nothing */
+ return 0;
+ case GIC_SH_COUNTERLO_OFS:
+ ret = gic_get_sh_count(gic);
+ return ret;
+ case GIC_SH_COUNTERHI_OFS:
+ return 0;
+ case GIC_SH_POL_31_0_OFS:
+ case GIC_SH_POL_63_32_OFS:
+ case GIC_SH_POL_95_64_OFS:
+ case GIC_SH_POL_127_96_OFS:
+ case GIC_SH_POL_159_128_OFS:
+ case GIC_SH_POL_191_160_OFS:
+ case GIC_SH_POL_223_192_OFS:
+ case GIC_SH_POL_255_224_OFS:
+ base = (addr - GIC_SH_POL_31_0_OFS) * 8;
+ for (i = 0; i < size * 8; i++) {
+ ret |= (gic->gic_irqs[i].polarity & 1) << i;
+ }
+ return ret;
+ case GIC_SH_TRIG_31_0_OFS:
+ case GIC_SH_TRIG_63_32_OFS:
+ case GIC_SH_TRIG_95_64_OFS:
+ case GIC_SH_TRIG_127_96_OFS:
+ case GIC_SH_TRIG_159_128_OFS:
+ case GIC_SH_TRIG_191_160_OFS:
+ case GIC_SH_TRIG_223_192_OFS:
+ case GIC_SH_TRIG_255_224_OFS:
+ base = (addr - GIC_SH_TRIG_31_0_OFS) * 8;
+ for (i = 0; i < size * 8; i++) {
+ ret |= (gic->gic_irqs[i].trigger_type & 1) << i;
+ }
+ return ret;
+ case GIC_SH_PEND_31_0_OFS:
+ case GIC_SH_PEND_63_32_OFS:
+ case GIC_SH_PEND_95_64_OFS:
+ case GIC_SH_PEND_127_96_OFS:
+ case GIC_SH_PEND_159_128_OFS:
+ case GIC_SH_PEND_191_160_OFS:
+ case GIC_SH_PEND_223_192_OFS:
+ case GIC_SH_PEND_255_224_OFS:
+ base = (addr - GIC_SH_PEND_31_0_OFS) * 8;
+ for (i = 0; i < size * 8; i++) {
+ ret |= (gic->gic_irqs[i].pending & 1) << i;
+ }
+ return ret;
+ case GIC_SH_MASK_31_0_OFS:
+ case GIC_SH_MASK_63_32_OFS:
+ case GIC_SH_MASK_95_64_OFS:
+ case GIC_SH_MASK_127_96_OFS:
+ case GIC_SH_MASK_159_128_OFS:
+ case GIC_SH_MASK_191_160_OFS:
+ case GIC_SH_MASK_223_192_OFS:
+ case GIC_SH_MASK_255_224_OFS:
+ base = (addr - GIC_SH_MASK_31_0_OFS) * 8;
+ for (i = 0; i < size * 8; i++) {
+ ret |= (gic->gic_irqs[i].enabled & 1) << i;
+ }
+ return ret;
+ default:
+ if (addr < GIC_SH_MAP0_PIN_OFS) {
+ qemu_log_mask(LOG_UNIMP,
+ "Warning *** read %d bytes at GIC offset 0x%" PRIx64 "\n",
+ size, addr);
+ }
+ break;
+ }
+
+ /* Global Interrupt Map SrcX to Pin register */
+ if (addr >= GIC_SH_MAP0_PIN_OFS && addr <= GIC_SH_MAP255_PIN_OFS) {
+ int irq_src = (addr - GIC_SH_MAP0_PIN_OFS) / 4;
+ ret = gic->gic_irqs[irq_src].map_pin;
+ return ret;
+ }
+
+ /* Global Interrupt Map SrcX to VPE register */
+ if (addr >= GIC_SH_MAP0_VPE31_0_OFS && addr <= GIC_SH_MAP255_VPE63_32_OFS) {
+ int irq_src = (addr - GIC_SH_MAP0_VPE31_0_OFS) / 32;
+ ret = 1 << (gic->gic_irqs[irq_src].map_vpe);
+ return ret;
+ }
+
+ /* VPE-Local Register */
+ if (addr >= GIC_VPELOCAL_BASE_ADDR && addr < GIC_VPEOTHER_BASE_ADDR) {
+ return gic_read_vpe(gic, vp_index, addr - GIC_VPELOCAL_BASE_ADDR, size);
+ }
+
+ /* VPE-Other Register */
+ if (addr >= GIC_VPEOTHER_BASE_ADDR && addr < GIC_USERMODE_BASE_ADDR) {
+ uint32_t other_index = gic->vps[vp_index].other_addr;
+ return gic_read_vpe(gic, other_index, addr - GIC_VPEOTHER_BASE_ADDR,
+ size);
+ }
+
+ qemu_log_mask(LOG_UNIMP, "GIC unimplemented register %" PRIx64 "\n", addr);
+ return 0ULL;
+}
+
+/* GIC Write VPE Local/Other Registers */
+static void gic_write_vpe(MIPSGICState *gic, uint32_t vp_index, hwaddr addr,
+ uint64_t data, unsigned size)
+{
+ switch (addr) {
+ case GIC_VPE_CTL_OFS:
+ gic->vps[vp_index].ctl &= ~1;
+ gic->vps[vp_index].ctl |= data & 1;
+ break;
+ case GIC_VPE_RMASK_OFS:
+ gic->vps[vp_index].mask &= ~(data & 0x3f) & 0x3f;
+ break;
+ case GIC_VPE_SMASK_OFS:
+ gic->vps[vp_index].mask |= (data & 0x3f);
+ break;
+ case GIC_VPE_WD_MAP_OFS:
+ gic->vps[vp_index].wd_map = data & 0xE000003F;
+ break;
+ case GIC_VPE_COMPARE_MAP_OFS:
+ gic->vps[vp_index].compare_map = data & 0xE000003F;
+ break;
+ case GIC_VPE_TIMER_MAP_OFS:
+ gic->vps[vp_index].timer_map = data & 0xE000003F;
+ break;
+ case GIC_VPE_OTHER_ADDR_OFS:
+ if (data < gic->num_cpu) {
+ gic->vps[vp_index].other_addr = data;
+ }
+ break;
+ case GIC_VPE_OTHER_ADDR_OFS + 4:
+ /* do nothing */
+ break;
+ case GIC_VPE_COMPARE_LO_OFS:
+ gic_store_vpe_compare(gic, vp_index, data);
+ break;
+ case GIC_VPE_COMPARE_HI_OFS:
+ /* do nothing */
+ break;
+ default:
+ qemu_log_mask(LOG_UNIMP,
+ "Warning *** write %d bytes at GIC offset LOCAL/OTHER "
+ "0x%" PRIx64" 0x%08lx\n", size, addr, data);
+ break;
+ }
+}
+
+static void gic_write(void *opaque, hwaddr addr, uint64_t data, unsigned size)
+{
+ int intr;
+ MIPSGICState *gic = (MIPSGICState *) opaque;
+ uint32_t vp_index = gic_get_current_cpu(gic);
+ int i, base;
+
+ switch (addr) {
+ case GIC_SH_CONFIG_OFS:
+ {
+ uint32_t pre = gic->gic_sh_config;
+ gic->gic_sh_config = (gic->gic_sh_config & 0xEFFFFFFF) |
+ (data & 0x10000000);
+ if (pre != gic->gic_sh_config) {
+ if ((gic->gic_sh_config & 0x10000000)) {
+ gic_timer_stop_count(gic);
+ }
+ if (!(gic->gic_sh_config & 0x10000000)) {
+ gic_timer_start_count(gic);
+ }
+ }
+ }
+ break;
+ case GIC_SH_CONFIG_OFS + 4:
+ /* do nothing */
+ break;
+ case GIC_SH_COUNTERLO_OFS:
+ if (gic->gic_sh_config & 0x10000000) {
+ gic_store_sh_count(gic, data);
+ }
+ break;
+ case GIC_SH_COUNTERHI_OFS:
+ /* do nothing */
+ break;
+ case GIC_SH_POL_31_0_OFS:
+ case GIC_SH_POL_63_32_OFS:
+ case GIC_SH_POL_95_64_OFS:
+ case GIC_SH_POL_127_96_OFS:
+ case GIC_SH_POL_159_128_OFS:
+ case GIC_SH_POL_191_160_OFS:
+ case GIC_SH_POL_223_192_OFS:
+ case GIC_SH_POL_255_224_OFS:
+ base = (addr - GIC_SH_POL_31_0_OFS) * 8;
+ for (i = 0; i < size * 8; i++) {
+ gic->gic_irqs[base + i].polarity = (data >> i) & 1;
+ }
+ break;
+ case GIC_SH_TRIG_31_0_OFS:
+ case GIC_SH_TRIG_63_32_OFS:
+ case GIC_SH_TRIG_95_64_OFS:
+ case GIC_SH_TRIG_127_96_OFS:
+ case GIC_SH_TRIG_159_128_OFS:
+ case GIC_SH_TRIG_191_160_OFS:
+ case GIC_SH_TRIG_223_192_OFS:
+ case GIC_SH_TRIG_255_224_OFS:
+ base = (addr - GIC_SH_TRIG_31_0_OFS) * 8;
+ for (i = 0; i < size * 8; i++) {
+ gic->gic_irqs[base + i].trigger_type = (data >> i) & 1;
+ }
+ break;
+ case GIC_SH_RMASK_31_0_OFS:
+ case GIC_SH_RMASK_63_32_OFS:
+ case GIC_SH_RMASK_95_64_OFS:
+ case GIC_SH_RMASK_127_96_OFS:
+ case GIC_SH_RMASK_159_128_OFS:
+ case GIC_SH_RMASK_191_160_OFS:
+ case GIC_SH_RMASK_223_192_OFS:
+ case GIC_SH_RMASK_255_224_OFS:
+ base = (addr - GIC_SH_RMASK_31_0_OFS) * 8;
+ for (i = 0; i < size * 8; i++) {
+ gic->gic_irqs[base + i].enabled &= !((data >> i) & 1);
+ }
+ break;
+ case GIC_SH_WEDGE_OFS:
+ /* Figure out which VPE/HW Interrupt this maps to */
+ intr = data & 0x7FFFFFFF;
+ /* Mask/Enabled Checks */
+ if (data & 0x80000000) {
+ qemu_set_irq(gic->irqs[intr], 1);
+ } else {
+ qemu_set_irq(gic->irqs[intr], 0);
+ }
+ break;
+ case GIC_SH_SMASK_31_0_OFS:
+ case GIC_SH_SMASK_63_32_OFS:
+ case GIC_SH_SMASK_95_64_OFS:
+ case GIC_SH_SMASK_127_96_OFS:
+ case GIC_SH_SMASK_159_128_OFS:
+ case GIC_SH_SMASK_191_160_OFS:
+ case GIC_SH_SMASK_223_192_OFS:
+ case GIC_SH_SMASK_255_224_OFS:
+ base = (addr - GIC_SH_SMASK_31_0_OFS) * 8;
+ for (i = 0; i < size * 8; i++) {
+ gic->gic_irqs[base + i].enabled |= (data >> i) & 1;
+ }
+ break;
+
+ default:
+ if (addr < GIC_SH_MAP0_PIN_OFS) {
+ qemu_log_mask(LOG_UNIMP,
+ "Warning *** write %d bytes at GIC offset 0x%" PRIx64
+ " 0x%08lx\n",
+ size, addr, data);
+ }
+ break;
+ }
+
+ /* Other cases */
+ if (addr >= GIC_SH_MAP0_PIN_OFS && addr <= GIC_SH_MAP255_PIN_OFS) {
+ int irq_src = (addr - GIC_SH_MAP0_PIN_OFS) / 4;
+ gic->gic_irqs[irq_src].map_pin = data;
+ }
+ if (addr >= GIC_SH_MAP0_VPE31_0_OFS && addr <= GIC_SH_MAP255_VPE63_32_OFS) {
+ int irq_src = (addr - GIC_SH_MAP0_VPE31_0_OFS) / 32;
+ gic->gic_irqs[irq_src].map_vpe = (data) ? ctz64(data) : 0;
+ }
+
+ /* VPE-Local Register */
+ if (addr >= GIC_VPELOCAL_BASE_ADDR && addr < GIC_VPEOTHER_BASE_ADDR) {
+ gic_write_vpe(gic, vp_index, addr - GIC_VPELOCAL_BASE_ADDR,
+ data, size);
+ }
+
+ /* VPE-Other Register */
+ if (addr >= GIC_VPEOTHER_BASE_ADDR && addr < GIC_USERMODE_BASE_ADDR) {
+ uint32_t other_index = gic->vps[vp_index].other_addr;
+ gic_write_vpe(gic, other_index, addr - GIC_VPEOTHER_BASE_ADDR,
+ data, size);
+ }
+}
+
+static void gic_set_irq(void *opaque, int n_IRQ, int level)
+{
+ int vpe = -1, pin = -1;
+ MIPSGICState *gic = (MIPSGICState *) opaque;
+
+
+ gic->gic_irqs[n_IRQ].pending = (bool) level;
+
+ /* Mapping: assume MAP_TO_PIN */
+ pin = gic->gic_irqs[n_IRQ].map_pin & GIC_MAP_MSK;
+ vpe = gic->gic_irqs[n_IRQ].map_vpe;
+
+ if (vpe < 0 || vpe >= gic->num_cpu) {
+ return;
+ }
+
+ gic_set_vp_irq(gic, vpe, pin, level);
+}
+
+static void gic_reset(void *opaque)
+{
+ int i;
+ MIPSGICState *gic = (MIPSGICState *) opaque;
+
+ gic->gic_sh_config = 0x100f0000 | gic->num_cpu;
+ gic->gic_sh_counterlo = 0;
+
+ for (i = 0; i < gic->num_cpu; i++) {
+ gic->vps[i].ctl = 0x0;
+ gic->vps[i].pend = 0x0;
+ gic->vps[i].mask = 0x1; /* COMPARE_MASK ONLY */
+ gic->vps[i].wd_map = GIC_MAP_TO_NMI_MSK;
+ gic->vps[i].compare_map = GIC_MAP_TO_PIN_MSK;
+ gic->vps[i].timer_map = GIC_MAP_TO_PIN_MSK | 0x5;
+ gic->vps[i].comparelo = 0x0;
+ gic->vps[i].comparehi = 0x0;
+ gic->vps[i].other_addr = 0x0;
+ }
+
+ for (i = 0; i < gic->num_irq; i++) {
+ gic->gic_irqs[i].enabled = false;
+ gic->gic_irqs[i].pending = false;
+ gic->gic_irqs[i].polarity = false;
+ gic->gic_irqs[i].trigger_type = false;
+ gic->gic_irqs[i].dual_edge = false;
+ gic->gic_irqs[i].map_pin = GIC_MAP_TO_PIN_MSK;
+ gic->gic_irqs[i].map_vpe = 0;
+ }
+}
+
+static const MemoryRegionOps gic_ops = {
+ .read = gic_read,
+ .write = gic_write,
+ .endianness = DEVICE_NATIVE_ENDIAN,
+ .impl = {
+ .max_access_size = 8,
+ },
+};
+
+static void mips_gic_init(Object *obj)
+{
+ SysBusDevice *sbd = SYS_BUS_DEVICE(obj);
+ MIPSGICState *s = MIPS_GIC(obj);
+
+ memory_region_init_io(&s->gic_mem, OBJECT(s), &gic_ops, s,
+ "mips-gic", GIC_ADDRSPACE_SZ);
+ sysbus_init_mmio(sbd, &s->gic_mem);
+ qemu_register_reset(gic_reset, s);
+}
+
+static void mips_gic_realize(DeviceState *dev, Error **errp)
+{
+ MIPSGICState *s = MIPS_GIC(dev);
+ qemu_irq *irqs = g_new(qemu_irq, s->num_irq);
+ CPUState *cs = first_cpu;
+ int i;
+
+ if (s->num_cpu > GIC_MAX_VPS) {
+ error_setg(errp, "Exceed maximum CPUs %d", s->num_cpu);
+ return;
+ }
+ if (s->num_irq > GIC_MAX_INTRS) {
+ error_setg(errp, "Exceed maximum GIC IRQs %d", s->num_irq);
+ return;
+ }
+
+ s->vps = g_new(MIPSGICVPState, s->num_cpu);
+ s->gic_irqs = g_new(MIPSGICIRQState, s->num_irq);
+
+ /* Register the CPU env for all cpus with the GIC */
+ for (i = 0; i < s->num_cpu; i++) {
+ if (cs != NULL) {
+ s->vps[i].env = cs->env_ptr;
+ cs = CPU_NEXT(cs);
+ } else {
+ fprintf(stderr, "Unable to initialize GIC - CPUState for "
+ "CPU #%d not valid!", i);
+ return;
+ }
+ }
+
+ gic_timer_init(s, s->num_cpu);
+
+ qdev_init_gpio_in(dev, gic_set_irq, s->num_irq);
+ for (i = 0; i < s->num_irq; i++) {
+ irqs[i] = qdev_get_gpio_in(dev, i);
+
+ s->gic_irqs[i].irq = (qemu_irq *) irqs[i];
+ }
+ s->irqs = irqs;
+}
+
+static Property mips_gic_properties[] = {
+ DEFINE_PROP_INT32("num-cpu", MIPSGICState, num_cpu, 1),
+ DEFINE_PROP_INT32("num-irq", MIPSGICState, num_irq, 256),
+ DEFINE_PROP_END_OF_LIST(),
+};
+
+static void mips_gic_class_init(ObjectClass *klass, void *data)
+{
+ DeviceClass *dc = DEVICE_CLASS(klass);
+
+ dc->props = mips_gic_properties;
+ dc->realize = mips_gic_realize;
+}
+
+static const TypeInfo mips_gic_info = {
+ .name = TYPE_MIPS_GIC,
+ .parent = TYPE_SYS_BUS_DEVICE,
+ .instance_size = sizeof(MIPSGICState),
+ .instance_init = mips_gic_init,
+ .class_init = mips_gic_class_init,
+};
+
+static void mips_gic_register_types(void)
+{
+ type_register_static(&mips_gic_info);
+}
+
+type_init(mips_gic_register_types)
diff --git a/hw/mips/mips_gic.h b/hw/mips/mips_gic.h
new file mode 100644
index 0000000..e5c9bf8
--- /dev/null
+++ b/hw/mips/mips_gic.h
@@ -0,0 +1,298 @@
+/*
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License. See the file "COPYING" in the main directory of this archive
+ * for more details.
+ *
+ * Copyright (C) 2000, 07 MIPS Technologies, Inc.
+ * Copyright (C) 2015 Imagination Technologies
+ *
+ */
+#ifndef _MIPS_GIC_H
+#define _MIPS_GIC_H
+
+/*
+ * GIC Specific definitions
+ */
+
+/* The MIPS default location */
+#define GIC_BASE_ADDR 0x1bdc0000ULL
+#define GIC_ADDRSPACE_SZ (128 * 1024)
+
+/* GIC Address Space Offsets */
+#define GIC_SHARED_BASE_ADDR 0x0000
+#define GIC_VPELOCAL_BASE_ADDR 0x8000
+#define GIC_VPEOTHER_BASE_ADDR 0xC000
+#define GIC_USERMODE_BASE_ADDR 0x10000
+
+/* Constants */
+#define GIC_POL_POS 1
+#define GIC_POL_NEG 0
+#define GIC_TRIG_EDGE 1
+#define GIC_TRIG_LEVEL 0
+
+#define MSK(n) ((1 << (n)) - 1)
+
+/* GIC Address Space */
+#define SHARED_SECTION_OFS 0x0000
+#define SHARED_SECTION_SIZE 0x8000
+#define VPE_LOCAL_SECTION_OFS 0x8000
+#define VPE_LOCAL_SECTION_SIZE 0x4000
+#define VPE_OTHER_SECTION_OFS 0xc000
+#define VPE_OTHER_SECTION_SIZE 0x4000
+#define USM_VISIBLE_SECTION_OFS 0x10000
+#define USM_VISIBLE_SECTION_SIZE 0x10000
+
+/* Register Map for Shared Section */
+
+#define GIC_SH_CONFIG_OFS 0x0000
+
+/* Shared Global Counter */
+#define GIC_SH_COUNTERLO_OFS 0x0010
+#define GIC_SH_COUNTERHI_OFS 0x0014
+#define GIC_SH_REVISIONID_OFS 0x0020
+
+/* Interrupt Polarity */
+#define GIC_SH_POL_31_0_OFS 0x0100
+#define GIC_SH_POL_63_32_OFS 0x0104
+#define GIC_SH_POL_95_64_OFS 0x0108
+#define GIC_SH_POL_127_96_OFS 0x010c
+#define GIC_SH_POL_159_128_OFS 0x0110
+#define GIC_SH_POL_191_160_OFS 0x0114
+#define GIC_SH_POL_223_192_OFS 0x0118
+#define GIC_SH_POL_255_224_OFS 0x011c
+
+/* Edge/Level Triggering */
+#define GIC_SH_TRIG_31_0_OFS 0x0180
+#define GIC_SH_TRIG_63_32_OFS 0x0184
+#define GIC_SH_TRIG_95_64_OFS 0x0188
+#define GIC_SH_TRIG_127_96_OFS 0x018c
+#define GIC_SH_TRIG_159_128_OFS 0x0190
+#define GIC_SH_TRIG_191_160_OFS 0x0194
+#define GIC_SH_TRIG_223_192_OFS 0x0198
+#define GIC_SH_TRIG_255_224_OFS 0x019c
+
+/* Dual Edge Triggering */
+#define GIC_SH_DUAL_31_0_OFS 0x0200
+#define GIC_SH_DUAL_63_32_OFS 0x0204
+#define GIC_SH_DUAL_95_64_OFS 0x0208
+#define GIC_SH_DUAL_127_96_OFS 0x020c
+#define GIC_SH_DUAL_159_128_OFS 0x0210
+#define GIC_SH_DUAL_191_160_OFS 0x0214
+#define GIC_SH_DUAL_223_192_OFS 0x0218
+#define GIC_SH_DUAL_255_224_OFS 0x021c
+
+/* Set/Clear corresponding bit in Edge Detect Register */
+#define GIC_SH_WEDGE_OFS 0x0280
+
+/* Reset Mask - Disables Interrupt */
+#define GIC_SH_RMASK_31_0_OFS 0x0300
+#define GIC_SH_RMASK_63_32_OFS 0x0304
+#define GIC_SH_RMASK_95_64_OFS 0x0308
+#define GIC_SH_RMASK_127_96_OFS 0x030c
+#define GIC_SH_RMASK_159_128_OFS 0x0310
+#define GIC_SH_RMASK_191_160_OFS 0x0314
+#define GIC_SH_RMASK_223_192_OFS 0x0318
+#define GIC_SH_RMASK_255_224_OFS 0x031c
+
+/* Set Mask (WO) - Enables Interrupt */
+#define GIC_SH_SMASK_31_0_OFS 0x0380
+#define GIC_SH_SMASK_63_32_OFS 0x0384
+#define GIC_SH_SMASK_95_64_OFS 0x0388
+#define GIC_SH_SMASK_127_96_OFS 0x038c
+#define GIC_SH_SMASK_159_128_OFS 0x0390
+#define GIC_SH_SMASK_191_160_OFS 0x0394
+#define GIC_SH_SMASK_223_192_OFS 0x0398
+#define GIC_SH_SMASK_255_224_OFS 0x039c
+
+/* Global Interrupt Mask Register (RO) - Bit Set == Interrupt enabled */
+#define GIC_SH_MASK_31_0_OFS 0x0400
+#define GIC_SH_MASK_63_32_OFS 0x0404
+#define GIC_SH_MASK_95_64_OFS 0x0408
+#define GIC_SH_MASK_127_96_OFS 0x040c
+#define GIC_SH_MASK_159_128_OFS 0x0410
+#define GIC_SH_MASK_191_160_OFS 0x0414
+#define GIC_SH_MASK_223_192_OFS 0x0418
+#define GIC_SH_MASK_255_224_OFS 0x041c
+
+/* Pending Global Interrupts (RO) */
+#define GIC_SH_PEND_31_0_OFS 0x0480
+#define GIC_SH_PEND_63_32_OFS 0x0484
+#define GIC_SH_PEND_95_64_OFS 0x0488
+#define GIC_SH_PEND_127_96_OFS 0x048c
+#define GIC_SH_PEND_159_128_OFS 0x0490
+#define GIC_SH_PEND_191_160_OFS 0x0494
+#define GIC_SH_PEND_223_192_OFS 0x0498
+#define GIC_SH_PEND_255_224_OFS 0x049c
+
+#define GIC_SH_MAP0_PIN_OFS 0x0500
+#define GIC_SH_MAP255_PIN_OFS 0x08fc
+
+#define GIC_SH_MAP0_VPE31_0_OFS 0x2000
+#define GIC_SH_MAP255_VPE63_32_OFS 0x3fe4
+
+/* Register Map for Local Section */
+#define GIC_VPE_CTL_OFS 0x0000
+#define GIC_VPE_PEND_OFS 0x0004
+#define GIC_VPE_MASK_OFS 0x0008
+#define GIC_VPE_RMASK_OFS 0x000c
+#define GIC_VPE_SMASK_OFS 0x0010
+#define GIC_VPE_WD_MAP_OFS 0x0040
+#define GIC_VPE_COMPARE_MAP_OFS 0x0044
+#define GIC_VPE_TIMER_MAP_OFS 0x0048
+#define GIC_VPE_PERFCTR_MAP_OFS 0x0050
+#define GIC_VPE_SWINT0_MAP_OFS 0x0054
+#define GIC_VPE_SWINT1_MAP_OFS 0x0058
+#define GIC_VPE_OTHER_ADDR_OFS 0x0080
+#define GIC_VPE_IDENT_OFS 0x0088
+#define GIC_VPE_WD_CONFIG0_OFS 0x0090
+#define GIC_VPE_WD_COUNT0_OFS 0x0094
+#define GIC_VPE_WD_INITIAL0_OFS 0x0098
+#define GIC_VPE_COMPARE_LO_OFS 0x00a0
+#define GIC_VPE_COMPARE_HI_OFS 0x00a4
+
+/* Masks */
+#define GIC_SH_CONFIG_COUNTSTOP_SHF 28
+#define GIC_SH_CONFIG_COUNTSTOP_MSK (MSK(1) << GIC_SH_CONFIG_COUNTSTOP_SHF)
+
+#define GIC_SH_CONFIG_COUNTBITS_SHF 24
+#define GIC_SH_CONFIG_COUNTBITS_MSK (MSK(4) << GIC_SH_CONFIG_COUNTBITS_SHF)
+
+#define GIC_SH_CONFIG_NUMINTRS_SHF 16
+#define GIC_SH_CONFIG_NUMINTRS_MSK (MSK(8) << GIC_SH_CONFIG_NUMINTRS_SHF)
+
+#define GIC_SH_CONFIG_NUMVPES_SHF 0
+#define GIC_SH_CONFIG_NUMVPES_MSK (MSK(8) << GIC_SH_CONFIG_NUMVPES_SHF)
+
+#define GIC_SH_WEDGE_SET(intr) ((intr) | (0x1 << 31))
+#define GIC_SH_WEDGE_CLR(intr) ((intr) & ~(0x1 << 31))
+
+#define GIC_MAP_TO_PIN_SHF 31
+#define GIC_MAP_TO_PIN_MSK (MSK(1) << GIC_MAP_TO_PIN_SHF)
+#define GIC_MAP_TO_NMI_SHF 30
+#define GIC_MAP_TO_NMI_MSK (MSK(1) << GIC_MAP_TO_NMI_SHF)
+#define GIC_MAP_TO_YQ_SHF 29
+#define GIC_MAP_TO_YQ_MSK (MSK(1) << GIC_MAP_TO_YQ_SHF)
+#define GIC_MAP_SHF 0
+#define GIC_MAP_MSK (MSK(6) << GIC_MAP_SHF)
+
+/* GIC_VPE_CTL Masks */
+#define GIC_VPE_CTL_PERFCNT_RTBL_SHF 2
+#define GIC_VPE_CTL_PERFCNT_RTBL_MSK (MSK(1) << GIC_VPE_CTL_PERFCNT_RTBL_SHF)
+#define GIC_VPE_CTL_TIMER_RTBL_SHF 1
+#define GIC_VPE_CTL_TIMER_RTBL_MSK (MSK(1) << GIC_VPE_CTL_TIMER_RTBL_SHF)
+#define GIC_VPE_CTL_EIC_MODE_SHF 0
+#define GIC_VPE_CTL_EIC_MODE_MSK (MSK(1) << GIC_VPE_CTL_EIC_MODE_SHF)
+
+/* GIC_VPE_PEND Masks */
+#define GIC_VPE_PEND_WD_SHF 0
+#define GIC_VPE_PEND_WD_MSK (MSK(1) << GIC_VPE_PEND_WD_SHF)
+#define GIC_VPE_PEND_CMP_SHF 1
+#define GIC_VPE_PEND_CMP_MSK (MSK(1) << GIC_VPE_PEND_CMP_SHF)
+#define GIC_VPE_PEND_TIMER_SHF 2
+#define GIC_VPE_PEND_TIMER_MSK (MSK(1) << GIC_VPE_PEND_TIMER_SHF)
+#define GIC_VPE_PEND_PERFCOUNT_SHF 3
+#define GIC_VPE_PEND_PERFCOUNT_MSK (MSK(1) << GIC_VPE_PEND_PERFCOUNT_SHF)
+#define GIC_VPE_PEND_SWINT0_SHF 4
+#define GIC_VPE_PEND_SWINT0_MSK (MSK(1) << GIC_VPE_PEND_SWINT0_SHF)
+#define GIC_VPE_PEND_SWINT1_SHF 5
+#define GIC_VPE_PEND_SWINT1_MSK (MSK(1) << GIC_VPE_PEND_SWINT1_SHF)
+
+/* GIC_VPE_RMASK Masks */
+#define GIC_VPE_RMASK_WD_SHF 0
+#define GIC_VPE_RMASK_WD_MSK (MSK(1) << GIC_VPE_RMASK_WD_SHF)
+#define GIC_VPE_RMASK_CMP_SHF 1
+#define GIC_VPE_RMASK_CMP_MSK (MSK(1) << GIC_VPE_RMASK_CMP_SHF)
+#define GIC_VPE_RMASK_TIMER_SHF 2
+#define GIC_VPE_RMASK_TIMER_MSK (MSK(1) << GIC_VPE_RMASK_TIMER_SHF)
+#define GIC_VPE_RMASK_PERFCNT_SHF 3
+#define GIC_VPE_RMASK_PERFCNT_MSK (MSK(1) << GIC_VPE_RMASK_PERFCNT_SHF)
+#define GIC_VPE_RMASK_SWINT0_SHF 4
+#define GIC_VPE_RMASK_SWINT0_MSK (MSK(1) << GIC_VPE_RMASK_SWINT0_SHF)
+#define GIC_VPE_RMASK_SWINT1_SHF 5
+#define GIC_VPE_RMASK_SWINT1_MSK (MSK(1) << GIC_VPE_RMASK_SWINT1_SHF)
+
+/* GIC_VPE_SMASK Masks */
+#define GIC_VPE_SMASK_WD_SHF 0
+#define GIC_VPE_SMASK_WD_MSK (MSK(1) << GIC_VPE_SMASK_WD_SHF)
+#define GIC_VPE_SMASK_CMP_SHF 1
+#define GIC_VPE_SMASK_CMP_MSK (MSK(1) << GIC_VPE_SMASK_CMP_SHF)
+#define GIC_VPE_SMASK_TIMER_SHF 2
+#define GIC_VPE_SMASK_TIMER_MSK (MSK(1) << GIC_VPE_SMASK_TIMER_SHF)
+#define GIC_VPE_SMASK_PERFCNT_SHF 3
+#define GIC_VPE_SMASK_PERFCNT_MSK (MSK(1) << GIC_VPE_SMASK_PERFCNT_SHF)
+#define GIC_VPE_SMASK_SWINT0_SHF 4
+#define GIC_VPE_SMASK_SWINT0_MSK (MSK(1) << GIC_VPE_SMASK_SWINT0_SHF)
+#define GIC_VPE_SMASK_SWINT1_SHF 5
+#define GIC_VPE_SMASK_SWINT1_MSK (MSK(1) << GIC_VPE_SMASK_SWINT1_SHF)
+
+#define GIC_CPU_PIN_OFFSET 2
+
+#define TYPE_MIPS_GIC "mips-gic"
+#define MIPS_GIC(obj) OBJECT_CHECK(MIPSGICState, (obj), TYPE_MIPS_GIC)
+
+/* Support up to 32 VPEs and 256 IRQs */
+#define GIC_MAX_VPS 32
+#define GIC_MAX_INTRS 256
+
+typedef struct MIPSGICState MIPSGICState;
+typedef struct MIPSGICTimerState MIPSGICTimerState;
+typedef struct MIPSGICIRQState MIPSGICIRQState;
+typedef struct MIPSGICVPState MIPSGICVPState;
+
+struct MIPSGICTimerState {
+ QEMUTimer *qtimer;
+ uint32_t vp_index;
+ MIPSGICState *gic;
+};
+
+struct MIPSGICIRQState {
+ bool enabled;
+ bool pending;
+ bool polarity;
+ bool trigger_type;
+ bool dual_edge;
+ uint32_t map_pin;
+ uint64_t map_vpe;
+ qemu_irq *irq;
+};
+
+struct MIPSGICVPState {
+ uint32_t ctl;
+ uint32_t pend;
+ uint32_t mask;
+ uint32_t wd_map;
+ uint32_t compare_map;
+ uint32_t timer_map;
+ uint32_t comparelo;
+ uint32_t comparehi;
+ uint32_t other_addr;
+
+ CPUMIPSState *env;
+ MIPSGICTimerState *gic_timer;
+};
+
+struct MIPSGICState {
+ SysBusDevice parent_obj;
+
+ MemoryRegion gic_mem;
+ qemu_irq *irqs;
+
+ /* Shared Section Registers */
+ uint32_t gic_sh_config;
+ uint32_t gic_sh_counterlo;
+
+ MIPSGICIRQState *gic_irqs;
+
+ /* VPE Local Section Registers */
+ /* VPE Other Section Registers, aliased to local,
+ * use the other addr to access the correct instance */
+
+ MIPSGICVPState *vps;
+
+ /* User Mode Visible Section Registers */
+
+ int32_t num_cpu;
+ int32_t num_irq;
+};
+
+#endif /* _MIPS_GIC_H */
--
1.7.1
^ permalink raw reply related [flat|nested] 14+ messages in thread
* [Qemu-devel] [PATCH 4/4] mips: add gic support to malta
2015-10-15 23:52 [Qemu-devel] [PATCH 0/4] mips: add Global Interrupt Controller Yongbok Kim
` (2 preceding siblings ...)
2015-10-15 23:52 ` [Qemu-devel] [PATCH 3/4] mips: add Global Interrupt Controller Yongbok Kim
@ 2015-10-15 23:52 ` Yongbok Kim
2015-10-21 15:53 ` Leon Alrae
2015-10-19 14:36 ` [Qemu-devel] [PATCH 0/4] mips: add Global Interrupt Controller James Hogan
` (2 subsequent siblings)
6 siblings, 1 reply; 14+ messages in thread
From: Yongbok Kim @ 2015-10-15 23:52 UTC (permalink / raw)
To: qemu-devel; +Cc: pbonzini, leon.alrae, afaerber, aurelien
Add Global Interrupt Controller support to Malta board.
The I8259 is connected to pin 3 of the GIC.
Increase number of CPUs supported to 32.
Signed-off-by: Yongbok Kim <yongbok.kim@imgtec.com>
---
hw/mips/mips_malta.c | 71 ++++++++++++++++++++++++++++++++++++++++++++++++-
target-mips/cpu.h | 1 +
2 files changed, 70 insertions(+), 2 deletions(-)
diff --git a/hw/mips/mips_malta.c b/hw/mips/mips_malta.c
index c1f570a..d2611f0 100644
--- a/hw/mips/mips_malta.c
+++ b/hw/mips/mips_malta.c
@@ -54,6 +54,8 @@
#include "hw/empty_slot.h"
#include "sysemu/kvm.h"
#include "exec/semihost.h"
+#include "hw/mips/mips_gcr.h"
+#include "hw/mips/mips_gic.h"
//#define DEBUG_BOARD_INIT
@@ -92,6 +94,8 @@ typedef struct {
typedef struct {
SysBusDevice parent_obj;
+ MIPSGCRState gcr;
+ MIPSGICState gic;
qemu_irq *i8259;
} MaltaState;
@@ -566,6 +570,50 @@ static MaltaFPGAState *malta_fpga_init(MemoryRegion *address_space,
return s;
}
+static void gcr_init(MaltaState *s, Error **err)
+{
+ SysBusDevice *gcrbusdev;
+ DeviceState *gcrdev;
+
+ object_initialize(&s->gcr, sizeof(s->gcr), TYPE_MIPS_GCR);
+ qdev_set_parent_bus(DEVICE(&s->gcr), sysbus_get_default());
+
+ gcrdev = DEVICE(&s->gcr);
+
+ object_property_set_int(OBJECT(&s->gcr), smp_cpus, "num-cpu", err);
+ object_property_set_int(OBJECT(&s->gcr), 0x800, "gcr-rev", err);
+ object_property_set_bool(OBJECT(&s->gcr), true, "realized", err);
+ if (*err != NULL) {
+ return;
+ }
+
+ gcrbusdev = SYS_BUS_DEVICE(gcrdev);
+ sysbus_mmio_map(gcrbusdev, 0, GCR_BASE_ADDR);
+}
+
+static void *gic_init(MaltaState *s, Error **err)
+{
+ SysBusDevice *gicbusdev;
+ DeviceState *gicdev;
+
+ object_initialize(&s->gic, sizeof(s->gic), TYPE_MIPS_GIC);
+ qdev_set_parent_bus(DEVICE(&s->gic), sysbus_get_default());
+
+ gicdev = DEVICE(&s->gic);
+
+ object_property_set_int(OBJECT(&s->gic), smp_cpus, "num-cpu", err);
+ object_property_set_int(OBJECT(&s->gic), 256, "num-irq", err);
+ object_property_set_bool(OBJECT(&s->gic), true, "realized", err);
+ if (*err != NULL) {
+ return NULL;
+ }
+
+ gicbusdev = SYS_BUS_DEVICE(gicdev);
+ sysbus_mmio_map(gicbusdev, 0, GIC_BASE_ADDR);
+
+ return (void *) s->gic.irqs;
+}
+
/* Network support */
static void network_init(PCIBus *pci_bus)
{
@@ -1136,6 +1184,21 @@ void mips_malta_init(MachineState *machine)
cpu_mips_irq_init_cpu(env);
cpu_mips_clock_init(env);
+ /* GCR/GIC */
+ if (env->CP0_Config3 & (1 << CP0C3_CMGCR)) {
+ Error *err = NULL;
+ gcr_init(s, &err);
+ if (err != NULL) {
+ error_report("%s", error_get_pretty(err));
+ exit(1);
+ }
+ env->gic_irqs = gic_init(s, &err);
+ if (err != NULL) {
+ error_report("%s", error_get_pretty(err));
+ exit(1);
+ }
+ }
+
/*
* We have a circular dependency problem: pci_bus depends on isa_irq,
* isa_irq is provided by i8259, i8259 depends on ISA, ISA depends
@@ -1155,7 +1218,11 @@ void mips_malta_init(MachineState *machine)
/* Interrupt controller */
/* The 8259 is attached to the MIPS CPU INT0 pin, ie interrupt 2 */
- s->i8259 = i8259_init(isa_bus, env->irq[2]);
+ if (env->gic_irqs) {
+ s->i8259 = i8259_init(isa_bus, env->gic_irqs[3]);
+ } else {
+ s->i8259 = i8259_init(isa_bus, env->irq[2]);
+ }
isa_bus_irqs(isa_bus, s->i8259);
pci_piix4_ide_init(pci_bus, hd, piix4_devfn + 1);
@@ -1209,7 +1276,7 @@ static void mips_malta_machine_init(MachineClass *mc)
{
mc->desc = "MIPS Malta Core LV";
mc->init = mips_malta_init;
- mc->max_cpus = 16;
+ mc->max_cpus = 32;
mc->is_default = 1;
}
diff --git a/target-mips/cpu.h b/target-mips/cpu.h
index 639ef37..fdd5643 100644
--- a/target-mips/cpu.h
+++ b/target-mips/cpu.h
@@ -594,6 +594,7 @@ struct CPUMIPSState {
const mips_def_t *cpu_model;
void *irq[8];
+ void **gic_irqs;
QEMUTimer *timer; /* Internal timer */
};
--
1.7.1
^ permalink raw reply related [flat|nested] 14+ messages in thread
* Re: [Qemu-devel] [PATCH 0/4] mips: add Global Interrupt Controller
2015-10-15 23:52 [Qemu-devel] [PATCH 0/4] mips: add Global Interrupt Controller Yongbok Kim
` (3 preceding siblings ...)
2015-10-15 23:52 ` [Qemu-devel] [PATCH 4/4] mips: add gic support to malta Yongbok Kim
@ 2015-10-19 14:36 ` James Hogan
2015-10-19 14:58 ` Yongbok Kim
2015-10-21 16:01 ` Peter Maydell
2015-10-21 16:36 ` Peter Crosthwaite
6 siblings, 1 reply; 14+ messages in thread
From: James Hogan @ 2015-10-19 14:36 UTC (permalink / raw)
To: Yongbok Kim
Cc: qemu-devel, Markos Chandras, pbonzini, leon.alrae, afaerber,
aurelien
[-- Attachment #1: Type: text/plain, Size: 2556 bytes --]
Hi Yongbok,
On Fri, Oct 16, 2015 at 12:52:05AM +0100, Yongbok Kim wrote:
> Add support of MIPS GIC.
> The patchset implements the Global Interrupt Controller.
>
> With the patchset Linux Kernel detects GIC and utilises it.
> # cat /proc/interrupts
> CPU0
> 0: 2 XT-PIC 0 timer
> 2: 0 XT-PIC 2 cascade
> 4: 462 XT-PIC 4 serial
> 8: 0 XT-PIC 8 rtc0
> 11: 0 XT-PIC 11 uhci_hcd:usb1
> 14: 1026 XT-PIC 14 ide0
> 15: 0 XT-PIC 15 ide1
> 21: 0 MIPS 5 CoreHi
> 23: 44 MIPS 7 timer
> 25: 12290 MIPS GIC Local 1 timer
> 34: 1490 MIPS GIC 10 XT-PIC cascade
> 98: 1490 MIPS GIC 74
> ERR: 0
>
> Limitation:
> Level triggering only
> No User-Mode Visible Section
Note that there are Linux kernel patches from Alex Smith / Markos
Chandras which will imminently hit linux-next, which implement a proper
VDSO, including support for userland time access using the user-mode
visible section of the GIC. It sounds likely that this patch will cause
breakage when those patches are applied.
Cheers
James
> GIC CounterHi not implemented (Countbits = 32bits)
> DINT not implemented
> Local WatchDog, Fast Debug Channel, Perf Counter not implemented
>
> It is based on the earlier un-merged GIC implementation.
> https://lists.gnu.org/archive/html/qemu-devel/2013-03/msg00194.html
>
> For more information,
> http://imgtec.com/mips/warrior/p-class-p5600-multiprocessor-core/
> http://imgtec.com/mips/warrior/i-class-i6400-multiprocessor-core/
>
>
> Yongbok Kim (4):
> target-mips: add CMGCRBase register
> mips: add Global Config Register block (part)
> mips: add Global Interrupt Controller
> mips: add gic support to malta
>
> hw/mips/Makefile.objs | 2 +-
> hw/mips/mips_gcr.c | 120 ++++++++
> hw/mips/mips_gcr.h | 57 ++++
> hw/mips/mips_gic.c | 653 ++++++++++++++++++++++++++++++++++++++++++
> hw/mips/mips_gic.h | 298 +++++++++++++++++++
> hw/mips/mips_malta.c | 71 +++++-
> target-mips/cpu.h | 4 +-
> target-mips/translate.c | 17 ++
> target-mips/translate_init.c | 3 +-
> 9 files changed, 1220 insertions(+), 5 deletions(-)
> create mode 100644 hw/mips/mips_gcr.c
> create mode 100644 hw/mips/mips_gcr.h
> create mode 100644 hw/mips/mips_gic.c
> create mode 100644 hw/mips/mips_gic.h
>
>
[-- Attachment #2: Digital signature --]
[-- Type: application/pgp-signature, Size: 819 bytes --]
^ permalink raw reply [flat|nested] 14+ messages in thread
* Re: [Qemu-devel] [PATCH 0/4] mips: add Global Interrupt Controller
2015-10-19 14:36 ` [Qemu-devel] [PATCH 0/4] mips: add Global Interrupt Controller James Hogan
@ 2015-10-19 14:58 ` Yongbok Kim
0 siblings, 0 replies; 14+ messages in thread
From: Yongbok Kim @ 2015-10-19 14:58 UTC (permalink / raw)
To: James Hogan
Cc: qemu-devel, Markos Chandras, pbonzini, leon.alrae, afaerber,
aurelien
On 19/10/2015 15:36, James Hogan wrote:
> Hi Yongbok,
>
>> Limitation:
>> Level triggering only
>> No User-Mode Visible Section
>
> Note that there are Linux kernel patches from Alex Smith / Markos
> Chandras which will imminently hit linux-next, which implement a proper
> VDSO, including support for userland time access using the user-mode
> visible section of the GIC. It sounds likely that this patch will cause
> breakage when those patches are applied.
>
> Cheers
> James
>
Hi James,
Thanks for letting me know that.
I will add the user section at the V2 of the patchset.
Regards,
Yongbok
^ permalink raw reply [flat|nested] 14+ messages in thread
* Re: [Qemu-devel] [PATCH 1/4] target-mips: add CMGCRBase register
2015-10-15 23:52 ` [Qemu-devel] [PATCH 1/4] target-mips: add CMGCRBase register Yongbok Kim
@ 2015-10-19 15:07 ` Leon Alrae
0 siblings, 0 replies; 14+ messages in thread
From: Leon Alrae @ 2015-10-19 15:07 UTC (permalink / raw)
To: Yongbok Kim, qemu-devel; +Cc: pbonzini, afaerber, aurelien
On 16/10/15 00:52, Yongbok Kim wrote:
> Physical base address for the memory-mapped Coherency Manager Global
> Configuration Register space.
> The MIPS default location for the GCR_BASE address is 0x1FBF_8.
> This register only exists if Config3 CMGCR is set to one.
>
> Signed-off-by: Yongbok Kim <yongbok.kim@imgtec.com>
> ---
> target-mips/cpu.h | 3 ++-
> target-mips/translate.c | 17 +++++++++++++++++
> target-mips/translate_init.c | 3 ++-
> 3 files changed, 21 insertions(+), 2 deletions(-)
>
> diff --git a/target-mips/cpu.h b/target-mips/cpu.h
> index f32a0fd..639ef37 100644
> --- a/target-mips/cpu.h
> +++ b/target-mips/cpu.h
> @@ -389,6 +389,7 @@ struct CPUMIPSState {
> target_ulong CP0_EPC;
> int32_t CP0_PRid;
> int32_t CP0_EBase;
> + target_ulong CP0_CMGCRBase;
> int32_t CP0_Config0;
> #define CP0C0_M 31
> #define CP0C0_K23 28
> @@ -431,7 +432,7 @@ struct CPUMIPSState {
> int32_t CP0_Config3;
> #define CP0C3_M 31
> #define CP0C3_BPG 30
> -#define CP0C3_CMCGR 29
> +#define CP0C3_CMGCR 29
> #define CP0C3_MSAP 28
> #define CP0C3_BP 27
> #define CP0C3_BI 26
> diff --git a/target-mips/translate.c b/target-mips/translate.c
> index 897839c..c74e8e7 100644
> --- a/target-mips/translate.c
> +++ b/target-mips/translate.c
> @@ -1426,6 +1426,7 @@ typedef struct DisasContext {
> bool mvh;
> int CP0_LLAddr_shift;
> bool ps;
> + bool cmgcr;
> } DisasContext;
>
> enum {
> @@ -5273,6 +5274,12 @@ static void gen_mfc0(DisasContext *ctx, TCGv arg, int reg, int sel)
> gen_mfc0_load32(arg, offsetof(CPUMIPSState, CP0_EBase));
> rn = "EBase";
> break;
> + case 3:
> + check_insn(ctx, ISA_MIPS32R2);
> + CP0_CHECK(ctx->cmgcr);
> + gen_mfc0_load32(arg, offsetof(CPUMIPSState, CP0_CMGCRBase));
gen_mfc0_load32 assumes 32-bit CP0 register whereas this one is
target_ulong. The tcg_gen_ld_tl + tcg_gen_ext32s_tl pair should be used
here.
> @@ -19567,6 +19580,7 @@ void gen_intermediate_code(CPUMIPSState *env, struct TranslationBlock *tb)
> ctx.ulri = (env->CP0_Config3 >> CP0C3_ULRI) & 1;
> ctx.ps = ((env->active_fpu.fcr0 >> FCR0_PS) & 1) ||
> (env->insn_flags & (INSN_LOONGSON2E | INSN_LOONGSON2F));
> + ctx.cmgcr = env->CP0_Config3 & (1 << CP0C3_CMGCR);
Wouldn't it be better to follow the style which is used for other ctx
fields? I.e. (x >> y) & 1
Regards,
Leon
^ permalink raw reply [flat|nested] 14+ messages in thread
* Re: [Qemu-devel] [PATCH 3/4] mips: add Global Interrupt Controller
2015-10-15 23:52 ` [Qemu-devel] [PATCH 3/4] mips: add Global Interrupt Controller Yongbok Kim
@ 2015-10-21 15:49 ` Leon Alrae
0 siblings, 0 replies; 14+ messages in thread
From: Leon Alrae @ 2015-10-21 15:49 UTC (permalink / raw)
To: Yongbok Kim, qemu-devel; +Cc: pbonzini, afaerber, aurelien
On 16/10/15 00:52, Yongbok Kim wrote:
> The Global Interrupt Controller (GIC) is responsible for mapping each
> internal and external interrupt to the correct location for servicing.
>
> Limitations:
> Level triggering only
> No User-Mode Visible Section
> GIC CounterHi not implemented (Countbits = 32bits)
> DINT not implemented
> Local WatchDog, Fast Debug Channel, Perf Counter not implemented
>
> Signed-off-by: Yongbok Kim <yongbok.kim@imgtec.com>
> ---
> hw/mips/Makefile.objs | 2 +-
> hw/mips/mips_gic.c | 653 +++++++++++++++++++++++++++++++++++++++++++++++++
> hw/mips/mips_gic.h | 298 ++++++++++++++++++++++
Files are in mips directory thus "mips_" prefix seems redundant.
> 3 files changed, 952 insertions(+), 1 deletions(-)
> create mode 100644 hw/mips/mips_gic.c
> create mode 100644 hw/mips/mips_gic.h
>
> diff --git a/hw/mips/Makefile.objs b/hw/mips/Makefile.objs
> index d247d95..6cd9d67 100644
> --- a/hw/mips/Makefile.objs
> +++ b/hw/mips/Makefile.objs
> @@ -1,5 +1,5 @@
> obj-y += mips_r4k.o mips_malta.o mips_mipssim.o
> -obj-y += addr.o cputimer.o mips_int.o mips_gcr.o
> +obj-y += addr.o cputimer.o mips_int.o mips_gcr.o mips_gic.o
> obj-$(CONFIG_JAZZ) += mips_jazz.o
> obj-$(CONFIG_FULONG) += mips_fulong2e.o
> obj-y += gt64xxx_pci.o
> diff --git a/hw/mips/mips_gic.c b/hw/mips/mips_gic.c
> new file mode 100644
> index 0000000..27ae7ab
> --- /dev/null
> +++ b/hw/mips/mips_gic.c
> @@ -0,0 +1,653 @@
> +/*
> + * This file is subject to the terms and conditions of the GNU General Public
> + * License. See the file "COPYING" in the main directory of this archive
> + * for more details.
> + *
> + * Copyright (C) 2012 MIPS Technologies, Inc. All rights reserved.
> + * Authors: Sanjay Lal <sanjayl@kymasys.com>
> + *
> + * Copyright (C) 2015 Imagination Technologies
> + */
> +
> +#include "hw/hw.h"
> +#include "hw/sysbus.h"
> +#include "qemu/bitmap.h"
> +#include "exec/memory.h"
> +#include "sysemu/sysemu.h"
> +#include "qom/cpu.h"
> +#include "exec/address-spaces.h"
> +
> +#ifdef CONFIG_KVM
> +#include "sysemu/kvm.h"
> +#include "kvm_mips.h"
> +#endif
> +
> +#include "hw/mips/mips_gic.h"
> +
> +#define TIMER_PERIOD 10 /* 10 ns period for 100 Mhz frequency */
> +
> +static inline int gic_get_current_cpu(MIPSGICState *g)
> +{
> + if (g->num_cpu > 1) {
> + return current_cpu->cpu_index;
> + }
> + return 0;
> +}
> +
> +static void gic_set_vp_irq(MIPSGICState *gic, int vpe, int pin, int level)
> +{
> + int ored_level = level;
> + int i;
> + /* ORing pending registers sharing same pin */
> + if (!ored_level) {
> + for (i = 0; i < gic->num_irq; i++) {
> + if ((gic->gic_irqs[i].map_pin & GIC_MAP_MSK) == pin &&
> + gic->gic_irqs[i].map_vpe == vpe &&
> + gic->gic_irqs[i].enabled) {
> + ored_level |= gic->gic_irqs[i].pending;
> + }
> + if (ored_level) {
> + /* no need to iterate all interrupts */
> + break;
> + }
> + }
I think we should keep information which pins are shared, so we wouldn't
need to check all the gic pins every time.
> + if (((gic->vps[vpe].compare_map & GIC_MAP_MSK) == pin) &&
> + (gic->vps[vpe].mask & GIC_VPE_SMASK_CMP_MSK)) {
> + /* ORing with local pending register (count/compare) */
> + ored_level |= ((gic->vps[vpe].pend >> 1) & 1);
> + }
> + }
> +
> +#ifdef CONFIG_KVM
> + if (kvm_enabled()) {
> + kvm_mips_set_ipi_interrupt(gic->vps[vpe].env, pin + GIC_CPU_PIN_OFFSET,
> + ored_level);
> + }
> +#endif
> + qemu_set_irq(gic->vps[vpe].env->irq[pin + GIC_CPU_PIN_OFFSET], ored_level);
> +}
> +
> +/* GIC VPE Local Timer */
> +static uint32_t gic_vpe_timer_update(MIPSGICState *gic, uint32_t vp_index)
> +{
> + uint64_t now, next;
> + uint32_t wait;
> +
> + now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
> + wait = gic->vps[vp_index].comparelo - gic->gic_sh_counterlo -
> + (uint32_t)(now / TIMER_PERIOD);
> + next = now + (uint64_t)wait * TIMER_PERIOD;
> +
> + timer_mod(gic->vps[vp_index].gic_timer->qtimer , next);
> + return wait;
> +}
> +
> +static void gic_vpe_timer_expire(MIPSGICState *gic, uint32_t vp_index)
> +{
> + uint32_t pin;
> + pin = (gic->vps[vp_index].compare_map & GIC_MAP_MSK);
> + gic_vpe_timer_update(gic, vp_index);
> + gic->vps[vp_index].pend |= (1 << 1);
> +
> + if (gic->vps[vp_index].pend &
> + (gic->vps[vp_index].mask & GIC_VPE_SMASK_CMP_MSK)) {
> + if (gic->vps[vp_index].compare_map & 0x80000000) {
> + /* it is safe to set the irq high regardless of other GIC IRQs */
> + qemu_irq_raise(gic->vps[vp_index].env->irq
> + [pin + GIC_CPU_PIN_OFFSET]);
> + }
> + }
> +}
> +
> +static uint32_t gic_get_sh_count(MIPSGICState *gic)
> +{
> + int i;
> + if (gic->gic_sh_config & (1 << 28)) {
> + return gic->gic_sh_counterlo;
> + } else {
> + uint64_t now;
> + now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
> + for (i = 0; i < gic->num_cpu; i++) {
> + if (timer_pending(gic->vps[i].gic_timer->qtimer)
> + && timer_expired(gic->vps[i].gic_timer->qtimer , now)) {
> + /* The timer has already expired. */
> + gic_vpe_timer_expire(gic, i);
> + }
> + }
> + return gic->gic_sh_counterlo + (uint32_t)(now / TIMER_PERIOD);
> + }
> +}
> +
> +static void gic_store_sh_count(MIPSGICState *gic, uint64_t count)
> +{
> + int i;
> +
> + if ((gic->gic_sh_config & 0x10000000) || !gic->vps[0].gic_timer) {
> + gic->gic_sh_counterlo = count;
> + } else {
> + /* Store new count register */
> + gic->gic_sh_counterlo = count -
> + (uint32_t)(qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) / TIMER_PERIOD);
> + /* Update timer timer */
> + for (i = 0; i < gic->num_cpu; i++) {
> + gic_vpe_timer_update(gic, i);
> + }
> + }
> +}
> +
> +static void gic_store_vpe_compare(MIPSGICState *gic, uint32_t vp_index,
> + uint64_t compare)
> +{
> + uint32_t wait;
> +
> + gic->vps[vp_index].comparelo = (uint32_t) compare;
> + wait = gic_vpe_timer_update(gic, vp_index);
wait isn't used anywhere in this function.
> +
> + gic->vps[vp_index].pend &= ~(1 << 1);
> + if (gic->vps[vp_index].compare_map & 0x80000000) {
> + uint32_t pin = (gic->vps[vp_index].compare_map & GIC_MAP_MSK);
> + gic_set_vp_irq(gic, vp_index, pin, 0);
> + }
> +}
> +
> +static void gic_vpe_timer_cb(void *opaque)
> +{
> + MIPSGICTimerState *gic_timer = opaque;
> + gic_timer->gic->gic_sh_counterlo++;
> + gic_vpe_timer_expire(gic_timer->gic, gic_timer->vp_index);
> + gic_timer->gic->gic_sh_counterlo--;
> +}
> +
> +static void gic_timer_start_count(MIPSGICState *gic)
> +{
> + gic_store_sh_count(gic, gic->gic_sh_counterlo);
> +}
> +
> +static void gic_timer_stop_count(MIPSGICState *gic)
> +{
> + int i;
> +
> + /* Store the current value */
> + gic->gic_sh_counterlo +=
> + (uint32_t)(qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) / TIMER_PERIOD);
> + for (i = 0; i < gic->num_cpu; i++) {
> + timer_del(gic->vps[i].gic_timer->qtimer);
> + }
> +}
> +
> +static void gic_timer_init(MIPSGICState *gic, uint32_t ncpus)
> +{
> + int i;
> + for (i = 0; i < ncpus; i++) {
> + gic->vps[i].gic_timer = g_malloc0(sizeof(MIPSGICTimerState));
> + gic->vps[i].gic_timer->gic = gic;
> + gic->vps[i].gic_timer->vp_index = i;
> + gic->vps[i].gic_timer->qtimer = timer_new_ns(QEMU_CLOCK_VIRTUAL,
> + &gic_vpe_timer_cb,
> + gic->vps[i].gic_timer);
> + }
> + gic_store_sh_count(gic, gic->gic_sh_counterlo);
> +}
You could consider moving the gic timer stuff into a separate file.
> +
> +/* GIC Read VPE Local/Other Registers */
> +static uint64_t gic_read_vpe(MIPSGICState *gic, uint32_t vp_index, hwaddr addr,
> + unsigned size)
> +{
> + switch (addr) {
> + case GIC_VPE_CTL_OFS:
> + return gic->vps[vp_index].ctl;
> + case GIC_VPE_PEND_OFS:
> + gic_get_sh_count(gic);
> + return gic->vps[vp_index].pend;
> + case GIC_VPE_MASK_OFS:
> + return gic->vps[vp_index].mask;
> + case GIC_VPE_WD_MAP_OFS:
> + return gic->vps[vp_index].wd_map;
> + case GIC_VPE_COMPARE_MAP_OFS:
> + return gic->vps[vp_index].compare_map;
> + case GIC_VPE_TIMER_MAP_OFS:
> + return gic->vps[vp_index].timer_map;
> + case GIC_VPE_OTHER_ADDR_OFS:
> + return gic->vps[vp_index].other_addr;
> + case GIC_VPE_IDENT_OFS:
> + return vp_index;
> + case GIC_VPE_COMPARE_LO_OFS:
> + return gic->vps[vp_index].comparelo;
> + case GIC_VPE_COMPARE_HI_OFS:
> + return gic->vps[vp_index].comparehi;
> + default:
> + qemu_log_mask(LOG_UNIMP,
> + "Warning *** read %d bytes at GIC offset LOCAL/OTHER 0x%"
> + PRIx64 "\n",
> + size, addr);
> + break;
> + }
> + return 0;
> +}
> +
> +static uint64_t gic_read(void *opaque, hwaddr addr, unsigned size)
> +{
> + MIPSGICState *gic = (MIPSGICState *) opaque;
> + uint32_t vp_index = gic_get_current_cpu(gic);
> + uint64_t ret = 0;
> + int i, base;
base is set in a few places below but isn't used anywhere.
> +
> + switch (addr) {
> + case GIC_SH_CONFIG_OFS:
> + return gic->gic_sh_config;
> + case GIC_SH_CONFIG_OFS + 4:
> + /* do nothing */
> + return 0;
> + case GIC_SH_COUNTERLO_OFS:
> + ret = gic_get_sh_count(gic);
> + return ret;
> + case GIC_SH_COUNTERHI_OFS:
> + return 0;
> + case GIC_SH_POL_31_0_OFS:
> + case GIC_SH_POL_63_32_OFS:
> + case GIC_SH_POL_95_64_OFS:
> + case GIC_SH_POL_127_96_OFS:
> + case GIC_SH_POL_159_128_OFS:
> + case GIC_SH_POL_191_160_OFS:
> + case GIC_SH_POL_223_192_OFS:
> + case GIC_SH_POL_255_224_OFS:
> + base = (addr - GIC_SH_POL_31_0_OFS) * 8;
> + for (i = 0; i < size * 8; i++) {
> + ret |= (gic->gic_irqs[i].polarity & 1) << i;
> + }
> + return ret;
> + case GIC_SH_TRIG_31_0_OFS:
> + case GIC_SH_TRIG_63_32_OFS:
> + case GIC_SH_TRIG_95_64_OFS:
> + case GIC_SH_TRIG_127_96_OFS:
> + case GIC_SH_TRIG_159_128_OFS:
> + case GIC_SH_TRIG_191_160_OFS:
> + case GIC_SH_TRIG_223_192_OFS:
> + case GIC_SH_TRIG_255_224_OFS:
> + base = (addr - GIC_SH_TRIG_31_0_OFS) * 8;
> + for (i = 0; i < size * 8; i++) {
> + ret |= (gic->gic_irqs[i].trigger_type & 1) << i;
> + }
> + return ret;
> + case GIC_SH_PEND_31_0_OFS:
> + case GIC_SH_PEND_63_32_OFS:
> + case GIC_SH_PEND_95_64_OFS:
> + case GIC_SH_PEND_127_96_OFS:
> + case GIC_SH_PEND_159_128_OFS:
> + case GIC_SH_PEND_191_160_OFS:
> + case GIC_SH_PEND_223_192_OFS:
> + case GIC_SH_PEND_255_224_OFS:
> + base = (addr - GIC_SH_PEND_31_0_OFS) * 8;
> + for (i = 0; i < size * 8; i++) {
> + ret |= (gic->gic_irqs[i].pending & 1) << i;
> + }
> + return ret;
> + case GIC_SH_MASK_31_0_OFS:
> + case GIC_SH_MASK_63_32_OFS:
> + case GIC_SH_MASK_95_64_OFS:
> + case GIC_SH_MASK_127_96_OFS:
> + case GIC_SH_MASK_159_128_OFS:
> + case GIC_SH_MASK_191_160_OFS:
> + case GIC_SH_MASK_223_192_OFS:
> + case GIC_SH_MASK_255_224_OFS:
> + base = (addr - GIC_SH_MASK_31_0_OFS) * 8;
> + for (i = 0; i < size * 8; i++) {
> + ret |= (gic->gic_irqs[i].enabled & 1) << i;
> + }
> + return ret;
> + default:
> + if (addr < GIC_SH_MAP0_PIN_OFS) {
> + qemu_log_mask(LOG_UNIMP,
> + "Warning *** read %d bytes at GIC offset 0x%" PRIx64 "\n",
> + size, addr);
> + }
> + break;
> + }
> +
> + /* Global Interrupt Map SrcX to Pin register */
> + if (addr >= GIC_SH_MAP0_PIN_OFS && addr <= GIC_SH_MAP255_PIN_OFS) {
> + int irq_src = (addr - GIC_SH_MAP0_PIN_OFS) / 4;
> + ret = gic->gic_irqs[irq_src].map_pin;
> + return ret;
> + }
> +
> + /* Global Interrupt Map SrcX to VPE register */
> + if (addr >= GIC_SH_MAP0_VPE31_0_OFS && addr <= GIC_SH_MAP255_VPE63_32_OFS) {
> + int irq_src = (addr - GIC_SH_MAP0_VPE31_0_OFS) / 32;
> + ret = 1 << (gic->gic_irqs[irq_src].map_vpe);
> + return ret;
> + }
> +
> + /* VPE-Local Register */
> + if (addr >= GIC_VPELOCAL_BASE_ADDR && addr < GIC_VPEOTHER_BASE_ADDR) {
> + return gic_read_vpe(gic, vp_index, addr - GIC_VPELOCAL_BASE_ADDR, size);
> + }
> +
> + /* VPE-Other Register */
> + if (addr >= GIC_VPEOTHER_BASE_ADDR && addr < GIC_USERMODE_BASE_ADDR) {
> + uint32_t other_index = gic->vps[vp_index].other_addr;
> + return gic_read_vpe(gic, other_index, addr - GIC_VPEOTHER_BASE_ADDR,
> + size);
> + }
Why these are coded in separate if statements instead of switch above?
> +
> + qemu_log_mask(LOG_UNIMP, "GIC unimplemented register %" PRIx64 "\n", addr);
> + return 0ULL;
Why ULL suffix for 0?
> +}
> +
> +/* GIC Write VPE Local/Other Registers */
> +static void gic_write_vpe(MIPSGICState *gic, uint32_t vp_index, hwaddr addr,
> + uint64_t data, unsigned size)
> +{
> + switch (addr) {
> + case GIC_VPE_CTL_OFS:
> + gic->vps[vp_index].ctl &= ~1;
> + gic->vps[vp_index].ctl |= data & 1;
> + break;
> + case GIC_VPE_RMASK_OFS:
> + gic->vps[vp_index].mask &= ~(data & 0x3f) & 0x3f;
> + break;
> + case GIC_VPE_SMASK_OFS:
> + gic->vps[vp_index].mask |= (data & 0x3f);
> + break;
> + case GIC_VPE_WD_MAP_OFS:
> + gic->vps[vp_index].wd_map = data & 0xE000003F;
> + break;
> + case GIC_VPE_COMPARE_MAP_OFS:
> + gic->vps[vp_index].compare_map = data & 0xE000003F;
> + break;
> + case GIC_VPE_TIMER_MAP_OFS:
> + gic->vps[vp_index].timer_map = data & 0xE000003F;
> + break;
> + case GIC_VPE_OTHER_ADDR_OFS:
> + if (data < gic->num_cpu) {
> + gic->vps[vp_index].other_addr = data;
> + }
> + break;
> + case GIC_VPE_OTHER_ADDR_OFS + 4:
> + /* do nothing */
> + break;
> + case GIC_VPE_COMPARE_LO_OFS:
> + gic_store_vpe_compare(gic, vp_index, data);
> + break;
> + case GIC_VPE_COMPARE_HI_OFS:
> + /* do nothing */
> + break;
> + default:
> + qemu_log_mask(LOG_UNIMP,
> + "Warning *** write %d bytes at GIC offset LOCAL/OTHER "
> + "0x%" PRIx64" 0x%08lx\n", size, addr, data);
> + break;
> + }
> +}
> +
> +static void gic_write(void *opaque, hwaddr addr, uint64_t data, unsigned size)
> +{
> + int intr;
> + MIPSGICState *gic = (MIPSGICState *) opaque;
> + uint32_t vp_index = gic_get_current_cpu(gic);
> + int i, base;
> +
> + switch (addr) {
> + case GIC_SH_CONFIG_OFS:
> + {
> + uint32_t pre = gic->gic_sh_config;
> + gic->gic_sh_config = (gic->gic_sh_config & 0xEFFFFFFF) |
> + (data & 0x10000000);
> + if (pre != gic->gic_sh_config) {
> + if ((gic->gic_sh_config & 0x10000000)) {
> + gic_timer_stop_count(gic);
> + }
> + if (!(gic->gic_sh_config & 0x10000000)) {
> + gic_timer_start_count(gic);
> + }
> + }
> + }
> + break;
> + case GIC_SH_CONFIG_OFS + 4:
> + /* do nothing */
> + break;
> + case GIC_SH_COUNTERLO_OFS:
> + if (gic->gic_sh_config & 0x10000000) {
> + gic_store_sh_count(gic, data);
> + }
> + break;
> + case GIC_SH_COUNTERHI_OFS:
> + /* do nothing */
> + break;
> + case GIC_SH_POL_31_0_OFS:
> + case GIC_SH_POL_63_32_OFS:
> + case GIC_SH_POL_95_64_OFS:
> + case GIC_SH_POL_127_96_OFS:
> + case GIC_SH_POL_159_128_OFS:
> + case GIC_SH_POL_191_160_OFS:
> + case GIC_SH_POL_223_192_OFS:
> + case GIC_SH_POL_255_224_OFS:
> + base = (addr - GIC_SH_POL_31_0_OFS) * 8;
> + for (i = 0; i < size * 8; i++) {
> + gic->gic_irqs[base + i].polarity = (data >> i) & 1;
> + }
> + break;
> + case GIC_SH_TRIG_31_0_OFS:
> + case GIC_SH_TRIG_63_32_OFS:
> + case GIC_SH_TRIG_95_64_OFS:
> + case GIC_SH_TRIG_127_96_OFS:
> + case GIC_SH_TRIG_159_128_OFS:
> + case GIC_SH_TRIG_191_160_OFS:
> + case GIC_SH_TRIG_223_192_OFS:
> + case GIC_SH_TRIG_255_224_OFS:
> + base = (addr - GIC_SH_TRIG_31_0_OFS) * 8;
> + for (i = 0; i < size * 8; i++) {
> + gic->gic_irqs[base + i].trigger_type = (data >> i) & 1;
> + }
> + break;
> + case GIC_SH_RMASK_31_0_OFS:
> + case GIC_SH_RMASK_63_32_OFS:
> + case GIC_SH_RMASK_95_64_OFS:
> + case GIC_SH_RMASK_127_96_OFS:
> + case GIC_SH_RMASK_159_128_OFS:
> + case GIC_SH_RMASK_191_160_OFS:
> + case GIC_SH_RMASK_223_192_OFS:
> + case GIC_SH_RMASK_255_224_OFS:
> + base = (addr - GIC_SH_RMASK_31_0_OFS) * 8;
> + for (i = 0; i < size * 8; i++) {
> + gic->gic_irqs[base + i].enabled &= !((data >> i) & 1);
> + }
> + break;
> + case GIC_SH_WEDGE_OFS:
> + /* Figure out which VPE/HW Interrupt this maps to */
> + intr = data & 0x7FFFFFFF;
> + /* Mask/Enabled Checks */
> + if (data & 0x80000000) {
> + qemu_set_irq(gic->irqs[intr], 1);
> + } else {
> + qemu_set_irq(gic->irqs[intr], 0);
> + }
This does not look correct. According to the spec interrupt asserted by
write edge register can be still masked. Probaly gic_set_irq() should be
used rather than changing the level directly.
> + break;
> + case GIC_SH_SMASK_31_0_OFS:
> + case GIC_SH_SMASK_63_32_OFS:
> + case GIC_SH_SMASK_95_64_OFS:
> + case GIC_SH_SMASK_127_96_OFS:
> + case GIC_SH_SMASK_159_128_OFS:
> + case GIC_SH_SMASK_191_160_OFS:
> + case GIC_SH_SMASK_223_192_OFS:
> + case GIC_SH_SMASK_255_224_OFS:
> + base = (addr - GIC_SH_SMASK_31_0_OFS) * 8;
> + for (i = 0; i < size * 8; i++) {
> + gic->gic_irqs[base + i].enabled |= (data >> i) & 1;
> + }
> + break;
> +
> + default:
> + if (addr < GIC_SH_MAP0_PIN_OFS) {
> + qemu_log_mask(LOG_UNIMP,
> + "Warning *** write %d bytes at GIC offset 0x%" PRIx64
> + " 0x%08lx\n",
> + size, addr, data);
> + }
> + break;
> + }
> +
> + /* Other cases */
> + if (addr >= GIC_SH_MAP0_PIN_OFS && addr <= GIC_SH_MAP255_PIN_OFS) {
> + int irq_src = (addr - GIC_SH_MAP0_PIN_OFS) / 4;
> + gic->gic_irqs[irq_src].map_pin = data;
> + }
> + if (addr >= GIC_SH_MAP0_VPE31_0_OFS && addr <= GIC_SH_MAP255_VPE63_32_OFS) {
> + int irq_src = (addr - GIC_SH_MAP0_VPE31_0_OFS) / 32;
> + gic->gic_irqs[irq_src].map_vpe = (data) ? ctz64(data) : 0;
> + }
> +
> + /* VPE-Local Register */
> + if (addr >= GIC_VPELOCAL_BASE_ADDR && addr < GIC_VPEOTHER_BASE_ADDR) {
> + gic_write_vpe(gic, vp_index, addr - GIC_VPELOCAL_BASE_ADDR,
> + data, size);
> + }
> +
> + /* VPE-Other Register */
> + if (addr >= GIC_VPEOTHER_BASE_ADDR && addr < GIC_USERMODE_BASE_ADDR) {
> + uint32_t other_index = gic->vps[vp_index].other_addr;
> + gic_write_vpe(gic, other_index, addr - GIC_VPEOTHER_BASE_ADDR,
> + data, size);
> + }
> +}
> +
> +static void gic_set_irq(void *opaque, int n_IRQ, int level)
> +{
> + int vpe = -1, pin = -1;
You can initialize them with the actual value here.
> + MIPSGICState *gic = (MIPSGICState *) opaque;
> +
> +
> + gic->gic_irqs[n_IRQ].pending = (bool) level;
How about (level != 0) instead of casting?
> +
> + /* Mapping: assume MAP_TO_PIN */
> + pin = gic->gic_irqs[n_IRQ].map_pin & GIC_MAP_MSK;
> + vpe = gic->gic_irqs[n_IRQ].map_vpe;
> +
> + if (vpe < 0 || vpe >= gic->num_cpu) {
> + return;
> + }
> +
> + gic_set_vp_irq(gic, vpe, pin, level);
> +}
> +
> +static void gic_reset(void *opaque)
> +{
> + int i;
> + MIPSGICState *gic = (MIPSGICState *) opaque;
> +
> + gic->gic_sh_config = 0x100f0000 | gic->num_cpu;
> + gic->gic_sh_counterlo = 0;
> +
> + for (i = 0; i < gic->num_cpu; i++) {
> + gic->vps[i].ctl = 0x0;
> + gic->vps[i].pend = 0x0;
> + gic->vps[i].mask = 0x1; /* COMPARE_MASK ONLY */
> + gic->vps[i].wd_map = GIC_MAP_TO_NMI_MSK;
> + gic->vps[i].compare_map = GIC_MAP_TO_PIN_MSK;
> + gic->vps[i].timer_map = GIC_MAP_TO_PIN_MSK | 0x5;
> + gic->vps[i].comparelo = 0x0;
> + gic->vps[i].comparehi = 0x0;
> + gic->vps[i].other_addr = 0x0;
> + }
> +
> + for (i = 0; i < gic->num_irq; i++) {
> + gic->gic_irqs[i].enabled = false;
> + gic->gic_irqs[i].pending = false;
> + gic->gic_irqs[i].polarity = false;
> + gic->gic_irqs[i].trigger_type = false;
> + gic->gic_irqs[i].dual_edge = false;
> + gic->gic_irqs[i].map_pin = GIC_MAP_TO_PIN_MSK;
> + gic->gic_irqs[i].map_vpe = 0;
> + }
> +}
> +
> +static const MemoryRegionOps gic_ops = {
> + .read = gic_read,
> + .write = gic_write,
> + .endianness = DEVICE_NATIVE_ENDIAN,
> + .impl = {
> + .max_access_size = 8,
> + },
> +};
> +
> +static void mips_gic_init(Object *obj)
> +{
> + SysBusDevice *sbd = SYS_BUS_DEVICE(obj);
> + MIPSGICState *s = MIPS_GIC(obj);
> +
> + memory_region_init_io(&s->gic_mem, OBJECT(s), &gic_ops, s,
> + "mips-gic", GIC_ADDRSPACE_SZ);
> + sysbus_init_mmio(sbd, &s->gic_mem);
> + qemu_register_reset(gic_reset, s);
> +}
> +
> +static void mips_gic_realize(DeviceState *dev, Error **errp)
> +{
> + MIPSGICState *s = MIPS_GIC(dev);
> + qemu_irq *irqs = g_new(qemu_irq, s->num_irq);
> + CPUState *cs = first_cpu;
> + int i;
> +
> + if (s->num_cpu > GIC_MAX_VPS) {
> + error_setg(errp, "Exceed maximum CPUs %d", s->num_cpu);
> + return;
> + }
> + if (s->num_irq > GIC_MAX_INTRS) {
> + error_setg(errp, "Exceed maximum GIC IRQs %d", s->num_irq);
> + return;
> + }
> +
> + s->vps = g_new(MIPSGICVPState, s->num_cpu);
> + s->gic_irqs = g_new(MIPSGICIRQState, s->num_irq);
> +
> + /* Register the CPU env for all cpus with the GIC */
> + for (i = 0; i < s->num_cpu; i++) {
> + if (cs != NULL) {
> + s->vps[i].env = cs->env_ptr;
> + cs = CPU_NEXT(cs);
> + } else {
> + fprintf(stderr, "Unable to initialize GIC - CPUState for "
> + "CPU #%d not valid!", i);
> + return;
> + }
> + }
> +
> + gic_timer_init(s, s->num_cpu);
> +
> + qdev_init_gpio_in(dev, gic_set_irq, s->num_irq);
> + for (i = 0; i < s->num_irq; i++) {
> + irqs[i] = qdev_get_gpio_in(dev, i);
> +
> + s->gic_irqs[i].irq = (qemu_irq *) irqs[i];
For me this looks like the gic_irqs[i].irq has incorrect type. Need for
casting here doesn't look right.
> + }
> + s->irqs = irqs;
What are s->irqs for? The only place where they seem to be used is WEDGE
(where they actually shouldn't I think).
> +}
> +
> +static Property mips_gic_properties[] = {
> + DEFINE_PROP_INT32("num-cpu", MIPSGICState, num_cpu, 1),
> + DEFINE_PROP_INT32("num-irq", MIPSGICState, num_irq, 256),
> + DEFINE_PROP_END_OF_LIST(),
> +};
> +
> +static void mips_gic_class_init(ObjectClass *klass, void *data)
> +{
> + DeviceClass *dc = DEVICE_CLASS(klass);
> +
> + dc->props = mips_gic_properties;
> + dc->realize = mips_gic_realize;
> +}
> +
> +static const TypeInfo mips_gic_info = {
> + .name = TYPE_MIPS_GIC,
> + .parent = TYPE_SYS_BUS_DEVICE,
> + .instance_size = sizeof(MIPSGICState),
> + .instance_init = mips_gic_init,
> + .class_init = mips_gic_class_init,
> +};
> +
> +static void mips_gic_register_types(void)
> +{
> + type_register_static(&mips_gic_info);
> +}
> +
> +type_init(mips_gic_register_types)
> diff --git a/hw/mips/mips_gic.h b/hw/mips/mips_gic.h
> new file mode 100644
> index 0000000..e5c9bf8
> --- /dev/null
> +++ b/hw/mips/mips_gic.h
> @@ -0,0 +1,298 @@
> +/*
> + * This file is subject to the terms and conditions of the GNU General Public
> + * License. See the file "COPYING" in the main directory of this archive
> + * for more details.
> + *
> + * Copyright (C) 2000, 07 MIPS Technologies, Inc.
> + * Copyright (C) 2015 Imagination Technologies
> + *
> + */
> +#ifndef _MIPS_GIC_H
> +#define _MIPS_GIC_H
> +
> +/*
> + * GIC Specific definitions
> + */
> +
> +/* The MIPS default location */
> +#define GIC_BASE_ADDR 0x1bdc0000ULL
> +#define GIC_ADDRSPACE_SZ (128 * 1024)
> +
> +/* GIC Address Space Offsets */
> +#define GIC_SHARED_BASE_ADDR 0x0000
> +#define GIC_VPELOCAL_BASE_ADDR 0x8000
> +#define GIC_VPEOTHER_BASE_ADDR 0xC000
> +#define GIC_USERMODE_BASE_ADDR 0x10000
> +
> +/* Constants */
> +#define GIC_POL_POS 1
> +#define GIC_POL_NEG 0
> +#define GIC_TRIG_EDGE 1
> +#define GIC_TRIG_LEVEL 0
> +
> +#define MSK(n) ((1 << (n)) - 1)
> +
> +/* GIC Address Space */
> +#define SHARED_SECTION_OFS 0x0000
> +#define SHARED_SECTION_SIZE 0x8000
> +#define VPE_LOCAL_SECTION_OFS 0x8000
> +#define VPE_LOCAL_SECTION_SIZE 0x4000
> +#define VPE_OTHER_SECTION_OFS 0xc000
> +#define VPE_OTHER_SECTION_SIZE 0x4000
> +#define USM_VISIBLE_SECTION_OFS 0x10000
> +#define USM_VISIBLE_SECTION_SIZE 0x10000
> +
> +/* Register Map for Shared Section */
> +
> +#define GIC_SH_CONFIG_OFS 0x0000
> +
> +/* Shared Global Counter */
> +#define GIC_SH_COUNTERLO_OFS 0x0010
> +#define GIC_SH_COUNTERHI_OFS 0x0014
> +#define GIC_SH_REVISIONID_OFS 0x0020
> +
> +/* Interrupt Polarity */
> +#define GIC_SH_POL_31_0_OFS 0x0100
> +#define GIC_SH_POL_63_32_OFS 0x0104
> +#define GIC_SH_POL_95_64_OFS 0x0108
> +#define GIC_SH_POL_127_96_OFS 0x010c
> +#define GIC_SH_POL_159_128_OFS 0x0110
> +#define GIC_SH_POL_191_160_OFS 0x0114
> +#define GIC_SH_POL_223_192_OFS 0x0118
> +#define GIC_SH_POL_255_224_OFS 0x011c
> +
> +/* Edge/Level Triggering */
> +#define GIC_SH_TRIG_31_0_OFS 0x0180
> +#define GIC_SH_TRIG_63_32_OFS 0x0184
> +#define GIC_SH_TRIG_95_64_OFS 0x0188
> +#define GIC_SH_TRIG_127_96_OFS 0x018c
> +#define GIC_SH_TRIG_159_128_OFS 0x0190
> +#define GIC_SH_TRIG_191_160_OFS 0x0194
> +#define GIC_SH_TRIG_223_192_OFS 0x0198
> +#define GIC_SH_TRIG_255_224_OFS 0x019c
> +
> +/* Dual Edge Triggering */
> +#define GIC_SH_DUAL_31_0_OFS 0x0200
> +#define GIC_SH_DUAL_63_32_OFS 0x0204
> +#define GIC_SH_DUAL_95_64_OFS 0x0208
> +#define GIC_SH_DUAL_127_96_OFS 0x020c
> +#define GIC_SH_DUAL_159_128_OFS 0x0210
> +#define GIC_SH_DUAL_191_160_OFS 0x0214
> +#define GIC_SH_DUAL_223_192_OFS 0x0218
> +#define GIC_SH_DUAL_255_224_OFS 0x021c
> +
> +/* Set/Clear corresponding bit in Edge Detect Register */
> +#define GIC_SH_WEDGE_OFS 0x0280
> +
> +/* Reset Mask - Disables Interrupt */
> +#define GIC_SH_RMASK_31_0_OFS 0x0300
> +#define GIC_SH_RMASK_63_32_OFS 0x0304
> +#define GIC_SH_RMASK_95_64_OFS 0x0308
> +#define GIC_SH_RMASK_127_96_OFS 0x030c
> +#define GIC_SH_RMASK_159_128_OFS 0x0310
> +#define GIC_SH_RMASK_191_160_OFS 0x0314
> +#define GIC_SH_RMASK_223_192_OFS 0x0318
> +#define GIC_SH_RMASK_255_224_OFS 0x031c
> +
> +/* Set Mask (WO) - Enables Interrupt */
> +#define GIC_SH_SMASK_31_0_OFS 0x0380
> +#define GIC_SH_SMASK_63_32_OFS 0x0384
> +#define GIC_SH_SMASK_95_64_OFS 0x0388
> +#define GIC_SH_SMASK_127_96_OFS 0x038c
> +#define GIC_SH_SMASK_159_128_OFS 0x0390
> +#define GIC_SH_SMASK_191_160_OFS 0x0394
> +#define GIC_SH_SMASK_223_192_OFS 0x0398
> +#define GIC_SH_SMASK_255_224_OFS 0x039c
> +
> +/* Global Interrupt Mask Register (RO) - Bit Set == Interrupt enabled */
> +#define GIC_SH_MASK_31_0_OFS 0x0400
> +#define GIC_SH_MASK_63_32_OFS 0x0404
> +#define GIC_SH_MASK_95_64_OFS 0x0408
> +#define GIC_SH_MASK_127_96_OFS 0x040c
> +#define GIC_SH_MASK_159_128_OFS 0x0410
> +#define GIC_SH_MASK_191_160_OFS 0x0414
> +#define GIC_SH_MASK_223_192_OFS 0x0418
> +#define GIC_SH_MASK_255_224_OFS 0x041c
> +
> +/* Pending Global Interrupts (RO) */
> +#define GIC_SH_PEND_31_0_OFS 0x0480
> +#define GIC_SH_PEND_63_32_OFS 0x0484
> +#define GIC_SH_PEND_95_64_OFS 0x0488
> +#define GIC_SH_PEND_127_96_OFS 0x048c
> +#define GIC_SH_PEND_159_128_OFS 0x0490
> +#define GIC_SH_PEND_191_160_OFS 0x0494
> +#define GIC_SH_PEND_223_192_OFS 0x0498
> +#define GIC_SH_PEND_255_224_OFS 0x049c
> +
> +#define GIC_SH_MAP0_PIN_OFS 0x0500
> +#define GIC_SH_MAP255_PIN_OFS 0x08fc
> +
> +#define GIC_SH_MAP0_VPE31_0_OFS 0x2000
> +#define GIC_SH_MAP255_VPE63_32_OFS 0x3fe4
> +
> +/* Register Map for Local Section */
> +#define GIC_VPE_CTL_OFS 0x0000
> +#define GIC_VPE_PEND_OFS 0x0004
> +#define GIC_VPE_MASK_OFS 0x0008
> +#define GIC_VPE_RMASK_OFS 0x000c
> +#define GIC_VPE_SMASK_OFS 0x0010
> +#define GIC_VPE_WD_MAP_OFS 0x0040
> +#define GIC_VPE_COMPARE_MAP_OFS 0x0044
> +#define GIC_VPE_TIMER_MAP_OFS 0x0048
> +#define GIC_VPE_PERFCTR_MAP_OFS 0x0050
> +#define GIC_VPE_SWINT0_MAP_OFS 0x0054
> +#define GIC_VPE_SWINT1_MAP_OFS 0x0058
> +#define GIC_VPE_OTHER_ADDR_OFS 0x0080
> +#define GIC_VPE_IDENT_OFS 0x0088
> +#define GIC_VPE_WD_CONFIG0_OFS 0x0090
> +#define GIC_VPE_WD_COUNT0_OFS 0x0094
> +#define GIC_VPE_WD_INITIAL0_OFS 0x0098
> +#define GIC_VPE_COMPARE_LO_OFS 0x00a0
> +#define GIC_VPE_COMPARE_HI_OFS 0x00a4
> +
> +/* Masks */
> +#define GIC_SH_CONFIG_COUNTSTOP_SHF 28
> +#define GIC_SH_CONFIG_COUNTSTOP_MSK (MSK(1) << GIC_SH_CONFIG_COUNTSTOP_SHF)
> +
> +#define GIC_SH_CONFIG_COUNTBITS_SHF 24
> +#define GIC_SH_CONFIG_COUNTBITS_MSK (MSK(4) << GIC_SH_CONFIG_COUNTBITS_SHF)
> +
> +#define GIC_SH_CONFIG_NUMINTRS_SHF 16
> +#define GIC_SH_CONFIG_NUMINTRS_MSK (MSK(8) << GIC_SH_CONFIG_NUMINTRS_SHF)
> +
> +#define GIC_SH_CONFIG_NUMVPES_SHF 0
> +#define GIC_SH_CONFIG_NUMVPES_MSK (MSK(8) << GIC_SH_CONFIG_NUMVPES_SHF)
> +
> +#define GIC_SH_WEDGE_SET(intr) ((intr) | (0x1 << 31))
> +#define GIC_SH_WEDGE_CLR(intr) ((intr) & ~(0x1 << 31))
> +
> +#define GIC_MAP_TO_PIN_SHF 31
> +#define GIC_MAP_TO_PIN_MSK (MSK(1) << GIC_MAP_TO_PIN_SHF)
> +#define GIC_MAP_TO_NMI_SHF 30
> +#define GIC_MAP_TO_NMI_MSK (MSK(1) << GIC_MAP_TO_NMI_SHF)
> +#define GIC_MAP_TO_YQ_SHF 29
> +#define GIC_MAP_TO_YQ_MSK (MSK(1) << GIC_MAP_TO_YQ_SHF)
> +#define GIC_MAP_SHF 0
> +#define GIC_MAP_MSK (MSK(6) << GIC_MAP_SHF)
> +
> +/* GIC_VPE_CTL Masks */
> +#define GIC_VPE_CTL_PERFCNT_RTBL_SHF 2
> +#define GIC_VPE_CTL_PERFCNT_RTBL_MSK (MSK(1) << GIC_VPE_CTL_PERFCNT_RTBL_SHF)
> +#define GIC_VPE_CTL_TIMER_RTBL_SHF 1
> +#define GIC_VPE_CTL_TIMER_RTBL_MSK (MSK(1) << GIC_VPE_CTL_TIMER_RTBL_SHF)
> +#define GIC_VPE_CTL_EIC_MODE_SHF 0
> +#define GIC_VPE_CTL_EIC_MODE_MSK (MSK(1) << GIC_VPE_CTL_EIC_MODE_SHF)
> +
> +/* GIC_VPE_PEND Masks */
> +#define GIC_VPE_PEND_WD_SHF 0
> +#define GIC_VPE_PEND_WD_MSK (MSK(1) << GIC_VPE_PEND_WD_SHF)
> +#define GIC_VPE_PEND_CMP_SHF 1
> +#define GIC_VPE_PEND_CMP_MSK (MSK(1) << GIC_VPE_PEND_CMP_SHF)
> +#define GIC_VPE_PEND_TIMER_SHF 2
> +#define GIC_VPE_PEND_TIMER_MSK (MSK(1) << GIC_VPE_PEND_TIMER_SHF)
> +#define GIC_VPE_PEND_PERFCOUNT_SHF 3
> +#define GIC_VPE_PEND_PERFCOUNT_MSK (MSK(1) << GIC_VPE_PEND_PERFCOUNT_SHF)
> +#define GIC_VPE_PEND_SWINT0_SHF 4
> +#define GIC_VPE_PEND_SWINT0_MSK (MSK(1) << GIC_VPE_PEND_SWINT0_SHF)
> +#define GIC_VPE_PEND_SWINT1_SHF 5
> +#define GIC_VPE_PEND_SWINT1_MSK (MSK(1) << GIC_VPE_PEND_SWINT1_SHF)
> +
> +/* GIC_VPE_RMASK Masks */
> +#define GIC_VPE_RMASK_WD_SHF 0
> +#define GIC_VPE_RMASK_WD_MSK (MSK(1) << GIC_VPE_RMASK_WD_SHF)
> +#define GIC_VPE_RMASK_CMP_SHF 1
> +#define GIC_VPE_RMASK_CMP_MSK (MSK(1) << GIC_VPE_RMASK_CMP_SHF)
> +#define GIC_VPE_RMASK_TIMER_SHF 2
> +#define GIC_VPE_RMASK_TIMER_MSK (MSK(1) << GIC_VPE_RMASK_TIMER_SHF)
> +#define GIC_VPE_RMASK_PERFCNT_SHF 3
> +#define GIC_VPE_RMASK_PERFCNT_MSK (MSK(1) << GIC_VPE_RMASK_PERFCNT_SHF)
> +#define GIC_VPE_RMASK_SWINT0_SHF 4
> +#define GIC_VPE_RMASK_SWINT0_MSK (MSK(1) << GIC_VPE_RMASK_SWINT0_SHF)
> +#define GIC_VPE_RMASK_SWINT1_SHF 5
> +#define GIC_VPE_RMASK_SWINT1_MSK (MSK(1) << GIC_VPE_RMASK_SWINT1_SHF)
> +
> +/* GIC_VPE_SMASK Masks */
> +#define GIC_VPE_SMASK_WD_SHF 0
> +#define GIC_VPE_SMASK_WD_MSK (MSK(1) << GIC_VPE_SMASK_WD_SHF)
> +#define GIC_VPE_SMASK_CMP_SHF 1
> +#define GIC_VPE_SMASK_CMP_MSK (MSK(1) << GIC_VPE_SMASK_CMP_SHF)
> +#define GIC_VPE_SMASK_TIMER_SHF 2
> +#define GIC_VPE_SMASK_TIMER_MSK (MSK(1) << GIC_VPE_SMASK_TIMER_SHF)
> +#define GIC_VPE_SMASK_PERFCNT_SHF 3
> +#define GIC_VPE_SMASK_PERFCNT_MSK (MSK(1) << GIC_VPE_SMASK_PERFCNT_SHF)
> +#define GIC_VPE_SMASK_SWINT0_SHF 4
> +#define GIC_VPE_SMASK_SWINT0_MSK (MSK(1) << GIC_VPE_SMASK_SWINT0_SHF)
> +#define GIC_VPE_SMASK_SWINT1_SHF 5
> +#define GIC_VPE_SMASK_SWINT1_MSK (MSK(1) << GIC_VPE_SMASK_SWINT1_SHF)
> +
> +#define GIC_CPU_PIN_OFFSET 2
> +
> +#define TYPE_MIPS_GIC "mips-gic"
> +#define MIPS_GIC(obj) OBJECT_CHECK(MIPSGICState, (obj), TYPE_MIPS_GIC)
> +
> +/* Support up to 32 VPEs and 256 IRQs */
> +#define GIC_MAX_VPS 32
> +#define GIC_MAX_INTRS 256
> +
> +typedef struct MIPSGICState MIPSGICState;
> +typedef struct MIPSGICTimerState MIPSGICTimerState;
> +typedef struct MIPSGICIRQState MIPSGICIRQState;
> +typedef struct MIPSGICVPState MIPSGICVPState;
> +
> +struct MIPSGICTimerState {
> + QEMUTimer *qtimer;
> + uint32_t vp_index;
> + MIPSGICState *gic;
> +};
> +
> +struct MIPSGICIRQState {
> + bool enabled;
> + bool pending;
> + bool polarity;
> + bool trigger_type;
> + bool dual_edge;
> + uint32_t map_pin;
> + uint64_t map_vpe;
> + qemu_irq *irq;
This should represent just a single qemu_irq, thus why is this a pointer?
> +};
> +
> +struct MIPSGICVPState {
> + uint32_t ctl;
> + uint32_t pend;
> + uint32_t mask;
> + uint32_t wd_map;
> + uint32_t compare_map;
> + uint32_t timer_map;
> + uint32_t comparelo;
> + uint32_t comparehi;
comparehi is always zero, also description says that CompareHi is not
implemented. I think this field (as well as others which are not used if
there are more) should be just removed.
> + uint32_t other_addr;
> +
> + CPUMIPSState *env;
> + MIPSGICTimerState *gic_timer;
> +};
> +
> +struct MIPSGICState {
> + SysBusDevice parent_obj;
> +
> + MemoryRegion gic_mem;
This is inside MIPSGICState, "gic_" prefix seems redundant.
> + qemu_irq *irqs;
It's not clear to me why this is needed. External interrupt pins are
already represented by gic_irqs.irq.
> +
> + /* Shared Section Registers */
> + uint32_t gic_sh_config;
> + uint32_t gic_sh_counterlo;
> +
> + MIPSGICIRQState *gic_irqs;
> +
> + /* VPE Local Section Registers */
> + /* VPE Other Section Registers, aliased to local,
> + * use the other addr to access the correct instance */
> +
> + MIPSGICVPState *vps;
> +
> + /* User Mode Visible Section Registers */
There are no User Mode Visible Section Registers, the comment is just
misleading.
Regards,
Leon
> +
> + int32_t num_cpu;
> + int32_t num_irq;
> +};
> +
> +#endif /* _MIPS_GIC_H */
>
^ permalink raw reply [flat|nested] 14+ messages in thread
* Re: [Qemu-devel] [PATCH 4/4] mips: add gic support to malta
2015-10-15 23:52 ` [Qemu-devel] [PATCH 4/4] mips: add gic support to malta Yongbok Kim
@ 2015-10-21 15:53 ` Leon Alrae
0 siblings, 0 replies; 14+ messages in thread
From: Leon Alrae @ 2015-10-21 15:53 UTC (permalink / raw)
To: Yongbok Kim, qemu-devel; +Cc: pbonzini, afaerber, aurelien
On 16/10/15 00:52, Yongbok Kim wrote:
> /*
> * We have a circular dependency problem: pci_bus depends on isa_irq,
> * isa_irq is provided by i8259, i8259 depends on ISA, ISA depends
> @@ -1155,7 +1218,11 @@ void mips_malta_init(MachineState *machine)
>
> /* Interrupt controller */
> /* The 8259 is attached to the MIPS CPU INT0 pin, ie interrupt 2 */
> - s->i8259 = i8259_init(isa_bus, env->irq[2]);
> + if (env->gic_irqs) {
> + s->i8259 = i8259_init(isa_bus, env->gic_irqs[3]);
Probably s->gic.gic_irqs[3].irq should be used here.
BTW I think "gic_irqs" can be replaced with a better name, something
like "irq_state".
> + } else {
> + s->i8259 = i8259_init(isa_bus, env->irq[2]);
> + }
>
> isa_bus_irqs(isa_bus, s->i8259);
> pci_piix4_ide_init(pci_bus, hd, piix4_devfn + 1);
> @@ -1209,7 +1276,7 @@ static void mips_malta_machine_init(MachineClass *mc)
> {
> mc->desc = "MIPS Malta Core LV";
> mc->init = mips_malta_init;
> - mc->max_cpus = 16;
> + mc->max_cpus = 32;
> mc->is_default = 1;
> }
>
> diff --git a/target-mips/cpu.h b/target-mips/cpu.h
> index 639ef37..fdd5643 100644
> --- a/target-mips/cpu.h
> +++ b/target-mips/cpu.h
> @@ -594,6 +594,7 @@ struct CPUMIPSState {
>
> const mips_def_t *cpu_model;
> void *irq[8];
> + void **gic_irqs;
I don't think CPUMIPSState need any information about gic external
interrupts.
Leon
> QEMUTimer *timer; /* Internal timer */
> };
>
>
^ permalink raw reply [flat|nested] 14+ messages in thread
* Re: [Qemu-devel] [PATCH 0/4] mips: add Global Interrupt Controller
2015-10-15 23:52 [Qemu-devel] [PATCH 0/4] mips: add Global Interrupt Controller Yongbok Kim
` (4 preceding siblings ...)
2015-10-19 14:36 ` [Qemu-devel] [PATCH 0/4] mips: add Global Interrupt Controller James Hogan
@ 2015-10-21 16:01 ` Peter Maydell
2015-10-21 16:28 ` Yongbok Kim
2015-10-21 16:36 ` Peter Crosthwaite
6 siblings, 1 reply; 14+ messages in thread
From: Peter Maydell @ 2015-10-21 16:01 UTC (permalink / raw)
To: Yongbok Kim
Cc: Paolo Bonzini, Leon Alrae, QEMU Developers, Aurelien Jarno,
Andreas Färber
On 16 October 2015 at 00:52, Yongbok Kim <yongbok.kim@imgtec.com> wrote:
> Add support of MIPS GIC.
> The patchset implements the Global Interrupt Controller.
>
> With the patchset Linux Kernel detects GIC and utilises it.
> # cat /proc/interrupts
> CPU0
> 0: 2 XT-PIC 0 timer
> 2: 0 XT-PIC 2 cascade
> 4: 462 XT-PIC 4 serial
> 8: 0 XT-PIC 8 rtc0
> 11: 0 XT-PIC 11 uhci_hcd:usb1
> 14: 1026 XT-PIC 14 ide0
> 15: 0 XT-PIC 15 ide1
> 21: 0 MIPS 5 CoreHi
> 23: 44 MIPS 7 timer
> 25: 12290 MIPS GIC Local 1 timer
> 34: 1490 MIPS GIC 10 XT-PIC cascade
> 98: 1490 MIPS GIC 74
> ERR: 0
>
> Limitation:
> Level triggering only
> No User-Mode Visible Section
> GIC CounterHi not implemented (Countbits = 32bits)
> DINT not implemented
> Local WatchDog, Fast Debug Channel, Perf Counter not implemented
>
> It is based on the earlier un-merged GIC implementation.
> https://lists.gnu.org/archive/html/qemu-devel/2013-03/msg00194.html
>
> For more information,
> http://imgtec.com/mips/warrior/p-class-p5600-multiprocessor-core/
> http://imgtec.com/mips/warrior/i-class-i6400-multiprocessor-core/
>
>
> Yongbok Kim (4):
> target-mips: add CMGCRBase register
> mips: add Global Config Register block (part)
> mips: add Global Interrupt Controller
> mips: add gic support to malta
>
> hw/mips/Makefile.objs | 2 +-
> hw/mips/mips_gcr.c | 120 ++++++++
> hw/mips/mips_gcr.h | 57 ++++
> hw/mips/mips_gic.c | 653 ++++++++++++++++++++++++++++++++++++++++++
> hw/mips/mips_gic.h | 298 +++++++++++++++++++
> hw/mips/mips_malta.c | 71 +++++-
> target-mips/cpu.h | 4 +-
> target-mips/translate.c | 17 ++
> target-mips/translate_init.c | 3 +-
> 9 files changed, 1220 insertions(+), 5 deletions(-)
> create mode 100644 hw/mips/mips_gcr.c
> create mode 100644 hw/mips/mips_gcr.h
> create mode 100644 hw/mips/mips_gic.c
> create mode 100644 hw/mips/mips_gic.h
Should this live in hw/intc/ if it is an interrupt controller?
thanks
-- PMM
^ permalink raw reply [flat|nested] 14+ messages in thread
* Re: [Qemu-devel] [PATCH 0/4] mips: add Global Interrupt Controller
2015-10-21 16:01 ` Peter Maydell
@ 2015-10-21 16:28 ` Yongbok Kim
2015-10-21 20:13 ` Peter Maydell
0 siblings, 1 reply; 14+ messages in thread
From: Yongbok Kim @ 2015-10-21 16:28 UTC (permalink / raw)
To: Peter Maydell; +Cc: Paolo Bonzini, Leon Alrae, QEMU Developers, Aurelien Jarno
On 21/10/2015 17:01, Peter Maydell wrote:
> On 16 October 2015 at 00:52, Yongbok Kim <yongbok.kim@imgtec.com> wrote:
>> Add support of MIPS GIC.
>>
>> hw/mips/Makefile.objs | 2 +-
>> hw/mips/mips_gcr.c | 120 ++++++++
>> hw/mips/mips_gcr.h | 57 ++++
>> hw/mips/mips_gic.c | 653 ++++++++++++++++++++++++++++++++++++++++++
>> hw/mips/mips_gic.h | 298 +++++++++++++++++++
>> hw/mips/mips_malta.c | 71 +++++-
>> target-mips/cpu.h | 4 +-
>> target-mips/translate.c | 17 ++
>> target-mips/translate_init.c | 3 +-
>> 9 files changed, 1220 insertions(+), 5 deletions(-)
>> create mode 100644 hw/mips/mips_gcr.c
>> create mode 100644 hw/mips/mips_gcr.h
>> create mode 100644 hw/mips/mips_gic.c
>> create mode 100644 hw/mips/mips_gic.h
>
> Should this live in hw/intc/ if it is an interrupt controller?
>
> thanks
> -- PMM
>
Actually I have considered that but I just hesitated to put the files in
the hw/intc as all other MIPS related hw component files are in hw/mips. I
agree with your opinion. From the v2 the mips_gic.{c|h} files will be in
the directory. I think the mips_gcr.{c|h} is quite MIPS specific and it
doesn't fit into any other directory than hw/mips.
Regards,
Yongbok
^ permalink raw reply [flat|nested] 14+ messages in thread
* Re: [Qemu-devel] [PATCH 0/4] mips: add Global Interrupt Controller
2015-10-15 23:52 [Qemu-devel] [PATCH 0/4] mips: add Global Interrupt Controller Yongbok Kim
` (5 preceding siblings ...)
2015-10-21 16:01 ` Peter Maydell
@ 2015-10-21 16:36 ` Peter Crosthwaite
6 siblings, 0 replies; 14+ messages in thread
From: Peter Crosthwaite @ 2015-10-21 16:36 UTC (permalink / raw)
To: Yongbok Kim
Cc: Paolo Bonzini, Leon Alrae, qemu-devel@nongnu.org Developers,
Aurelien Jarno, Andreas Färber
On Thu, Oct 15, 2015 at 4:52 PM, Yongbok Kim <yongbok.kim@imgtec.com> wrote:
> Add support of MIPS GIC.
> The patchset implements the Global Interrupt Controller.
>
> With the patchset Linux Kernel detects GIC and utilises it.
> # cat /proc/interrupts
> CPU0
> 0: 2 XT-PIC 0 timer
> 2: 0 XT-PIC 2 cascade
> 4: 462 XT-PIC 4 serial
> 8: 0 XT-PIC 8 rtc0
> 11: 0 XT-PIC 11 uhci_hcd:usb1
> 14: 1026 XT-PIC 14 ide0
> 15: 0 XT-PIC 15 ide1
> 21: 0 MIPS 5 CoreHi
> 23: 44 MIPS 7 timer
> 25: 12290 MIPS GIC Local 1 timer
> 34: 1490 MIPS GIC 10 XT-PIC cascade
> 98: 1490 MIPS GIC 74
> ERR: 0
>
> Limitation:
> Level triggering only
> No User-Mode Visible Section
> GIC CounterHi not implemented (Countbits = 32bits)
> DINT not implemented
> Local WatchDog, Fast Debug Channel, Perf Counter not implemented
>
> It is based on the earlier un-merged GIC implementation.
> https://lists.gnu.org/archive/html/qemu-devel/2013-03/msg00194.html
>
> For more information,
> http://imgtec.com/mips/warrior/p-class-p5600-multiprocessor-core/
> http://imgtec.com/mips/warrior/i-class-i6400-multiprocessor-core/
>
>
> Yongbok Kim (4):
> target-mips: add CMGCRBase register
> mips: add Global Config Register block (part)
> mips: add Global Interrupt Controller
> mips: add gic support to malta
>
> hw/mips/Makefile.objs | 2 +-
> hw/mips/mips_gcr.c | 120 ++++++++
> hw/mips/mips_gcr.h | 57 ++++
> hw/mips/mips_gic.c | 653 ++++++++++++++++++++++++++++++++++++++++++
> hw/mips/mips_gic.h | 298 +++++++++++++++++++
New interrupt controllers should go in the hw/intc directory. GCR
probably belongs in misc (there are similar concepts there for other
arches like arm_sysctl).
Regards,
Peter
> hw/mips/mips_malta.c | 71 +++++-
> target-mips/cpu.h | 4 +-
> target-mips/translate.c | 17 ++
> target-mips/translate_init.c | 3 +-
> 9 files changed, 1220 insertions(+), 5 deletions(-)
> create mode 100644 hw/mips/mips_gcr.c
> create mode 100644 hw/mips/mips_gcr.h
> create mode 100644 hw/mips/mips_gic.c
> create mode 100644 hw/mips/mips_gic.h
>
>
^ permalink raw reply [flat|nested] 14+ messages in thread
* Re: [Qemu-devel] [PATCH 0/4] mips: add Global Interrupt Controller
2015-10-21 16:28 ` Yongbok Kim
@ 2015-10-21 20:13 ` Peter Maydell
0 siblings, 0 replies; 14+ messages in thread
From: Peter Maydell @ 2015-10-21 20:13 UTC (permalink / raw)
To: Yongbok Kim; +Cc: Paolo Bonzini, Leon Alrae, QEMU Developers, Aurelien Jarno
On 21 October 2015 at 17:28, Yongbok Kim <yongbok.kim@imgtec.com> wrote:
> On 21/10/2015 17:01, Peter Maydell wrote:
>> Should this live in hw/intc/ if it is an interrupt controller?
> Actually I have considered that but I just hesitated to put the files in
> the hw/intc as all other MIPS related hw component files are in hw/mips. I
> agree with your opinion. From the v2 the mips_gic.{c|h} files will be in
> the directory. I think the mips_gcr.{c|h} is quite MIPS specific and it
> doesn't fit into any other directory than hw/mips.
I think the general approach we've gone for is that if we can
find a suitable device-type subdirectory (like intc/) we should
use that (the ARM GIC is in hw/intc, for instance). hw/<architecture>
should be for top level machine definitions, SoC models, and
similar things, rather than individual devices.
For mips_gcr, I think putting it in hw/misc/ is probably
best (for instance we put the ARM Cortex-A9 snoop control
unit here).
You might also consider whether it's helpful to provide
a "wrapper" object like the hw/cpu/a15mpcore.c etc ones
which instantiates a bunch of CPUs and the GCR and the GIC.
(I don't know enough about MIPS to know whether that would
make sense, so I leave it up to you.)
thanks
-- PMM
^ permalink raw reply [flat|nested] 14+ messages in thread
end of thread, other threads:[~2015-10-21 20:13 UTC | newest]
Thread overview: 14+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2015-10-15 23:52 [Qemu-devel] [PATCH 0/4] mips: add Global Interrupt Controller Yongbok Kim
2015-10-15 23:52 ` [Qemu-devel] [PATCH 1/4] target-mips: add CMGCRBase register Yongbok Kim
2015-10-19 15:07 ` Leon Alrae
2015-10-15 23:52 ` [Qemu-devel] [PATCH 2/4] mips: add Global Config Register block (part) Yongbok Kim
2015-10-15 23:52 ` [Qemu-devel] [PATCH 3/4] mips: add Global Interrupt Controller Yongbok Kim
2015-10-21 15:49 ` Leon Alrae
2015-10-15 23:52 ` [Qemu-devel] [PATCH 4/4] mips: add gic support to malta Yongbok Kim
2015-10-21 15:53 ` Leon Alrae
2015-10-19 14:36 ` [Qemu-devel] [PATCH 0/4] mips: add Global Interrupt Controller James Hogan
2015-10-19 14:58 ` Yongbok Kim
2015-10-21 16:01 ` Peter Maydell
2015-10-21 16:28 ` Yongbok Kim
2015-10-21 20:13 ` Peter Maydell
2015-10-21 16:36 ` Peter Crosthwaite
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).