blob: 33616652a8f526976e5db61bbd7406443f2abc5b [file] [log] [blame]
/*
* 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.
*/
#include "dev/arm/watchdog_generic.hh"
#include "dev/arm/base_gic.hh"
#include "params/GenericWatchdog.hh"
GenericWatchdog::GenericWatchdog(const GenericWatchdogParams &p)
: PioDevice(p),
timeoutEvent([this]{ timeout(); }, name()),
controlStatus(0),
offset(0),
compare(0),
iidr(0),
refreshFrame(p.refresh_start, p.refresh_start + 0x10000),
controlFrame(p.control_start, p.control_start + 0x10000),
pioLatency(p.pio_latency),
cnt(*p.system_counter),
cntListener(*this),
ws0(p.ws0->get()),
ws1(p.ws1->get())
{
cnt.registerListener(&cntListener);
}
AddrRangeList
GenericWatchdog::getAddrRanges() const
{
AddrRangeList ranges;
ranges.push_back(refreshFrame);
ranges.push_back(controlFrame);
return ranges;
}
Tick
GenericWatchdog::read(PacketPtr pkt)
{
const Addr addr = pkt->getAddr();
const size_t size = pkt->getSize();
panic_if(size != 4, "GenericWatchdog::read: Invalid size %i\n", size);
uint32_t resp = 0;
if (refreshFrame.contains(addr)) {
resp = readRefresh(addr);
} else if (controlFrame.contains(addr)) {
resp = readControl(addr);
} else {
panic("%s unknown address %#x\n", __func__, addr);
}
pkt->setUintX(resp, ByteOrder::little);
pkt->makeResponse();
return pioLatency;
}
uint32_t
GenericWatchdog::readRefresh(Addr addr)
{
const auto daddr = static_cast<RefreshOffset>(
addr - refreshFrame.start());
switch (daddr) {
case RefreshOffset::WRR:
// A read of the refresh register has no effect and returns 0
return 0;
case RefreshOffset::W_IIDR:
return iidr;
default:
panic("%s unknown address %#x\n", __func__, addr);
}
}
uint32_t
GenericWatchdog::readControl(Addr addr)
{
const auto daddr = static_cast<ControlOffset>(
addr - controlFrame.start());
switch (daddr) {
case ControlOffset::WCS:
return controlStatus;
case ControlOffset::WOR:
return offset;
case ControlOffset::WCV_LO:
return bits(compare, 31, 0);
case ControlOffset::WCV_HI:
return bits(compare, 63, 32);
case ControlOffset::W_IIDR:
return iidr;
default:
panic("%s unknown address %#x\n", __func__, addr);
}
}
Tick
GenericWatchdog::write(PacketPtr pkt)
{
const Addr addr = pkt->getAddr();
const size_t size = pkt->getSize();
panic_if(size != 4, "GenericWatchdog::write: Invalid size %i\n", size);
uint32_t data = pkt->getUintX(ByteOrder::little);
if (refreshFrame.contains(addr)) {
writeRefresh(addr, data);
} else if (controlFrame.contains(addr)) {
writeControl(addr, data);
} else {
panic("%s unknown address %#x\n", __func__, addr);
}
pkt->makeResponse();
return pioLatency;
}
void
GenericWatchdog::writeRefresh(Addr addr, uint32_t data)
{
const auto daddr = static_cast<RefreshOffset>(
addr - refreshFrame.start());
switch (daddr) {
case RefreshOffset::WRR:
explicitRefresh();
break;
default:
panic("%s unknown address %#x\n", __func__, addr);
}
}
void
GenericWatchdog::writeControl(Addr addr, uint32_t data)
{
const auto daddr = static_cast<ControlOffset>(
addr - controlFrame.start());
switch (daddr) {
case ControlOffset::WCS:
controlStatus = data & 0x1;
explicitRefresh();
break;
case ControlOffset::WOR:
offset = data;
explicitRefresh();
break;
case ControlOffset::WCV_LO:
compare = insertBits(compare, 31, 0, data);
break;
case ControlOffset::WCV_HI:
compare = insertBits(compare, 63, 32, data);
break;
default:
panic("%s unknown address %#x\n", __func__, addr);
}
}
void
GenericWatchdog::explicitRefresh()
{
// Watchdog signals are cleared in case of an explicit refresh
controlStatus.ws0 = 0;
controlStatus.ws1 = 0;
ws0->clear();
ws1->clear();
refresh();
}
void
GenericWatchdog::refresh()
{
// Update compare value
compare = cnt.value() + offset;
// Ask the System Counter how long we have to wait until
// it reaches the new compare value
Tick timeout_time = cnt.whenValue(compare);
reschedule(timeoutEvent, timeout_time, true);
}
void
GenericWatchdog::timeout()
{
if (!controlStatus.enabled)
return;
if (!controlStatus.ws0) {
controlStatus.ws0 = 1;
ws0->raise();
} else {
controlStatus.ws1 = 1;
ws1->raise();
}
refresh();
}
void
GenericWatchdog::serialize(CheckpointOut &cp) const
{
SERIALIZE_SCALAR(controlStatus);
SERIALIZE_SCALAR(offset);
SERIALIZE_SCALAR(compare);
bool ev_scheduled = timeoutEvent.scheduled();
SERIALIZE_SCALAR(ev_scheduled);
if (ev_scheduled)
SERIALIZE_SCALAR(timeoutEvent.when());
}
void
GenericWatchdog::unserialize(CheckpointIn &cp)
{
UNSERIALIZE_SCALAR(controlStatus);
UNSERIALIZE_SCALAR(offset);
UNSERIALIZE_SCALAR(compare);
bool ev_scheduled;
UNSERIALIZE_SCALAR(ev_scheduled);
if (ev_scheduled) {
Tick when;
UNSERIALIZE_SCALAR(when);
reschedule(timeoutEvent, when, true);
}
}