timer_sp804.cc revision 12086:069c529a76fd
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
50Sp804::Sp804(Params *p)
51    : AmbaPioDevice(p, 0xfff), 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
57Sp804::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
65Tick
66Sp804::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
84void
85Sp804::Timer::read(PacketPtr pkt, Addr daddr)
86{
87    switch(daddr) {
88      case LoadReg:
89        pkt->set<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->set<uint32_t>(time);
99        break;
100      case ControlReg:
101        pkt->set<uint32_t>(control);
102        break;
103      case RawISR:
104        pkt->set<uint32_t>(rawInt);
105        break;
106      case MaskedISR:
107        pkt->set<uint32_t>(pendingInt);
108        break;
109      case BGLoad:
110        pkt->set<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", pkt->get<uint32_t>(), daddr);
117}
118
119Tick
120Sp804::write(PacketPtr pkt)
121{
122    assert(pkt->getAddr() >= pioAddr && pkt->getAddr() < pioAddr + pioSize);
123    assert(pkt->getSize() == 4);
124    Addr daddr = pkt->getAddr() - pioAddr;
125    DPRINTF(Timer, "Writing to DualTimer at offset: %#x\n", daddr);
126
127    if (daddr < Timer::Size)
128        timer0.write(pkt, daddr);
129    else if ((daddr - Timer::Size) < Timer::Size)
130        timer1.write(pkt, daddr - Timer::Size);
131    else if (!readId(pkt, ambaId, pioAddr))
132        panic("Tried to write SP804 at offset %#x that doesn't exist\n", daddr);
133    pkt->makeAtomicResponse();
134    return pioDelay;
135}
136
137void
138Sp804::Timer::write(PacketPtr pkt, Addr daddr)
139{
140    DPRINTF(Timer, "Writing %#x to Timer at offset: %#x\n", pkt->get<uint32_t>(), daddr);
141    switch (daddr) {
142      case LoadReg:
143        loadValue = pkt->get<uint32_t>();
144        restartCounter(loadValue);
145        break;
146      case CurrentReg:
147        // Spec says this value can't be written, but linux writes it anyway
148        break;
149      case ControlReg:
150        bool old_enable;
151        old_enable = control.timerEnable;
152        control = pkt->get<uint32_t>();
153        if ((old_enable == 0) && control.timerEnable)
154            restartCounter(loadValue);
155        break;
156      case IntClear:
157        rawInt = false;
158        if (pendingInt) {
159            pendingInt = false;
160            DPRINTF(Timer, "Clearing interrupt\n");
161            parent->gic->clearInt(intNum);
162        }
163        break;
164      case BGLoad:
165        loadValue = pkt->get<uint32_t>();
166        break;
167      default:
168        panic("Tried to write SP804 timer at offset %#x\n", daddr);
169        break;
170    }
171}
172
173void
174Sp804::Timer::restartCounter(uint32_t val)
175{
176    DPRINTF(Timer, "Resetting counter with value %#x\n", val);
177    if (!control.timerEnable)
178        return;
179
180    Tick time = clock * power(16, control.timerPrescale);
181    if (control.timerSize)
182        time *= val;
183    else
184        time *= bits(val,15,0);
185
186    if (zeroEvent.scheduled()) {
187        DPRINTF(Timer, "-- Event was already schedule, de-scheduling\n");
188        parent->deschedule(zeroEvent);
189    }
190    parent->schedule(zeroEvent, curTick() + time);
191    DPRINTF(Timer, "-- Scheduling new event for: %d\n", curTick() + time);
192}
193
194void
195Sp804::Timer::counterAtZero()
196{
197    if (!control.timerEnable)
198        return;
199
200    DPRINTF(Timer, "Counter reached zero\n");
201
202    rawInt = true;
203    bool old_pending = pendingInt;
204    if (control.intEnable)
205        pendingInt = true;
206    if (pendingInt && !old_pending) {
207        DPRINTF(Timer, "-- Causing interrupt\n");
208        parent->gic->sendInt(intNum);
209    }
210
211    if (control.oneShot)
212        return;
213
214    // Free-running
215    if (control.timerMode == 0)
216        restartCounter(0xffffffff);
217    else
218        restartCounter(loadValue);
219}
220
221void
222Sp804::Timer::serialize(CheckpointOut &cp) const
223{
224    DPRINTF(Checkpoint, "Serializing Arm Sp804\n");
225
226    uint32_t control_serial = control;
227    SERIALIZE_SCALAR(control_serial);
228
229    SERIALIZE_SCALAR(rawInt);
230    SERIALIZE_SCALAR(pendingInt);
231    SERIALIZE_SCALAR(loadValue);
232
233    bool is_in_event = zeroEvent.scheduled();
234    SERIALIZE_SCALAR(is_in_event);
235
236    Tick event_time;
237    if (is_in_event){
238        event_time = zeroEvent.when();
239        SERIALIZE_SCALAR(event_time);
240    }
241}
242
243void
244Sp804::Timer::unserialize(CheckpointIn &cp)
245{
246    DPRINTF(Checkpoint, "Unserializing Arm Sp804\n");
247
248    uint32_t control_serial;
249    UNSERIALIZE_SCALAR(control_serial);
250    control = control_serial;
251
252    UNSERIALIZE_SCALAR(rawInt);
253    UNSERIALIZE_SCALAR(pendingInt);
254    UNSERIALIZE_SCALAR(loadValue);
255
256    bool is_in_event;
257    UNSERIALIZE_SCALAR(is_in_event);
258
259    Tick event_time;
260    if (is_in_event){
261        UNSERIALIZE_SCALAR(event_time);
262        parent->schedule(zeroEvent, event_time);
263    }
264}
265
266
267
268void
269Sp804::serialize(CheckpointOut &cp) const
270{
271    timer0.serializeSection(cp, "timer0");
272    timer1.serializeSection(cp, "timer1");
273}
274
275void
276Sp804::unserialize(CheckpointIn &cp)
277{
278    timer0.unserializeSection(cp, "timer0");
279    timer1.unserializeSection(cp, "timer1");
280}
281
282Sp804 *
283Sp804Params::create()
284{
285    return new Sp804(this);
286}
287