mc146818.cc revision 7683
15392Sgblack@eecs.umich.edu/*
25392Sgblack@eecs.umich.edu * Copyright (c) 2004-2005 The Regents of The University of Michigan
35392Sgblack@eecs.umich.edu * All rights reserved.
45392Sgblack@eecs.umich.edu *
55392Sgblack@eecs.umich.edu * Redistribution and use in source and binary forms, with or without
65392Sgblack@eecs.umich.edu * modification, are permitted provided that the following conditions are
75392Sgblack@eecs.umich.edu * met: redistributions of source code must retain the above copyright
85392Sgblack@eecs.umich.edu * notice, this list of conditions and the following disclaimer;
95392Sgblack@eecs.umich.edu * redistributions in binary form must reproduce the above copyright
105392Sgblack@eecs.umich.edu * notice, this list of conditions and the following disclaimer in the
115392Sgblack@eecs.umich.edu * documentation and/or other materials provided with the distribution;
125392Sgblack@eecs.umich.edu * neither the name of the copyright holders nor the names of its
135392Sgblack@eecs.umich.edu * contributors may be used to endorse or promote products derived from
145392Sgblack@eecs.umich.edu * this software without specific prior written permission.
155392Sgblack@eecs.umich.edu *
165392Sgblack@eecs.umich.edu * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
175392Sgblack@eecs.umich.edu * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
185392Sgblack@eecs.umich.edu * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
195392Sgblack@eecs.umich.edu * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
205392Sgblack@eecs.umich.edu * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
215392Sgblack@eecs.umich.edu * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
225392Sgblack@eecs.umich.edu * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
235392Sgblack@eecs.umich.edu * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
245392Sgblack@eecs.umich.edu * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
255392Sgblack@eecs.umich.edu * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
265392Sgblack@eecs.umich.edu * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
275392Sgblack@eecs.umich.edu *
285392Sgblack@eecs.umich.edu * Authors: Ali Saidi
295392Sgblack@eecs.umich.edu *          Andrew Schultz
305392Sgblack@eecs.umich.edu *          Miguel Serrano
315392Sgblack@eecs.umich.edu */
325392Sgblack@eecs.umich.edu
335392Sgblack@eecs.umich.edu#include <sys/time.h>
345392Sgblack@eecs.umich.edu#include <time.h>
355392Sgblack@eecs.umich.edu
365392Sgblack@eecs.umich.edu#include <string>
375392Sgblack@eecs.umich.edu
385813Sgblack@eecs.umich.edu#include "base/bitfield.hh"
395392Sgblack@eecs.umich.edu#include "base/time.hh"
405392Sgblack@eecs.umich.edu#include "base/trace.hh"
415392Sgblack@eecs.umich.edu#include "dev/mc146818.hh"
425392Sgblack@eecs.umich.edu#include "dev/rtcreg.h"
435392Sgblack@eecs.umich.edu
445392Sgblack@eecs.umich.eduusing namespace std;
455392Sgblack@eecs.umich.edu
466620Sgblack@eecs.umich.edustatic uint8_t
476620Sgblack@eecs.umich.edubcdize(uint8_t val)
485392Sgblack@eecs.umich.edu{
496620Sgblack@eecs.umich.edu    uint8_t result;
506620Sgblack@eecs.umich.edu    result = val % 10;
516620Sgblack@eecs.umich.edu    result += (val / 10) << 4;
526620Sgblack@eecs.umich.edu    return result;
536620Sgblack@eecs.umich.edu}
545392Sgblack@eecs.umich.edu
556621Sgblack@eecs.umich.edustatic uint8_t
566621Sgblack@eecs.umich.eduunbcdize(uint8_t val)
576621Sgblack@eecs.umich.edu{
586621Sgblack@eecs.umich.edu    uint8_t result;
596621Sgblack@eecs.umich.edu    result = val & 0xf;
606621Sgblack@eecs.umich.edu    result += (val >> 4) * 10;
616621Sgblack@eecs.umich.edu    return result;
626621Sgblack@eecs.umich.edu}
636621Sgblack@eecs.umich.edu
646620Sgblack@eecs.umich.eduvoid
656620Sgblack@eecs.umich.eduMC146818::setTime(const struct tm time)
666620Sgblack@eecs.umich.edu{
676620Sgblack@eecs.umich.edu    curTime = time;
685392Sgblack@eecs.umich.edu    year = time.tm_year;
695392Sgblack@eecs.umich.edu    // Unix is 0-11 for month, data seet says start at 1
705392Sgblack@eecs.umich.edu    mon = time.tm_mon + 1;
715392Sgblack@eecs.umich.edu    mday = time.tm_mday;
725392Sgblack@eecs.umich.edu    hour = time.tm_hour;
735392Sgblack@eecs.umich.edu    min = time.tm_min;
745392Sgblack@eecs.umich.edu    sec = time.tm_sec;
755392Sgblack@eecs.umich.edu
765392Sgblack@eecs.umich.edu    // Datasheet says 1 is sunday
775392Sgblack@eecs.umich.edu    wday = time.tm_wday + 1;
785392Sgblack@eecs.umich.edu
796620Sgblack@eecs.umich.edu    if (!(stat_regB & RTCB_BIN)) {
806620Sgblack@eecs.umich.edu        // The datasheet says that the year field can be either BCD or
816620Sgblack@eecs.umich.edu        // years since 1900.  Linux seems to be happy with years since
826620Sgblack@eecs.umich.edu        // 1900.
836620Sgblack@eecs.umich.edu        year = bcdize(year % 100);
846620Sgblack@eecs.umich.edu        mon = bcdize(mon);
856620Sgblack@eecs.umich.edu        mday = bcdize(mday);
866620Sgblack@eecs.umich.edu        hour = bcdize(hour);
876620Sgblack@eecs.umich.edu        min = bcdize(min);
886620Sgblack@eecs.umich.edu        sec = bcdize(sec);
896620Sgblack@eecs.umich.edu    }
906620Sgblack@eecs.umich.edu}
916620Sgblack@eecs.umich.edu
926620Sgblack@eecs.umich.eduMC146818::MC146818(EventManager *em, const string &n, const struct tm time,
936620Sgblack@eecs.umich.edu                   bool bcd, Tick frequency)
946620Sgblack@eecs.umich.edu    : EventManager(em), _name(n), event(this, frequency), tickEvent(this)
956620Sgblack@eecs.umich.edu{
966620Sgblack@eecs.umich.edu    memset(clock_data, 0, sizeof(clock_data));
976620Sgblack@eecs.umich.edu    stat_regA = RTCA_32768HZ | RTCA_1024HZ;
986620Sgblack@eecs.umich.edu    stat_regB = RTCB_PRDC_IE | RTCB_24HR;
996620Sgblack@eecs.umich.edu    if (!bcd)
1006620Sgblack@eecs.umich.edu        stat_regB |= RTCB_BIN;
1016620Sgblack@eecs.umich.edu
1026620Sgblack@eecs.umich.edu    setTime(time);
1035392Sgblack@eecs.umich.edu    DPRINTFN("Real-time clock set to %s", asctime(&time));
1045392Sgblack@eecs.umich.edu}
1055392Sgblack@eecs.umich.edu
1065606Snate@binkert.orgMC146818::~MC146818()
1075606Snate@binkert.org{
1087683Ssteve.reinhardt@amd.com    deschedule(tickEvent);
1097683Ssteve.reinhardt@amd.com    deschedule(event);
1105606Snate@binkert.org}
1115606Snate@binkert.org
1125392Sgblack@eecs.umich.eduvoid
1135392Sgblack@eecs.umich.eduMC146818::writeData(const uint8_t addr, const uint8_t data)
1145392Sgblack@eecs.umich.edu{
1156621Sgblack@eecs.umich.edu    if (addr < RTC_STAT_REGA) {
1165392Sgblack@eecs.umich.edu        clock_data[addr] = data;
1176621Sgblack@eecs.umich.edu        curTime.tm_sec = unbcdize(sec);
1186621Sgblack@eecs.umich.edu        curTime.tm_min = unbcdize(min);
1196621Sgblack@eecs.umich.edu        curTime.tm_hour = unbcdize(hour);
1206621Sgblack@eecs.umich.edu        curTime.tm_mday = unbcdize(mday);
1216621Sgblack@eecs.umich.edu        curTime.tm_mon = unbcdize(mon) - 1;
1226621Sgblack@eecs.umich.edu        curTime.tm_year = ((unbcdize(year) + 50) % 100) + 1950;
1236621Sgblack@eecs.umich.edu        curTime.tm_wday = unbcdize(wday) - 1;
1246621Sgblack@eecs.umich.edu    } else {
1255392Sgblack@eecs.umich.edu        switch (addr) {
1265392Sgblack@eecs.umich.edu          case RTC_STAT_REGA:
1275813Sgblack@eecs.umich.edu            // The "update in progress" bit is read only.
1285813Sgblack@eecs.umich.edu            if ((data & ~RTCA_UIP) != (RTCA_32768HZ | RTCA_1024HZ))
1295392Sgblack@eecs.umich.edu                panic("Unimplemented RTC register A value write!\n");
1305813Sgblack@eecs.umich.edu            replaceBits(stat_regA, data, 6, 0);
1315392Sgblack@eecs.umich.edu            break;
1325392Sgblack@eecs.umich.edu          case RTC_STAT_REGB:
1336617Sgblack@eecs.umich.edu            if ((data & ~(RTCB_PRDC_IE | RTCB_SQWE)) != RTCB_24HR)
1345392Sgblack@eecs.umich.edu                panic("Write to RTC reg B bits that are not implemented!\n");
1355392Sgblack@eecs.umich.edu
1365392Sgblack@eecs.umich.edu            if (data & RTCB_PRDC_IE) {
1375392Sgblack@eecs.umich.edu                if (!event.scheduled())
1385392Sgblack@eecs.umich.edu                    event.scheduleIntr();
1395392Sgblack@eecs.umich.edu            } else {
1405392Sgblack@eecs.umich.edu                if (event.scheduled())
1415606Snate@binkert.org                    deschedule(event);
1425392Sgblack@eecs.umich.edu            }
1435392Sgblack@eecs.umich.edu            stat_regB = data;
1445392Sgblack@eecs.umich.edu            break;
1455392Sgblack@eecs.umich.edu          case RTC_STAT_REGC:
1465392Sgblack@eecs.umich.edu          case RTC_STAT_REGD:
1475392Sgblack@eecs.umich.edu            panic("RTC status registers C and D are not implemented.\n");
1485392Sgblack@eecs.umich.edu            break;
1495392Sgblack@eecs.umich.edu        }
1505392Sgblack@eecs.umich.edu    }
1515392Sgblack@eecs.umich.edu}
1525392Sgblack@eecs.umich.edu
1535392Sgblack@eecs.umich.eduuint8_t
1545392Sgblack@eecs.umich.eduMC146818::readData(uint8_t addr)
1555392Sgblack@eecs.umich.edu{
1565392Sgblack@eecs.umich.edu    if (addr < RTC_STAT_REGA)
1575392Sgblack@eecs.umich.edu        return clock_data[addr];
1585392Sgblack@eecs.umich.edu    else {
1595392Sgblack@eecs.umich.edu        switch (addr) {
1605392Sgblack@eecs.umich.edu          case RTC_STAT_REGA:
1615392Sgblack@eecs.umich.edu            // toggle UIP bit for linux
1625392Sgblack@eecs.umich.edu            stat_regA ^= RTCA_UIP;
1635392Sgblack@eecs.umich.edu            return stat_regA;
1645392Sgblack@eecs.umich.edu            break;
1655392Sgblack@eecs.umich.edu          case RTC_STAT_REGB:
1665392Sgblack@eecs.umich.edu            return stat_regB;
1675392Sgblack@eecs.umich.edu            break;
1685392Sgblack@eecs.umich.edu          case RTC_STAT_REGC:
1695392Sgblack@eecs.umich.edu          case RTC_STAT_REGD:
1705392Sgblack@eecs.umich.edu            return 0x00;
1715392Sgblack@eecs.umich.edu            break;
1725392Sgblack@eecs.umich.edu          default:
1735392Sgblack@eecs.umich.edu            panic("Shouldn't be here");
1745392Sgblack@eecs.umich.edu        }
1755392Sgblack@eecs.umich.edu    }
1765392Sgblack@eecs.umich.edu}
1775392Sgblack@eecs.umich.edu
1786620Sgblack@eecs.umich.edustatic time_t
1796620Sgblack@eecs.umich.edumkutctime(struct tm *time)
1806620Sgblack@eecs.umich.edu{
1816620Sgblack@eecs.umich.edu    time_t ret;
1826620Sgblack@eecs.umich.edu    char *tz;
1836620Sgblack@eecs.umich.edu
1846620Sgblack@eecs.umich.edu    tz = getenv("TZ");
1856620Sgblack@eecs.umich.edu    setenv("TZ", "", 1);
1866620Sgblack@eecs.umich.edu    tzset();
1876620Sgblack@eecs.umich.edu    ret = mktime(time);
1886620Sgblack@eecs.umich.edu    if (tz)
1896620Sgblack@eecs.umich.edu        setenv("TZ", tz, 1);
1906620Sgblack@eecs.umich.edu    else
1916620Sgblack@eecs.umich.edu        unsetenv("TZ");
1926620Sgblack@eecs.umich.edu    tzset();
1936620Sgblack@eecs.umich.edu    return ret;
1946620Sgblack@eecs.umich.edu}
1956620Sgblack@eecs.umich.edu
1966620Sgblack@eecs.umich.eduvoid
1976620Sgblack@eecs.umich.eduMC146818::tickClock()
1986620Sgblack@eecs.umich.edu{
1996620Sgblack@eecs.umich.edu    if (stat_regB & RTCB_NO_UPDT)
2006620Sgblack@eecs.umich.edu        return;
2016620Sgblack@eecs.umich.edu    time_t calTime = mkutctime(&curTime);
2026620Sgblack@eecs.umich.edu    calTime++;
2036620Sgblack@eecs.umich.edu    setTime(*gmtime(&calTime));
2046620Sgblack@eecs.umich.edu}
2056620Sgblack@eecs.umich.edu
2065392Sgblack@eecs.umich.eduvoid
2075392Sgblack@eecs.umich.eduMC146818::serialize(const string &base, ostream &os)
2085392Sgblack@eecs.umich.edu{
2095392Sgblack@eecs.umich.edu    arrayParamOut(os, base + ".clock_data", clock_data, sizeof(clock_data));
2105392Sgblack@eecs.umich.edu    paramOut(os, base + ".stat_regA", stat_regA);
2115392Sgblack@eecs.umich.edu    paramOut(os, base + ".stat_regB", stat_regB);
2126677SBrad.Beckmann@amd.com
2136677SBrad.Beckmann@amd.com    //
2146677SBrad.Beckmann@amd.com    // save the timer tick and rtc clock tick values to correctly reschedule
2156677SBrad.Beckmann@amd.com    // them during unserialize
2166677SBrad.Beckmann@amd.com    //
2177683Ssteve.reinhardt@amd.com    Tick rtcTimerInterruptTickOffset = event.when() - curTick;
2186677SBrad.Beckmann@amd.com    SERIALIZE_SCALAR(rtcTimerInterruptTickOffset);
2197683Ssteve.reinhardt@amd.com    Tick rtcClockTickOffset = event.when() - curTick;
2206677SBrad.Beckmann@amd.com    SERIALIZE_SCALAR(rtcClockTickOffset);
2215392Sgblack@eecs.umich.edu}
2225392Sgblack@eecs.umich.edu
2235392Sgblack@eecs.umich.eduvoid
2245392Sgblack@eecs.umich.eduMC146818::unserialize(const string &base, Checkpoint *cp,
2255392Sgblack@eecs.umich.edu                      const string &section)
2265392Sgblack@eecs.umich.edu{
2275392Sgblack@eecs.umich.edu    arrayParamIn(cp, section, base + ".clock_data", clock_data,
2285392Sgblack@eecs.umich.edu                 sizeof(clock_data));
2295392Sgblack@eecs.umich.edu    paramIn(cp, section, base + ".stat_regA", stat_regA);
2305392Sgblack@eecs.umich.edu    paramIn(cp, section, base + ".stat_regB", stat_regB);
2315392Sgblack@eecs.umich.edu
2326677SBrad.Beckmann@amd.com    //
2336677SBrad.Beckmann@amd.com    // properly schedule the timer and rtc clock events
2346677SBrad.Beckmann@amd.com    //
2356677SBrad.Beckmann@amd.com    Tick rtcTimerInterruptTickOffset;
2366677SBrad.Beckmann@amd.com    UNSERIALIZE_SCALAR(rtcTimerInterruptTickOffset);
2376677SBrad.Beckmann@amd.com    reschedule(event, curTick + rtcTimerInterruptTickOffset);
2386677SBrad.Beckmann@amd.com    Tick rtcClockTickOffset;
2396677SBrad.Beckmann@amd.com    UNSERIALIZE_SCALAR(rtcClockTickOffset);
2406677SBrad.Beckmann@amd.com    reschedule(tickEvent, curTick + rtcClockTickOffset);
2415392Sgblack@eecs.umich.edu}
2425392Sgblack@eecs.umich.edu
2435392Sgblack@eecs.umich.eduMC146818::RTCEvent::RTCEvent(MC146818 * _parent, Tick i)
2445606Snate@binkert.org    : parent(_parent), interval(i)
2455392Sgblack@eecs.umich.edu{
2465392Sgblack@eecs.umich.edu    DPRINTF(MC146818, "RTC Event Initilizing\n");
2475606Snate@binkert.org    parent->schedule(this, curTick + interval);
2485392Sgblack@eecs.umich.edu}
2495392Sgblack@eecs.umich.edu
2505392Sgblack@eecs.umich.eduvoid
2515392Sgblack@eecs.umich.eduMC146818::RTCEvent::scheduleIntr()
2525392Sgblack@eecs.umich.edu{
2535606Snate@binkert.org    parent->schedule(this, curTick + interval);
2545392Sgblack@eecs.umich.edu}
2555392Sgblack@eecs.umich.edu
2565392Sgblack@eecs.umich.eduvoid
2575392Sgblack@eecs.umich.eduMC146818::RTCEvent::process()
2585392Sgblack@eecs.umich.edu{
2595392Sgblack@eecs.umich.edu    DPRINTF(MC146818, "RTC Timer Interrupt\n");
2605606Snate@binkert.org    parent->schedule(this, curTick + interval);
2615392Sgblack@eecs.umich.edu    parent->handleEvent();
2625392Sgblack@eecs.umich.edu}
2635392Sgblack@eecs.umich.edu
2645392Sgblack@eecs.umich.educonst char *
2655392Sgblack@eecs.umich.eduMC146818::RTCEvent::description() const
2665392Sgblack@eecs.umich.edu{
2675392Sgblack@eecs.umich.edu    return "RTC interrupt";
2685392Sgblack@eecs.umich.edu}
2696620Sgblack@eecs.umich.edu
2706620Sgblack@eecs.umich.eduvoid
2716620Sgblack@eecs.umich.eduMC146818::RTCTickEvent::process()
2726620Sgblack@eecs.umich.edu{
2736620Sgblack@eecs.umich.edu    DPRINTF(MC146818, "RTC clock tick\n");
2747064Snate@binkert.org    parent->schedule(this, curTick + SimClock::Int::s);
2756620Sgblack@eecs.umich.edu    parent->tickClock();
2766620Sgblack@eecs.umich.edu}
2776620Sgblack@eecs.umich.edu
2786620Sgblack@eecs.umich.educonst char *
2796620Sgblack@eecs.umich.eduMC146818::RTCTickEvent::description() const
2806620Sgblack@eecs.umich.edu{
2816620Sgblack@eecs.umich.edu    return "RTC clock tick";
2826620Sgblack@eecs.umich.edu}
283