mc146818.cc revision 10905:a6ca6831e775
112771Sqtt2@cornell.edu/*
212771Sqtt2@cornell.edu * Copyright (c) 2004-2005 The Regents of The University of Michigan
312771Sqtt2@cornell.edu * All rights reserved.
412771Sqtt2@cornell.edu *
512771Sqtt2@cornell.edu * Redistribution and use in source and binary forms, with or without
612771Sqtt2@cornell.edu * modification, are permitted provided that the following conditions are
712771Sqtt2@cornell.edu * met: redistributions of source code must retain the above copyright
812771Sqtt2@cornell.edu * notice, this list of conditions and the following disclaimer;
912771Sqtt2@cornell.edu * redistributions in binary form must reproduce the above copyright
1012771Sqtt2@cornell.edu * notice, this list of conditions and the following disclaimer in the
1112771Sqtt2@cornell.edu * documentation and/or other materials provided with the distribution;
1212771Sqtt2@cornell.edu * neither the name of the copyright holders nor the names of its
1312771Sqtt2@cornell.edu * contributors may be used to endorse or promote products derived from
1412771Sqtt2@cornell.edu * this software without specific prior written permission.
1512771Sqtt2@cornell.edu *
1612771Sqtt2@cornell.edu * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
1712771Sqtt2@cornell.edu * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
1812771Sqtt2@cornell.edu * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
1912771Sqtt2@cornell.edu * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
2012771Sqtt2@cornell.edu * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
2112771Sqtt2@cornell.edu * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
2212771Sqtt2@cornell.edu * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
2312771Sqtt2@cornell.edu * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
2412771Sqtt2@cornell.edu * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
2512771Sqtt2@cornell.edu * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
2612771Sqtt2@cornell.edu * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
2712771Sqtt2@cornell.edu *
2812771Sqtt2@cornell.edu * Authors: Ali Saidi
2912771Sqtt2@cornell.edu *          Andrew Schultz
3012771Sqtt2@cornell.edu *          Miguel Serrano
3112771Sqtt2@cornell.edu */
3212771Sqtt2@cornell.edu
3312771Sqtt2@cornell.edu#include <sys/time.h>
3412771Sqtt2@cornell.edu
3512771Sqtt2@cornell.edu#include <ctime>
3612771Sqtt2@cornell.edu#include <string>
3712771Sqtt2@cornell.edu
3812771Sqtt2@cornell.edu#include "base/bitfield.hh"
3912771Sqtt2@cornell.edu#include "base/time.hh"
4012771Sqtt2@cornell.edu#include "base/trace.hh"
4112771Sqtt2@cornell.edu#include "debug/MC146818.hh"
4212771Sqtt2@cornell.edu#include "dev/mc146818.hh"
4312771Sqtt2@cornell.edu#include "dev/rtcreg.h"
4412771Sqtt2@cornell.edu
4512771Sqtt2@cornell.eduusing namespace std;
4612771Sqtt2@cornell.edu
4712771Sqtt2@cornell.edustatic uint8_t
4812771Sqtt2@cornell.edubcdize(uint8_t val)
4912771Sqtt2@cornell.edu{
5012771Sqtt2@cornell.edu    uint8_t result;
5112771Sqtt2@cornell.edu    result = val % 10;
5212771Sqtt2@cornell.edu    result += (val / 10) << 4;
5312771Sqtt2@cornell.edu    return result;
5412771Sqtt2@cornell.edu}
5512771Sqtt2@cornell.edu
5612771Sqtt2@cornell.edustatic uint8_t
5712771Sqtt2@cornell.eduunbcdize(uint8_t val)
5812771Sqtt2@cornell.edu{
5912771Sqtt2@cornell.edu    uint8_t result;
6012771Sqtt2@cornell.edu    result = val & 0xf;
6112771Sqtt2@cornell.edu    result += (val >> 4) * 10;
6212771Sqtt2@cornell.edu    return result;
6312771Sqtt2@cornell.edu}
6412771Sqtt2@cornell.edu
6512771Sqtt2@cornell.eduvoid
6612771Sqtt2@cornell.eduMC146818::setTime(const struct tm time)
6712771Sqtt2@cornell.edu{
6812771Sqtt2@cornell.edu    curTime = time;
6912771Sqtt2@cornell.edu    year = time.tm_year;
7012771Sqtt2@cornell.edu    // Unix is 0-11 for month, data seet says start at 1
7112771Sqtt2@cornell.edu    mon = time.tm_mon + 1;
7212771Sqtt2@cornell.edu    mday = time.tm_mday;
7312771Sqtt2@cornell.edu    hour = time.tm_hour;
7412771Sqtt2@cornell.edu    min = time.tm_min;
7512771Sqtt2@cornell.edu    sec = time.tm_sec;
7612771Sqtt2@cornell.edu
7712771Sqtt2@cornell.edu    // Datasheet says 1 is sunday
7812771Sqtt2@cornell.edu    wday = time.tm_wday + 1;
7912771Sqtt2@cornell.edu
8012771Sqtt2@cornell.edu    if (!stat_regB.dm) {
8112771Sqtt2@cornell.edu        // The datasheet says that the year field can be either BCD or
8212771Sqtt2@cornell.edu        // years since 1900.  Linux seems to be happy with years since
8312771Sqtt2@cornell.edu        // 1900.
8412771Sqtt2@cornell.edu        year = bcdize(year % 100);
8512771Sqtt2@cornell.edu        mon = bcdize(mon);
8612771Sqtt2@cornell.edu        mday = bcdize(mday);
8712771Sqtt2@cornell.edu        hour = bcdize(hour);
8812771Sqtt2@cornell.edu        min = bcdize(min);
8912771Sqtt2@cornell.edu        sec = bcdize(sec);
9012771Sqtt2@cornell.edu    }
9112771Sqtt2@cornell.edu}
9212771Sqtt2@cornell.edu
9312771Sqtt2@cornell.eduMC146818::MC146818(EventManager *em, const string &n, const struct tm time,
9412771Sqtt2@cornell.edu                   bool bcd, Tick frequency)
9512771Sqtt2@cornell.edu    : EventManager(em), _name(n), event(this, frequency), tickEvent(this)
9612771Sqtt2@cornell.edu{
9712771Sqtt2@cornell.edu    memset(clock_data, 0, sizeof(clock_data));
9812771Sqtt2@cornell.edu
9912771Sqtt2@cornell.edu    stat_regA = 0;
10012771Sqtt2@cornell.edu    stat_regA.dv = RTCA_DV_32768HZ;
10112771Sqtt2@cornell.edu    stat_regA.rs = RTCA_RS_1024HZ;
10212771Sqtt2@cornell.edu
10312771Sqtt2@cornell.edu    stat_regB = 0;
10412771Sqtt2@cornell.edu    stat_regB.pie = 1;
10512771Sqtt2@cornell.edu    stat_regB.format24h = 1;
10612771Sqtt2@cornell.edu    stat_regB.dm = bcd ? 0 : 1;
10712771Sqtt2@cornell.edu
10812771Sqtt2@cornell.edu    setTime(time);
10912771Sqtt2@cornell.edu    DPRINTFN("Real-time clock set to %s", asctime(&time));
11012771Sqtt2@cornell.edu}
11112771Sqtt2@cornell.edu
11212771Sqtt2@cornell.eduMC146818::~MC146818()
11312771Sqtt2@cornell.edu{
11412771Sqtt2@cornell.edu    deschedule(tickEvent);
11512771Sqtt2@cornell.edu    deschedule(event);
11612771Sqtt2@cornell.edu}
11712771Sqtt2@cornell.edu
11812771Sqtt2@cornell.edubool
11912771Sqtt2@cornell.eduMC146818::rega_dv_disabled(const RtcRegA &reg)
120{
121    return reg.dv == RTCA_DV_DISABLED0 ||
122        reg.dv == RTCA_DV_DISABLED1;
123}
124
125void
126MC146818::startup()
127{
128    assert(!event.scheduled());
129    assert(!tickEvent.scheduled());
130
131    if (stat_regB.pie)
132        schedule(event, curTick() + event.offset);
133    if (!rega_dv_disabled(stat_regA))
134        schedule(tickEvent, curTick() + tickEvent.offset);
135}
136
137void
138MC146818::writeData(const uint8_t addr, const uint8_t data)
139{
140    bool panic_unsupported(false);
141
142    if (addr < RTC_STAT_REGA) {
143        clock_data[addr] = data;
144        curTime.tm_sec = unbcdize(sec);
145        curTime.tm_min = unbcdize(min);
146        curTime.tm_hour = unbcdize(hour);
147        curTime.tm_mday = unbcdize(mday);
148        curTime.tm_mon = unbcdize(mon) - 1;
149        curTime.tm_year = ((unbcdize(year) + 50) % 100) + 1950;
150        curTime.tm_wday = unbcdize(wday) - 1;
151    } else {
152        switch (addr) {
153          case RTC_STAT_REGA: {
154              RtcRegA old_rega(stat_regA);
155              stat_regA = data;
156              // The "update in progress" bit is read only.
157              stat_regA.uip = old_rega;
158
159              if (!rega_dv_disabled(stat_regA) &&
160                  stat_regA.dv != RTCA_DV_32768HZ) {
161                  inform("RTC: Unimplemented divider configuration: %i\n",
162                        stat_regA.dv);
163                  panic_unsupported = true;
164              }
165
166              if (stat_regA.rs != RTCA_RS_1024HZ) {
167                  inform("RTC: Unimplemented interrupt rate: %i\n",
168                        stat_regA.rs);
169                  panic_unsupported = true;
170              }
171
172              if (rega_dv_disabled(stat_regA)) {
173                  // The divider is disabled, make sure that we don't
174                  // schedule any ticks.
175                  if (tickEvent.scheduled())
176                      deschedule(tickEvent);
177              } else if (rega_dv_disabled(old_rega))  {
178                  // According to the specification, the next tick
179                  // happens after 0.5s when the divider chain goes
180                  // from reset to active. So, we simply schedule the
181                  // tick after 0.5s.
182                  assert(!tickEvent.scheduled());
183                  schedule(tickEvent, curTick() + SimClock::Int::s / 2);
184              }
185          } break;
186          case RTC_STAT_REGB:
187            stat_regB = data;
188            if (stat_regB.aie || stat_regB.uie) {
189                inform("RTC: Unimplemented interrupt configuration: %s %s\n",
190                      stat_regB.aie ? "alarm" : "",
191                      stat_regB.uie ? "update" : "");
192                panic_unsupported = true;
193            }
194
195            if (stat_regB.dm) {
196                inform("RTC: The binary interface is not fully implemented.\n");
197                panic_unsupported = true;
198            }
199
200            if (!stat_regB.format24h) {
201                inform("RTC: The 12h time format not supported.\n");
202                panic_unsupported = true;
203            }
204
205            if (stat_regB.dse) {
206                inform("RTC: Automatic daylight saving time not supported.\n");
207                panic_unsupported = true;
208            }
209
210            if (stat_regB.pie) {
211                if (!event.scheduled())
212                    event.scheduleIntr();
213            } else {
214                if (event.scheduled())
215                    deschedule(event);
216            }
217            break;
218          case RTC_STAT_REGC:
219          case RTC_STAT_REGD:
220            panic("RTC status registers C and D are not implemented.\n");
221            break;
222        }
223    }
224
225    if (panic_unsupported)
226        panic("Unimplemented RTC configuration!\n");
227
228}
229
230uint8_t
231MC146818::readData(uint8_t addr)
232{
233    if (addr < RTC_STAT_REGA)
234        return clock_data[addr];
235    else {
236        switch (addr) {
237          case RTC_STAT_REGA:
238            // toggle UIP bit for linux
239            stat_regA.uip = !stat_regA.uip;
240            return stat_regA;
241            break;
242          case RTC_STAT_REGB:
243            return stat_regB;
244            break;
245          case RTC_STAT_REGC:
246          case RTC_STAT_REGD:
247            return 0x00;
248            break;
249          default:
250            panic("Shouldn't be here");
251        }
252    }
253}
254
255void
256MC146818::tickClock()
257{
258    assert(!rega_dv_disabled(stat_regA));
259
260    if (stat_regB.set)
261        return;
262    time_t calTime = mkutctime(&curTime);
263    calTime++;
264    setTime(*gmtime(&calTime));
265}
266
267void
268MC146818::serialize(const string &base, CheckpointOut &cp) const
269{
270    uint8_t regA_serial(stat_regA);
271    uint8_t regB_serial(stat_regB);
272
273    arrayParamOut(cp, base + ".clock_data", clock_data, sizeof(clock_data));
274    paramOut(cp, base + ".stat_regA", (uint8_t)regA_serial);
275    paramOut(cp, base + ".stat_regB", (uint8_t)regB_serial);
276
277    //
278    // save the timer tick and rtc clock tick values to correctly reschedule
279    // them during unserialize
280    //
281    Tick rtcTimerInterruptTickOffset = event.when() - curTick();
282    SERIALIZE_SCALAR(rtcTimerInterruptTickOffset);
283    Tick rtcClockTickOffset = tickEvent.when() - curTick();
284    SERIALIZE_SCALAR(rtcClockTickOffset);
285}
286
287void
288MC146818::unserialize(const string &base, CheckpointIn &cp)
289{
290    uint8_t tmp8;
291
292    arrayParamIn(cp, base + ".clock_data", clock_data,
293                 sizeof(clock_data));
294
295    paramIn(cp, base + ".stat_regA", tmp8);
296    stat_regA = tmp8;
297    paramIn(cp, base + ".stat_regB", tmp8);
298    stat_regB = tmp8;
299
300    //
301    // properly schedule the timer and rtc clock events
302    //
303    Tick rtcTimerInterruptTickOffset;
304    UNSERIALIZE_SCALAR(rtcTimerInterruptTickOffset);
305    event.offset = rtcTimerInterruptTickOffset;
306    Tick rtcClockTickOffset;
307    UNSERIALIZE_SCALAR(rtcClockTickOffset);
308    tickEvent.offset = rtcClockTickOffset;
309}
310
311MC146818::RTCEvent::RTCEvent(MC146818 * _parent, Tick i)
312    : parent(_parent), interval(i), offset(i)
313{
314    DPRINTF(MC146818, "RTC Event Initilizing\n");
315}
316
317void
318MC146818::RTCEvent::scheduleIntr()
319{
320    parent->schedule(this, curTick() + interval);
321}
322
323void
324MC146818::RTCEvent::process()
325{
326    DPRINTF(MC146818, "RTC Timer Interrupt\n");
327    parent->schedule(this, curTick() + interval);
328    parent->handleEvent();
329}
330
331const char *
332MC146818::RTCEvent::description() const
333{
334    return "RTC interrupt";
335}
336
337void
338MC146818::RTCTickEvent::process()
339{
340    DPRINTF(MC146818, "RTC clock tick\n");
341    parent->schedule(this, curTick() + SimClock::Int::s);
342    parent->tickClock();
343}
344
345const char *
346MC146818::RTCTickEvent::description() const
347{
348    return "RTC clock tick";
349}
350