diff options
Diffstat (limited to 'src/dev/arm/gic.cc')
-rw-r--r-- | src/dev/arm/gic.cc | 459 |
1 files changed, 459 insertions, 0 deletions
diff --git a/src/dev/arm/gic.cc b/src/dev/arm/gic.cc new file mode 100644 index 000000000..87d0d17b7 --- /dev/null +++ b/src/dev/arm/gic.cc @@ -0,0 +1,459 @@ +/* + * 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 + */ + +#include "base/trace.hh" +#include "cpu/intr_control.hh" +#include "dev/arm/gic.hh" +#include "dev/platform.hh" +#include "dev/terminal.hh" +#include "mem/packet.hh" +#include "mem/packet_access.hh" + +Gic::Gic(const Params *p) + : PioDevice(p),distAddr(p->dist_addr), cpuAddr(p->cpu_addr), + distPioDelay(p->dist_pio_delay), cpuPioDelay(p->cpu_pio_delay), + enabled(false), itLines(p->it_lines) +{ + itLinesLog2 = ceilLog2(itLines); + + for (int x = 0; x < 8; x++) { + cpuEnabled[x] = false; + cpuPriority[x] = 0; + cpuBpr[x] = 0; + } + + for (int x = 0; x < 32; x++) { + intEnabled[x] = 0; + pendingInt[x] = 0; + activeInt[x] = 0; + } + + for (int x = 0; x < 1020; x++) { + intPriority[x] = 0; + cpuTarget[x] = 0; + } + + for (int x = 0; x < 64; x++) { + intConfig[x] = 0; + } +} + +Tick +Gic::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 +Gic::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 +Gic::readDistributor(PacketPtr pkt) +{ + Addr daddr = pkt->getAddr() - distAddr; + pkt->allocate(); + + DPRINTF(Interrupt, "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) << 2; + assert(int_num < 1020); + + pkt->set<uint32_t>(intPriority[int_num] | + intPriority[int_num+1] << 8 | + intPriority[int_num+2] << 16 | + intPriority[int_num+3] << 24) ; + goto done; + } + + if (daddr >= ICDIPTR_ST && daddr < ICDIPTR_ED + 4) { + Addr int_num; + int_num = (daddr-ICDIPTR_ST) << 2; + assert(int_num < 1020); + + // First 31 interrupts only target single processor + if (int_num > 31) { + pkt->set<uint32_t>(cpuTarget[int_num] | + cpuTarget[int_num+1] << 8 | + cpuTarget[int_num+2] << 16 | + cpuTarget[int_num+3] << 24) ; + } else { + /** @todo should be processor id */ + pkt->set<uint32_t>(0); + } + 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: + /* @todo this needs to refelct the number of CPUs in the system */ + uint32_t tmp; + tmp = 0 << 5 | // cpu number + (itLines/32 -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 +Gic::readCpu(PacketPtr pkt) +{ + Addr daddr = pkt->getAddr() - cpuAddr; + pkt->allocate(); + + DPRINTF(Interrupt, "gic cpu read register %#x\n", daddr); + + switch(daddr) { + case ICCICR: + pkt->set<uint32_t>(cpuEnabled[0]); + break; + case ICCPMR: + pkt->set<uint32_t>(cpuPriority[0]); + break; + case ICCBPR: + pkt->set<uint32_t>(cpuBpr[0]); + break; + case ICCIAR: + DPRINTF(Interrupt, "CPU reading IAR = %d\n", cpuHighestInt[0]); + pkt->set<uint32_t>(cpuHighestInt[0]); + activeInt[intNumToWord(cpuHighestInt[0])] |= + 1 << intNumToBit(cpuHighestInt[0]); + pendingInt[intNumToWord(cpuHighestInt[0])] &= + ~(1 << intNumToBit(cpuHighestInt[0])); + cpuHighestInt[0] = SPURIOUS_INT; + updateIntState(-1); + platform->intrctrl->clear(0, ArmISA::INT_IRQ, 0); + break; + case ICCRPR: + pkt->set<uint32_t>(0); + panic("Need to implement RPR"); + 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 +Gic::writeDistributor(PacketPtr pkt) +{ + Addr daddr = pkt->getAddr() - distAddr; + pkt->allocate(); + + DPRINTF(Interrupt, "gic distributor write register %#x val: %#x\n", + daddr, 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>(); + 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>(); + updateIntState((daddr-ICDICPR_ST)>>2); + goto done; + } + + if (daddr >= ICDIPR_ST && daddr < ICDIPR_ED + 4) { + Addr int_num = (daddr-ICDIPR_ST) << 2; + assert(int_num < 1020); + uint32_t tmp = pkt->get<uint32_t>(); + intPriority[int_num] = tmp & 0xff; + intPriority[int_num+1] = (tmp >> 8) & 0xff; + intPriority[int_num+2] = (tmp >> 16) & 0xff; + intPriority[int_num+3] = (tmp >> 24) & 0xff; + updateIntState((daddr-ICDIPR_ST)>>2); + goto done; + } + + if (daddr >= ICDIPTR_ST && daddr < ICDIPTR_ED + 4) { + Addr int_num = (daddr-ICDIPTR_ST) << 2; + assert(int_num < 1020); + + // First 31 interrupts only target single processor + if (int_num > 31) { + uint32_t tmp = pkt->get<uint32_t>(); + cpuTarget[int_num] = tmp & 0xff; + cpuTarget[int_num+1] = (tmp >> 8) & 0xff; + cpuTarget[int_num+2] = (tmp >> 16) & 0xff; + cpuTarget[int_num+3] = (tmp >> 24) & 0xff; + 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>(); + goto done; + } + + switch(daddr) { + case ICDDCR: + enabled = pkt->get<uint32_t>(); + break; + case ICDSGIR: + softInt(pkt->get<uint32_t>()); + break; + default: + panic("Tried to write Gic distributor at offset %#x\n", daddr); + break; + } + +done: + pkt->makeAtomicResponse(); + return distPioDelay; +} + +Tick +Gic::writeCpu(PacketPtr pkt) +{ + Addr daddr = pkt->getAddr() - cpuAddr; + pkt->allocate(); + + DPRINTF(Interrupt, "gic cpu write register %#x val: %#x\n", + daddr, pkt->get<uint32_t>()); + + switch(daddr) { + case ICCICR: + cpuEnabled[0] = pkt->get<uint32_t>(); + updateIntState(-1); + break; + case ICCPMR: + cpuPriority[0] = pkt->get<uint32_t>(); + updateIntState(-1); + break; + case ICCBPR: + cpuBpr[0] = pkt->get<uint32_t>(); + updateIntState(-1); + break; + case ICCEOIR: + uint32_t tmp; + tmp = pkt->get<uint32_t>(); + if (!(activeInt[intNumToWord(tmp)] & (1 << intNumToBit(tmp)))) + panic("Done handling interrupt that isn't active?\n"); + activeInt[intNumToWord(tmp)] &= ~(1 << intNumToBit(tmp)); + DPRINTF(Interrupt, "CPU done handling interrupt IAR = %d\n", tmp); + break; + default: + panic("Tried to write Gic cpu at offset %#x\n", daddr); + break; + } + pkt->makeAtomicResponse(); + return cpuPioDelay; +} + +void +Gic::softInt(SWI swi) +{ + warn("Should be causing software interrupt"); +} + +void +Gic::updateIntState(int hint) +{ + /*@todo use hint to do less work. */ + int highest_int = -1; + uint8_t highest_pri = 0xff; + + for (int x = 0; x < itLinesLog2; x++) { + if (intEnabled[x] & pendingInt[x]) { + for (int y = 0; y < 32; y++) { + if (bits(intEnabled[x], y) & bits(pendingInt[x], y)) + if (intPriority[x*32+y] < highest_pri) { + highest_pri = intPriority[x*32+y]; + highest_int = x*32 + y; + } + } + } + } + + if (highest_int == -1) + return; + + cpuHighestInt[0] = highest_int; + + + /* @todo make this work for more than one cpu, need to handle 1:N, N:N + * models */ + if (cpuEnabled[0] && highest_pri < cpuPriority[0]) { + /* @todo delay interrupt by some time to deal with calculation delay */ + /* @todo only interrupt if we've haven't already interrupted for this + * int !!!!!!!!!! */ + DPRINTF(Interrupt, "Posting interrupt %d to cpu0\n", highest_int); + platform->intrctrl->post(0, ArmISA::INT_IRQ, 0); + } +} + + +void +Gic::sendInt(uint32_t num) +{ + DPRINTF(Interrupt, "Received Interupt number %d\n", num); + pendingInt[intNumToWord(num)] |= 1 << intNumToBit(num); + updateIntState(intNumToWord(num)); + +} + +void +Gic::clearInt(uint32_t number) +{ + /* @todo assume edge triggered only at the moment. Nothing to do. */ +} + +void +Gic::addressRanges(AddrRangeList &range_list) +{ + range_list.clear(); + range_list.push_back(RangeSize(distAddr, DIST_SIZE)); + range_list.push_back(RangeSize(cpuAddr, CPU_SIZE)); +} + + +void +Gic::serialize(std::ostream &os) +{ + panic("Need to implement serialization\n"); +} + +void +Gic::unserialize(Checkpoint *cp, const std::string §ion) +{ + panic("Need to implement serialization\n"); +} + +Gic * +GicParams::create() +{ + return new Gic(this); +} |