dev-arm: Implement GIC-400 model from GicV2
[gem5.git] / src / dev / arm / timer_sp804.cc
1 /*
2 * Copyright (c) 2010 ARM Limited
3 * All rights reserved
4 *
5 * The license below extends only to copyright in the software and shall
6 * not be construed as granting a license to any other intellectual
7 * property including but not limited to intellectual property relating
8 * to a hardware implementation of the functionality of the software
9 * licensed hereunder. You may use the software subject to the license
10 * terms below provided that you ensure that this notice is replicated
11 * unmodified and in its entirety in all distributions of the software,
12 * modified or unmodified, in source code or in binary form.
13 *
14 * Redistribution and use in source and binary forms, with or without
15 * modification, are permitted provided that the following conditions are
16 * met: redistributions of source code must retain the above copyright
17 * notice, this list of conditions and the following disclaimer;
18 * redistributions in binary form must reproduce the above copyright
19 * notice, this list of conditions and the following disclaimer in the
20 * documentation and/or other materials provided with the distribution;
21 * neither the name of the copyright holders nor the names of its
22 * contributors may be used to endorse or promote products derived from
23 * this software without specific prior written permission.
24 *
25 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
26 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
27 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
28 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
29 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
30 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
31 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
32 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
33 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
34 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
35 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
36 *
37 * Authors: Ali Saidi
38 */
39
40 #include "dev/arm/timer_sp804.hh"
41
42 #include "base/intmath.hh"
43 #include "base/trace.hh"
44 #include "debug/Checkpoint.hh"
45 #include "debug/Timer.hh"
46 #include "dev/arm/base_gic.hh"
47 #include "mem/packet.hh"
48 #include "mem/packet_access.hh"
49
50 Sp804::Sp804(Params *p)
51 : AmbaPioDevice(p, 0x1000), gic(p->gic),
52 timer0(name() + ".timer0", this, p->int_num0, p->clock0),
53 timer1(name() + ".timer1", this, p->int_num1, p->clock1)
54 {
55 }
56
57 Sp804::Timer::Timer(std::string __name, Sp804 *_parent, int int_num, Tick _clock)
58 : _name(__name), parent(_parent), intNum(int_num), clock(_clock), control(0x20),
59 rawInt(false), pendingInt(false), loadValue(0xffffffff),
60 zeroEvent([this]{ counterAtZero(); }, name())
61 {
62 }
63
64
65 Tick
66 Sp804::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 DPRINTF(Timer, "Reading from DualTimer at offset: %#x\n", daddr);
72
73 if (daddr < Timer::Size)
74 timer0.read(pkt, daddr);
75 else if ((daddr - Timer::Size) < Timer::Size)
76 timer1.read(pkt, daddr - Timer::Size);
77 else if (!readId(pkt, ambaId, pioAddr))
78 panic("Tried to read SP804 at offset %#x that doesn't exist\n", daddr);
79 pkt->makeAtomicResponse();
80 return pioDelay;
81 }
82
83
84 void
85 Sp804::Timer::read(PacketPtr pkt, Addr daddr)
86 {
87 switch(daddr) {
88 case LoadReg:
89 pkt->setLE<uint32_t>(loadValue);
90 break;
91 case CurrentReg:
92 DPRINTF(Timer, "Event schedule for %d, clock=%d, prescale=%d\n",
93 zeroEvent.when(), clock, control.timerPrescale);
94 Tick time;
95 time = zeroEvent.when() - curTick();
96 time = time / clock / power(16, control.timerPrescale);
97 DPRINTF(Timer, "-- returning counter at %d\n", time);
98 pkt->setLE<uint32_t>(time);
99 break;
100 case ControlReg:
101 pkt->setLE<uint32_t>(control);
102 break;
103 case RawISR:
104 pkt->setLE<uint32_t>(rawInt);
105 break;
106 case MaskedISR:
107 pkt->setLE<uint32_t>(pendingInt);
108 break;
109 case BGLoad:
110 pkt->setLE<uint32_t>(loadValue);
111 break;
112 default:
113 panic("Tried to read SP804 timer at offset %#x\n", daddr);
114 break;
115 }
116 DPRINTF(Timer, "Reading %#x from Timer at offset: %#x\n",
117 pkt->getLE<uint32_t>(), daddr);
118 }
119
120 Tick
121 Sp804::write(PacketPtr pkt)
122 {
123 assert(pkt->getAddr() >= pioAddr && pkt->getAddr() < pioAddr + pioSize);
124 assert(pkt->getSize() == 4);
125 Addr daddr = pkt->getAddr() - pioAddr;
126 DPRINTF(Timer, "Writing to DualTimer at offset: %#x\n", daddr);
127
128 if (daddr < Timer::Size)
129 timer0.write(pkt, daddr);
130 else if ((daddr - Timer::Size) < Timer::Size)
131 timer1.write(pkt, daddr - Timer::Size);
132 else if (!readId(pkt, ambaId, pioAddr))
133 panic("Tried to write SP804 at offset %#x that doesn't exist\n", daddr);
134 pkt->makeAtomicResponse();
135 return pioDelay;
136 }
137
138 void
139 Sp804::Timer::write(PacketPtr pkt, Addr daddr)
140 {
141 DPRINTF(Timer, "Writing %#x to Timer at offset: %#x\n",
142 pkt->getLE<uint32_t>(), daddr);
143 switch (daddr) {
144 case LoadReg:
145 loadValue = pkt->getLE<uint32_t>();
146 restartCounter(loadValue);
147 break;
148 case CurrentReg:
149 // Spec says this value can't be written, but linux writes it anyway
150 break;
151 case ControlReg:
152 bool old_enable;
153 old_enable = control.timerEnable;
154 control = pkt->getLE<uint32_t>();
155 if ((old_enable == 0) && control.timerEnable)
156 restartCounter(loadValue);
157 break;
158 case IntClear:
159 rawInt = false;
160 if (pendingInt) {
161 pendingInt = false;
162 DPRINTF(Timer, "Clearing interrupt\n");
163 parent->gic->clearInt(intNum);
164 }
165 break;
166 case BGLoad:
167 loadValue = pkt->getLE<uint32_t>();
168 break;
169 default:
170 panic("Tried to write SP804 timer at offset %#x\n", daddr);
171 break;
172 }
173 }
174
175 void
176 Sp804::Timer::restartCounter(uint32_t val)
177 {
178 DPRINTF(Timer, "Resetting counter with value %#x\n", val);
179 if (!control.timerEnable)
180 return;
181
182 Tick time = clock * power(16, control.timerPrescale);
183 if (control.timerSize)
184 time *= val;
185 else
186 time *= bits(val,15,0);
187
188 if (zeroEvent.scheduled()) {
189 DPRINTF(Timer, "-- Event was already schedule, de-scheduling\n");
190 parent->deschedule(zeroEvent);
191 }
192 parent->schedule(zeroEvent, curTick() + time);
193 DPRINTF(Timer, "-- Scheduling new event for: %d\n", curTick() + time);
194 }
195
196 void
197 Sp804::Timer::counterAtZero()
198 {
199 if (!control.timerEnable)
200 return;
201
202 DPRINTF(Timer, "Counter reached zero\n");
203
204 rawInt = true;
205 bool old_pending = pendingInt;
206 if (control.intEnable)
207 pendingInt = true;
208 if (pendingInt && !old_pending) {
209 DPRINTF(Timer, "-- Causing interrupt\n");
210 parent->gic->sendInt(intNum);
211 }
212
213 if (control.oneShot)
214 return;
215
216 // Free-running
217 if (control.timerMode == 0)
218 restartCounter(0xffffffff);
219 else
220 restartCounter(loadValue);
221 }
222
223 void
224 Sp804::Timer::serialize(CheckpointOut &cp) const
225 {
226 DPRINTF(Checkpoint, "Serializing Arm Sp804\n");
227
228 uint32_t control_serial = control;
229 SERIALIZE_SCALAR(control_serial);
230
231 SERIALIZE_SCALAR(rawInt);
232 SERIALIZE_SCALAR(pendingInt);
233 SERIALIZE_SCALAR(loadValue);
234
235 bool is_in_event = zeroEvent.scheduled();
236 SERIALIZE_SCALAR(is_in_event);
237
238 Tick event_time;
239 if (is_in_event){
240 event_time = zeroEvent.when();
241 SERIALIZE_SCALAR(event_time);
242 }
243 }
244
245 void
246 Sp804::Timer::unserialize(CheckpointIn &cp)
247 {
248 DPRINTF(Checkpoint, "Unserializing Arm Sp804\n");
249
250 uint32_t control_serial;
251 UNSERIALIZE_SCALAR(control_serial);
252 control = control_serial;
253
254 UNSERIALIZE_SCALAR(rawInt);
255 UNSERIALIZE_SCALAR(pendingInt);
256 UNSERIALIZE_SCALAR(loadValue);
257
258 bool is_in_event;
259 UNSERIALIZE_SCALAR(is_in_event);
260
261 Tick event_time;
262 if (is_in_event){
263 UNSERIALIZE_SCALAR(event_time);
264 parent->schedule(zeroEvent, event_time);
265 }
266 }
267
268
269
270 void
271 Sp804::serialize(CheckpointOut &cp) const
272 {
273 timer0.serializeSection(cp, "timer0");
274 timer1.serializeSection(cp, "timer1");
275 }
276
277 void
278 Sp804::unserialize(CheckpointIn &cp)
279 {
280 timer0.unserializeSection(cp, "timer0");
281 timer1.unserializeSection(cp, "timer1");
282 }
283
284 Sp804 *
285 Sp804Params::create()
286 {
287 return new Sp804(this);
288 }