diff options
Diffstat (limited to 'src/dev/arm/watchdog_sp805.cc')
-rw-r--r-- | src/dev/arm/watchdog_sp805.cc | 274 |
1 files changed, 274 insertions, 0 deletions
diff --git a/src/dev/arm/watchdog_sp805.cc b/src/dev/arm/watchdog_sp805.cc new file mode 100644 index 000000000..1cf4a3643 --- /dev/null +++ b/src/dev/arm/watchdog_sp805.cc @@ -0,0 +1,274 @@ +/* + * Copyright (c) 2020 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. + * + * 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: Adrian Herrera + */ + +#include "dev/arm/watchdog_sp805.hh" + +#include "base/logging.hh" +#include "debug/Sp805.hh" +#include "mem/packet_access.hh" +#include "params/Sp805.hh" + +Sp805::Sp805(Sp805Params const* params) + : AmbaIntDevice(params, 0x1000), + timeoutInterval(0xffffffff), + timeoutStartTick(MaxTick), + persistedValue(timeoutInterval), + enabled(false), + resetEnabled(false), + intRaised(false), + writeAccessEnabled(true), + integrationTestEnabled(false), + timeoutEvent([this] { timeoutExpired(); }, name()) +{ +} + +Tick +Sp805::read(PacketPtr pkt) +{ + const Addr addr = pkt->getAddr() - pioAddr; + const size_t size = pkt->getSize(); + panic_if(size != 4, "Sp805::read: Invalid size %i\n", size); + + uint64_t resp = 0; + switch (addr) { + case WDOGLOAD: + resp = timeoutInterval; + break; + case WDOGVALUE: + resp = value(); + break; + case WDOGCONTROL: + resp = enabled | (resetEnabled << 1); + break; + case WDOGINTCLR: + warn("Sp805::read: WO reg (0x%x) [WDOGINTCLR]\n", addr); + break; + case WDOGRIS: + resp = intRaised; + break; + case WDOGMIS: + resp = intRaised & enabled; + break; + case WDOGLOCK: + resp = writeAccessEnabled; + break; + case WDOGITCR: + resp = integrationTestEnabled; + break; + case WDOGITOP: + warn("Sp805::read: WO reg (0x%x) [WDOGITOP]\n", addr); + break; + default: + if (readId(pkt, ambaId, pioAddr)) + resp = pkt->getUintX(LittleEndianByteOrder); + else + warn("Sp805::read: Unexpected address (0x%x:%i), assuming RAZ\n", + addr, size); + } + + DPRINTF(Sp805, "Sp805::read: 0x%x<-0x%x(%i)\n", resp, addr, size); + + pkt->setUintX(resp, LittleEndianByteOrder); + pkt->makeResponse(); + return pioDelay; +} + +Tick +Sp805::write(PacketPtr pkt) +{ + const Addr addr = pkt->getAddr() - pioAddr; + const size_t size = pkt->getSize(); + panic_if(size != 4, "Sp805::write: Invalid size %i\n", size); + + uint64_t data = pkt->getUintX(LittleEndianByteOrder); + switch (addr) { + case WDOGLOAD: + if (writeAccessEnabled) { + // When WdogLoad is written 0x0, immediately trigger an interrupt + if (!timeoutInterval) + sendInt(); + else + timeoutInterval = data; + if (enabled) + restartCounter(); + } + break; + case WDOGVALUE: + warn("Sp805::write: RO reg (0x%x) [WDOGVALUE]\n", addr); + break; + case WDOGCONTROL: + if (writeAccessEnabled) { + bool was_enabled = enabled; + enabled = bits(data, 0); + resetEnabled = bits(data, 1); + // If watchdog becomes enabled, restart the counter + if (!was_enabled && enabled) + restartCounter(); + // If watchdog becomes disabled, stop the counter + else if (timeoutEvent.scheduled() && !enabled) + stopCounter(); + } + break; + case WDOGINTCLR: + if (writeAccessEnabled) { + // Clear the interrupt and restart the counter if enabled + clearInt(); + if (enabled) + restartCounter(); + } + break; + case WDOGRIS: + warn("Sp805::write: RO reg (0x%x) [WDOGRIS]\n", addr); + break; + case WDOGMIS: + warn("Sp805::write: RO reg (0x%x) [WDOGMIS]\n", addr); + break; + case WDOGLOCK: + writeAccessEnabled = (data == WDOGLOCK_MAGIC); + break; + case WDOGITCR ... WDOGITOP: + warn("Sp805::write: No support for integration test harness\n"); + break; + default: + warn("Sp805::write: Unexpected address (0x%x:%i), assuming WI\n", + addr, size); + } + + DPRINTF(Sp805, "Sp805::write: 0x%x->0x%x(%i)\n", data, addr, size); + + pkt->makeResponse(); + return pioDelay; +} + +uint32_t +Sp805::value() const +{ + return timeoutEvent.scheduled() ? timeoutInterval - + ((timeoutEvent.when() - timeoutStartTick) / clockPeriod()) + : persistedValue; +} + +void +Sp805::timeoutExpired() +{ + timeoutStartTick = MaxTick; + sendInt(); + restartCounter(); +} + +void +Sp805::restartCounter() +{ + reschedule(timeoutEvent, clockEdge(Cycles(timeoutInterval)), true); + timeoutStartTick = curTick(); +} + +void +Sp805::stopCounter() +{ + persistedValue = value(); + deschedule(timeoutEvent); + timeoutStartTick = MaxTick; +} + +void +Sp805::sendInt() +{ + // If the previously sent interrupt has not been served, + // assert system reset if enabled + if (intRaised & enabled) { + if (resetEnabled) + warn("Watchdog timed out, system reset asserted\n"); + } else { + intRaised = true; + gic->sendInt(intNum); + } +} + +void +Sp805::clearInt() +{ + intRaised = false; + gic->clearInt(intNum); +} + +void +Sp805::serialize(CheckpointOut &cp) const +{ + SERIALIZE_SCALAR(timeoutInterval); + SERIALIZE_SCALAR(timeoutStartTick); + SERIALIZE_SCALAR(persistedValue); + SERIALIZE_SCALAR(enabled); + SERIALIZE_SCALAR(resetEnabled); + SERIALIZE_SCALAR(intRaised); + SERIALIZE_SCALAR(writeAccessEnabled); + SERIALIZE_SCALAR(integrationTestEnabled); + + bool ev_scheduled = timeoutEvent.scheduled(); + SERIALIZE_SCALAR(ev_scheduled); + if (ev_scheduled) + SERIALIZE_SCALAR(timeoutEvent.when()); +} + +void +Sp805::unserialize(CheckpointIn &cp) +{ + UNSERIALIZE_SCALAR(timeoutInterval); + UNSERIALIZE_SCALAR(timeoutStartTick); + UNSERIALIZE_SCALAR(persistedValue); + UNSERIALIZE_SCALAR(enabled); + UNSERIALIZE_SCALAR(resetEnabled); + UNSERIALIZE_SCALAR(intRaised); + UNSERIALIZE_SCALAR(writeAccessEnabled); + UNSERIALIZE_SCALAR(integrationTestEnabled); + + bool ev_scheduled; + UNSERIALIZE_SCALAR(ev_scheduled); + if (ev_scheduled) { + Tick when; + UNSERIALIZE_SCALAR(when); + reschedule(timeoutEvent, when, true); + } +} + +Sp805 * +Sp805Params::create() +{ + return new Sp805(this); +} |