/* * Copyright (c) 2010-2013 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 "dev/arm/timer_cpulocal.hh" #include "base/intmath.hh" #include "base/trace.hh" #include "debug/Checkpoint.hh" #include "debug/Timer.hh" #include "dev/arm/base_gic.hh" #include "mem/packet.hh" #include "mem/packet_access.hh" CpuLocalTimer::CpuLocalTimer(Params *p) : BasicPioDevice(p, 0x38), 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].cpuNum = i; } } 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]{ timerAtZero(); }, name()), watchdogZeroEvent([this]{ watchdogAtZero(); }, name()) { } Tick CpuLocalTimer::read(PacketPtr pkt) { assert(pkt->getAddr() >= pioAddr && pkt->getAddr() < pioAddr + pioSize); assert(pkt->getSize() == 4); Addr daddr = pkt->getAddr() - pioAddr; ContextID 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(), parent->clockPeriod(), timerControl.prescalar); time = timerZeroEvent.when() - curTick(); time = time / parent->clockPeriod() / 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(), parent->clockPeriod(), watchdogControl.prescalar); time = watchdogZeroEvent.when() - curTick(); time = time / parent->clockPeriod() / 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; ContextID 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 = parent->clockPeriod() * 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 = parent->clockPeriod() * 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(CheckpointOut &cp) const { DPRINTF(Checkpoint, "Serializing Arm CpuLocalTimer\n"); SERIALIZE_SCALAR(intNumTimer); SERIALIZE_SCALAR(intNumWatchdog); 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(CheckpointIn &cp) { DPRINTF(Checkpoint, "Unserializing Arm CpuLocalTimer\n"); UNSERIALIZE_SCALAR(intNumTimer); UNSERIALIZE_SCALAR(intNumWatchdog); 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(CheckpointOut &cp) const { for (int i = 0; i < CPU_MAX; i++) localTimer[i].serializeSection(cp, csprintf("timer%d", i)); } void CpuLocalTimer::unserialize(CheckpointIn &cp) { for (int i = 0; i < CPU_MAX; i++) localTimer[i].unserializeSection(cp, csprintf("timer%d", i)); } CpuLocalTimer * CpuLocalTimerParams::create() { return new CpuLocalTimer(this); }