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....@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 <sanj...@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 */ >