1/*
2 * Copyright (c) 2006 The Regents of The University of Michigan
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are
7 * met: redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer;
9 * redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution;
12 * neither the name of the copyright holders nor the names of its
13 * contributors may be used to endorse or promote products derived from
14 * this software without specific prior written permission.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
17 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
18 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
19 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
20 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
21 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
22 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
26 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 *
28 * Authors: Ali Saidi
29 */
30
31/** @file
32 * This device implemetns the niagara I/O bridge chip. It manages incomming
33 * interrupts and posts them to the CPU when needed. It holds mask registers and
34 * various status registers for CPUs to check what interrupts are pending as
35 * well as facilities to send IPIs to other cpus.
36 */
37
38#include "dev/sparc/iob.hh"
39
40#include <cstring>
41
42#include "arch/sparc/faults.hh"
43#include "arch/sparc/interrupts.hh"
44#include "arch/sparc/isa_traits.hh"
45#include "base/bitfield.hh"
46#include "base/trace.hh"
47#include "cpu/intr_control.hh"
48#include "cpu/thread_context.hh"
49#include "debug/Iob.hh"
50#include "dev/platform.hh"
51#include "mem/packet_access.hh"
52#include "mem/port.hh"
53#include "sim/faults.hh"
54#include "sim/system.hh"
55
56Iob::Iob(const Params *p)
57    : PioDevice(p), ic(p->platform->intrctrl)
58{
59    iobManAddr = ULL(0x9800000000);
60    iobManSize = ULL(0x0100000000);
61    iobJBusAddr = ULL(0x9F00000000);
62    iobJBusSize = ULL(0x0100000000);
63    assert (params()->system->threadContexts.size() <= MaxNiagaraProcs);
64
65    pioDelay = p->pio_latency;
66
67    for (int x = 0; x < NumDeviceIds; ++x) {
68        intMan[x].cpu = 0;
69        intMan[x].vector = 0;
70        intCtl[x].mask = true;
71        intCtl[x].pend = false;
72    }
73
74}
75
76Tick
77Iob::read(PacketPtr pkt)
78{
79
80    if (pkt->getAddr() >= iobManAddr && pkt->getAddr() < iobManAddr + iobManSize)
81        readIob(pkt);
82    else if (pkt->getAddr() >= iobJBusAddr && pkt->getAddr() < iobJBusAddr+iobJBusSize)
83        readJBus(pkt);
84    else
85        panic("Invalid address reached Iob\n");
86
87    pkt->makeAtomicResponse();
88    return pioDelay;
89}
90
91void
92Iob::readIob(PacketPtr pkt)
93{
94        Addr accessAddr = pkt->getAddr() - iobManAddr;
95
96        assert(IntManAddr == 0);
97        if (accessAddr < IntManAddr + IntManSize) {
98            int index = (accessAddr - IntManAddr) >> 3;
99            uint64_t data = intMan[index].cpu << 8 | intMan[index].vector << 0;
100            pkt->setBE(data);
101            return;
102        }
103
104        if (accessAddr >= IntCtlAddr && accessAddr < IntCtlAddr + IntCtlSize) {
105            int index = (accessAddr - IntCtlAddr) >> 3;
106            uint64_t data = intCtl[index].mask  ? 1 << 2 : 0 |
107                intCtl[index].pend  ? 1 << 0 : 0;
108            pkt->setBE(data);
109            return;
110        }
111
112        if (accessAddr == JIntVecAddr) {
113            pkt->setBE(jIntVec);
114            return;
115        }
116
117        panic("Read to unknown IOB offset 0x%x\n", accessAddr);
118}
119
120void
121Iob::readJBus(PacketPtr pkt)
122{
123        Addr accessAddr = pkt->getAddr() - iobJBusAddr;
124        ContextID cpuid = pkt->req->contextId();
125        int index;
126        uint64_t data;
127
128
129
130
131        if (accessAddr >= JIntData0Addr && accessAddr < JIntData1Addr) {
132            index = (accessAddr - JIntData0Addr) >> 3;
133            pkt->setBE(jBusData0[index]);
134            return;
135        }
136
137        if (accessAddr >= JIntData1Addr && accessAddr < JIntDataA0Addr) {
138            index = (accessAddr - JIntData1Addr) >> 3;
139            pkt->setBE(jBusData1[index]);
140            return;
141        }
142
143        if (accessAddr == JIntDataA0Addr) {
144            pkt->setBE(jBusData0[cpuid]);
145            return;
146        }
147
148        if (accessAddr == JIntDataA1Addr) {
149            pkt->setBE(jBusData1[cpuid]);
150            return;
151        }
152
153        if (accessAddr >= JIntBusyAddr && accessAddr < JIntBusyAddr + JIntBusySize) {
154            index = (accessAddr - JIntBusyAddr) >> 3;
155            data = jIntBusy[index].busy ? 1 << 5 : 0 |
156                   jIntBusy[index].source;
157            pkt->setBE(data);
158            return;
159        }
160        if (accessAddr == JIntABusyAddr) {
161            data = jIntBusy[cpuid].busy ? 1 << 5 : 0 |
162                   jIntBusy[cpuid].source;
163            pkt->setBE(data);
164            return;
165        };
166
167        panic("Read to unknown JBus offset 0x%x\n", accessAddr);
168}
169
170Tick
171Iob::write(PacketPtr pkt)
172{
173    if (pkt->getAddr() >= iobManAddr && pkt->getAddr() < iobManAddr + iobManSize)
174        writeIob(pkt);
175    else if (pkt->getAddr() >= iobJBusAddr && pkt->getAddr() < iobJBusAddr+iobJBusSize)
176        writeJBus(pkt);
177    else
178        panic("Invalid address reached Iob\n");
179
180
181    pkt->makeAtomicResponse();
182    return pioDelay;
183}
184
185void
186Iob::writeIob(PacketPtr pkt)
187{
188        Addr accessAddr = pkt->getAddr() - iobManAddr;
189        int index;
190        uint64_t data;
191
192        assert(IntManAddr == 0);
193        if (accessAddr < IntManAddr + IntManSize) {
194            index = (accessAddr - IntManAddr) >> 3;
195            data = pkt->getBE<uint64_t>();
196            intMan[index].cpu = bits(data,12,8);
197            intMan[index].vector = bits(data,5,0);
198            DPRINTF(Iob, "Wrote IntMan %d cpu %d, vec %d\n", index,
199                    intMan[index].cpu, intMan[index].vector);
200            return;
201        }
202
203        if (accessAddr >= IntCtlAddr && accessAddr < IntCtlAddr + IntCtlSize) {
204            index = (accessAddr - IntCtlAddr) >> 3;
205            data = pkt->getBE<uint64_t>();
206            intCtl[index].mask = bits(data,2,2);
207            if (bits(data,1,1))
208                intCtl[index].pend = false;
209            DPRINTF(Iob, "Wrote IntCtl %d pend %d cleared %d\n", index,
210                    intCtl[index].pend, bits(data,2,2));
211            return;
212        }
213
214        if (accessAddr == JIntVecAddr) {
215            jIntVec = bits(pkt->getBE<uint64_t>(), 5,0);
216            DPRINTF(Iob, "Wrote jIntVec %d\n", jIntVec);
217            return;
218        }
219
220        if (accessAddr >= IntVecDisAddr && accessAddr < IntVecDisAddr + IntVecDisSize) {
221            Type type;
222            int cpu_id;
223            int vector;
224            index = (accessAddr - IntManAddr) >> 3;
225            data = pkt->getBE<uint64_t>();
226            type = (Type)bits(data,17,16);
227            cpu_id = bits(data, 12,8);
228            vector = bits(data,5,0);
229            generateIpi(type,cpu_id, vector);
230            return;
231        }
232
233        panic("Write to unknown IOB offset 0x%x\n", accessAddr);
234}
235
236void
237Iob::writeJBus(PacketPtr pkt)
238{
239        Addr accessAddr = pkt->getAddr() - iobJBusAddr;
240        ContextID cpuid = pkt->req->contextId();
241        int index;
242        uint64_t data;
243
244        if (accessAddr >= JIntBusyAddr && accessAddr < JIntBusyAddr + JIntBusySize) {
245            index = (accessAddr - JIntBusyAddr) >> 3;
246            data = pkt->getBE<uint64_t>();
247            jIntBusy[index].busy = bits(data,5,5);
248            DPRINTF(Iob, "Wrote jIntBusy index %d busy: %d\n", index,
249                    jIntBusy[index].busy);
250            return;
251        }
252        if (accessAddr == JIntABusyAddr) {
253            data = pkt->getBE<uint64_t>();
254            jIntBusy[cpuid].busy = bits(data,5,5);
255            DPRINTF(Iob, "Wrote jIntBusy index %d busy: %d\n", cpuid,
256                    jIntBusy[cpuid].busy);
257            return;
258        };
259
260        panic("Write to unknown JBus offset 0x%x\n", accessAddr);
261}
262
263void
264Iob::receiveDeviceInterrupt(DeviceId devid)
265{
266    assert(devid < NumDeviceIds);
267    if (intCtl[devid].mask)
268        return;
269    intCtl[devid].mask = true;
270    intCtl[devid].pend = true;
271    DPRINTF(Iob, "Receiving Device interrupt: %d for cpu %d vec %d\n",
272            devid, intMan[devid].cpu, intMan[devid].vector);
273    ic->post(intMan[devid].cpu, SparcISA::IT_INT_VEC, intMan[devid].vector);
274}
275
276
277void
278Iob::generateIpi(Type type, int cpu_id, int vector)
279{
280    SparcISA::SparcFault<SparcISA::PowerOnReset> *por = new SparcISA::PowerOnReset();
281    if (cpu_id >= sys->numContexts())
282        return;
283
284    switch (type) {
285      case 0: // interrupt
286        DPRINTF(Iob, "Generating interrupt because of I/O write to cpu: %d vec %d\n",
287                cpu_id, vector);
288        ic->post(cpu_id, SparcISA::IT_INT_VEC, vector);
289        break;
290      case 1: // reset
291        warn("Sending reset to CPU: %d\n", cpu_id);
292        if (vector != por->trapType())
293            panic("Don't know how to set non-POR reset to cpu\n");
294        por->invoke(sys->threadContexts[cpu_id]);
295        sys->threadContexts[cpu_id]->activate();
296        break;
297      case 2: // idle -- this means stop executing and don't wake on interrupts
298        DPRINTF(Iob, "Idling CPU because of I/O write cpu: %d\n", cpu_id);
299        sys->threadContexts[cpu_id]->halt();
300        break;
301      case 3: // resume
302        DPRINTF(Iob, "Resuming CPU because of I/O write cpu: %d\n", cpu_id);
303        sys->threadContexts[cpu_id]->activate();
304        break;
305      default:
306        panic("Invalid type to generate ipi\n");
307    }
308}
309
310bool
311Iob::receiveJBusInterrupt(int cpu_id, int source, uint64_t d0, uint64_t d1)
312{
313    // If we are already dealing with an interrupt for that cpu we can't deal
314    // with another one right now... come back later
315    if (jIntBusy[cpu_id].busy)
316        return false;
317
318    DPRINTF(Iob, "Receiving jBus interrupt: %d for cpu %d vec %d\n",
319            source, cpu_id, jIntVec);
320
321    jIntBusy[cpu_id].busy = true;
322    jIntBusy[cpu_id].source = source;
323    jBusData0[cpu_id] = d0;
324    jBusData1[cpu_id] = d1;
325
326    ic->post(cpu_id, SparcISA::IT_INT_VEC, jIntVec);
327    return true;
328}
329
330AddrRangeList
331Iob::getAddrRanges() const
332{
333    AddrRangeList ranges;
334    ranges.push_back(RangeSize(iobManAddr, iobManSize));
335    ranges.push_back(RangeSize(iobJBusAddr, iobJBusSize));
336    return ranges;
337}
338
339
340void
341Iob::serialize(CheckpointOut &cp) const
342{
343
344    SERIALIZE_SCALAR(jIntVec);
345    SERIALIZE_ARRAY(jBusData0, MaxNiagaraProcs);
346    SERIALIZE_ARRAY(jBusData1, MaxNiagaraProcs);
347    for (int x = 0; x < NumDeviceIds; x++) {
348        ScopedCheckpointSection sec(cp, csprintf("Int%d", x));
349        paramOut(cp, "cpu", intMan[x].cpu);
350        paramOut(cp, "vector", intMan[x].vector);
351        paramOut(cp, "mask", intCtl[x].mask);
352        paramOut(cp, "pend", intCtl[x].pend);
353    };
354    for (int x = 0; x < MaxNiagaraProcs; x++) {
355        ScopedCheckpointSection sec(cp, csprintf("jIntBusy%d", x));
356        paramOut(cp, "busy", jIntBusy[x].busy);
357        paramOut(cp, "source", jIntBusy[x].source);
358    };
359}
360
361void
362Iob::unserialize(CheckpointIn &cp)
363{
364    UNSERIALIZE_SCALAR(jIntVec);
365    UNSERIALIZE_ARRAY(jBusData0, MaxNiagaraProcs);
366    UNSERIALIZE_ARRAY(jBusData1, MaxNiagaraProcs);
367    for (int x = 0; x < NumDeviceIds; x++) {
368        ScopedCheckpointSection sec(cp, csprintf("Int%d", x));
369        paramIn(cp, "cpu", intMan[x].cpu);
370        paramIn(cp, "vector", intMan[x].vector);
371        paramIn(cp, "mask", intCtl[x].mask);
372        paramIn(cp, "pend", intCtl[x].pend);
373    };
374    for (int x = 0; x < MaxNiagaraProcs; x++) {
375        ScopedCheckpointSection sec(cp, csprintf("jIntBusy%d", x));
376        paramIn(cp, "busy", jIntBusy[x].busy);
377        paramIn(cp, "source", jIntBusy[x].source);
378    };
379}
380
381Iob *
382IobParams::create()
383{
384    return new Iob(this);
385}
386