From 2f2f508c74b532ef4b9e083879a6c27b17e7bcb1 Mon Sep 17 00:00:00 2001 From: Adrian Herrera Date: Thu, 24 Oct 2019 12:47:22 +0100 Subject: [PATCH] dev-arm: add Watchdog Module SP805 model This provides a model of the Arm Watchdog Module SP805. This is based on the public TRM rev. r1p0 (ARM DDI 0270B). Integration test harness is not supported. Auto-generation of device tree entries is provided. Change-Id: I6157cec2212d0a1d2685bcfa983d2acbae1f3377 Reviewed-by: Giacomo Travaglini Reviewed-on: https://gem5-review.googlesource.com/c/public/gem5/+/24205 Maintainer: Giacomo Travaglini Tested-by: kokoro --- src/dev/arm/RealView.py | 28 ++++ src/dev/arm/SConscript | 2 + src/dev/arm/watchdog_sp805.cc | 274 ++++++++++++++++++++++++++++++++++ src/dev/arm/watchdog_sp805.hh | 135 +++++++++++++++++ 4 files changed, 439 insertions(+) create mode 100644 src/dev/arm/watchdog_sp805.cc create mode 100644 src/dev/arm/watchdog_sp805.hh diff --git a/src/dev/arm/RealView.py b/src/dev/arm/RealView.py index e773096b7..73f654ddf 100644 --- a/src/dev/arm/RealView.py +++ b/src/dev/arm/RealView.py @@ -402,6 +402,34 @@ class Sp804(AmbaPioDevice): clock1 = Param.Clock('1MHz', "Clock speed of the input") amba_id = 0x00141804 +class Sp805(AmbaIntDevice): + """ +Arm Watchdog Module (SP805) +Reference: + Arm Watchdog Module (SP805) - Technical Reference Manual - rev. r1p0 + Doc. ID: ARM DDI 0270B + """ + + type = 'Sp805' + cxx_header = 'dev/arm/watchdog_sp805.hh' + + amba_id = 0x00141805 + + def generateDeviceTree(self, state): + node = self.generateBasicPioDeviceNode(state, 'watchdog', + self.pio_addr, 0x1000, [int(self.int_num)]) + node.appendCompatible(['arm,sp805', 'arm,primecell']) + clocks = [state.phandle(self.clk_domain.unproxy(self))] + clock_names = ['wdogclk'] + platform = self._parent.unproxy(self) + if self in platform._off_chip_devices(): + clocks.append(state.phandle(platform.dcc.osc_smb)) + clock_names.append('apb_pclk') + node.append(FdtPropertyWords('clocks', clocks)) + node.append(FdtPropertyStrings('clock-names', clock_names)) + + yield node + class A9GlobalTimer(BasicPioDevice): type = 'A9GlobalTimer' cxx_header = "dev/arm/timer_a9global.hh" diff --git a/src/dev/arm/SConscript b/src/dev/arm/SConscript index c2df3ef51..66a368df0 100644 --- a/src/dev/arm/SConscript +++ b/src/dev/arm/SConscript @@ -81,6 +81,7 @@ if env['TARGET_ISA'] == 'arm': Source('smmu_v3_slaveifc.cc'); Source('smmu_v3_transl.cc'); Source('timer_sp804.cc') + Source('watchdog_sp805.cc') Source('gpu_nomali.cc') Source('pci_host.cc') Source('rv_ctrl.cc') @@ -104,6 +105,7 @@ if env['TARGET_ISA'] == 'arm': DebugFlag('RVCTRL') DebugFlag('SMMUv3') DebugFlag('SMMUv3Hazard') + DebugFlag('Sp805') DebugFlag('EnergyCtrl') DebugFlag('UFSHostDevice') DebugFlag('VGIC') 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); +} diff --git a/src/dev/arm/watchdog_sp805.hh b/src/dev/arm/watchdog_sp805.hh new file mode 100644 index 000000000..93654e7b7 --- /dev/null +++ b/src/dev/arm/watchdog_sp805.hh @@ -0,0 +1,135 @@ +/* + * 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 + */ + +#ifndef __DEV_ARM_WATCHDOG_SP805_HH__ +#define __DEV_ARM_WATCHDOG_SP805_HH__ + +#include "dev/arm/amba_device.hh" + +class Sp805Params; + +/** + * @file + * Arm Watchdog Module (SP805) + * Reference: + * Arm Watchdog Module (SP805) - Technical Reference Manual - rev. r1p0 + * Doc. ID: ARM DDI 0270B + */ +class Sp805 : public AmbaIntDevice +{ + public: + Sp805(Sp805Params const* params); + + void serialize(CheckpointOut &cp) const override; + void unserialize(CheckpointIn &cp) override; + + protected: + Tick read(PacketPtr pkt) override; + Tick write(PacketPtr pkt) override; + + private: + enum Offset : Addr { + WDOGLOAD = 0x000, + WDOGVALUE = 0x004, + WDOGCONTROL = 0x008, + WDOGINTCLR = 0x00c, + WDOGRIS = 0x010, + WDOGMIS = 0x014, + // 0x018 - 0xbfc -> Reserved + WDOGLOCK = 0xc00, + // 0xc04 - 0xefc -> Reserved + WDOGITCR = 0xf00, + WDOGITOP = 0xf04, + // 0xf08 - 0xfdc -> Reserved + // 0xfe0 - 0xfff -> CoreSight / Peripheral ID (AMBA ID) + }; + + /** Timeout interval (in cycles) as specified in WdogLoad */ + uint32_t timeoutInterval; + + /** Timeout start tick to keep track of the counter value */ + Tick timeoutStartTick; + + /** Value as persisted when the watchdog is stopped */ + uint32_t persistedValue; + + /** Indicates if watchdog (counter and interrupt) is enabled */ + bool enabled; + + /** Indicates if reset behaviour is enabled when counter reaches 0 */ + bool resetEnabled; + + /** Indicates if an interrupt has been raised by the counter reaching 0 */ + bool intRaised; + + /** Indicates if write access to registers is enabled */ + bool writeAccessEnabled; + + /** Indicates if integration test harness is enabled */ + bool integrationTestEnabled; + + /** Timeout event, triggered when the counter value reaches 0 */ + EventFunctionWrapper timeoutEvent; + + /** Returns the current counter value */ + uint32_t value(void) const; + + /** Triggered when value reaches 0 */ + void timeoutExpired(void); + + /** Restarts the counter to the current timeout interval */ + void restartCounter(void); + + /** Stops the counter when watchdog becomes disabled */ + void stopCounter(void); + + /** + * Raises an interrupt. If there is already a pending interrupt and + * reset behaviour is enabled, asserts system reset + */ + void sendInt(void); + + /** Clears any active interrupts */ + void clearInt(void); + + /** If written into WdogLock, registers are unlocked for writes */ + static constexpr uint32_t WDOGLOCK_MAGIC = 0x1acce551; +}; + +#endif // __DEV_ARM_WATCHDOG_SP805_HH__ -- 2.30.2