From 14b8a17f284ad398e39636da907f3055462f75cd Mon Sep 17 00:00:00 2001 From: Andreas Sandberg Date: Mon, 3 Jun 2013 12:28:41 +0200 Subject: [PATCH] dev: Clean up MC146818 register (A & B) handling Rewrite reg A & B handling to use the bitunion stuff instead of bit masking. Add better error messages when the kernel tries to enable unsupported stuff. --- src/dev/mc146818.cc | 104 +++++++++++++++++++++++++++++++++++--------- src/dev/mc146818.hh | 34 ++++++++++++++- src/dev/rtcreg.h | 20 ++++----- 3 files changed, 124 insertions(+), 34 deletions(-) diff --git a/src/dev/mc146818.cc b/src/dev/mc146818.cc index b3b0136f0..276c98581 100644 --- a/src/dev/mc146818.cc +++ b/src/dev/mc146818.cc @@ -77,7 +77,7 @@ MC146818::setTime(const struct tm time) // Datasheet says 1 is sunday wday = time.tm_wday + 1; - if (!(stat_regB & RTCB_BIN)) { + if (!stat_regB.dm) { // The datasheet says that the year field can be either BCD or // years since 1900. Linux seems to be happy with years since // 1900. @@ -95,10 +95,15 @@ MC146818::MC146818(EventManager *em, const string &n, const struct tm time, : EventManager(em), _name(n), event(this, frequency), tickEvent(this) { memset(clock_data, 0, sizeof(clock_data)); - stat_regA = RTCA_32768HZ | RTCA_1024HZ; - stat_regB = RTCB_PRDC_IE | RTCB_24HR; - if (!bcd) - stat_regB |= RTCB_BIN; + + stat_regA = 0; + stat_regA.dv = RTCA_DV_32768HZ; + stat_regA.rs = RTCA_RS_1024HZ; + + stat_regB = 0; + stat_regB.pie = 1; + stat_regB.format24h = 1; + stat_regB.dm = bcd ? 0 : 1; setTime(time); DPRINTFN("Real-time clock set to %s", asctime(&time)); @@ -110,9 +115,18 @@ MC146818::~MC146818() deschedule(event); } +bool +MC146818::rega_dv_disabled(const RtcRegA ®) +{ + return reg.dv == RTCA_DV_DISABLED0 || + reg.dv == RTCA_DV_DISABLED1; +} + void MC146818::writeData(const uint8_t addr, const uint8_t data) { + bool panic_unsupported(false); + if (addr < RTC_STAT_REGA) { clock_data[addr] = data; curTime.tm_sec = unbcdize(sec); @@ -124,24 +138,60 @@ MC146818::writeData(const uint8_t addr, const uint8_t data) curTime.tm_wday = unbcdize(wday) - 1; } else { switch (addr) { - case RTC_STAT_REGA: - // The "update in progress" bit is read only. - if ((data & ~RTCA_UIP) != (RTCA_32768HZ | RTCA_1024HZ)) - panic("Unimplemented RTC register A value write!\n"); - replaceBits(stat_regA, data, 6, 0); - break; + case RTC_STAT_REGA: { + RtcRegA old_rega(stat_regA); + stat_regA = data; + // The "update in progress" bit is read only. + stat_regA.uip = old_rega; + + if (stat_regA.dv != RTCA_DV_32768HZ) { + inform("RTC: Unimplemented divider configuration: %i\n", + stat_regA.dv); + panic_unsupported = true; + } + + if (stat_regA.rs != RTCA_RS_1024HZ) { + inform("RTC: Unimplemented interrupt rate: %i\n", + stat_regA.rs); + panic_unsupported = true; + } + } break; case RTC_STAT_REGB: - if ((data & ~(RTCB_PRDC_IE | RTCB_SQWE)) != RTCB_24HR) - panic("Write to RTC reg B bits that are not implemented!\n"); + stat_regB = data; + if (stat_regB.set) { + inform("RTC: Updating stopping not implemented.\n"); + panic_unsupported = true; + } + + if (stat_regB.aie || stat_regB.uie) { + inform("RTC: Unimplemented interrupt configuration: %s %s\n", + stat_regB.aie ? "alarm" : "", + stat_regB.uie ? "update" : ""); + panic_unsupported = true; + } + + if (stat_regB.dm) { + inform("RTC: The binary interface is not fully implemented.\n"); + panic_unsupported = true; + } + + if (!stat_regB.format24h) { + inform("RTC: The 12h time format not supported.\n"); + panic_unsupported = true; + } + + if (stat_regB.dse) { + inform("RTC: Automatic daylight saving time not supported.\n"); + panic_unsupported = true; + } - if (data & RTCB_PRDC_IE) { + if (stat_regB.pie) { if (!event.scheduled()) event.scheduleIntr(); } else { if (event.scheduled()) deschedule(event); } - stat_regB = data; break; case RTC_STAT_REGC: case RTC_STAT_REGD: @@ -149,6 +199,10 @@ MC146818::writeData(const uint8_t addr, const uint8_t data) break; } } + + if (panic_unsupported) + panic("Unimplemented RTC configuration!\n"); + } uint8_t @@ -160,7 +214,7 @@ MC146818::readData(uint8_t addr) switch (addr) { case RTC_STAT_REGA: // toggle UIP bit for linux - stat_regA ^= RTCA_UIP; + stat_regA.uip = !stat_regA.uip; return stat_regA; break; case RTC_STAT_REGB: @@ -179,7 +233,7 @@ MC146818::readData(uint8_t addr) void MC146818::tickClock() { - if (stat_regB & RTCB_NO_UPDT) + if (stat_regB.set) return; time_t calTime = mkutctime(&curTime); calTime++; @@ -189,9 +243,12 @@ MC146818::tickClock() void MC146818::serialize(const string &base, ostream &os) { + uint8_t regA_serial(stat_regA); + uint8_t regB_serial(stat_regB); + arrayParamOut(os, base + ".clock_data", clock_data, sizeof(clock_data)); - paramOut(os, base + ".stat_regA", stat_regA); - paramOut(os, base + ".stat_regB", stat_regB); + paramOut(os, base + ".stat_regA", (uint8_t)regA_serial); + paramOut(os, base + ".stat_regB", (uint8_t)regB_serial); // // save the timer tick and rtc clock tick values to correctly reschedule @@ -207,10 +264,15 @@ void MC146818::unserialize(const string &base, Checkpoint *cp, const string §ion) { + uint8_t tmp8; + arrayParamIn(cp, section, base + ".clock_data", clock_data, sizeof(clock_data)); - paramIn(cp, section, base + ".stat_regA", stat_regA); - paramIn(cp, section, base + ".stat_regB", stat_regB); + + paramIn(cp, section, base + ".stat_regA", tmp8); + stat_regA = tmp8; + paramIn(cp, section, base + ".stat_regB", tmp8); + stat_regB = tmp8; // // properly schedule the timer and rtc clock events diff --git a/src/dev/mc146818.hh b/src/dev/mc146818.hh index 0c7baf47b..76cd40c37 100644 --- a/src/dev/mc146818.hh +++ b/src/dev/mc146818.hh @@ -33,6 +33,7 @@ #ifndef __DEV_MC146818_HH__ #define __DEV_MC146818_HH__ +#include "base/bitunion.hh" #include "sim/eventq_impl.hh" /** Real-Time Clock (MC146818) */ @@ -112,11 +113,40 @@ class MC146818 : public EventManager void setTime(const struct tm time); + BitUnion8(RtcRegA) + Bitfield<7> uip; /// 1 = date and time update in progress + Bitfield<6, 4> dv; /// Divider configuration + /** Rate selection + 0 = Disabled + For 32768 Hz time bases: + Freq = 32768Hz / 2**(n-1) for n >= 3 + Freq = 256Hz if n = 1 + Freq = 128Hz if n = 2 + Othwerise: + Freq = 32768Hz / 2**(n-1) + */ + Bitfield<3, 0> rs; + EndBitUnion(RtcRegA) + + /// Is the DV field in regA set to disabled? + static inline bool rega_dv_disabled(const RtcRegA ®); + + BitUnion8(RtcRegB) + Bitfield<7> set; /// stop clock updates + Bitfield<6> pie; /// 1 = enable periodic clock interrupt + Bitfield<5> aie; /// 1 = enable alarm interrupt + Bitfield<4> uie; /// 1 = enable update-ended interrupt + Bitfield<3> sqwe; /// 1 = output sqare wave at SQW pin + Bitfield<2> dm; /// 0 = BCD, 1 = Binary coded time + Bitfield<1> format24h; /// 0 = 12 hours, 1 = 24 hours + Bitfield<0> dse; /// USA Daylight Savings Time enable + EndBitUnion(RtcRegB) + /** RTC status register A */ - uint8_t stat_regA; + RtcRegA stat_regA; /** RTC status register B */ - uint8_t stat_regB; + RtcRegB stat_regB; public: MC146818(EventManager *em, const std::string &name, const struct tm time, diff --git a/src/dev/rtcreg.h b/src/dev/rtcreg.h index b1406c464..0a7caecf5 100644 --- a/src/dev/rtcreg.h +++ b/src/dev/rtcreg.h @@ -42,19 +42,17 @@ static const int RTC_MON = 0x08; static const int RTC_YEAR = 0x09; static const int RTC_STAT_REGA = 0x0A; -static const int RTCA_1024HZ = 0x06; /* 1024Hz periodic interrupt frequency */ -static const int RTCA_32768HZ = 0x20; /* 22-stage divider, 32.768KHz timebase */ -static const int RTCA_UIP = 0x80; /* 1 = date and time update in progress */ + +static const int RTCA_DV_4194304HZ = 0x0; +static const int RTCA_DV_1048576HZ = 0x1; +static const int RTCA_DV_32768HZ = 0x2; +static const int RTCA_DV_DISABLED0 = 0x6; +static const int RTCA_DV_DISABLED1 = 0x7; + +static const int RTCA_RS_DISABLED = 0x0; +static const int RTCA_RS_1024HZ = 0x6; static const int RTC_STAT_REGB = 0x0B; -static const int RTCB_DST = 0x01; /* USA Daylight Savings Time enable */ -static const int RTCB_24HR = 0x02; /* 0 = 12 hours, 1 = 24 hours */ -static const int RTCB_BIN = 0x04; /* 0 = BCD, 1 = Binary coded time */ -static const int RTCB_SQWE = 0x08; /* 1 = output sqare wave at SQW pin */ -static const int RTCB_UPDT_IE = 0x10; /* 1 = enable update-ended interrupt */ -static const int RTCB_ALRM_IE = 0x20; /* 1 = enable alarm interrupt */ -static const int RTCB_PRDC_IE = 0x40; /* 1 = enable periodic clock interrupt */ -static const int RTCB_NO_UPDT = 0x80; /* stop clock updates */ static const int RTC_STAT_REGC = 0x0C; static const int RTC_STAT_REGD = 0x0D; -- 2.30.2