From f778c46b5ec37c2050b51c1e1610b2e33c018031 Mon Sep 17 00:00:00 2001 From: Geoffrey Blake Date: Fri, 19 Aug 2011 15:08:05 -0500 Subject: ARM: Add per-cpu local timers for ARM. Cortex-A9 processors can have a local timer and watchdog counter. It is enabled by default in Linux and up to this point we've had to disable them since a model wasn't available. This change allows a default MP ARM Linux configuration to boot. --- src/dev/arm/RealView.py | 9 + src/dev/arm/SConscript | 1 + src/dev/arm/gic.cc | 2 + src/dev/arm/timer_cpulocal.cc | 440 ++++++++++++++++++++++++++++++++++++++++++ src/dev/arm/timer_cpulocal.hh | 199 +++++++++++++++++++ 5 files changed, 651 insertions(+) create mode 100644 src/dev/arm/timer_cpulocal.cc create mode 100644 src/dev/arm/timer_cpulocal.hh diff --git a/src/dev/arm/RealView.py b/src/dev/arm/RealView.py index c92905b47..70337e7d9 100644 --- a/src/dev/arm/RealView.py +++ b/src/dev/arm/RealView.py @@ -108,6 +108,13 @@ class Sp804(AmbaDevice): clock1 = Param.Clock('1MHz', "Clock speed of the input") amba_id = 0x00141804 +class CpuLocalTimer(BasicPioDevice): + type = 'CpuLocalTimer' + gic = Param.Gic(Parent.any, "Gic to use for interrupting") + int_num_timer = Param.UInt32("Interrrupt number used per-cpu to GIC") + int_num_watchdog = Param.UInt32("Interrupt number for per-cpu watchdog to GIC") + clock = Param.Clock('1GHz', "Clock speed at which the timer counts") + class Pl050(AmbaIntDevice): type = 'Pl050' vnc = Param.VncServer(Parent.any, "Vnc server for remote frame buffer display") @@ -134,6 +141,7 @@ class RealViewPBX(RealView): gic = Gic() timer0 = Sp804(int_num0=36, int_num1=36, pio_addr=0x10011000) timer1 = Sp804(int_num0=37, int_num1=37, pio_addr=0x10012000) + local_cpu_timer = CpuLocalTimer(int_num_timer=29, int_num_watchdog=30, pio_addr=0x1f000600) clcd = Pl111(pio_addr=0x10020000, int_num=55) kmi0 = Pl050(pio_addr=0x10006000, int_num=52) kmi1 = Pl050(pio_addr=0x10007000, int_num=53, is_mouse=True) @@ -170,6 +178,7 @@ class RealViewPBX(RealView): self.gic.pio = bus.port self.l2x0_fake.pio = bus.port self.a9scu.pio = bus.port + self.local_cpu_timer.pio = bus.port # Attach I/O devices to specified bus object. Can't do this # earlier, since the bus object itself is typically defined at the diff --git a/src/dev/arm/SConscript b/src/dev/arm/SConscript index a320d9f8e..07a3e14ae 100644 --- a/src/dev/arm/SConscript +++ b/src/dev/arm/SConscript @@ -52,6 +52,7 @@ if env['FULL_SYSTEM'] and env['TARGET_ISA'] == 'arm': Source('timer_sp804.cc') Source('rv_ctrl.cc') Source('realview.cc') + Source('timer_cpulocal.cc') DebugFlag('AMBA') DebugFlag('PL111') diff --git a/src/dev/arm/gic.cc b/src/dev/arm/gic.cc index b2dd2bbb7..13e27f34d 100644 --- a/src/dev/arm/gic.cc +++ b/src/dev/arm/gic.cc @@ -679,6 +679,8 @@ Gic::sendInt(uint32_t num) void Gic::sendPPInt(uint32_t num, uint32_t cpu) { + DPRINTF(Interrupt, "Received Interrupt number %d, cpuTarget %#x: \n", + num, cpu); cpuPpiPending[cpu] |= 1 << (num - SGI_MAX); updateIntState(intNumToWord(num)); } diff --git a/src/dev/arm/timer_cpulocal.cc b/src/dev/arm/timer_cpulocal.cc new file mode 100644 index 000000000..a3a126f2f --- /dev/null +++ b/src/dev/arm/timer_cpulocal.cc @@ -0,0 +1,440 @@ +/* + * Copyright (c) 2010-2011 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: Ali Saidi + * Geoffrey Blake + */ + +#include "base/intmath.hh" +#include "base/trace.hh" +#include "debug/Checkpoint.hh" +#include "debug/Timer.hh" +#include "dev/arm/gic.hh" +#include "dev/arm/timer_cpulocal.hh" +#include "mem/packet.hh" +#include "mem/packet_access.hh" + +CpuLocalTimer::CpuLocalTimer(Params *p) + : BasicPioDevice(p), gic(p->gic) +{ + // Initialize the timer registers for each per cpu timer + for (int i = 0; i < CPU_MAX; i++) { + std::stringstream oss; + oss << name() << ".timer" << i; + localTimer[i]._name = oss.str(); + localTimer[i].parent = this; + localTimer[i].intNumTimer = p->int_num_timer; + localTimer[i].intNumWatchdog = p->int_num_watchdog; + localTimer[i].clock = p->clock; + localTimer[i].cpuNum = i; + } + pioSize = 0x38; +} + +CpuLocalTimer::Timer::Timer() + : timerControl(0x0), watchdogControl(0x0), rawIntTimer(false), rawIntWatchdog(false), + rawResetWatchdog(false), watchdogDisableReg(0x0), pendingIntTimer(false), pendingIntWatchdog(false), + timerLoadValue(0x0), watchdogLoadValue(0x0), timerZeroEvent(this), watchdogZeroEvent(this) +{ +} + +Tick +CpuLocalTimer::read(PacketPtr pkt) +{ + assert(pkt->getAddr() >= pioAddr && pkt->getAddr() < pioAddr + pioSize); + assert(pkt->getSize() == 4); + Addr daddr = pkt->getAddr() - pioAddr; + pkt->allocate(); + int cpu_id = pkt->req->contextId(); + DPRINTF(Timer, "Reading from CpuLocalTimer at offset: %#x\n", daddr); + assert(cpu_id >= 0); + assert(cpu_id < CPU_MAX); + + if (daddr < Timer::Size) + localTimer[cpu_id].read(pkt, daddr); + else + panic("Tried to read CpuLocalTimer at offset %#x that doesn't exist\n", daddr); + pkt->makeAtomicResponse(); + return pioDelay; +} + + +void +CpuLocalTimer::Timer::read(PacketPtr pkt, Addr daddr) +{ + DPRINTF(Timer, "Reading from CpuLocalTimer at offset: %#x\n", daddr); + Tick time; + + switch(daddr) { + case TimerLoadReg: + pkt->set(timerLoadValue); + break; + case TimerCounterReg: + DPRINTF(Timer, "Event schedule for timer %d, clock=%d, prescale=%d\n", + timerZeroEvent.when(), clock, timerControl.prescalar); + time = timerZeroEvent.when() - curTick(); + time = time / clock / power(16, timerControl.prescalar); + DPRINTF(Timer, "-- returning counter at %d\n", time); + pkt->set(time); + break; + case TimerControlReg: + pkt->set(timerControl); + break; + case TimerIntStatusReg: + pkt->set(rawIntTimer); + break; + case WatchdogLoadReg: + pkt->set(watchdogLoadValue); + break; + case WatchdogCounterReg: + DPRINTF(Timer, "Event schedule for watchdog %d, clock=%d, prescale=%d\n", + watchdogZeroEvent.when(), clock, watchdogControl.prescalar); + time = watchdogZeroEvent.when() - curTick(); + time = time / clock / power(16, watchdogControl.prescalar); + DPRINTF(Timer, "-- returning counter at %d\n", time); + pkt->set(time); + break; + case WatchdogControlReg: + pkt->set(watchdogControl); + break; + case WatchdogIntStatusReg: + pkt->set(rawIntWatchdog); + break; + case WatchdogResetStatusReg: + pkt->set(rawResetWatchdog); + break; + case WatchdogDisableReg: + panic("Tried to read from WatchdogDisableRegister\n"); + break; + default: + panic("Tried to read CpuLocalTimer at offset %#x\n", daddr); + break; + } +} + +Tick +CpuLocalTimer::write(PacketPtr pkt) +{ + assert(pkt->getAddr() >= pioAddr && pkt->getAddr() < pioAddr + pioSize); + assert(pkt->getSize() == 4); + Addr daddr = pkt->getAddr() - pioAddr; + pkt->allocate(); + int cpu_id = pkt->req->contextId(); + DPRINTF(Timer, "Writing to CpuLocalTimer at offset: %#x\n", daddr); + assert(cpu_id >= 0); + assert(cpu_id < CPU_MAX); + + if (daddr < Timer::Size) + localTimer[cpu_id].write(pkt, daddr); + else + panic("Tried to write CpuLocalTimer at offset %#x that doesn't exist\n", daddr); + pkt->makeAtomicResponse(); + return pioDelay; +} + +void +CpuLocalTimer::Timer::write(PacketPtr pkt, Addr daddr) +{ + DPRINTF(Timer, "Writing to CpuLocalTimer at offset: %#x\n", daddr); + bool old_enable; + bool old_wd_mode; + uint32_t old_val; + + switch (daddr) { + case TimerLoadReg: + // Writing to this register also resets the counter register and + // starts decrementing if the counter is enabled. + timerLoadValue = pkt->get(); + restartTimerCounter(timerLoadValue); + break; + case TimerCounterReg: + // Can be written, doesn't start counting unless the timer is enabled + restartTimerCounter(pkt->get()); + break; + case TimerControlReg: + old_enable = timerControl.enable; + timerControl = pkt->get(); + if ((old_enable == 0) && timerControl.enable) + restartTimerCounter(timerLoadValue); + break; + case TimerIntStatusReg: + rawIntTimer = false; + if (pendingIntTimer) { + pendingIntTimer = false; + DPRINTF(Timer, "Clearing interrupt\n"); + } + break; + case WatchdogLoadReg: + watchdogLoadValue = pkt->get(); + restartWatchdogCounter(watchdogLoadValue); + break; + case WatchdogCounterReg: + // Can't be written when in watchdog mode, but can in timer mode + if (!watchdogControl.watchdogMode) { + restartWatchdogCounter(pkt->get()); + } + break; + case WatchdogControlReg: + old_enable = watchdogControl.enable; + old_wd_mode = watchdogControl.watchdogMode; + watchdogControl = pkt->get(); + if ((old_enable == 0) && watchdogControl.enable) + restartWatchdogCounter(watchdogLoadValue); + // cannot disable watchdog using control register + if ((old_wd_mode == 1) && watchdogControl.watchdogMode == 0) + watchdogControl.watchdogMode = 1; + break; + case WatchdogIntStatusReg: + rawIntWatchdog = false; + if (pendingIntWatchdog) { + pendingIntWatchdog = false; + DPRINTF(Timer, "Clearing watchdog interrupt\n"); + } + break; + case WatchdogResetStatusReg: + rawResetWatchdog = false; + DPRINTF(Timer, "Clearing watchdog reset flag\n"); + break; + case WatchdogDisableReg: + old_val = watchdogDisableReg; + watchdogDisableReg = pkt->get(); + // if this sequence is observed, turn off watchdog mode + if (old_val == 0x12345678 && watchdogDisableReg == 0x87654321) + watchdogControl.watchdogMode = 0; + break; + default: + panic("Tried to write CpuLocalTimer timer at offset %#x\n", daddr); + break; + } +} + +//XXX: Two functions are needed because the control registers are different types +void +CpuLocalTimer::Timer::restartTimerCounter(uint32_t val) +{ + DPRINTF(Timer, "Resetting timer counter with value %#x\n", val); + if (!timerControl.enable) + return; + + Tick time = clock * power(16, timerControl.prescalar); + time *= val; + + if (timerZeroEvent.scheduled()) { + DPRINTF(Timer, "-- Event was already schedule, de-scheduling\n"); + parent->deschedule(timerZeroEvent); + } + parent->schedule(timerZeroEvent, curTick() + time); + DPRINTF(Timer, "-- Scheduling new event for: %d\n", curTick() + time); +} + +void +CpuLocalTimer::Timer::restartWatchdogCounter(uint32_t val) +{ + DPRINTF(Timer, "Resetting watchdog counter with value %#x\n", val); + if (!watchdogControl.enable) + return; + + Tick time = clock * power(16, watchdogControl.prescalar); + time *= val; + + if (watchdogZeroEvent.scheduled()) { + DPRINTF(Timer, "-- Event was already schedule, de-scheduling\n"); + parent->deschedule(watchdogZeroEvent); + } + parent->schedule(watchdogZeroEvent, curTick() + time); + DPRINTF(Timer, "-- Scheduling new event for: %d\n", curTick() + time); +} +////// + +void +CpuLocalTimer::Timer::timerAtZero() +{ + if (!timerControl.enable) + return; + + DPRINTF(Timer, "Timer Counter reached zero\n"); + + rawIntTimer = true; + bool old_pending = pendingIntTimer; + if (timerControl.intEnable) + pendingIntTimer = true; + if (pendingIntTimer && ~old_pending) { + DPRINTF(Timer, "-- Causing interrupt\n"); + parent->gic->sendPPInt(intNumTimer, cpuNum); + } + + if (!timerControl.autoReload) + return; + else + restartTimerCounter(timerLoadValue); +} + +void +CpuLocalTimer::Timer::watchdogAtZero() +{ + if (!watchdogControl.enable) + return; + + DPRINTF(Timer, "Watchdog Counter reached zero\n"); + + rawIntWatchdog = true; + bool old_pending = pendingIntWatchdog; + // generates an interrupt only if the watchdog is in timer + // mode. + if (watchdogControl.intEnable && !watchdogControl.watchdogMode) + pendingIntWatchdog = true; + else if (watchdogControl.watchdogMode) { + rawResetWatchdog = true; + fatal("gem5 ARM Model does not support true watchdog operation!\n"); + //XXX: Should we ever support a true watchdog reset? + } + + if (pendingIntWatchdog && ~old_pending) { + DPRINTF(Timer, "-- Causing interrupt\n"); + parent->gic->sendPPInt(intNumWatchdog, cpuNum); + } + + if (watchdogControl.watchdogMode) + return; + else if (watchdogControl.autoReload) + restartWatchdogCounter(watchdogLoadValue); +} + +void +CpuLocalTimer::Timer::serialize(std::ostream &os) +{ + DPRINTF(Checkpoint, "Serializing Arm CpuLocalTimer\n"); + SERIALIZE_SCALAR(intNumTimer); + SERIALIZE_SCALAR(intNumWatchdog); + SERIALIZE_SCALAR(clock); + + uint32_t timer_control_serial = timerControl; + uint32_t watchdog_control_serial = watchdogControl; + SERIALIZE_SCALAR(timer_control_serial); + SERIALIZE_SCALAR(watchdog_control_serial); + + SERIALIZE_SCALAR(rawIntTimer); + SERIALIZE_SCALAR(rawIntWatchdog); + SERIALIZE_SCALAR(rawResetWatchdog); + SERIALIZE_SCALAR(watchdogDisableReg); + SERIALIZE_SCALAR(pendingIntTimer); + SERIALIZE_SCALAR(pendingIntWatchdog); + SERIALIZE_SCALAR(timerLoadValue); + SERIALIZE_SCALAR(watchdogLoadValue); + + bool timer_is_in_event = timerZeroEvent.scheduled(); + SERIALIZE_SCALAR(timer_is_in_event); + bool watchdog_is_in_event = watchdogZeroEvent.scheduled(); + SERIALIZE_SCALAR(watchdog_is_in_event); + + Tick timer_event_time; + if (timer_is_in_event){ + timer_event_time = timerZeroEvent.when(); + SERIALIZE_SCALAR(timer_event_time); + } + Tick watchdog_event_time; + if (watchdog_is_in_event){ + watchdog_event_time = watchdogZeroEvent.when(); + SERIALIZE_SCALAR(watchdog_event_time); + } +} + +void +CpuLocalTimer::Timer::unserialize(Checkpoint *cp, const std::string §ion) +{ + DPRINTF(Checkpoint, "Unserializing Arm CpuLocalTimer\n"); + + UNSERIALIZE_SCALAR(intNumTimer); + UNSERIALIZE_SCALAR(intNumWatchdog); + UNSERIALIZE_SCALAR(clock); + + uint32_t timer_control_serial; + UNSERIALIZE_SCALAR(timer_control_serial); + timerControl = timer_control_serial; + uint32_t watchdog_control_serial; + UNSERIALIZE_SCALAR(watchdog_control_serial); + watchdogControl = watchdog_control_serial; + + UNSERIALIZE_SCALAR(rawIntTimer); + UNSERIALIZE_SCALAR(rawIntWatchdog); + UNSERIALIZE_SCALAR(rawResetWatchdog); + UNSERIALIZE_SCALAR(watchdogDisableReg); + UNSERIALIZE_SCALAR(pendingIntTimer); + UNSERIALIZE_SCALAR(pendingIntWatchdog); + UNSERIALIZE_SCALAR(timerLoadValue); + UNSERIALIZE_SCALAR(watchdogLoadValue); + + bool timer_is_in_event; + UNSERIALIZE_SCALAR(timer_is_in_event); + bool watchdog_is_in_event; + UNSERIALIZE_SCALAR(watchdog_is_in_event); + + Tick timer_event_time; + if (timer_is_in_event){ + UNSERIALIZE_SCALAR(timer_event_time); + parent->schedule(timerZeroEvent, timer_event_time); + } + Tick watchdog_event_time; + if (watchdog_is_in_event) { + UNSERIALIZE_SCALAR(watchdog_event_time); + parent->schedule(watchdogZeroEvent, watchdog_event_time); + } +} + + + +void +CpuLocalTimer::serialize(std::ostream &os) +{ + for (int i = 0; i < CPU_MAX; i++) { + nameOut(os, csprintf("%s.timer%d", name(), i)); + localTimer[i].serialize(os); + } +} + +void +CpuLocalTimer::unserialize(Checkpoint *cp, const std::string §ion) +{ + for (int i = 0; i < CPU_MAX; i++) { + localTimer[i].unserialize(cp, csprintf("%s.timer%d", section, i)); + } +} + +CpuLocalTimer * +CpuLocalTimerParams::create() +{ + return new CpuLocalTimer(this); +} diff --git a/src/dev/arm/timer_cpulocal.hh b/src/dev/arm/timer_cpulocal.hh new file mode 100644 index 000000000..5357ac0eb --- /dev/null +++ b/src/dev/arm/timer_cpulocal.hh @@ -0,0 +1,199 @@ +/* + * Copyright (c) 2010-2011 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: Ali Saidi + * Geoffrey Blake + */ + +#ifndef __DEV_ARM_LOCALTIMER_HH__ +#define __DEV_ARM_LOCALTIMER_HH__ + +#include "base/range.hh" +#include "dev/io_device.hh" +#include "params/CpuLocalTimer.hh" + +/** @file + * This implements the cpu local timer from the Cortex-A9 MPCore + * Technical Reference Manual rev r2p2 (ARM DDI 0407F) + */ + +class Gic; + +class CpuLocalTimer : public BasicPioDevice +{ + protected: + class Timer + { + + public: + enum { + TimerLoadReg = 0x00, + TimerCounterReg = 0x04, + TimerControlReg = 0x08, + TimerIntStatusReg = 0x0C, + WatchdogLoadReg = 0x20, + WatchdogCounterReg = 0x24, + WatchdogControlReg = 0x28, + WatchdogIntStatusReg = 0x2C, + WatchdogResetStatusReg = 0x30, + WatchdogDisableReg = 0x34, + Size = 0x38 + }; + + BitUnion32(TimerCtrl) + Bitfield<0> enable; + Bitfield<1> autoReload; + Bitfield<2> intEnable; + Bitfield<3,7> reserved; + Bitfield<8,15> prescalar; + EndBitUnion(TimerCtrl) + + BitUnion32(WatchdogCtrl) + Bitfield<0> enable; + Bitfield<1> autoReload; + Bitfield<2> intEnable; + Bitfield<3> watchdogMode; + Bitfield<4,7> reserved; + Bitfield<8,15> prescalar; + EndBitUnion(WatchdogCtrl) + + protected: + std::string _name; + + /** Pointer to parent class */ + CpuLocalTimer *parent; + + /** Number of interrupt to cause/clear */ + uint32_t intNumTimer; + uint32_t intNumWatchdog; + + /** Cpu this timer is attached to */ + uint32_t cpuNum; + + /** Number of ticks in a clock input */ + Tick clock; + + /** Control register as specified above */ + TimerCtrl timerControl; + WatchdogCtrl watchdogControl; + + /** If timer has caused an interrupt. This is irrespective of + * interrupt enable */ + bool rawIntTimer; + bool rawIntWatchdog; + bool rawResetWatchdog; + uint32_t watchdogDisableReg; + + /** If an interrupt is currently pending. Logical and of Timer or + * Watchdog Ctrl.enable and rawIntTimer or rawIntWatchdog */ + bool pendingIntTimer; + bool pendingIntWatchdog; + + /** Value to load into counters when periodic mode reaches 0 */ + uint32_t timerLoadValue; + uint32_t watchdogLoadValue; + + /** Called when the counter reaches 0 */ + void timerAtZero(); + EventWrapper timerZeroEvent; + + void watchdogAtZero(); + EventWrapper watchdogZeroEvent; + public: + /** Restart the counter ticking at val + * @param val the value to start at */ + void restartTimerCounter(uint32_t val); + void restartWatchdogCounter(uint32_t val); + + Timer(); + + std::string name() const { return _name; } + + /** Handle read for a single timer */ + void read(PacketPtr pkt, Addr daddr); + + /** Handle write for a single timer */ + void write(PacketPtr pkt, Addr daddr); + + void serialize(std::ostream &os); + void unserialize(Checkpoint *cp, const std::string §ion); + + friend class CpuLocalTimer; + }; + + static const int CPU_MAX = 8; + + /** Pointer to the GIC for causing an interrupt */ + Gic *gic; + + /** Timers that do the actual work */ + Timer localTimer[CPU_MAX]; + + public: + typedef CpuLocalTimerParams Params; + const Params * + params() const + { + return dynamic_cast(_params); + } + /** + * The constructor for RealView just registers itself with the MMU. + * @param p params structure + */ + CpuLocalTimer(Params *p); + + /** + * Handle a read to the device + * @param pkt The memory request. + * @return Returns latency of device read + */ + virtual Tick read(PacketPtr pkt); + + /** + * Handle a write to the device. + * @param pkt The memory request. + * @return Returns latency of device write + */ + virtual Tick write(PacketPtr pkt); + + + virtual void serialize(std::ostream &os); + virtual void unserialize(Checkpoint *cp, const std::string §ion); +}; + + +#endif // __DEV_ARM_SP804_HH__ + -- cgit v1.2.3