1/* 2 * Copyright (c) 2017 Gedare Bloom 3 * Copyright (c) 2010 ARM Limited 4 * All rights reserved 5 * 6 * The license below extends only to copyright in the software and shall 7 * not be construed as granting a license to any other intellectual 8 * property including but not limited to intellectual property relating 9 * to a hardware implementation of the functionality of the software 10 * licensed hereunder. You may use the software subject to the license 11 * terms below provided that you ensure that this notice is replicated 12 * unmodified and in its entirety in all distributions of the software, 13 * modified or unmodified, in source code or in binary form. 14 * 15 * Redistribution and use in source and binary forms, with or without 16 * modification, are permitted provided that the following conditions are 17 * met: redistributions of source code must retain the above copyright 18 * notice, this list of conditions and the following disclaimer; 19 * redistributions in binary form must reproduce the above copyright 20 * notice, this list of conditions and the following disclaimer in the 21 * documentation and/or other materials provided with the distribution; 22 * neither the name of the copyright holders nor the names of its 23 * contributors may be used to endorse or promote products derived from 24 * this software without specific prior written permission. 25 * 26 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 27 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 28 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 29 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 30 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 31 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 32 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 33 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 34 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 35 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 36 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 37 * 38 * Authors: Ali Saidi 39 * Gedare Bloom 40 */ 41 42#include "dev/arm/timer_a9global.hh" 43 44#include "base/intmath.hh" 45#include "base/trace.hh" 46#include "debug/Checkpoint.hh" 47#include "debug/Timer.hh" 48#include "dev/arm/base_gic.hh" 49#include "mem/packet.hh" 50#include "mem/packet_access.hh" 51 52A9GlobalTimer::A9GlobalTimer(Params *p) 53 : BasicPioDevice(p, 0x1C), gic(p->gic), 54 global_timer(name() + ".globaltimer", this, p->int_num) 55{ 56} 57 58A9GlobalTimer::Timer::Timer(std::string __name, A9GlobalTimer *_parent, 59 int int_num) 60 : _name(__name), parent(_parent), intNum(int_num), control(0x0), 61 rawInt(false), pendingInt(false), autoIncValue(0x0), cmpValEvent(this) 62{ 63} 64 65Tick 66A9GlobalTimer::read(PacketPtr pkt) 67{ 68 assert(pkt->getAddr() >= pioAddr && pkt->getAddr() < pioAddr + pioSize); 69 assert(pkt->getSize() == 4); 70 Addr daddr = pkt->getAddr() - pioAddr; 71 72 if (daddr < Timer::Size) 73 global_timer.read(pkt, daddr); 74 else 75 panic("Tried to read A9GlobalTimer at offset %#x that doesn't exist\n", 76 daddr); 77 pkt->makeAtomicResponse(); 78 return pioDelay; 79} 80 81uint64_t 82A9GlobalTimer::Timer::getTimeCounterFromTicks(Tick ticks) 83{ 84 return ticks / parent->clockPeriod() / (control.prescalar + 1) - 1; 85} 86 87void 88A9GlobalTimer::Timer::read(PacketPtr pkt, Addr daddr) 89{ 90 DPRINTF(Timer, "Reading from A9GlobalTimer at offset: %#x\n", daddr); 91 uint64_t time; 92 93 switch(daddr) { 94 case CounterRegLow32: 95 time = getTimeCounterFromTicks(curTick()); 96 DPRINTF(Timer, "-- returning lower 32-bits of counter: %u\n", time); 97 pkt->setLE<uint32_t>(time); 98 break; 99 case CounterRegHigh32: 100 time = getTimeCounterFromTicks(curTick()); 101 time >>= 32; 102 DPRINTF(Timer, "-- returning upper 32-bits of counter: %u\n", time); 103 pkt->setLE<uint32_t>(time); 104 break; 105 case ControlReg: 106 pkt->setLE<uint32_t>(control); 107 break; 108 case IntStatusReg: 109 pkt->setLE<uint32_t>(rawInt); 110 break; 111 case CmpValRegLow32: 112 DPRINTF(Timer, "Event schedule for %d, clock=%d, prescale=%d\n", 113 cmpValEvent.when(), parent->clockPeriod(), control.prescalar); 114 if (cmpValEvent.scheduled()) { 115 time = getTimeCounterFromTicks(cmpValEvent.when() - curTick()); 116 } else { 117 time = 0; 118 } 119 DPRINTF(Timer, "-- returning lower 32-bits of comparator: %u\n", time); 120 pkt->setLE<uint32_t>(time); 121 break; 122 case CmpValRegHigh32: 123 DPRINTF(Timer, "Event schedule for %d, clock=%d, prescale=%d\n", 124 cmpValEvent.when(), parent->clockPeriod(), control.prescalar); 125 if (cmpValEvent.scheduled()) { 126 time = getTimeCounterFromTicks(cmpValEvent.when() - curTick()); 127 time >>= 32; 128 } else { 129 time = 0; 130 } 131 DPRINTF(Timer, "-- returning upper 32-bits of comparator: %u\n", time); 132 pkt->setLE<uint32_t>(time); 133 break; 134 case AutoIncrementReg: 135 pkt->setLE<uint32_t>(autoIncValue); 136 break; 137 default: 138 panic("Tried to read A9GlobalTimer at offset %#x\n", daddr); 139 break; 140 } 141 DPRINTF(Timer, "Reading %#x from A9GlobalTimer at offset: %#x\n", 142 pkt->getLE<uint32_t>(), daddr); 143} 144 145Tick 146A9GlobalTimer::write(PacketPtr pkt) 147{ 148 assert(pkt->getAddr() >= pioAddr && pkt->getAddr() < pioAddr + pioSize); 149 assert(pkt->getSize() == 4); 150 Addr daddr = pkt->getAddr() - pioAddr; 151 DPRINTF(Timer, "Writing to A9GlobalTimer at offset: %#x\n", daddr); 152 153 warn_once("A9 Global Timer doesn't support banked per-cpu registers\n"); 154 155 if (daddr < Timer::Size) 156 global_timer.write(pkt, daddr); 157 else 158 panic("Tried to write A9GlobalTimer at offset %#x doesn't exist\n", 159 daddr); 160 pkt->makeAtomicResponse(); 161 return pioDelay; 162} 163 164void 165A9GlobalTimer::Timer::write(PacketPtr pkt, Addr daddr) 166{ 167 DPRINTF(Timer, "Writing %#x to A9GlobalTimer at offset: %#x\n", 168 pkt->getLE<uint32_t>(), daddr); 169 switch (daddr) { 170 case CounterRegLow32: 171 case CounterRegHigh32: 172 DPRINTF(Timer, "Ignoring unsupported write to Global Timer Counter\n"); 173 break; 174 case ControlReg: 175 bool old_enable; 176 bool old_cmpEnable; 177 old_enable = control.enable; 178 old_cmpEnable = control.cmpEnable; 179 control = pkt->getLE<uint32_t>(); 180 if ((old_enable == 0) && control.enable) 181 restartCounter(); 182 if ((old_cmpEnable == 0) && control.cmpEnable) 183 restartCounter(); 184 break; 185 case IntStatusReg: 186 /* TODO: should check that '1' was written. */ 187 rawInt = false; 188 if (pendingInt) { 189 pendingInt = false; 190 DPRINTF(Timer, "Clearing interrupt\n"); 191 parent->gic->clearInt(intNum); 192 } 193 break; 194 case CmpValRegLow32: 195 cmpVal &= 0xFFFFFFFF00000000ULL; 196 cmpVal |= (uint64_t)pkt->getLE<uint32_t>(); 197 break; 198 case CmpValRegHigh32: 199 cmpVal &= 0x00000000FFFFFFFFULL; 200 cmpVal |= ((uint64_t)pkt->getLE<uint32_t>() << 32); 201 break; 202 case AutoIncrementReg: 203 autoIncValue = pkt->getLE<uint32_t>(); 204 break; 205 default: 206 panic("Tried to write A9GlobalTimer at offset %#x\n", daddr); 207 break; 208 } 209} 210 211void 212A9GlobalTimer::Timer::restartCounter() 213{ 214 if (!control.enable) 215 return; 216 DPRINTF(Timer, "Restarting counter with value %#x\n", cmpVal); 217 218 Tick time = parent->clockPeriod() * (control.prescalar + 1) * (cmpVal + 1); 219 220 if (time < curTick()) { 221 DPRINTF(Timer, "-- Event time %#x < curTick %#x\n", time, curTick()); 222 return; 223 } 224 if (cmpValEvent.scheduled()) { 225 DPRINTF(Timer, "-- Event was already schedule, de-scheduling\n"); 226 parent->deschedule(cmpValEvent); 227 } 228 parent->schedule(cmpValEvent, time); 229 DPRINTF(Timer, "-- Scheduling new event for: %d\n", time); 230} 231 232void 233A9GlobalTimer::Timer::counterAtCmpVal() 234{ 235 if (!control.enable) 236 return; 237 238 DPRINTF(Timer, "Counter reached cmpVal\n"); 239 240 rawInt = true; 241 bool old_pending = pendingInt; 242 if (control.intEnable) 243 pendingInt = true; 244 if (pendingInt && !old_pending) { 245 DPRINTF(Timer, "-- Causing interrupt\n"); 246 parent->gic->sendPPInt(intNum, 0); /* FIXME: cpuNum */ 247 } 248 249 if (control.autoIncrement == 0) // one-shot 250 return; 251 252 cmpVal += (uint64_t)autoIncValue; 253 restartCounter(); 254} 255 256void 257A9GlobalTimer::Timer::serialize(CheckpointOut &cp) const 258{ 259 DPRINTF(Checkpoint, "Serializing Arm A9GlobalTimer\n"); 260 261 uint32_t control_serial = control; 262 SERIALIZE_SCALAR(control_serial); 263 264 SERIALIZE_SCALAR(rawInt); 265 SERIALIZE_SCALAR(pendingInt); 266 SERIALIZE_SCALAR(cmpVal); 267 SERIALIZE_SCALAR(autoIncValue); 268 269 bool is_in_event = cmpValEvent.scheduled(); 270 SERIALIZE_SCALAR(is_in_event); 271 272 Tick event_time; 273 if (is_in_event){ 274 event_time = cmpValEvent.when(); 275 SERIALIZE_SCALAR(event_time); 276 } 277} 278 279void 280A9GlobalTimer::Timer::unserialize(CheckpointIn &cp) 281{ 282 DPRINTF(Checkpoint, "Unserializing Arm A9GlobalTimer\n"); 283 284 uint32_t control_serial; 285 UNSERIALIZE_SCALAR(control_serial); 286 control = control_serial; 287 288 UNSERIALIZE_SCALAR(rawInt); 289 UNSERIALIZE_SCALAR(pendingInt); 290 UNSERIALIZE_SCALAR(cmpVal); 291 UNSERIALIZE_SCALAR(autoIncValue); 292 293 bool is_in_event; 294 UNSERIALIZE_SCALAR(is_in_event); 295 296 Tick event_time; 297 if (is_in_event){ 298 UNSERIALIZE_SCALAR(event_time); 299 parent->schedule(cmpValEvent, event_time); 300 } 301} 302 303void 304A9GlobalTimer::serialize(CheckpointOut &cp) const 305{ 306 global_timer.serialize(cp); 307} 308 309void 310A9GlobalTimer::unserialize(CheckpointIn &cp) 311{ 312 global_timer.unserialize(cp); 313} 314 315A9GlobalTimer * 316A9GlobalTimerParams::create() 317{ 318 return new A9GlobalTimer(this); 319} 320