summaryrefslogtreecommitdiff
path: root/src/dev/arm/gic_pl390.cc
diff options
context:
space:
mode:
authorAndreas Sandberg <Andreas.Sandberg@ARM.com>2012-10-25 14:05:24 +0100
committerAndreas Sandberg <Andreas.Sandberg@ARM.com>2012-10-25 14:05:24 +0100
commit81be8b9d157a038c1d8f3ebf7e2ec8eba0eac935 (patch)
treea10132c3d1938bab3cd79626aead978ca1760d20 /src/dev/arm/gic_pl390.cc
parentb904bd5437ead0dfc2c4c0977f3d29d63299c601 (diff)
downloadgem5-81be8b9d157a038c1d8f3ebf7e2ec8eba0eac935.tar.xz
arm: Create a GIC base class and make the PL390 derive from it
This patch moves the GIC interface to a separate base class and makes all interrupt devices use that base class instead of a pointer to the PL390 implementation. This allows us to have multiple GIC implementations. Future implementations will allow in-kernel GIC implementations when using hardware virtualization. --HG-- rename : src/dev/arm/gic.cc => src/dev/arm/gic_pl390.cc rename : src/dev/arm/gic.hh => src/dev/arm/gic_pl390.hh
Diffstat (limited to 'src/dev/arm/gic_pl390.cc')
-rw-r--r--src/dev/arm/gic_pl390.cc843
1 files changed, 843 insertions, 0 deletions
diff --git a/src/dev/arm/gic_pl390.cc b/src/dev/arm/gic_pl390.cc
new file mode 100644
index 000000000..0cd435938
--- /dev/null
+++ b/src/dev/arm/gic_pl390.cc
@@ -0,0 +1,843 @@
+/*
+ * Copyright (c) 2010 ARM Limited
+ * All rights reserved
+ *
+ * The license below extends only to copyright in the software and shall
+ * not be construed as granting a license to any other intellectual
+ * property including but not limited to intellectual property relating
+ * to a hardware implementation of the functionality of the software
+ * licensed hereunder. You may use the software subject to the license
+ * terms below provided that you ensure that this notice is replicated
+ * unmodified and in its entirety in all distributions of the software,
+ * modified or unmodified, in source code or in binary form.
+ *
+ * Copyright (c) 2005 The Regents of The University of Michigan
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met: redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer;
+ * redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution;
+ * neither the name of the copyright holders nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * Authors: Ali Saidi
+ * Prakash Ramrakhyani
+ */
+
+#include "base/trace.hh"
+#include "debug/Checkpoint.hh"
+#include "debug/GIC.hh"
+#include "debug/IPI.hh"
+#include "debug/Interrupt.hh"
+#include "dev/arm/gic_pl390.hh"
+#include "dev/arm/realview.hh"
+#include "dev/terminal.hh"
+#include "mem/packet.hh"
+#include "mem/packet_access.hh"
+
+Pl390::Pl390(const Params *p)
+ : BaseGic(p), distAddr(p->dist_addr),
+ cpuAddr(p->cpu_addr), distPioDelay(p->dist_pio_delay),
+ cpuPioDelay(p->cpu_pio_delay), intLatency(p->int_latency),
+ enabled(false), itLines(p->it_lines)
+{
+ itLinesLog2 = ceilLog2(itLines);
+
+ for (int x = 0; x < CPU_MAX; x++) {
+ cpuEnabled[x] = false;
+ cpuPriority[x] = 0xff;
+ cpuBpr[x] = 0;
+ // Initialize cpu highest int
+ cpuHighestInt[x] = SPURIOUS_INT;
+ postIntEvent[x] = new PostIntEvent(x, p->platform);
+ }
+ DPRINTF(Interrupt, "cpuEnabled[0]=%d cpuEnabled[1]=%d\n", cpuEnabled[0],
+ cpuEnabled[1]);
+
+ for (int x = 0; x < INT_BITS_MAX; x++) {
+ intEnabled[x] = 0;
+ pendingInt[x] = 0;
+ activeInt[x] = 0;
+ }
+
+ for (int x = 0; x < INT_LINES_MAX; x++) {
+ intPriority[x] = 0;
+ cpuTarget[x] = 0;
+ }
+
+ for (int x = 0; x < INT_BITS_MAX*2; x++) {
+ intConfig[x] = 0;
+ }
+
+ for (int x = 0; x < SGI_MAX; x++) {
+ cpuSgiActive[x] = 0;
+ cpuSgiPending[x] = 0;
+ }
+ for (int x = 0; x < CPU_MAX; x++) {
+ cpuPpiActive[x] = 0;
+ cpuPpiPending[x] = 0;
+ }
+
+ for (int i = 0; i < CPU_MAX; i++) {
+ for (int j = 0; j < (SGI_MAX + PPI_MAX); j++) {
+ bankedIntPriority[i][j] = 0;
+ }
+ }
+
+ RealView *rv = dynamic_cast<RealView*>(p->platform);
+ assert(rv);
+ rv->setGic(this);
+
+}
+
+Tick
+Pl390::read(PacketPtr pkt)
+{
+
+ Addr addr = pkt->getAddr();
+
+ if (addr >= distAddr && addr < distAddr + DIST_SIZE)
+ return readDistributor(pkt);
+ else if (addr >= cpuAddr && addr < cpuAddr + CPU_SIZE)
+ return readCpu(pkt);
+ else
+ panic("Read to unknown address %#x\n", pkt->getAddr());
+}
+
+
+Tick
+Pl390::write(PacketPtr pkt)
+{
+
+ Addr addr = pkt->getAddr();
+
+ if (addr >= distAddr && addr < distAddr + DIST_SIZE)
+ return writeDistributor(pkt);
+ else if (addr >= cpuAddr && addr < cpuAddr + CPU_SIZE)
+ return writeCpu(pkt);
+ else
+ panic("Write to unknown address %#x\n", pkt->getAddr());
+}
+
+Tick
+Pl390::readDistributor(PacketPtr pkt)
+{
+ Addr daddr = pkt->getAddr() - distAddr;
+ pkt->allocate();
+
+ int ctx_id = pkt->req->contextId();
+
+ DPRINTF(GIC, "gic distributor read register %#x\n", daddr);
+
+ if (daddr >= ICDISER_ST && daddr < ICDISER_ED + 4) {
+ assert((daddr-ICDISER_ST) >> 2 < 32);
+ pkt->set<uint32_t>(intEnabled[(daddr-ICDISER_ST)>>2]);
+ goto done;
+ }
+
+ if (daddr >= ICDICER_ST && daddr < ICDICER_ED + 4) {
+ assert((daddr-ICDICER_ST) >> 2 < 32);
+ pkt->set<uint32_t>(intEnabled[(daddr-ICDICER_ST)>>2]);
+ goto done;
+ }
+
+ if (daddr >= ICDISPR_ST && daddr < ICDISPR_ED + 4) {
+ assert((daddr-ICDISPR_ST) >> 2 < 32);
+ pkt->set<uint32_t>(pendingInt[(daddr-ICDISPR_ST)>>2]);
+ goto done;
+ }
+
+ if (daddr >= ICDICPR_ST && daddr < ICDICPR_ED + 4) {
+ assert((daddr-ICDICPR_ST) >> 2 < 32);
+ pkt->set<uint32_t>(pendingInt[(daddr-ICDICPR_ST)>>2]);
+ goto done;
+ }
+
+ if (daddr >= ICDABR_ST && daddr < ICDABR_ED + 4) {
+ assert((daddr-ICDABR_ST) >> 2 < 32);
+ pkt->set<uint32_t>(activeInt[(daddr-ICDABR_ST)>>2]);
+ goto done;
+ }
+
+ if (daddr >= ICDIPR_ST && daddr < ICDIPR_ED + 4) {
+ Addr int_num;
+ int_num = daddr - ICDIPR_ST;
+ assert(int_num < INT_LINES_MAX);
+ DPRINTF(Interrupt, "Reading interrupt priority at int# %#x \n",int_num);
+
+ uint8_t* int_p;
+ if (int_num < (SGI_MAX + PPI_MAX))
+ int_p = bankedIntPriority[ctx_id];
+ else
+ int_p = intPriority;
+
+ switch (pkt->getSize()) {
+ case 1:
+ pkt->set<uint8_t>(int_p[int_num]);
+ break;
+ case 2:
+ assert((int_num + 1) < INT_LINES_MAX);
+ pkt->set<uint16_t>(int_p[int_num] |
+ int_p[int_num+1] << 8);
+ break;
+ case 4:
+ assert((int_num + 3) < INT_LINES_MAX);
+ pkt->set<uint32_t>(int_p[int_num] |
+ int_p[int_num+1] << 8 |
+ int_p[int_num+2] << 16 |
+ int_p[int_num+3] << 24);
+ break;
+ default:
+ panic("Invalid size while reading priority regs in GIC: %d\n",
+ pkt->getSize());
+ }
+ goto done;
+ }
+
+ if (daddr >= ICDIPTR_ST && daddr < ICDIPTR_ED + 4) {
+ Addr int_num;
+ int_num = (daddr-ICDIPTR_ST) ;
+ DPRINTF(GIC, "Reading processor target register for int# %#x \n",
+ int_num);
+ assert(int_num < INT_LINES_MAX);
+
+ // First 31 interrupts only target single processor (SGI)
+ if (int_num > 31) {
+ if (pkt->getSize() == 1) {
+ pkt->set<uint8_t>(cpuTarget[int_num]);
+ } else {
+ assert(pkt->getSize() == 4);
+ int_num = mbits(int_num, 31, 2);
+ pkt->set<uint32_t>(cpuTarget[int_num] |
+ cpuTarget[int_num+1] << 8 |
+ cpuTarget[int_num+2] << 16 |
+ cpuTarget[int_num+3] << 24) ;
+ }
+ } else {
+ int ctx_id = pkt->req->contextId();
+ assert(ctx_id < sys->numRunningContexts());
+ pkt->set<uint32_t>(ctx_id);
+ }
+ goto done;
+ }
+
+ if (daddr >= ICDICFR_ST && daddr < ICDICFR_ED + 4) {
+ assert((daddr-ICDICFR_ST) >> 2 < 64);
+ /** @todo software generated interrutps and PPIs
+ * can't be configured in some ways
+ */
+ pkt->set<uint32_t>(intConfig[(daddr-ICDICFR_ST)>>2]);
+ goto done;
+ }
+
+ switch(daddr) {
+ case ICDDCR:
+ pkt->set<uint32_t>(enabled);
+ break;
+ case ICDICTR:
+ uint32_t tmp;
+ tmp = ((sys->numRunningContexts() - 1) << 5) |
+ (itLines/INT_BITS_MAX -1);
+ pkt->set<uint32_t>(tmp);
+ break;
+ default:
+ panic("Tried to read Gic distributor at offset %#x\n", daddr);
+ break;
+ }
+done:
+ pkt->makeAtomicResponse();
+ return distPioDelay;
+}
+
+Tick
+Pl390::readCpu(PacketPtr pkt)
+{
+ Addr daddr = pkt->getAddr() - cpuAddr;
+ pkt->allocate();
+
+ assert(pkt->req->hasContextId());
+ int ctx_id = pkt->req->contextId();
+ assert(ctx_id < sys->numRunningContexts());
+
+ DPRINTF(GIC, "gic cpu read register %#x cpu context: %d\n", daddr,
+ ctx_id);
+
+ switch(daddr) {
+ case ICCICR:
+ pkt->set<uint32_t>(cpuEnabled[ctx_id]);
+ break;
+ case ICCPMR:
+ pkt->set<uint32_t>(cpuPriority[ctx_id]);
+ break;
+ case ICCBPR:
+ pkt->set<uint32_t>(cpuBpr[ctx_id]);
+ break;
+ case ICCIAR:
+ if (enabled && cpuEnabled[ctx_id]) {
+ int active_int = cpuHighestInt[ctx_id];
+ IAR iar = 0;
+ iar.ack_id = active_int;
+ iar.cpu_id = 0;
+ if (active_int < SGI_MAX) {
+ // this is a software interrupt from another CPU
+ if (!cpuSgiPending[active_int])
+ panic("Interrupt %d active but no CPU generated it?\n",
+ active_int);
+ for (int x = 0; x < CPU_MAX; x++) {
+ // See which CPU generated the interrupt
+ uint8_t cpugen =
+ bits(cpuSgiPending[active_int], 7 + 8 * x, 8 * x);
+ if (cpugen & (1 << ctx_id)) {
+ iar.cpu_id = x;
+ break;
+ }
+ }
+ uint64_t sgi_num = ULL(1) << (ctx_id + 8 * iar.cpu_id);
+ cpuSgiActive[iar.ack_id] |= sgi_num;
+ cpuSgiPending[iar.ack_id] &= ~sgi_num;
+ } else if (active_int < (SGI_MAX + PPI_MAX) ) {
+ uint32_t int_num = 1 << (cpuHighestInt[ctx_id] - SGI_MAX);
+ cpuPpiActive[ctx_id] |= int_num;
+ updateRunPri();
+ cpuPpiPending[ctx_id] &= ~int_num;
+
+ } else {
+ uint32_t int_num = 1 << intNumToBit(cpuHighestInt[ctx_id]);
+ activeInt[intNumToWord(cpuHighestInt[ctx_id])] |= int_num;
+ updateRunPri();
+ pendingInt[intNumToWord(cpuHighestInt[ctx_id])] &= ~int_num;
+ }
+
+ DPRINTF(Interrupt,"CPU %d reading IAR.id=%d IAR.cpu=%d, iar=0x%x\n",
+ ctx_id, iar.ack_id, iar.cpu_id, iar);
+ cpuHighestInt[ctx_id] = SPURIOUS_INT;
+ updateIntState(-1);
+ pkt->set<uint32_t>(iar);
+ platform->intrctrl->clear(ctx_id, ArmISA::INT_IRQ, 0);
+ } else {
+ pkt->set<uint32_t>(SPURIOUS_INT);
+ }
+
+ break;
+ case ICCRPR:
+ pkt->set<uint32_t>(iccrpr[0]);
+ break;
+ case ICCHPIR:
+ pkt->set<uint32_t>(0);
+ panic("Need to implement HPIR");
+ break;
+ default:
+ panic("Tried to read Gic cpu at offset %#x\n", daddr);
+ break;
+ }
+ pkt->makeAtomicResponse();
+ return cpuPioDelay;
+}
+
+
+Tick
+Pl390::writeDistributor(PacketPtr pkt)
+{
+ Addr daddr = pkt->getAddr() - distAddr;
+ pkt->allocate();
+
+ assert(pkt->req->hasContextId());
+ int ctx_id = pkt->req->contextId();
+
+ DPRINTF(GIC, "gic distributor write register %#x size %#x value %#x \n",
+ daddr, pkt->getSize(), pkt->get<uint32_t>());
+
+ if (daddr >= ICDISER_ST && daddr < ICDISER_ED + 4) {
+ assert((daddr-ICDISER_ST) >> 2 < 32);
+ intEnabled[(daddr-ICDISER_ST) >> 2] |= pkt->get<uint32_t>();
+ goto done;
+ }
+
+ if (daddr >= ICDICER_ST && daddr < ICDICER_ED + 4) {
+ assert((daddr-ICDICER_ST) >> 2 < 32);
+ intEnabled[(daddr-ICDICER_ST) >> 2] &= ~pkt->get<uint32_t>();
+ goto done;
+ }
+
+ if (daddr >= ICDISPR_ST && daddr < ICDISPR_ED + 4) {
+ assert((daddr-ICDISPR_ST) >> 2 < 32);
+ pendingInt[(daddr-ICDISPR_ST) >> 2] |= pkt->get<uint32_t>();
+ pendingInt[0] &= SGI_MASK; // Don't allow SGIs to be changed
+ updateIntState((daddr-ICDISPR_ST) >> 2);
+ goto done;
+ }
+
+ if (daddr >= ICDICPR_ST && daddr < ICDICPR_ED + 4) {
+ assert((daddr-ICDICPR_ST) >> 2 < 32);
+ pendingInt[(daddr-ICDICPR_ST) >> 2] &= ~pkt->get<uint32_t>();
+ pendingInt[0] &= SGI_MASK; // Don't allow SGIs to be changed
+ updateIntState((daddr-ICDICPR_ST) >> 2);
+ goto done;
+ }
+
+ if (daddr >= ICDIPR_ST && daddr < ICDIPR_ED + 4) {
+ Addr int_num = daddr - ICDIPR_ST;
+ assert(int_num < INT_LINES_MAX);
+ uint8_t* int_p;
+ if (int_num < (SGI_MAX + PPI_MAX))
+ int_p = bankedIntPriority[ctx_id];
+ else
+ int_p = intPriority;
+ uint32_t tmp;
+ switch(pkt->getSize()) {
+ case 1:
+ tmp = pkt->get<uint8_t>();
+ int_p[int_num] = bits(tmp, 7, 0);
+ break;
+ case 2:
+ tmp = pkt->get<uint16_t>();
+ int_p[int_num] = bits(tmp, 7, 0);
+ int_p[int_num + 1] = bits(tmp, 15, 8);
+ break;
+ case 4:
+ tmp = pkt->get<uint32_t>();
+ int_p[int_num] = bits(tmp, 7, 0);
+ int_p[int_num + 1] = bits(tmp, 15, 8);
+ int_p[int_num + 2] = bits(tmp, 23, 16);
+ int_p[int_num + 3] = bits(tmp, 31, 24);
+ break;
+ default:
+ panic("Invalid size when writing to priority regs in Gic: %d\n",
+ pkt->getSize());
+ }
+
+ updateIntState(-1);
+ updateRunPri();
+ goto done;
+ }
+
+ if (daddr >= ICDIPTR_ST && daddr < ICDIPTR_ED + 4) {
+ Addr int_num = (daddr-ICDIPTR_ST) ;
+ assert(int_num < INT_LINES_MAX);
+ // First 31 interrupts only target single processor
+ if (int_num >= SGI_MAX) {
+ if (pkt->getSize() == 1) {
+ uint8_t tmp = pkt->get<uint8_t>();
+ cpuTarget[int_num] = tmp & 0xff;
+ } else {
+ assert (pkt->getSize() == 4);
+ int_num = mbits(int_num, 31, 2);
+ uint32_t tmp = pkt->get<uint32_t>();
+ cpuTarget[int_num] = bits(tmp, 7, 0);
+ cpuTarget[int_num+1] = bits(tmp, 15, 8);
+ cpuTarget[int_num+2] = bits(tmp, 23, 16);
+ cpuTarget[int_num+3] = bits(tmp, 31, 24);
+ }
+ updateIntState((daddr-ICDIPTR_ST)>>2);
+ }
+ goto done;
+ }
+
+ if (daddr >= ICDICFR_ST && daddr < ICDICFR_ED + 4) {
+ assert((daddr-ICDICFR_ST) >> 2 < 64);
+ intConfig[(daddr-ICDICFR_ST)>>2] = pkt->get<uint32_t>();
+ if (pkt->get<uint32_t>() & NN_CONFIG_MASK)
+ warn("GIC N:N mode selected and not supported at this time\n");
+ goto done;
+ }
+
+ switch(daddr) {
+ case ICDDCR:
+ enabled = pkt->get<uint32_t>();
+ DPRINTF(Interrupt, "Distributor enable flag set to = %d\n", enabled);
+ break;
+ case ICDSGIR:
+ softInt(ctx_id, pkt->get<uint32_t>());
+ break;
+ default:
+ panic("Tried to write Gic distributor at offset %#x\n", daddr);
+ break;
+ }
+
+done:
+ pkt->makeAtomicResponse();
+ return distPioDelay;
+}
+
+Tick
+Pl390::writeCpu(PacketPtr pkt)
+{
+ Addr daddr = pkt->getAddr() - cpuAddr;
+ pkt->allocate();
+
+ assert(pkt->req->hasContextId());
+ int ctx_id = pkt->req->contextId();
+ IAR iar;
+
+ DPRINTF(GIC, "gic cpu write register cpu:%d %#x val: %#x\n",
+ ctx_id, daddr, pkt->get<uint32_t>());
+
+ switch(daddr) {
+ case ICCICR:
+ cpuEnabled[ctx_id] = pkt->get<uint32_t>();
+ break;
+ case ICCPMR:
+ cpuPriority[ctx_id] = pkt->get<uint32_t>();
+ break;
+ case ICCBPR:
+ cpuBpr[ctx_id] = pkt->get<uint32_t>();
+ break;
+ case ICCEOIR:
+ iar = pkt->get<uint32_t>();
+ if (iar.ack_id < SGI_MAX) {
+ // Clear out the bit that corrseponds to the cleared int
+ uint64_t clr_int = ULL(1) << (ctx_id + 8 * iar.cpu_id);
+ if (!(cpuSgiActive[iar.ack_id] & clr_int))
+ panic("Done handling a SGI that isn't active?\n");
+ cpuSgiActive[iar.ack_id] &= ~clr_int;
+ } else if (iar.ack_id < (SGI_MAX + PPI_MAX) ) {
+ uint32_t int_num = 1 << (iar.ack_id - SGI_MAX);
+ if (!(cpuPpiActive[ctx_id] & int_num))
+ panic("CPU %d Done handling a PPI interrupt that isn't active?\n", ctx_id);
+ cpuPpiActive[ctx_id] &= ~int_num;
+ } else {
+ uint32_t int_num = 1 << intNumToBit(iar.ack_id);
+ if (!(activeInt[intNumToWord(iar.ack_id)] & int_num))
+ panic("Done handling interrupt that isn't active?\n");
+ activeInt[intNumToWord(iar.ack_id)] &= ~int_num;
+ }
+ updateRunPri();
+ DPRINTF(Interrupt, "CPU %d done handling intr IAR = %d from cpu %d\n",
+ ctx_id, iar.ack_id, iar.cpu_id);
+ break;
+ default:
+ panic("Tried to write Gic cpu at offset %#x\n", daddr);
+ break;
+ }
+ if (cpuEnabled[ctx_id]) updateIntState(-1);
+ pkt->makeAtomicResponse();
+ return cpuPioDelay;
+}
+
+void
+Pl390::softInt(int ctx_id, SWI swi)
+{
+ switch (swi.list_type) {
+ case 1:
+ // interrupt all
+ uint8_t cpu_list;
+ cpu_list = 0;
+ for (int x = 0; x < CPU_MAX; x++)
+ cpu_list |= cpuEnabled[x] ? 1 << x : 0;
+ swi.cpu_list = cpu_list;
+ break;
+ case 2:
+ // interrupt requesting cpu only
+ swi.cpu_list = 1 << ctx_id;
+ break;
+ // else interrupt cpus specified
+ }
+
+ DPRINTF(IPI, "Generating softIRQ from CPU %d for %#x\n", ctx_id,
+ swi.cpu_list);
+ for (int i = 0; i < CPU_MAX; i++) {
+ DPRINTF(IPI, "Processing CPU %d\n", i);
+ if (!cpuEnabled[i])
+ continue;
+ if (swi.cpu_list & (1 << i))
+ cpuSgiPending[swi.sgi_id] |= (1 << i) << (8 * ctx_id);
+ DPRINTF(IPI, "SGI[%d]=%#x\n", swi.sgi_id, cpuSgiPending[swi.sgi_id]);
+ }
+ updateIntState(-1);
+}
+
+uint64_t
+Pl390::genSwiMask(int cpu)
+{
+ if (cpu > 7)
+ panic("Invalid CPU ID\n");
+ return ULL(0x0101010101010101) << cpu;
+}
+
+void
+Pl390::updateIntState(int hint)
+{
+ for (int cpu = 0; cpu < CPU_MAX; cpu++) {
+ if (!cpuEnabled[cpu])
+ continue;
+ if (cpu >= sys->numContexts())
+ break;
+
+ /*@todo use hint to do less work. */
+ int highest_int = SPURIOUS_INT;
+ // Priorities below that set in ICCPMR can be ignored
+ uint8_t highest_pri = cpuPriority[cpu];
+
+ // Check SGIs
+ for (int swi = 0; swi < SGI_MAX; swi++) {
+ if (!cpuSgiPending[swi])
+ continue;
+ if (cpuSgiPending[swi] & genSwiMask(cpu))
+ if (highest_pri > bankedIntPriority[cpu][swi]) {
+ highest_pri = bankedIntPriority[cpu][swi];
+ highest_int = swi;
+ }
+ }
+
+ // Check PPIs
+ if (cpuPpiPending[cpu]) {
+ for (int ppi = 0; ppi < PPI_MAX; ppi++) {
+ if (cpuPpiPending[cpu] & (1 << ppi))
+ if (highest_pri > bankedIntPriority[cpu][SGI_MAX + ppi]) {
+ highest_pri = bankedIntPriority[cpu][SGI_MAX + ppi];
+ highest_int = SGI_MAX + ppi;
+ }
+ }
+ }
+
+ bool mp_sys = sys->numRunningContexts() > 1;
+ // Check other ints
+ for (int x = 0; x < (itLines/INT_BITS_MAX); x++) {
+ if (intEnabled[x] & pendingInt[x]) {
+ for (int y = 0; y < INT_BITS_MAX; y++) {
+ uint32_t int_nm = x * INT_BITS_MAX + y;
+ DPRINTF(GIC, "Checking for interrupt# %d \n",int_nm);
+ /* Set current pending int as highest int for current cpu
+ if the interrupt's priority higher than current prioirty
+ and if currrent cpu is the target (for mp configs only)
+ */
+ if ((bits(intEnabled[x], y) & bits(pendingInt[x], y)) &&
+ (intPriority[int_nm] < highest_pri))
+ if ( (!mp_sys) || (cpuTarget[int_nm] & (1 << cpu))) {
+ highest_pri = intPriority[int_nm];
+ highest_int = int_nm;
+ }
+ }
+ }
+ }
+
+ cpuHighestInt[cpu] = highest_int;
+
+ if (highest_int == SPURIOUS_INT)
+ continue;
+
+ /* @todo make this work for more than one cpu, need to handle 1:N, N:N
+ * models */
+ if (enabled && cpuEnabled[cpu] && (highest_pri < cpuPriority[cpu]) &&
+ !(activeInt[intNumToWord(highest_int)]
+ & (1 << intNumToBit(highest_int)))) {
+
+ DPRINTF(Interrupt, "Posting interrupt %d to cpu%d\n", highest_int,
+ cpu);
+ postInt(cpu, curTick() + intLatency);
+ }
+ }
+}
+
+void
+Pl390::updateRunPri()
+{
+ for (int cpu = 0; cpu < CPU_MAX; cpu++) {
+ if (!cpuEnabled[cpu])
+ continue;
+ uint8_t maxPriority = 0xff;
+ for (int i = 0; i < itLines; i++){
+ if (i < SGI_MAX) {
+ if ((cpuSgiActive[i] & genSwiMask(cpu)) &&
+ (bankedIntPriority[cpu][i] < maxPriority))
+ maxPriority = bankedIntPriority[cpu][i];
+ } else if (i < (SGI_MAX + PPI_MAX)) {
+ if ((cpuPpiActive[cpu] & ( 1 << (i - SGI_MAX))) &&
+ (bankedIntPriority[cpu][i] < maxPriority))
+ maxPriority = bankedIntPriority[cpu][i];
+
+ } else {
+ if (activeInt[intNumToWord(i)] & (1 << intNumToBit(i)))
+ if (intPriority[i] < maxPriority)
+ maxPriority = intPriority[i];
+ }
+ }
+ iccrpr[cpu] = maxPriority;
+ }
+}
+
+void
+Pl390::sendInt(uint32_t num)
+{
+ DPRINTF(Interrupt, "Received Interupt number %d, cpuTarget %#x: \n",
+ num, cpuTarget[num]);
+ if (cpuTarget[num] & (cpuTarget[num] - 1))
+ panic("Multiple targets for peripheral interrupts is not supported\n");
+ pendingInt[intNumToWord(num)] |= 1 << intNumToBit(num);
+ updateIntState(intNumToWord(num));
+
+}
+
+void
+Pl390::sendPPInt(uint32_t num, uint32_t cpu)
+{
+ DPRINTF(Interrupt, "Received Interrupt number %d, cpuTarget %#x: \n",
+ num, cpu);
+ cpuPpiPending[cpu] |= 1 << (num - SGI_MAX);
+ updateIntState(intNumToWord(num));
+}
+
+void
+Pl390::clearInt(uint32_t number)
+{
+ /* @todo assume edge triggered only at the moment. Nothing to do. */
+}
+
+void
+Pl390::postInt(uint32_t cpu, Tick when)
+{
+ if (!(postIntEvent[cpu]->scheduled()))
+ eventq->schedule(postIntEvent[cpu], when);
+}
+
+AddrRangeList
+Pl390::getAddrRanges() const
+{
+ AddrRangeList ranges;
+ ranges.push_back(RangeSize(distAddr, DIST_SIZE));
+ ranges.push_back(RangeSize(cpuAddr, CPU_SIZE));
+ return ranges;
+}
+
+
+void
+Pl390::serialize(std::ostream &os)
+{
+ DPRINTF(Checkpoint, "Serializing Arm GIC\n");
+
+ SERIALIZE_SCALAR(distAddr);
+ SERIALIZE_SCALAR(cpuAddr);
+ SERIALIZE_SCALAR(distPioDelay);
+ SERIALIZE_SCALAR(cpuPioDelay);
+ SERIALIZE_SCALAR(enabled);
+ SERIALIZE_SCALAR(itLines);
+ SERIALIZE_SCALAR(itLinesLog2);
+ SERIALIZE_ARRAY(intEnabled, INT_BITS_MAX);
+ SERIALIZE_ARRAY(pendingInt, INT_BITS_MAX);
+ SERIALIZE_ARRAY(activeInt, INT_BITS_MAX);
+ SERIALIZE_ARRAY(iccrpr, CPU_MAX);
+ SERIALIZE_ARRAY(intPriority, INT_LINES_MAX);
+ SERIALIZE_ARRAY(cpuTarget, INT_LINES_MAX);
+ SERIALIZE_ARRAY(intConfig, INT_BITS_MAX * 2);
+ SERIALIZE_ARRAY(cpuEnabled, CPU_MAX);
+ SERIALIZE_ARRAY(cpuPriority, CPU_MAX);
+ SERIALIZE_ARRAY(cpuBpr, CPU_MAX);
+ SERIALIZE_ARRAY(cpuHighestInt, CPU_MAX);
+ SERIALIZE_ARRAY(cpuSgiActive, SGI_MAX);
+ SERIALIZE_ARRAY(cpuSgiPending, SGI_MAX);
+ SERIALIZE_ARRAY(cpuPpiActive, CPU_MAX);
+ SERIALIZE_ARRAY(cpuPpiPending, CPU_MAX);
+ SERIALIZE_ARRAY(*bankedIntPriority, CPU_MAX * (SGI_MAX + PPI_MAX));
+ SERIALIZE_SCALAR(irqEnable);
+ Tick interrupt_time[CPU_MAX];
+ for (uint32_t cpu = 0; cpu < CPU_MAX; cpu++) {
+ interrupt_time[cpu] = 0;
+ if (postIntEvent[cpu]->scheduled()) {
+ interrupt_time[cpu] = postIntEvent[cpu]->when();
+ }
+ }
+ SERIALIZE_ARRAY(interrupt_time, CPU_MAX);
+
+}
+
+void
+Pl390::unserialize(Checkpoint *cp, const std::string &section)
+{
+ DPRINTF(Checkpoint, "Unserializing Arm GIC\n");
+
+ UNSERIALIZE_SCALAR(distAddr);
+ UNSERIALIZE_SCALAR(cpuAddr);
+ UNSERIALIZE_SCALAR(distPioDelay);
+ UNSERIALIZE_SCALAR(cpuPioDelay);
+ UNSERIALIZE_SCALAR(enabled);
+ UNSERIALIZE_SCALAR(itLines);
+ UNSERIALIZE_SCALAR(itLinesLog2);
+ UNSERIALIZE_ARRAY(intEnabled, INT_BITS_MAX);
+ UNSERIALIZE_ARRAY(pendingInt, INT_BITS_MAX);
+ UNSERIALIZE_ARRAY(activeInt, INT_BITS_MAX);
+ UNSERIALIZE_ARRAY(iccrpr, CPU_MAX);
+ UNSERIALIZE_ARRAY(intPriority, INT_LINES_MAX);
+ UNSERIALIZE_ARRAY(cpuTarget, INT_LINES_MAX);
+ UNSERIALIZE_ARRAY(intConfig, INT_BITS_MAX * 2);
+ UNSERIALIZE_ARRAY(cpuEnabled, CPU_MAX);
+ UNSERIALIZE_ARRAY(cpuPriority, CPU_MAX);
+ UNSERIALIZE_ARRAY(cpuBpr, CPU_MAX);
+ UNSERIALIZE_ARRAY(cpuHighestInt, CPU_MAX);
+ UNSERIALIZE_ARRAY(cpuSgiActive, SGI_MAX);
+ UNSERIALIZE_ARRAY(cpuSgiPending, SGI_MAX);
+ UNSERIALIZE_ARRAY(cpuPpiActive, CPU_MAX);
+ UNSERIALIZE_ARRAY(cpuPpiPending, CPU_MAX);
+ UNSERIALIZE_ARRAY(*bankedIntPriority, CPU_MAX * (SGI_MAX + PPI_MAX));
+ UNSERIALIZE_SCALAR(irqEnable);
+
+ Tick interrupt_time[CPU_MAX];
+ UNSERIALIZE_ARRAY(interrupt_time, CPU_MAX);
+
+ for (uint32_t cpu = 0; cpu < CPU_MAX; cpu++) {
+ if (interrupt_time[cpu])
+ schedule(postIntEvent[cpu], interrupt_time[cpu]);
+ }
+
+}
+
+Pl390 *
+Pl390Params::create()
+{
+ return new Pl390(this);
+}
+
+/* Functions for debugging and testing */
+void
+Pl390::driveSPI(unsigned int spiVect)
+{
+ DPRINTF(GIC, "Received SPI Vector:%x Enable: %d\n", spiVect, irqEnable);
+ pendingInt[1] |= spiVect;
+ if (irqEnable && enabled) {
+ updateIntState(-1);
+ }
+}
+
+void
+Pl390::driveIrqEn( bool state)
+{
+ irqEnable = state;
+ DPRINTF(GIC, " Enabling Irq\n");
+ updateIntState(-1);
+}
+
+void
+Pl390::driveLegIRQ(bool state)
+{
+ if (irqEnable && !(!enabled && cpuEnabled[0])) {
+ if (state) {
+ DPRINTF(GIC, "Driving Legacy Irq\n");
+ platform->intrctrl->post(0, ArmISA::INT_IRQ, 0);
+ }
+ else platform->intrctrl->clear(0, ArmISA::INT_IRQ, 0);
+ }
+}
+
+void
+Pl390::driveLegFIQ(bool state)
+{
+ if (state)
+ platform->intrctrl->post(0, ArmISA::INT_FIQ, 0);
+ else platform->intrctrl->clear(0, ArmISA::INT_FIQ, 0);
+}