460 lines
13 KiB
C++
460 lines
13 KiB
C++
|
/*
|
||
|
* 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);
|
||
|
}
|