mc146818.cc revision 6677
14123Sbinkertn@umich.edu/*
24123Sbinkertn@umich.edu * Copyright (c) 2004-2005 The Regents of The University of Michigan
39983Sstever@gmail.com * All rights reserved.
49983Sstever@gmail.com *
54123Sbinkertn@umich.edu * Redistribution and use in source and binary forms, with or without
64123Sbinkertn@umich.edu * modification, are permitted provided that the following conditions are
74123Sbinkertn@umich.edu * met: redistributions of source code must retain the above copyright
84123Sbinkertn@umich.edu * notice, this list of conditions and the following disclaimer;
94123Sbinkertn@umich.edu * redistributions in binary form must reproduce the above copyright
104123Sbinkertn@umich.edu * notice, this list of conditions and the following disclaimer in the
114123Sbinkertn@umich.edu * documentation and/or other materials provided with the distribution;
124123Sbinkertn@umich.edu * neither the name of the copyright holders nor the names of its
134123Sbinkertn@umich.edu * contributors may be used to endorse or promote products derived from
144123Sbinkertn@umich.edu * this software without specific prior written permission.
154123Sbinkertn@umich.edu *
164123Sbinkertn@umich.edu * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
174123Sbinkertn@umich.edu * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
184123Sbinkertn@umich.edu * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
194123Sbinkertn@umich.edu * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
204123Sbinkertn@umich.edu * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
214123Sbinkertn@umich.edu * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
224123Sbinkertn@umich.edu * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
234123Sbinkertn@umich.edu * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
244123Sbinkertn@umich.edu * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
254123Sbinkertn@umich.edu * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
264123Sbinkertn@umich.edu * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
274123Sbinkertn@umich.edu *
284123Sbinkertn@umich.edu * Authors: Ali Saidi
294123Sbinkertn@umich.edu *          Andrew Schultz
304123Sbinkertn@umich.edu *          Miguel Serrano
314123Sbinkertn@umich.edu */
324123Sbinkertn@umich.edu
334123Sbinkertn@umich.edu#include <sys/time.h>
349983Sstever@gmail.com#include <time.h>
359983Sstever@gmail.com
369983Sstever@gmail.com#include <string>
374123Sbinkertn@umich.edu
384123Sbinkertn@umich.edu#include "base/bitfield.hh"
396216Snate@binkert.org#include "base/time.hh"
404123Sbinkertn@umich.edu#include "base/trace.hh"
419356Snilay@cs.wisc.edu#include "dev/mc146818.hh"
424123Sbinkertn@umich.edu#include "dev/rtcreg.h"
434123Sbinkertn@umich.edu
444123Sbinkertn@umich.eduusing namespace std;
456216Snate@binkert.org
464123Sbinkertn@umich.edustatic uint8_t
479983Sstever@gmail.combcdize(uint8_t val)
489983Sstever@gmail.com{
499983Sstever@gmail.com    uint8_t result;
509983Sstever@gmail.com    result = val % 10;
519983Sstever@gmail.com    result += (val / 10) << 4;
529983Sstever@gmail.com    return result;
539983Sstever@gmail.com}
549983Sstever@gmail.com
559983Sstever@gmail.comstatic uint8_t
569983Sstever@gmail.comunbcdize(uint8_t val)
579983Sstever@gmail.com{
589983Sstever@gmail.com    uint8_t result;
599983Sstever@gmail.com    result = val & 0xf;
609983Sstever@gmail.com    result += (val >> 4) * 10;
619983Sstever@gmail.com    return result;
629983Sstever@gmail.com}
639983Sstever@gmail.com
649983Sstever@gmail.comvoid
659983Sstever@gmail.comMC146818::setTime(const struct tm time)
669983Sstever@gmail.com{
679983Sstever@gmail.com    curTime = time;
689983Sstever@gmail.com    year = time.tm_year;
699983Sstever@gmail.com    // Unix is 0-11 for month, data seet says start at 1
709983Sstever@gmail.com    mon = time.tm_mon + 1;
719983Sstever@gmail.com    mday = time.tm_mday;
729983Sstever@gmail.com    hour = time.tm_hour;
739983Sstever@gmail.com    min = time.tm_min;
744123Sbinkertn@umich.edu    sec = time.tm_sec;
754123Sbinkertn@umich.edu
764123Sbinkertn@umich.edu    // Datasheet says 1 is sunday
774123Sbinkertn@umich.edu    wday = time.tm_wday + 1;
784123Sbinkertn@umich.edu
799983Sstever@gmail.com    if (!(stat_regB & RTCB_BIN)) {
804123Sbinkertn@umich.edu        // The datasheet says that the year field can be either BCD or
814123Sbinkertn@umich.edu        // years since 1900.  Linux seems to be happy with years since
829983Sstever@gmail.com        // 1900.
839983Sstever@gmail.com        year = bcdize(year % 100);
849983Sstever@gmail.com        mon = bcdize(mon);
859983Sstever@gmail.com        mday = bcdize(mday);
869983Sstever@gmail.com        hour = bcdize(hour);
879983Sstever@gmail.com        min = bcdize(min);
889983Sstever@gmail.com        sec = bcdize(sec);
899983Sstever@gmail.com    }
909983Sstever@gmail.com}
919983Sstever@gmail.com
929983Sstever@gmail.comMC146818::MC146818(EventManager *em, const string &n, const struct tm time,
939983Sstever@gmail.com                   bool bcd, Tick frequency)
949983Sstever@gmail.com    : EventManager(em), _name(n), event(this, frequency), tickEvent(this)
959983Sstever@gmail.com{
969983Sstever@gmail.com    memset(clock_data, 0, sizeof(clock_data));
979983Sstever@gmail.com    stat_regA = RTCA_32768HZ | RTCA_1024HZ;
989983Sstever@gmail.com    stat_regB = RTCB_PRDC_IE | RTCB_24HR;
999983Sstever@gmail.com    if (!bcd)
1009983Sstever@gmail.com        stat_regB |= RTCB_BIN;
1017823Ssteve.reinhardt@amd.com
1024123Sbinkertn@umich.edu    setTime(time);
1039174Satgutier@umich.edu    DPRINTFN("Real-time clock set to %s", asctime(&time));
1049174Satgutier@umich.edu}
1059174Satgutier@umich.edu
1064123Sbinkertn@umich.eduMC146818::~MC146818()
1074123Sbinkertn@umich.edu{
1089983Sstever@gmail.com}
1099983Sstever@gmail.com
1109983Sstever@gmail.comvoid
1119983Sstever@gmail.comMC146818::writeData(const uint8_t addr, const uint8_t data)
1129983Sstever@gmail.com{
1139983Sstever@gmail.com    if (addr < RTC_STAT_REGA) {
1149983Sstever@gmail.com        clock_data[addr] = data;
1159983Sstever@gmail.com        curTime.tm_sec = unbcdize(sec);
1169983Sstever@gmail.com        curTime.tm_min = unbcdize(min);
11710101Sandreas@sandberg.pp.se        curTime.tm_hour = unbcdize(hour);
1189983Sstever@gmail.com        curTime.tm_mday = unbcdize(mday);
1199983Sstever@gmail.com        curTime.tm_mon = unbcdize(mon) - 1;
1209983Sstever@gmail.com        curTime.tm_year = ((unbcdize(year) + 50) % 100) + 1950;
1219983Sstever@gmail.com        curTime.tm_wday = unbcdize(wday) - 1;
1229983Sstever@gmail.com    } else {
1239983Sstever@gmail.com        switch (addr) {
1249983Sstever@gmail.com          case RTC_STAT_REGA:
1259983Sstever@gmail.com            // The "update in progress" bit is read only.
1269983Sstever@gmail.com            if ((data & ~RTCA_UIP) != (RTCA_32768HZ | RTCA_1024HZ))
1279983Sstever@gmail.com                panic("Unimplemented RTC register A value write!\n");
1289983Sstever@gmail.com            replaceBits(stat_regA, data, 6, 0);
1299983Sstever@gmail.com            break;
1309983Sstever@gmail.com          case RTC_STAT_REGB:
1319983Sstever@gmail.com            if ((data & ~(RTCB_PRDC_IE | RTCB_SQWE)) != RTCB_24HR)
1329983Sstever@gmail.com                panic("Write to RTC reg B bits that are not implemented!\n");
1339983Sstever@gmail.com
1349983Sstever@gmail.com            if (data & RTCB_PRDC_IE) {
1359983Sstever@gmail.com                if (!event.scheduled())
1369983Sstever@gmail.com                    event.scheduleIntr();
1379983Sstever@gmail.com            } else {
1389983Sstever@gmail.com                if (event.scheduled())
1399983Sstever@gmail.com                    deschedule(event);
1409983Sstever@gmail.com            }
1419983Sstever@gmail.com            stat_regB = data;
1429983Sstever@gmail.com            break;
1439983Sstever@gmail.com          case RTC_STAT_REGC:
1449983Sstever@gmail.com          case RTC_STAT_REGD:
1459983Sstever@gmail.com            panic("RTC status registers C and D are not implemented.\n");
1469983Sstever@gmail.com            break;
1479983Sstever@gmail.com        }
1489983Sstever@gmail.com    }
1499983Sstever@gmail.com}
1509983Sstever@gmail.com
1519983Sstever@gmail.comuint8_t
1529983Sstever@gmail.comMC146818::readData(uint8_t addr)
1539983Sstever@gmail.com{
1549983Sstever@gmail.com    if (addr < RTC_STAT_REGA)
1559983Sstever@gmail.com        return clock_data[addr];
1569983Sstever@gmail.com    else {
1579983Sstever@gmail.com        switch (addr) {
1589983Sstever@gmail.com          case RTC_STAT_REGA:
1599983Sstever@gmail.com            // toggle UIP bit for linux
1609983Sstever@gmail.com            stat_regA ^= RTCA_UIP;
1619983Sstever@gmail.com            return stat_regA;
1629983Sstever@gmail.com            break;
1639983Sstever@gmail.com          case RTC_STAT_REGB:
1649983Sstever@gmail.com            return stat_regB;
1659983Sstever@gmail.com            break;
1669983Sstever@gmail.com          case RTC_STAT_REGC:
1679983Sstever@gmail.com          case RTC_STAT_REGD:
1689983Sstever@gmail.com            return 0x00;
1699983Sstever@gmail.com            break;
1709983Sstever@gmail.com          default:
1719983Sstever@gmail.com            panic("Shouldn't be here");
1729983Sstever@gmail.com        }
1739983Sstever@gmail.com    }
1749983Sstever@gmail.com}
1759983Sstever@gmail.com
1769983Sstever@gmail.comstatic time_t
1779983Sstever@gmail.commkutctime(struct tm *time)
1789983Sstever@gmail.com{
1799983Sstever@gmail.com    time_t ret;
1809983Sstever@gmail.com    char *tz;
1819983Sstever@gmail.com
1829983Sstever@gmail.com    tz = getenv("TZ");
1839983Sstever@gmail.com    setenv("TZ", "", 1);
1849983Sstever@gmail.com    tzset();
1859983Sstever@gmail.com    ret = mktime(time);
1869983Sstever@gmail.com    if (tz)
1874123Sbinkertn@umich.edu        setenv("TZ", tz, 1);
1884123Sbinkertn@umich.edu    else
1894123Sbinkertn@umich.edu        unsetenv("TZ");
1904123Sbinkertn@umich.edu    tzset();
1919983Sstever@gmail.com    return ret;
1929983Sstever@gmail.com}
1934123Sbinkertn@umich.edu
1944123Sbinkertn@umich.eduvoid
1959983Sstever@gmail.comMC146818::tickClock()
1964123Sbinkertn@umich.edu{
1979983Sstever@gmail.com    if (stat_regB & RTCB_NO_UPDT)
1984123Sbinkertn@umich.edu        return;
1994123Sbinkertn@umich.edu    time_t calTime = mkutctime(&curTime);
2009983Sstever@gmail.com    calTime++;
2014123Sbinkertn@umich.edu    setTime(*gmtime(&calTime));
2024123Sbinkertn@umich.edu}
2037822Ssteve.reinhardt@amd.com
2044123Sbinkertn@umich.eduvoid
2054123Sbinkertn@umich.eduMC146818::serialize(const string &base, ostream &os)
2064123Sbinkertn@umich.edu{
2074123Sbinkertn@umich.edu    arrayParamOut(os, base + ".clock_data", clock_data, sizeof(clock_data));
2084123Sbinkertn@umich.edu    paramOut(os, base + ".stat_regA", stat_regA);
2094123Sbinkertn@umich.edu    paramOut(os, base + ".stat_regB", stat_regB);
2104123Sbinkertn@umich.edu
2114123Sbinkertn@umich.edu    //
2124123Sbinkertn@umich.edu    // save the timer tick and rtc clock tick values to correctly reschedule
2139990Sandreas@sandberg.pp.se    // them during unserialize
2144123Sbinkertn@umich.edu    //
2154123Sbinkertn@umich.edu    Tick rtcTimerInterruptTickOffset = event.when() - curTick;
2164123Sbinkertn@umich.edu    SERIALIZE_SCALAR(rtcTimerInterruptTickOffset);
2174123Sbinkertn@umich.edu    Tick rtcClockTickOffset = event.when() - curTick;
2184123Sbinkertn@umich.edu    SERIALIZE_SCALAR(rtcClockTickOffset);
2194123Sbinkertn@umich.edu}
2204123Sbinkertn@umich.edu
2214123Sbinkertn@umich.eduvoid
2224123Sbinkertn@umich.eduMC146818::unserialize(const string &base, Checkpoint *cp,
2234123Sbinkertn@umich.edu                      const string &section)
2244123Sbinkertn@umich.edu{
2254123Sbinkertn@umich.edu    arrayParamIn(cp, section, base + ".clock_data", clock_data,
2264123Sbinkertn@umich.edu                 sizeof(clock_data));
227    paramIn(cp, section, base + ".stat_regA", stat_regA);
228    paramIn(cp, section, base + ".stat_regB", stat_regB);
229
230    //
231    // properly schedule the timer and rtc clock events
232    //
233    Tick rtcTimerInterruptTickOffset;
234    UNSERIALIZE_SCALAR(rtcTimerInterruptTickOffset);
235    reschedule(event, curTick + rtcTimerInterruptTickOffset);
236    Tick rtcClockTickOffset;
237    UNSERIALIZE_SCALAR(rtcClockTickOffset);
238    reschedule(tickEvent, curTick + rtcClockTickOffset);
239}
240
241MC146818::RTCEvent::RTCEvent(MC146818 * _parent, Tick i)
242    : parent(_parent), interval(i)
243{
244    DPRINTF(MC146818, "RTC Event Initilizing\n");
245    parent->schedule(this, curTick + interval);
246}
247
248void
249MC146818::RTCEvent::scheduleIntr()
250{
251    parent->schedule(this, curTick + interval);
252}
253
254void
255MC146818::RTCEvent::process()
256{
257    DPRINTF(MC146818, "RTC Timer Interrupt\n");
258    parent->schedule(this, curTick + interval);
259    parent->handleEvent();
260}
261
262const char *
263MC146818::RTCEvent::description() const
264{
265    return "RTC interrupt";
266}
267
268void
269MC146818::RTCTickEvent::process()
270{
271    DPRINTF(MC146818, "RTC clock tick\n");
272    parent->schedule(this, curTick + Clock::Int::s);
273    parent->tickClock();
274}
275
276const char *
277MC146818::RTCTickEvent::description() const
278{
279    return "RTC clock tick";
280}
281