/* * 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); }