/* * 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(intEnabled[(daddr-ICDISER_ST)>>2]); goto done; } if (daddr >= ICDICER_ST && daddr < ICDICER_ED + 4) { assert((daddr-ICDICER_ST) >> 2 < 32); pkt->set(intEnabled[(daddr-ICDICER_ST)>>2]); goto done; } if (daddr >= ICDISPR_ST && daddr < ICDISPR_ED + 4) { assert((daddr-ICDISPR_ST) >> 2 < 32); pkt->set(pendingInt[(daddr-ICDISPR_ST)>>2]); goto done; } if (daddr >= ICDICPR_ST && daddr < ICDICPR_ED + 4) { assert((daddr-ICDICPR_ST) >> 2 < 32); pkt->set(pendingInt[(daddr-ICDICPR_ST)>>2]); goto done; } if (daddr >= ICDABR_ST && daddr < ICDABR_ED + 4) { assert((daddr-ICDABR_ST) >> 2 < 32); pkt->set(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(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(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(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(intConfig[(daddr-ICDICFR_ST)>>2]); goto done; } switch(daddr) { case ICDDCR: pkt->set(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(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(cpuEnabled[0]); break; case ICCPMR: pkt->set(cpuPriority[0]); break; case ICCBPR: pkt->set(cpuBpr[0]); break; case ICCIAR: DPRINTF(Interrupt, "CPU reading IAR = %d\n", cpuHighestInt[0]); pkt->set(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(0); panic("Need to implement RPR"); break; case ICCHPIR: pkt->set(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()); if (daddr >= ICDISER_ST && daddr < ICDISER_ED + 4) { assert((daddr-ICDISER_ST) >> 2 < 32); intEnabled[(daddr-ICDISER_ST)>>2] |= pkt->get(); goto done; } if (daddr >= ICDICER_ST && daddr < ICDICER_ED + 4) { assert((daddr-ICDICER_ST) >> 2 < 32); intEnabled[(daddr-ICDICER_ST)>>2] &= ~pkt->get(); goto done; } if (daddr >= ICDISPR_ST && daddr < ICDISPR_ED + 4) { assert((daddr-ICDISPR_ST) >> 2 < 32); pendingInt[(daddr-ICDISPR_ST)>>2] |= pkt->get(); 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(); 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(); 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(); 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(); goto done; } switch(daddr) { case ICDDCR: enabled = pkt->get(); break; case ICDSGIR: softInt(pkt->get()); 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()); switch(daddr) { case ICCICR: cpuEnabled[0] = pkt->get(); updateIntState(-1); break; case ICCPMR: cpuPriority[0] = pkt->get(); updateIntState(-1); break; case ICCBPR: cpuBpr[0] = pkt->get(); updateIntState(-1); break; case ICCEOIR: uint32_t tmp; tmp = pkt->get(); 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); }