tsunami_io.cc revision 4762:c94e103c83ad
1/*
2 * Copyright (c) 2004-2005 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 *          Andrew Schultz
30 *          Miguel Serrano
31 */
32
33/** @file
34 * Tsunami I/O including PIC, PIT, RTC, DMA
35 */
36
37#include <sys/time.h>
38
39#include <deque>
40#include <string>
41#include <vector>
42
43#include "base/time.hh"
44#include "base/trace.hh"
45#include "dev/pitreg.h"
46#include "dev/rtcreg.h"
47#include "dev/alpha/tsunami_cchip.hh"
48#include "dev/alpha/tsunami.hh"
49#include "dev/alpha/tsunami_io.hh"
50#include "dev/alpha/tsunamireg.h"
51#include "mem/packet.hh"
52#include "mem/packet_access.hh"
53#include "mem/port.hh"
54#include "sim/system.hh"
55
56using namespace std;
57//Should this be AlphaISA?
58using namespace TheISA;
59
60TsunamiIO::RTC::RTC(const string &n, Tsunami* tsunami,
61                    const TsunamiIO::Params *p)
62    : _name(n), event(tsunami, p->frequency), addr(0)
63{
64    memset(clock_data, 0, sizeof(clock_data));
65    stat_regA = RTCA_32768HZ | RTCA_1024HZ;
66    stat_regB = RTCB_PRDC_IE |RTCB_BIN | RTCB_24HR;
67
68    year = p->time.tm_year;
69
70    if (p->year_is_bcd) {
71        // The datasheet says that the year field can be either BCD or
72        // years since 1900.  Linux seems to be happy with years since
73        // 1900.
74        year = year % 100;
75        int tens = year / 10;
76        int ones = year % 10;
77        year = (tens << 4) + ones;
78    }
79
80    // Unix is 0-11 for month, data seet says start at 1
81    mon = p->time.tm_mon + 1;
82    mday = p->time.tm_mday;
83    hour = p->time.tm_hour;
84    min = p->time.tm_min;
85    sec = p->time.tm_sec;
86
87    // Datasheet says 1 is sunday
88    wday = p->time.tm_wday + 1;
89
90    DPRINTFN("Real-time clock set to %s", asctime(&p->time));
91}
92
93void
94TsunamiIO::RTC::writeAddr(const uint8_t data)
95{
96    if (data <= RTC_STAT_REGD)
97        addr = data;
98    else
99        panic("RTC addresses over 0xD are not implemented.\n");
100}
101
102void
103TsunamiIO::RTC::writeData(const uint8_t data)
104{
105    if (addr < RTC_STAT_REGA)
106        clock_data[addr] = data;
107    else {
108        switch (addr) {
109          case RTC_STAT_REGA:
110            if (data != (RTCA_32768HZ | RTCA_1024HZ))
111                panic("Unimplemented RTC register A value write!\n");
112            stat_regA = data;
113            break;
114          case RTC_STAT_REGB:
115            if ((data & ~(RTCB_PRDC_IE | RTCB_SQWE)) != (RTCB_BIN | RTCB_24HR))
116                panic("Write to RTC reg B bits that are not implemented!\n");
117
118            if (data & RTCB_PRDC_IE) {
119                if (!event.scheduled())
120                    event.scheduleIntr();
121            } else {
122                if (event.scheduled())
123                    event.deschedule();
124            }
125            stat_regB = data;
126            break;
127          case RTC_STAT_REGC:
128          case RTC_STAT_REGD:
129            panic("RTC status registers C and D are not implemented.\n");
130            break;
131        }
132    }
133}
134
135uint8_t
136TsunamiIO::RTC::readData()
137{
138    if (addr < RTC_STAT_REGA)
139        return clock_data[addr];
140    else {
141        switch (addr) {
142          case RTC_STAT_REGA:
143            // toggle UIP bit for linux
144            stat_regA ^= RTCA_UIP;
145            return stat_regA;
146            break;
147          case RTC_STAT_REGB:
148            return stat_regB;
149            break;
150          case RTC_STAT_REGC:
151          case RTC_STAT_REGD:
152            return 0x00;
153            break;
154          default:
155            panic("Shouldn't be here");
156        }
157    }
158}
159
160void
161TsunamiIO::RTC::serialize(const string &base, ostream &os)
162{
163    paramOut(os, base + ".addr", addr);
164    arrayParamOut(os, base + ".clock_data", clock_data, sizeof(clock_data));
165    paramOut(os, base + ".stat_regA", stat_regA);
166    paramOut(os, base + ".stat_regB", stat_regB);
167}
168
169void
170TsunamiIO::RTC::unserialize(const string &base, Checkpoint *cp,
171                            const string &section)
172{
173    paramIn(cp, section, base + ".addr", addr);
174    arrayParamIn(cp, section, base + ".clock_data", clock_data,
175                 sizeof(clock_data));
176    paramIn(cp, section, base + ".stat_regA", stat_regA);
177    paramIn(cp, section, base + ".stat_regB", stat_regB);
178
179    // We're not unserializing the event here, but we need to
180    // rescehedule the event since curTick was moved forward by the
181    // checkpoint
182    event.reschedule(curTick + event.interval);
183}
184
185TsunamiIO::RTC::RTCEvent::RTCEvent(Tsunami*t, Tick i)
186    : Event(&mainEventQueue), tsunami(t), interval(i)
187{
188    DPRINTF(MC146818, "RTC Event Initilizing\n");
189    schedule(curTick + interval);
190}
191
192void
193TsunamiIO::RTC::RTCEvent::scheduleIntr()
194{
195  schedule(curTick + interval);
196}
197
198void
199TsunamiIO::RTC::RTCEvent::process()
200{
201    DPRINTF(MC146818, "RTC Timer Interrupt\n");
202    schedule(curTick + interval);
203    //Actually interrupt the processor here
204    tsunami->cchip->postRTC();
205}
206
207const char *
208TsunamiIO::RTC::RTCEvent::description()
209{
210    return "tsunami RTC interrupt";
211}
212
213TsunamiIO::PITimer::PITimer(const string &name)
214    : _name(name), counter0(name + ".counter0"), counter1(name + ".counter1"),
215      counter2(name + ".counter2")
216{
217    counter[0] = &counter0;
218    counter[1] = &counter0;
219    counter[2] = &counter0;
220}
221
222void
223TsunamiIO::PITimer::writeControl(const uint8_t data)
224{
225    int rw;
226    int sel;
227
228    sel = GET_CTRL_SEL(data);
229
230    if (sel == PIT_READ_BACK)
231       panic("PITimer Read-Back Command is not implemented.\n");
232
233    rw = GET_CTRL_RW(data);
234
235    if (rw == PIT_RW_LATCH_COMMAND)
236        counter[sel]->latchCount();
237    else {
238        counter[sel]->setRW(rw);
239        counter[sel]->setMode(GET_CTRL_MODE(data));
240        counter[sel]->setBCD(GET_CTRL_BCD(data));
241    }
242}
243
244void
245TsunamiIO::PITimer::serialize(const string &base, ostream &os)
246{
247    // serialize the counters
248    counter0.serialize(base + ".counter0", os);
249    counter1.serialize(base + ".counter1", os);
250    counter2.serialize(base + ".counter2", os);
251}
252
253void
254TsunamiIO::PITimer::unserialize(const string &base, Checkpoint *cp,
255                                const string &section)
256{
257    // unserialze the counters
258    counter0.unserialize(base + ".counter0", cp, section);
259    counter1.unserialize(base + ".counter1", cp, section);
260    counter2.unserialize(base + ".counter2", cp, section);
261}
262
263TsunamiIO::PITimer::Counter::Counter(const string &name)
264    : _name(name), event(this), count(0), latched_count(0), period(0),
265      mode(0), output_high(false), latch_on(false), read_byte(LSB),
266      write_byte(LSB)
267{
268
269}
270
271void
272TsunamiIO::PITimer::Counter::latchCount()
273{
274    // behave like a real latch
275    if(!latch_on) {
276        latch_on = true;
277        read_byte = LSB;
278        latched_count = count;
279    }
280}
281
282uint8_t
283TsunamiIO::PITimer::Counter::read()
284{
285    if (latch_on) {
286        switch (read_byte) {
287          case LSB:
288            read_byte = MSB;
289            return (uint8_t)latched_count;
290            break;
291          case MSB:
292            read_byte = LSB;
293            latch_on = false;
294            return latched_count >> 8;
295            break;
296          default:
297            panic("Shouldn't be here");
298        }
299    } else {
300        switch (read_byte) {
301          case LSB:
302            read_byte = MSB;
303            return (uint8_t)count;
304            break;
305          case MSB:
306            read_byte = LSB;
307            return count >> 8;
308            break;
309          default:
310            panic("Shouldn't be here");
311        }
312    }
313}
314
315void
316TsunamiIO::PITimer::Counter::write(const uint8_t data)
317{
318    switch (write_byte) {
319      case LSB:
320        count = (count & 0xFF00) | data;
321
322        if (event.scheduled())
323          event.deschedule();
324        output_high = false;
325        write_byte = MSB;
326        break;
327
328      case MSB:
329        count = (count & 0x00FF) | (data << 8);
330        period = count;
331
332        if (period > 0) {
333            DPRINTF(Tsunami, "Timer set to curTick + %d\n",
334                    count * event.interval);
335            event.schedule(curTick + count * event.interval);
336        }
337        write_byte = LSB;
338        break;
339    }
340}
341
342void
343TsunamiIO::PITimer::Counter::setRW(int rw_val)
344{
345    if (rw_val != PIT_RW_16BIT)
346        panic("Only LSB/MSB read/write is implemented.\n");
347}
348
349void
350TsunamiIO::PITimer::Counter::setMode(int mode_val)
351{
352    if(mode_val != PIT_MODE_INTTC && mode_val != PIT_MODE_RATEGEN &&
353       mode_val != PIT_MODE_SQWAVE)
354        panic("PIT mode %#x is not implemented: \n", mode_val);
355
356    mode = mode_val;
357}
358
359void
360TsunamiIO::PITimer::Counter::setBCD(int bcd_val)
361{
362    if (bcd_val != PIT_BCD_FALSE)
363        panic("PITimer does not implement BCD counts.\n");
364}
365
366bool
367TsunamiIO::PITimer::Counter::outputHigh()
368{
369    return output_high;
370}
371
372void
373TsunamiIO::PITimer::Counter::serialize(const string &base, ostream &os)
374{
375    paramOut(os, base + ".count", count);
376    paramOut(os, base + ".latched_count", latched_count);
377    paramOut(os, base + ".period", period);
378    paramOut(os, base + ".mode", mode);
379    paramOut(os, base + ".output_high", output_high);
380    paramOut(os, base + ".latch_on", latch_on);
381    paramOut(os, base + ".read_byte", read_byte);
382    paramOut(os, base + ".write_byte", write_byte);
383
384    Tick event_tick = 0;
385    if (event.scheduled())
386        event_tick = event.when();
387    paramOut(os, base + ".event_tick", event_tick);
388}
389
390void
391TsunamiIO::PITimer::Counter::unserialize(const string &base, Checkpoint *cp,
392                                         const string &section)
393{
394    paramIn(cp, section, base + ".count", count);
395    paramIn(cp, section, base + ".latched_count", latched_count);
396    paramIn(cp, section, base + ".period", period);
397    paramIn(cp, section, base + ".mode", mode);
398    paramIn(cp, section, base + ".output_high", output_high);
399    paramIn(cp, section, base + ".latch_on", latch_on);
400    paramIn(cp, section, base + ".read_byte", read_byte);
401    paramIn(cp, section, base + ".write_byte", write_byte);
402
403    Tick event_tick;
404    paramIn(cp, section, base + ".event_tick", event_tick);
405    if (event_tick)
406        event.schedule(event_tick);
407}
408
409TsunamiIO::PITimer::Counter::CounterEvent::CounterEvent(Counter* c_ptr)
410    : Event(&mainEventQueue)
411{
412    interval = (Tick)(Clock::Float::s / 1193180.0);
413    counter = c_ptr;
414}
415
416void
417TsunamiIO::PITimer::Counter::CounterEvent::process()
418{
419    DPRINTF(Tsunami, "Timer Interrupt\n");
420    switch (counter->mode) {
421      case PIT_MODE_INTTC:
422        counter->output_high = true;
423      case PIT_MODE_RATEGEN:
424      case PIT_MODE_SQWAVE:
425        break;
426      default:
427        panic("Unimplemented PITimer mode.\n");
428    }
429}
430
431const char *
432TsunamiIO::PITimer::Counter::CounterEvent::description()
433{
434    return "tsunami 8254 Interval timer";
435}
436
437TsunamiIO::TsunamiIO(const Params *p)
438    : BasicPioDevice(p), tsunami(p->tsunami), pitimer(p->name + "pitimer"),
439      rtc(p->name + ".rtc", p->tsunami, p)
440{
441    pioSize = 0x100;
442
443    // set the back pointer from tsunami to myself
444    tsunami->io = this;
445
446    timerData = 0;
447    picr = 0;
448    picInterrupting = false;
449}
450
451Tick
452TsunamiIO::frequency() const
453{
454    return Clock::Frequency / params()->frequency;
455}
456
457Tick
458TsunamiIO::read(PacketPtr pkt)
459{
460    assert(pkt->result == Packet::Unknown);
461    assert(pkt->getAddr() >= pioAddr && pkt->getAddr() < pioAddr + pioSize);
462
463    Addr daddr = pkt->getAddr() - pioAddr;
464
465    DPRINTF(Tsunami, "io read  va=%#x size=%d IOPorrt=%#x\n", pkt->getAddr(),
466            pkt->getSize(), daddr);
467
468    pkt->allocate();
469
470    if (pkt->getSize() == sizeof(uint8_t)) {
471        switch(daddr) {
472          // PIC1 mask read
473          case TSDEV_PIC1_MASK:
474            pkt->set(~mask1);
475            break;
476          case TSDEV_PIC2_MASK:
477            pkt->set(~mask2);
478            break;
479          case TSDEV_PIC1_ISR:
480              // !!! If this is modified 64bit case needs to be too
481              // Pal code has to do a 64 bit physical read because there is
482              // no load physical byte instruction
483              pkt->set(picr);
484              break;
485          case TSDEV_PIC2_ISR:
486              // PIC2 not implemnted... just return 0
487              pkt->set(0x00);
488              break;
489          case TSDEV_TMR0_DATA:
490            pkt->set(pitimer.counter0.read());
491            break;
492          case TSDEV_TMR1_DATA:
493            pkt->set(pitimer.counter1.read());
494            break;
495          case TSDEV_TMR2_DATA:
496            pkt->set(pitimer.counter2.read());
497            break;
498          case TSDEV_RTC_DATA:
499            pkt->set(rtc.readData());
500            break;
501          case TSDEV_CTRL_PORTB:
502            if (pitimer.counter2.outputHigh())
503                pkt->set(PORTB_SPKR_HIGH);
504            else
505                pkt->set(0x00);
506            break;
507          default:
508            panic("I/O Read - va%#x size %d\n", pkt->getAddr(), pkt->getSize());
509        }
510    } else if (pkt->getSize() == sizeof(uint64_t)) {
511        if (daddr == TSDEV_PIC1_ISR)
512            pkt->set<uint64_t>(picr);
513        else
514           panic("I/O Read - invalid addr - va %#x size %d\n",
515                   pkt->getAddr(), pkt->getSize());
516    } else {
517       panic("I/O Read - invalid size - va %#x size %d\n", pkt->getAddr(), pkt->getSize());
518    }
519    pkt->result = Packet::Success;
520    return pioDelay;
521}
522
523Tick
524TsunamiIO::write(PacketPtr pkt)
525{
526    assert(pkt->result == Packet::Unknown);
527    assert(pkt->getAddr() >= pioAddr && pkt->getAddr() < pioAddr + pioSize);
528    Addr daddr = pkt->getAddr() - pioAddr;
529
530    DPRINTF(Tsunami, "io write - va=%#x size=%d IOPort=%#x Data=%#x\n",
531            pkt->getAddr(), pkt->getSize(), pkt->getAddr() & 0xfff, (uint32_t)pkt->get<uint8_t>());
532
533    assert(pkt->getSize() == sizeof(uint8_t));
534
535    switch(daddr) {
536      case TSDEV_PIC1_MASK:
537        mask1 = ~(pkt->get<uint8_t>());
538        if ((picr & mask1) && !picInterrupting) {
539            picInterrupting = true;
540            tsunami->cchip->postDRIR(55);
541            DPRINTF(Tsunami, "posting pic interrupt to cchip\n");
542        }
543        if ((!(picr & mask1)) && picInterrupting) {
544            picInterrupting = false;
545            tsunami->cchip->clearDRIR(55);
546            DPRINTF(Tsunami, "clearing pic interrupt\n");
547        }
548        break;
549      case TSDEV_PIC2_MASK:
550        mask2 = pkt->get<uint8_t>();
551        //PIC2 Not implemented to interrupt
552        break;
553      case TSDEV_PIC1_ACK:
554        // clear the interrupt on the PIC
555        picr &= ~(1 << (pkt->get<uint8_t>() & 0xF));
556        if (!(picr & mask1))
557            tsunami->cchip->clearDRIR(55);
558        break;
559      case TSDEV_DMA1_MODE:
560        mode1 = pkt->get<uint8_t>();
561        break;
562      case TSDEV_DMA2_MODE:
563        mode2 = pkt->get<uint8_t>();
564        break;
565      case TSDEV_TMR0_DATA:
566        pitimer.counter0.write(pkt->get<uint8_t>());
567        break;
568      case TSDEV_TMR1_DATA:
569        pitimer.counter1.write(pkt->get<uint8_t>());
570        break;
571      case TSDEV_TMR2_DATA:
572        pitimer.counter2.write(pkt->get<uint8_t>());
573        break;
574      case TSDEV_TMR_CTRL:
575        pitimer.writeControl(pkt->get<uint8_t>());
576        break;
577      case TSDEV_RTC_ADDR:
578        rtc.writeAddr(pkt->get<uint8_t>());
579        break;
580      case TSDEV_RTC_DATA:
581        rtc.writeData(pkt->get<uint8_t>());
582        break;
583      case TSDEV_KBD:
584      case TSDEV_DMA1_CMND:
585      case TSDEV_DMA2_CMND:
586      case TSDEV_DMA1_MMASK:
587      case TSDEV_DMA2_MMASK:
588      case TSDEV_PIC2_ACK:
589      case TSDEV_DMA1_RESET:
590      case TSDEV_DMA2_RESET:
591      case TSDEV_DMA1_MASK:
592      case TSDEV_DMA2_MASK:
593      case TSDEV_CTRL_PORTB:
594        break;
595      default:
596        panic("I/O Write - va%#x size %d data %#x\n", pkt->getAddr(), pkt->getSize(), pkt->get<uint8_t>());
597    }
598
599    pkt->result = Packet::Success;
600    return pioDelay;
601}
602
603void
604TsunamiIO::postPIC(uint8_t bitvector)
605{
606    //PIC2 Is not implemented, because nothing of interest there
607    picr |= bitvector;
608    if (picr & mask1) {
609        tsunami->cchip->postDRIR(55);
610        DPRINTF(Tsunami, "posting pic interrupt to cchip\n");
611    }
612}
613
614void
615TsunamiIO::clearPIC(uint8_t bitvector)
616{
617    //PIC2 Is not implemented, because nothing of interest there
618    picr &= ~bitvector;
619    if (!(picr & mask1)) {
620        tsunami->cchip->clearDRIR(55);
621        DPRINTF(Tsunami, "clearing pic interrupt to cchip\n");
622    }
623}
624
625void
626TsunamiIO::serialize(ostream &os)
627{
628    SERIALIZE_SCALAR(timerData);
629    SERIALIZE_SCALAR(mask1);
630    SERIALIZE_SCALAR(mask2);
631    SERIALIZE_SCALAR(mode1);
632    SERIALIZE_SCALAR(mode2);
633    SERIALIZE_SCALAR(picr);
634    SERIALIZE_SCALAR(picInterrupting);
635
636    // Serialize the timers
637    pitimer.serialize("pitimer", os);
638    rtc.serialize("rtc", os);
639}
640
641void
642TsunamiIO::unserialize(Checkpoint *cp, const string &section)
643{
644    UNSERIALIZE_SCALAR(timerData);
645    UNSERIALIZE_SCALAR(mask1);
646    UNSERIALIZE_SCALAR(mask2);
647    UNSERIALIZE_SCALAR(mode1);
648    UNSERIALIZE_SCALAR(mode2);
649    UNSERIALIZE_SCALAR(picr);
650    UNSERIALIZE_SCALAR(picInterrupting);
651
652    // Unserialize the timers
653    pitimer.unserialize("pitimer", cp, section);
654    rtc.unserialize("rtc", cp, section);
655}
656
657TsunamiIO *
658TsunamiIOParams::create()
659{
660    return new TsunamiIO(this);
661}
662