From patchwork Thu Oct 15 23:52:08 2015 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Yongbok Kim X-Patchwork-Id: 530993 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Received: from lists.gnu.org (lists.gnu.org [IPv6:2001:4830:134:3::11]) (using TLSv1 with cipher AES256-SHA (256/256 bits)) (No client certificate requested) by ozlabs.org (Postfix) with ESMTPS id 70F2F140B0F for ; Fri, 16 Oct 2015 10:54:57 +1100 (AEDT) Received: from localhost ([::1]:50198 helo=lists.gnu.org) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1ZmsMJ-0004W5-Fp for incoming@patchwork.ozlabs.org; Thu, 15 Oct 2015 19:54:55 -0400 Received: from eggs.gnu.org ([2001:4830:134:3::10]:56900) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1ZmsLQ-0003NY-Ku for qemu-devel@nongnu.org; Thu, 15 Oct 2015 19:54:03 -0400 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1ZmsLM-0004YO-PH for qemu-devel@nongnu.org; Thu, 15 Oct 2015 19:54:00 -0400 Received: from mailapp01.imgtec.com ([195.59.15.196]:5917) by eggs.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1ZmsLM-0004YC-D1 for qemu-devel@nongnu.org; Thu, 15 Oct 2015 19:53:56 -0400 Received: from KLMAIL01.kl.imgtec.org (unknown [192.168.5.35]) by Websense Email Security Gateway with ESMTPS id 3459B6D3E213; Fri, 16 Oct 2015 00:53:50 +0100 (IST) Received: from hhmail02.hh.imgtec.org (10.100.10.20) by KLMAIL01.kl.imgtec.org (192.168.5.35) with Microsoft SMTP Server (TLS) id 14.3.195.1; Fri, 16 Oct 2015 00:53:53 +0100 Received: from localhost.localdomain (192.168.14.192) by hhmail02.hh.imgtec.org (10.100.10.20) with Microsoft SMTP Server (TLS) id 14.3.235.1; Fri, 16 Oct 2015 00:53:53 +0100 From: Yongbok Kim To: Date: Fri, 16 Oct 2015 00:52:08 +0100 Message-ID: <1444953129-35040-4-git-send-email-yongbok.kim@imgtec.com> X-Mailer: git-send-email 1.7.5.4 In-Reply-To: <1444953129-35040-1-git-send-email-yongbok.kim@imgtec.com> References: <1444953129-35040-1-git-send-email-yongbok.kim@imgtec.com> MIME-Version: 1.0 X-Originating-IP: [192.168.14.192] X-detected-operating-system: by eggs.gnu.org: GNU/Linux 3.x X-Received-From: 195.59.15.196 Cc: pbonzini@redhat.com, leon.alrae@imgtec.com, afaerber@suse.de, aurelien@aurel32.net Subject: [Qemu-devel] [PATCH 3/4] mips: add Global Interrupt Controller X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.14 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: qemu-devel-bounces+incoming=patchwork.ozlabs.org@nongnu.org Sender: qemu-devel-bounces+incoming=patchwork.ozlabs.org@nongnu.org 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 --- 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 + * + * 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 */