| /* |
| * Copyright (c) 2013, 2015, 2017 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: Giacomo Gabrielli |
| * Andreas Sandberg |
| */ |
| |
| #include "dev/arm/generic_timer.hh" |
| |
| #include "arch/arm/system.hh" |
| #include "debug/Timer.hh" |
| #include "dev/arm/base_gic.hh" |
| #include "mem/packet_access.hh" |
| #include "params/GenericTimer.hh" |
| #include "params/GenericTimerMem.hh" |
| |
| SystemCounter::SystemCounter() |
| : _freq(0), _period(0), _resetTick(0), _regCntkctl(0) |
| { |
| setFreq(0x01800000); |
| } |
| |
| void |
| SystemCounter::setFreq(uint32_t freq) |
| { |
| if (_freq != 0) { |
| // Altering the frequency after boot shouldn't be done in practice. |
| warn_once("The frequency of the system counter has already been set"); |
| } |
| _freq = freq; |
| _period = (1.0 / freq) * SimClock::Frequency; |
| _resetTick = curTick(); |
| } |
| |
| void |
| SystemCounter::serialize(CheckpointOut &cp) const |
| { |
| SERIALIZE_SCALAR(_regCntkctl); |
| SERIALIZE_SCALAR(_regCnthctl); |
| SERIALIZE_SCALAR(_freq); |
| SERIALIZE_SCALAR(_period); |
| SERIALIZE_SCALAR(_resetTick); |
| } |
| |
| void |
| SystemCounter::unserialize(CheckpointIn &cp) |
| { |
| // We didn't handle CNTKCTL in this class before, assume it's zero |
| // if it isn't present. |
| if (!UNSERIALIZE_OPT_SCALAR(_regCntkctl)) |
| _regCntkctl = 0; |
| if (!UNSERIALIZE_OPT_SCALAR(_regCnthctl)) |
| _regCnthctl = 0; |
| UNSERIALIZE_SCALAR(_freq); |
| UNSERIALIZE_SCALAR(_period); |
| UNSERIALIZE_SCALAR(_resetTick); |
| } |
| |
| |
| |
| ArchTimer::ArchTimer(const std::string &name, |
| SimObject &parent, |
| SystemCounter &sysctr, |
| const Interrupt &interrupt) |
| : _name(name), _parent(parent), _systemCounter(sysctr), |
| _interrupt(interrupt), |
| _control(0), _counterLimit(0), _offset(0), |
| _counterLimitReachedEvent([this]{ counterLimitReached(); }, name) |
| { |
| } |
| |
| void |
| ArchTimer::counterLimitReached() |
| { |
| _control.istatus = 1; |
| |
| if (!_control.enable) |
| return; |
| |
| DPRINTF(Timer, "Counter limit reached\n"); |
| if (!_control.imask) { |
| if (scheduleEvents()) { |
| DPRINTF(Timer, "Causing interrupt\n"); |
| _interrupt.send(); |
| } else { |
| DPRINTF(Timer, "Kvm mode; skipping simulated interrupt\n"); |
| } |
| } |
| } |
| |
| void |
| ArchTimer::updateCounter() |
| { |
| if (_counterLimitReachedEvent.scheduled()) |
| _parent.deschedule(_counterLimitReachedEvent); |
| if (value() >= _counterLimit) { |
| counterLimitReached(); |
| } else { |
| _control.istatus = 0; |
| if (scheduleEvents()) { |
| const auto period(_systemCounter.period()); |
| _parent.schedule(_counterLimitReachedEvent, |
| curTick() + (_counterLimit - value()) * period); |
| } |
| } |
| } |
| |
| void |
| ArchTimer::setCompareValue(uint64_t val) |
| { |
| _counterLimit = val; |
| updateCounter(); |
| } |
| |
| void |
| ArchTimer::setTimerValue(uint32_t val) |
| { |
| setCompareValue(value() + sext<32>(val)); |
| } |
| |
| void |
| ArchTimer::setControl(uint32_t val) |
| { |
| ArchTimerCtrl new_ctl = val; |
| if ((new_ctl.enable && !new_ctl.imask) && |
| !(_control.enable && !_control.imask)) { |
| // Re-evalute the timer condition |
| if (_counterLimit >= value()) { |
| _control.istatus = 1; |
| |
| DPRINTF(Timer, "Causing interrupt in control\n"); |
| //_interrupt.send(); |
| } |
| } |
| _control.enable = new_ctl.enable; |
| _control.imask = new_ctl.imask; |
| } |
| |
| void |
| ArchTimer::setOffset(uint64_t val) |
| { |
| _offset = val; |
| updateCounter(); |
| } |
| |
| uint64_t |
| ArchTimer::value() const |
| { |
| return _systemCounter.value() - _offset; |
| } |
| |
| void |
| ArchTimer::serialize(CheckpointOut &cp) const |
| { |
| paramOut(cp, "control_serial", _control); |
| SERIALIZE_SCALAR(_counterLimit); |
| SERIALIZE_SCALAR(_offset); |
| } |
| |
| void |
| ArchTimer::unserialize(CheckpointIn &cp) |
| { |
| paramIn(cp, "control_serial", _control); |
| // We didn't serialize an offset before we added support for the |
| // virtual timer. Consider it optional to maintain backwards |
| // compatibility. |
| if (!UNSERIALIZE_OPT_SCALAR(_offset)) |
| _offset = 0; |
| |
| // We no longer schedule an event here because we may enter KVM |
| // emulation. The event creation is delayed until drainResume(). |
| } |
| |
| DrainState |
| ArchTimer::drain() |
| { |
| if (_counterLimitReachedEvent.scheduled()) |
| _parent.deschedule(_counterLimitReachedEvent); |
| |
| return DrainState::Drained; |
| } |
| |
| void |
| ArchTimer::drainResume() |
| { |
| updateCounter(); |
| } |
| |
| void |
| ArchTimer::Interrupt::send() |
| { |
| if (_ppi) { |
| _gic.sendPPInt(_irq, _cpu); |
| } else { |
| _gic.sendInt(_irq); |
| } |
| } |
| |
| |
| void |
| ArchTimer::Interrupt::clear() |
| { |
| if (_ppi) { |
| _gic.clearPPInt(_irq, _cpu); |
| } else { |
| _gic.clearInt(_irq); |
| } |
| } |
| |
| |
| GenericTimer::GenericTimer(GenericTimerParams *p) |
| : ClockedObject(p), |
| system(*p->system), |
| gic(p->gic), |
| irqPhysS(p->int_phys_s), |
| irqPhysNS(p->int_phys_ns), |
| irqVirt(p->int_virt), |
| irqHyp(p->int_hyp) |
| { |
| fatal_if(!p->system, "No system specified, can't instantiate timer.\n"); |
| system.setGenericTimer(this); |
| } |
| |
| void |
| GenericTimer::serialize(CheckpointOut &cp) const |
| { |
| paramOut(cp, "cpu_count", timers.size()); |
| |
| systemCounter.serializeSection(cp, "sys_counter"); |
| |
| for (int i = 0; i < timers.size(); ++i) { |
| const CoreTimers &core(*timers[i]); |
| |
| // This should really be phys_timerN, but we are stuck with |
| // arch_timer for backwards compatibility. |
| core.physNS.serializeSection(cp, csprintf("arch_timer%d", i)); |
| core.physS.serializeSection(cp, csprintf("phys_s_timer%d", i)); |
| core.virt.serializeSection(cp, csprintf("virt_timer%d", i)); |
| core.hyp.serializeSection(cp, csprintf("hyp_timer%d", i)); |
| } |
| } |
| |
| void |
| GenericTimer::unserialize(CheckpointIn &cp) |
| { |
| systemCounter.unserializeSection(cp, "sys_counter"); |
| |
| // Try to unserialize the CPU count. Old versions of the timer |
| // model assumed a 8 CPUs, so we fall back to that if the field |
| // isn't present. |
| static const unsigned OLD_CPU_MAX = 8; |
| unsigned cpu_count; |
| if (!UNSERIALIZE_OPT_SCALAR(cpu_count)) { |
| warn("Checkpoint does not contain CPU count, assuming %i CPUs\n", |
| OLD_CPU_MAX); |
| cpu_count = OLD_CPU_MAX; |
| } |
| |
| for (int i = 0; i < cpu_count; ++i) { |
| CoreTimers &core(getTimers(i)); |
| // This should really be phys_timerN, but we are stuck with |
| // arch_timer for backwards compatibility. |
| core.physNS.unserializeSection(cp, csprintf("arch_timer%d", i)); |
| core.physS.unserializeSection(cp, csprintf("phys_s_timer%d", i)); |
| core.virt.unserializeSection(cp, csprintf("virt_timer%d", i)); |
| core.hyp.unserializeSection(cp, csprintf("hyp_timer%d", i)); |
| } |
| } |
| |
| |
| GenericTimer::CoreTimers & |
| GenericTimer::getTimers(int cpu_id) |
| { |
| if (cpu_id >= timers.size()) |
| createTimers(cpu_id + 1); |
| |
| return *timers[cpu_id]; |
| } |
| |
| void |
| GenericTimer::createTimers(unsigned cpus) |
| { |
| assert(timers.size() < cpus); |
| |
| const unsigned old_cpu_count(timers.size()); |
| timers.resize(cpus); |
| for (unsigned i = old_cpu_count; i < cpus; ++i) { |
| timers[i].reset( |
| new CoreTimers(*this, system, i, |
| irqPhysS, irqPhysNS, irqVirt, irqHyp)); |
| } |
| } |
| |
| |
| void |
| GenericTimer::setMiscReg(int reg, unsigned cpu, MiscReg val) |
| { |
| CoreTimers &core(getTimers(cpu)); |
| |
| switch (reg) { |
| case MISCREG_CNTFRQ: |
| case MISCREG_CNTFRQ_EL0: |
| systemCounter.setFreq(val); |
| return; |
| |
| case MISCREG_CNTKCTL: |
| case MISCREG_CNTKCTL_EL1: |
| systemCounter.setKernelControl(val); |
| return; |
| |
| case MISCREG_CNTHCTL: |
| case MISCREG_CNTHCTL_EL2: |
| systemCounter.setHypControl(val); |
| return; |
| |
| // Physical timer (NS) |
| case MISCREG_CNTP_CVAL_NS: |
| case MISCREG_CNTP_CVAL_EL0: |
| core.physNS.setCompareValue(val); |
| return; |
| |
| case MISCREG_CNTP_TVAL_NS: |
| case MISCREG_CNTP_TVAL_EL0: |
| core.physNS.setTimerValue(val); |
| return; |
| |
| case MISCREG_CNTP_CTL_NS: |
| case MISCREG_CNTP_CTL_EL0: |
| core.physNS.setControl(val); |
| return; |
| |
| // Count registers |
| case MISCREG_CNTPCT: |
| case MISCREG_CNTPCT_EL0: |
| case MISCREG_CNTVCT: |
| case MISCREG_CNTVCT_EL0: |
| warn("Ignoring write to read only count register: %s\n", |
| miscRegName[reg]); |
| return; |
| |
| // Virtual timer |
| case MISCREG_CNTVOFF: |
| case MISCREG_CNTVOFF_EL2: |
| core.virt.setOffset(val); |
| return; |
| |
| case MISCREG_CNTV_CVAL: |
| case MISCREG_CNTV_CVAL_EL0: |
| core.virt.setCompareValue(val); |
| return; |
| |
| case MISCREG_CNTV_TVAL: |
| case MISCREG_CNTV_TVAL_EL0: |
| core.virt.setTimerValue(val); |
| return; |
| |
| case MISCREG_CNTV_CTL: |
| case MISCREG_CNTV_CTL_EL0: |
| core.virt.setControl(val); |
| return; |
| |
| // Physical timer (S) |
| case MISCREG_CNTP_CTL_S: |
| case MISCREG_CNTPS_CTL_EL1: |
| core.physS.setControl(val); |
| return; |
| |
| case MISCREG_CNTP_CVAL_S: |
| case MISCREG_CNTPS_CVAL_EL1: |
| core.physS.setCompareValue(val); |
| return; |
| |
| case MISCREG_CNTP_TVAL_S: |
| case MISCREG_CNTPS_TVAL_EL1: |
| core.physS.setTimerValue(val); |
| return; |
| |
| // Hyp phys. timer, non-secure |
| case MISCREG_CNTHP_CTL: |
| case MISCREG_CNTHP_CTL_EL2: |
| core.hyp.setControl(val); |
| return; |
| |
| case MISCREG_CNTHP_CVAL: |
| case MISCREG_CNTHP_CVAL_EL2: |
| core.hyp.setCompareValue(val); |
| return; |
| |
| case MISCREG_CNTHP_TVAL: |
| case MISCREG_CNTHP_TVAL_EL2: |
| core.hyp.setTimerValue(val); |
| return; |
| |
| default: |
| warn("Writing to unknown register: %s\n", miscRegName[reg]); |
| return; |
| } |
| } |
| |
| |
| MiscReg |
| GenericTimer::readMiscReg(int reg, unsigned cpu) |
| { |
| CoreTimers &core(getTimers(cpu)); |
| |
| switch (reg) { |
| case MISCREG_CNTFRQ: |
| case MISCREG_CNTFRQ_EL0: |
| return systemCounter.freq(); |
| |
| case MISCREG_CNTKCTL: |
| case MISCREG_CNTKCTL_EL1: |
| return systemCounter.getKernelControl(); |
| |
| case MISCREG_CNTHCTL: |
| case MISCREG_CNTHCTL_EL2: |
| return systemCounter.getHypControl(); |
| |
| // Physical timer |
| case MISCREG_CNTP_CVAL_NS: |
| case MISCREG_CNTP_CVAL_EL0: |
| return core.physNS.compareValue(); |
| |
| case MISCREG_CNTP_TVAL_NS: |
| case MISCREG_CNTP_TVAL_EL0: |
| return core.physNS.timerValue(); |
| |
| case MISCREG_CNTP_CTL_EL0: |
| case MISCREG_CNTP_CTL_NS: |
| return core.physNS.control(); |
| |
| case MISCREG_CNTPCT: |
| case MISCREG_CNTPCT_EL0: |
| return core.physNS.value(); |
| |
| |
| // Virtual timer |
| case MISCREG_CNTVCT: |
| case MISCREG_CNTVCT_EL0: |
| return core.virt.value(); |
| |
| case MISCREG_CNTVOFF: |
| case MISCREG_CNTVOFF_EL2: |
| return core.virt.offset(); |
| |
| case MISCREG_CNTV_CVAL: |
| case MISCREG_CNTV_CVAL_EL0: |
| return core.virt.compareValue(); |
| |
| case MISCREG_CNTV_TVAL: |
| case MISCREG_CNTV_TVAL_EL0: |
| return core.virt.timerValue(); |
| |
| case MISCREG_CNTV_CTL: |
| case MISCREG_CNTV_CTL_EL0: |
| return core.virt.control(); |
| |
| // PL1 phys. timer, secure |
| case MISCREG_CNTP_CTL_S: |
| case MISCREG_CNTPS_CTL_EL1: |
| return core.physS.control(); |
| |
| case MISCREG_CNTP_CVAL_S: |
| case MISCREG_CNTPS_CVAL_EL1: |
| return core.physS.compareValue(); |
| |
| case MISCREG_CNTP_TVAL_S: |
| case MISCREG_CNTPS_TVAL_EL1: |
| return core.physS.timerValue(); |
| |
| // HYP phys. timer (NS) |
| case MISCREG_CNTHP_CTL: |
| case MISCREG_CNTHP_CTL_EL2: |
| return core.hyp.control(); |
| |
| case MISCREG_CNTHP_CVAL: |
| case MISCREG_CNTHP_CVAL_EL2: |
| return core.hyp.compareValue(); |
| |
| case MISCREG_CNTHP_TVAL: |
| case MISCREG_CNTHP_TVAL_EL2: |
| return core.hyp.timerValue(); |
| |
| default: |
| warn("Reading from unknown register: %s\n", miscRegName[reg]); |
| return 0; |
| } |
| } |
| |
| |
| |
| GenericTimerMem::GenericTimerMem(GenericTimerMemParams *p) |
| : PioDevice(p), |
| ctrlRange(RangeSize(p->base, TheISA::PageBytes)), |
| timerRange(RangeSize(p->base + TheISA::PageBytes, TheISA::PageBytes)), |
| addrRanges{ctrlRange, timerRange}, |
| systemCounter(), |
| physTimer(csprintf("%s.phys_timer0", name()), |
| *this, systemCounter, |
| ArchTimer::Interrupt(*p->gic, p->int_phys)), |
| virtTimer(csprintf("%s.virt_timer0", name()), |
| *this, systemCounter, |
| ArchTimer::Interrupt(*p->gic, p->int_virt)) |
| { |
| } |
| |
| void |
| GenericTimerMem::serialize(CheckpointOut &cp) const |
| { |
| paramOut(cp, "timer_count", 1); |
| |
| systemCounter.serializeSection(cp, "sys_counter"); |
| |
| physTimer.serializeSection(cp, "phys_timer0"); |
| virtTimer.serializeSection(cp, "virt_timer0"); |
| } |
| |
| void |
| GenericTimerMem::unserialize(CheckpointIn &cp) |
| { |
| systemCounter.unserializeSection(cp, "sys_counter"); |
| |
| unsigned timer_count; |
| UNSERIALIZE_SCALAR(timer_count); |
| // The timer count variable is just here for future versions where |
| // we support more than one set of timers. |
| if (timer_count != 1) |
| panic("Incompatible checkpoint: Only one set of timers supported"); |
| |
| physTimer.unserializeSection(cp, "phys_timer0"); |
| virtTimer.unserializeSection(cp, "virt_timer0"); |
| } |
| |
| Tick |
| GenericTimerMem::read(PacketPtr pkt) |
| { |
| const unsigned size(pkt->getSize()); |
| const Addr addr(pkt->getAddr()); |
| uint64_t value; |
| |
| pkt->makeResponse(); |
| if (ctrlRange.contains(addr)) { |
| value = ctrlRead(addr - ctrlRange.start(), size); |
| } else if (timerRange.contains(addr)) { |
| value = timerRead(addr - timerRange.start(), size); |
| } else { |
| panic("Invalid address: 0x%x\n", addr); |
| } |
| |
| DPRINTF(Timer, "Read 0x%x <- 0x%x(%i)\n", value, addr, size); |
| |
| if (size == 8) { |
| pkt->set<uint64_t>(value); |
| } else if (size == 4) { |
| pkt->set<uint32_t>(value); |
| } else { |
| panic("Unexpected access size: %i\n", size); |
| } |
| |
| return 0; |
| } |
| |
| Tick |
| GenericTimerMem::write(PacketPtr pkt) |
| { |
| const unsigned size(pkt->getSize()); |
| if (size != 8 && size != 4) |
| panic("Unexpected access size\n"); |
| |
| const Addr addr(pkt->getAddr()); |
| const uint64_t value(size == 8 ? |
| pkt->get<uint64_t>() : pkt->get<uint32_t>()); |
| |
| DPRINTF(Timer, "Write 0x%x -> 0x%x(%i)\n", value, addr, size); |
| if (ctrlRange.contains(addr)) { |
| ctrlWrite(addr - ctrlRange.start(), size, value); |
| } else if (timerRange.contains(addr)) { |
| timerWrite(addr - timerRange.start(), size, value); |
| } else { |
| panic("Invalid address: 0x%x\n", addr); |
| } |
| |
| pkt->makeResponse(); |
| return 0; |
| } |
| |
| uint64_t |
| GenericTimerMem::ctrlRead(Addr addr, size_t size) const |
| { |
| if (size == 4) { |
| switch (addr) { |
| case CTRL_CNTFRQ: |
| return systemCounter.freq(); |
| |
| case CTRL_CNTTIDR: |
| return 0x3; // Frame 0 implemented with virtual timers |
| |
| case CTRL_CNTNSAR: |
| case CTRL_CNTACR_BASE: |
| warn("Reading from unimplemented control register (0x%x)\n", addr); |
| return 0; |
| |
| case CTRL_CNTVOFF_LO_BASE: |
| return virtTimer.offset(); |
| |
| case CTRL_CNTVOFF_HI_BASE: |
| return virtTimer.offset() >> 32; |
| |
| default: |
| warn("Unexpected address (0x%x:%i), assuming RAZ\n", addr, size); |
| return 0; |
| } |
| } else if (size == 8) { |
| switch (addr) { |
| case CTRL_CNTVOFF_LO_BASE: |
| return virtTimer.offset(); |
| |
| default: |
| warn("Unexpected address (0x%x:%i), assuming RAZ\n", addr, size); |
| return 0; |
| } |
| } else { |
| panic("Invalid access size: %i\n", size); |
| } |
| } |
| |
| void |
| GenericTimerMem::ctrlWrite(Addr addr, size_t size, uint64_t value) |
| { |
| if (size == 4) { |
| switch (addr) { |
| case CTRL_CNTFRQ: |
| case CTRL_CNTNSAR: |
| case CTRL_CNTTIDR: |
| case CTRL_CNTACR_BASE: |
| warn("Write to unimplemented control register (0x%x)\n", addr); |
| return; |
| |
| case CTRL_CNTVOFF_LO_BASE: |
| virtTimer.setOffset( |
| insertBits(virtTimer.offset(), 31, 0, value)); |
| return; |
| |
| case CTRL_CNTVOFF_HI_BASE: |
| virtTimer.setOffset( |
| insertBits(virtTimer.offset(), 63, 32, value)); |
| return; |
| |
| default: |
| warn("Ignoring write to unexpected address (0x%x:%i)\n", |
| addr, size); |
| return; |
| } |
| } else if (size == 8) { |
| switch (addr) { |
| case CTRL_CNTVOFF_LO_BASE: |
| virtTimer.setOffset(value); |
| return; |
| |
| default: |
| warn("Ignoring write to unexpected address (0x%x:%i)\n", |
| addr, size); |
| return; |
| } |
| } else { |
| panic("Invalid access size: %i\n", size); |
| } |
| } |
| |
| uint64_t |
| GenericTimerMem::timerRead(Addr addr, size_t size) const |
| { |
| if (size == 4) { |
| switch (addr) { |
| case TIMER_CNTPCT_LO: |
| return physTimer.value(); |
| |
| case TIMER_CNTPCT_HI: |
| return physTimer.value() >> 32; |
| |
| case TIMER_CNTVCT_LO: |
| return virtTimer.value(); |
| |
| case TIMER_CNTVCT_HI: |
| return virtTimer.value() >> 32; |
| |
| case TIMER_CNTFRQ: |
| return systemCounter.freq(); |
| |
| case TIMER_CNTEL0ACR: |
| warn("Read from unimplemented timer register (0x%x)\n", addr); |
| return 0; |
| |
| case CTRL_CNTVOFF_LO_BASE: |
| return virtTimer.offset(); |
| |
| case CTRL_CNTVOFF_HI_BASE: |
| return virtTimer.offset() >> 32; |
| |
| case TIMER_CNTP_CVAL_LO: |
| return physTimer.compareValue(); |
| |
| case TIMER_CNTP_CVAL_HI: |
| return physTimer.compareValue() >> 32; |
| |
| case TIMER_CNTP_TVAL: |
| return physTimer.timerValue(); |
| |
| case TIMER_CNTP_CTL: |
| return physTimer.control(); |
| |
| case TIMER_CNTV_CVAL_LO: |
| return virtTimer.compareValue(); |
| |
| case TIMER_CNTV_CVAL_HI: |
| return virtTimer.compareValue() >> 32; |
| |
| case TIMER_CNTV_TVAL: |
| return virtTimer.timerValue(); |
| |
| case TIMER_CNTV_CTL: |
| return virtTimer.control(); |
| |
| default: |
| warn("Unexpected address (0x%x:%i), assuming RAZ\n", addr, size); |
| return 0; |
| } |
| } else if (size == 8) { |
| switch (addr) { |
| case TIMER_CNTPCT_LO: |
| return physTimer.value(); |
| |
| case TIMER_CNTVCT_LO: |
| return virtTimer.value(); |
| |
| case CTRL_CNTVOFF_LO_BASE: |
| return virtTimer.offset(); |
| |
| case TIMER_CNTP_CVAL_LO: |
| return physTimer.compareValue(); |
| |
| case TIMER_CNTV_CVAL_LO: |
| return virtTimer.compareValue(); |
| |
| default: |
| warn("Unexpected address (0x%x:%i), assuming RAZ\n", addr, size); |
| return 0; |
| } |
| } else { |
| panic("Invalid access size: %i\n", size); |
| } |
| } |
| |
| void |
| GenericTimerMem::timerWrite(Addr addr, size_t size, uint64_t value) |
| { |
| if (size == 4) { |
| switch (addr) { |
| case TIMER_CNTEL0ACR: |
| warn("Unimplemented timer register (0x%x)\n", addr); |
| return; |
| |
| case TIMER_CNTP_CVAL_LO: |
| physTimer.setCompareValue( |
| insertBits(physTimer.compareValue(), 31, 0, value)); |
| return; |
| |
| case TIMER_CNTP_CVAL_HI: |
| physTimer.setCompareValue( |
| insertBits(physTimer.compareValue(), 63, 32, value)); |
| return; |
| |
| case TIMER_CNTP_TVAL: |
| physTimer.setTimerValue(value); |
| return; |
| |
| case TIMER_CNTP_CTL: |
| physTimer.setControl(value); |
| return; |
| |
| case TIMER_CNTV_CVAL_LO: |
| virtTimer.setCompareValue( |
| insertBits(virtTimer.compareValue(), 31, 0, value)); |
| return; |
| |
| case TIMER_CNTV_CVAL_HI: |
| virtTimer.setCompareValue( |
| insertBits(virtTimer.compareValue(), 63, 32, value)); |
| return; |
| |
| case TIMER_CNTV_TVAL: |
| virtTimer.setTimerValue(value); |
| return; |
| |
| case TIMER_CNTV_CTL: |
| virtTimer.setControl(value); |
| return; |
| |
| default: |
| warn("Unexpected address (0x%x:%i), ignoring write\n", addr, size); |
| return; |
| } |
| } else if (size == 8) { |
| switch (addr) { |
| case TIMER_CNTP_CVAL_LO: |
| return physTimer.setCompareValue(value); |
| |
| case TIMER_CNTV_CVAL_LO: |
| return virtTimer.setCompareValue(value); |
| |
| default: |
| warn("Unexpected address (0x%x:%i), ignoring write\n", addr, size); |
| return; |
| } |
| } else { |
| panic("Invalid access size: %i\n", size); |
| } |
| } |
| |
| GenericTimer * |
| GenericTimerParams::create() |
| { |
| return new GenericTimer(this); |
| } |
| |
| GenericTimerMem * |
| GenericTimerMemParams::create() |
| { |
| return new GenericTimerMem(this); |
| } |