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