dev-arm: Adjust idreg value in RealViewCtrl
[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
39 #include "dev/arm/timer_a9global.hh"
40
41 #include "base/intmath.hh"
42 #include "base/trace.hh"
43 #include "debug/Checkpoint.hh"
44 #include "debug/Timer.hh"
45 #include "dev/arm/base_gic.hh"
46 #include "mem/packet.hh"
47 #include "mem/packet_access.hh"
48
49 A9GlobalTimer::A9GlobalTimer(Params *p)
50 : BasicPioDevice(p, 0x1C), gic(p->gic),
51 global_timer(name() + ".globaltimer", this, p->int_num)
52 {
53 }
54
55 A9GlobalTimer::Timer::Timer(std::string __name, A9GlobalTimer *_parent,
56 int int_num)
57 : _name(__name), parent(_parent), intNum(int_num), control(0x0),
58 rawInt(false), pendingInt(false), autoIncValue(0x0), cmpValEvent(this)
59 {
60 }
61
62 Tick
63 A9GlobalTimer::read(PacketPtr pkt)
64 {
65 assert(pkt->getAddr() >= pioAddr && pkt->getAddr() < pioAddr + pioSize);
66 assert(pkt->getSize() == 4);
67 Addr daddr = pkt->getAddr() - pioAddr;
68
69 if (daddr < Timer::Size)
70 global_timer.read(pkt, daddr);
71 else
72 panic("Tried to read A9GlobalTimer at offset %#x that doesn't exist\n",
73 daddr);
74 pkt->makeAtomicResponse();
75 return pioDelay;
76 }
77
78 uint64_t
79 A9GlobalTimer::Timer::getTimeCounterFromTicks(Tick ticks)
80 {
81 return ticks / parent->clockPeriod() / (control.prescalar + 1) - 1;
82 }
83
84 void
85 A9GlobalTimer::Timer::read(PacketPtr pkt, Addr daddr)
86 {
87 DPRINTF(Timer, "Reading from A9GlobalTimer at offset: %#x\n", daddr);
88 uint64_t time;
89
90 switch(daddr) {
91 case CounterRegLow32:
92 time = getTimeCounterFromTicks(curTick());
93 DPRINTF(Timer, "-- returning lower 32-bits of counter: %u\n", time);
94 pkt->setLE<uint32_t>(time);
95 break;
96 case CounterRegHigh32:
97 time = getTimeCounterFromTicks(curTick());
98 time >>= 32;
99 DPRINTF(Timer, "-- returning upper 32-bits of counter: %u\n", time);
100 pkt->setLE<uint32_t>(time);
101 break;
102 case ControlReg:
103 pkt->setLE<uint32_t>(control);
104 break;
105 case IntStatusReg:
106 pkt->setLE<uint32_t>(rawInt);
107 break;
108 case CmpValRegLow32:
109 DPRINTF(Timer, "Event schedule for %d, clock=%d, prescale=%d\n",
110 cmpValEvent.when(), parent->clockPeriod(), control.prescalar);
111 if (cmpValEvent.scheduled()) {
112 time = getTimeCounterFromTicks(cmpValEvent.when() - curTick());
113 } else {
114 time = 0;
115 }
116 DPRINTF(Timer, "-- returning lower 32-bits of comparator: %u\n", time);
117 pkt->setLE<uint32_t>(time);
118 break;
119 case CmpValRegHigh32:
120 DPRINTF(Timer, "Event schedule for %d, clock=%d, prescale=%d\n",
121 cmpValEvent.when(), parent->clockPeriod(), control.prescalar);
122 if (cmpValEvent.scheduled()) {
123 time = getTimeCounterFromTicks(cmpValEvent.when() - curTick());
124 time >>= 32;
125 } else {
126 time = 0;
127 }
128 DPRINTF(Timer, "-- returning upper 32-bits of comparator: %u\n", time);
129 pkt->setLE<uint32_t>(time);
130 break;
131 case AutoIncrementReg:
132 pkt->setLE<uint32_t>(autoIncValue);
133 break;
134 default:
135 panic("Tried to read A9GlobalTimer at offset %#x\n", daddr);
136 break;
137 }
138 DPRINTF(Timer, "Reading %#x from A9GlobalTimer at offset: %#x\n",
139 pkt->getLE<uint32_t>(), daddr);
140 }
141
142 Tick
143 A9GlobalTimer::write(PacketPtr pkt)
144 {
145 assert(pkt->getAddr() >= pioAddr && pkt->getAddr() < pioAddr + pioSize);
146 assert(pkt->getSize() == 4);
147 Addr daddr = pkt->getAddr() - pioAddr;
148 DPRINTF(Timer, "Writing to A9GlobalTimer at offset: %#x\n", daddr);
149
150 warn_once("A9 Global Timer doesn't support banked per-cpu registers\n");
151
152 if (daddr < Timer::Size)
153 global_timer.write(pkt, daddr);
154 else
155 panic("Tried to write A9GlobalTimer at offset %#x doesn't exist\n",
156 daddr);
157 pkt->makeAtomicResponse();
158 return pioDelay;
159 }
160
161 void
162 A9GlobalTimer::Timer::write(PacketPtr pkt, Addr daddr)
163 {
164 DPRINTF(Timer, "Writing %#x to A9GlobalTimer at offset: %#x\n",
165 pkt->getLE<uint32_t>(), daddr);
166 switch (daddr) {
167 case CounterRegLow32:
168 case CounterRegHigh32:
169 DPRINTF(Timer, "Ignoring unsupported write to Global Timer Counter\n");
170 break;
171 case ControlReg:
172 bool old_enable;
173 bool old_cmpEnable;
174 old_enable = control.enable;
175 old_cmpEnable = control.cmpEnable;
176 control = pkt->getLE<uint32_t>();
177 if ((old_enable == 0) && control.enable)
178 restartCounter();
179 if ((old_cmpEnable == 0) && control.cmpEnable)
180 restartCounter();
181 break;
182 case IntStatusReg:
183 /* TODO: should check that '1' was written. */
184 rawInt = false;
185 if (pendingInt) {
186 pendingInt = false;
187 DPRINTF(Timer, "Clearing interrupt\n");
188 parent->gic->clearInt(intNum);
189 }
190 break;
191 case CmpValRegLow32:
192 cmpVal &= 0xFFFFFFFF00000000ULL;
193 cmpVal |= (uint64_t)pkt->getLE<uint32_t>();
194 break;
195 case CmpValRegHigh32:
196 cmpVal &= 0x00000000FFFFFFFFULL;
197 cmpVal |= ((uint64_t)pkt->getLE<uint32_t>() << 32);
198 break;
199 case AutoIncrementReg:
200 autoIncValue = pkt->getLE<uint32_t>();
201 break;
202 default:
203 panic("Tried to write A9GlobalTimer at offset %#x\n", daddr);
204 break;
205 }
206 }
207
208 void
209 A9GlobalTimer::Timer::restartCounter()
210 {
211 if (!control.enable)
212 return;
213 DPRINTF(Timer, "Restarting counter with value %#x\n", cmpVal);
214
215 Tick time = parent->clockPeriod() * (control.prescalar + 1) * (cmpVal + 1);
216
217 if (time < curTick()) {
218 DPRINTF(Timer, "-- Event time %#x < curTick %#x\n", time, curTick());
219 return;
220 }
221 if (cmpValEvent.scheduled()) {
222 DPRINTF(Timer, "-- Event was already schedule, de-scheduling\n");
223 parent->deschedule(cmpValEvent);
224 }
225 parent->schedule(cmpValEvent, time);
226 DPRINTF(Timer, "-- Scheduling new event for: %d\n", time);
227 }
228
229 void
230 A9GlobalTimer::Timer::counterAtCmpVal()
231 {
232 if (!control.enable)
233 return;
234
235 DPRINTF(Timer, "Counter reached cmpVal\n");
236
237 rawInt = true;
238 bool old_pending = pendingInt;
239 if (control.intEnable)
240 pendingInt = true;
241 if (pendingInt && !old_pending) {
242 DPRINTF(Timer, "-- Causing interrupt\n");
243 parent->gic->sendPPInt(intNum, 0); /* FIXME: cpuNum */
244 }
245
246 if (control.autoIncrement == 0) // one-shot
247 return;
248
249 cmpVal += (uint64_t)autoIncValue;
250 restartCounter();
251 }
252
253 void
254 A9GlobalTimer::Timer::serialize(CheckpointOut &cp) const
255 {
256 DPRINTF(Checkpoint, "Serializing Arm A9GlobalTimer\n");
257
258 uint32_t control_serial = control;
259 SERIALIZE_SCALAR(control_serial);
260
261 SERIALIZE_SCALAR(rawInt);
262 SERIALIZE_SCALAR(pendingInt);
263 SERIALIZE_SCALAR(cmpVal);
264 SERIALIZE_SCALAR(autoIncValue);
265
266 bool is_in_event = cmpValEvent.scheduled();
267 SERIALIZE_SCALAR(is_in_event);
268
269 Tick event_time;
270 if (is_in_event){
271 event_time = cmpValEvent.when();
272 SERIALIZE_SCALAR(event_time);
273 }
274 }
275
276 void
277 A9GlobalTimer::Timer::unserialize(CheckpointIn &cp)
278 {
279 DPRINTF(Checkpoint, "Unserializing Arm A9GlobalTimer\n");
280
281 uint32_t control_serial;
282 UNSERIALIZE_SCALAR(control_serial);
283 control = control_serial;
284
285 UNSERIALIZE_SCALAR(rawInt);
286 UNSERIALIZE_SCALAR(pendingInt);
287 UNSERIALIZE_SCALAR(cmpVal);
288 UNSERIALIZE_SCALAR(autoIncValue);
289
290 bool is_in_event;
291 UNSERIALIZE_SCALAR(is_in_event);
292
293 Tick event_time;
294 if (is_in_event){
295 UNSERIALIZE_SCALAR(event_time);
296 parent->schedule(cmpValEvent, event_time);
297 }
298 }
299
300 void
301 A9GlobalTimer::serialize(CheckpointOut &cp) const
302 {
303 global_timer.serialize(cp);
304 }
305
306 void
307 A9GlobalTimer::unserialize(CheckpointIn &cp)
308 {
309 global_timer.unserialize(cp);
310 }
311
312 A9GlobalTimer *
313 A9GlobalTimerParams::create()
314 {
315 return new A9GlobalTimer(this);
316 }