diff options
author | Andreas Sandberg <Andreas.Sandberg@ARM.com> | 2012-10-25 14:05:24 +0100 |
---|---|---|
committer | Andreas Sandberg <Andreas.Sandberg@ARM.com> | 2012-10-25 14:05:24 +0100 |
commit | 81be8b9d157a038c1d8f3ebf7e2ec8eba0eac935 (patch) | |
tree | a10132c3d1938bab3cd79626aead978ca1760d20 /src/dev/arm/gic_pl390.cc | |
parent | b904bd5437ead0dfc2c4c0977f3d29d63299c601 (diff) | |
download | gem5-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.cc | 843 |
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 §ion) +{ + 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); +} |