dev, arm: add a9mpcore global timer device
[gem5.git] / src / dev / arm / timer_a9global.cc
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
52 A9GlobalTimer::A9GlobalTimer(Params *p)
53 : BasicPioDevice(p, 0x1C), gic(p->gic),
54 global_timer(name() + ".globaltimer", this, p->int_num)
55 {
56 }
57
58 A9GlobalTimer::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
65 Tick
66 A9GlobalTimer::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
81 uint64_t
82 A9GlobalTimer::Timer::getTimeCounterFromTicks(Tick ticks)
83 {
84 return ticks / parent->clockPeriod() / (control.prescalar + 1) - 1;
85 }
86
87 void
88 A9GlobalTimer::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->set<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->set<uint32_t>(time);
104 break;
105 case ControlReg:
106 pkt->set<uint32_t>(control);
107 break;
108 case IntStatusReg:
109 pkt->set<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->set<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->set<uint32_t>(time);
133 break;
134 case AutoIncrementReg:
135 pkt->set<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->get<uint32_t>(), daddr);
143 }
144
145 Tick
146 A9GlobalTimer::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
164 void
165 A9GlobalTimer::Timer::write(PacketPtr pkt, Addr daddr)
166 {
167 DPRINTF(Timer, "Writing %#x to A9GlobalTimer at offset: %#x\n",
168 pkt->get<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->get<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->get<uint32_t>();
197 break;
198 case CmpValRegHigh32:
199 cmpVal &= 0x00000000FFFFFFFFULL;
200 cmpVal |= ((uint64_t)pkt->get<uint32_t>() << 32);
201 break;
202 case AutoIncrementReg:
203 autoIncValue = pkt->get<uint32_t>();
204 break;
205 default:
206 panic("Tried to write A9GlobalTimer at offset %#x\n", daddr);
207 break;
208 }
209 }
210
211 void
212 A9GlobalTimer::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
232 void
233 A9GlobalTimer::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
256 void
257 A9GlobalTimer::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
279 void
280 A9GlobalTimer::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
303 void
304 A9GlobalTimer::serialize(CheckpointOut &cp) const
305 {
306 global_timer.serialize(cp);
307 }
308
309 void
310 A9GlobalTimer::unserialize(CheckpointIn &cp)
311 {
312 global_timer.unserialize(cp);
313 }
314
315 A9GlobalTimer *
316 A9GlobalTimerParams::create()
317 {
318 return new A9GlobalTimer(this);
319 }