singrdk/base/Kernel/Singularity.Hal.ApicPC/RTClock.cs

387 lines
14 KiB
C#

///////////////////////////////////////////////////////////////////////////////
//
// Microsoft Research Singularity
//
// Copyright (c) Microsoft Corporation. All rights reserved.
//
// File: RTClock.cs
//
// Useful reference URLs:
// http://developer.intel.com/design/archives/periphrl/index.htm
// http://developer.intel.com/design/archives/periphrl/docs/7203.htm
// http://developer.intel.com/design/archives/periphrl/docs/23124406.htm
// http://www.cebix.net/downloads/bebox/bq3285.pdf
//
//#define SAMPLE_PC
using Microsoft.Singularity.Io;
using System;
using System.Diagnostics;
using System.Runtime.CompilerServices;
using System.Threading;
using Microsoft.Singularity.Configuration;
namespace Microsoft.Singularity.Hal
{
[DriverCategory]
[Signature("/pnp/PNP0B00")]
public sealed class RtcResources : DriverCategoryDeclaration
{
[IoPortRange(0, Default = 0x0070, Length = 0x02)]
public IoPortRange port1;
[IoIrqRange(1, Default = 0x08)]
public IoIrqRange irq1;
}
/// <remarks>
/// Class <c>RTClock</c> represents the system
/// Real-Time Clock. RTC chips in PCs are based on the
/// Motorola MC146818A. It provides timing resolution
/// of 1 second, but can generate periodic interrupts
/// more frequently.
/// </remarks>
[ CLSCompliant(false) ]
public sealed class RTClock
{
// Data registers
private const byte DS1287_SECONDS = 0x00;
private const byte DS1287_SECONDS_ALARM = 0x01;
private const byte DS1287_MINUTES = 0x02;
private const byte DS1287_MINUTES_ALARM = 0x03;
private const byte DS1287_HOURS = 0x04;
private const byte DS1287_HOURS_ALARM = 0x05;
private const byte DS1287_DAY_OF_WEEK = 0x06; // 1 is Sunday
private const byte DS1287_DAY_OF_MONTH = 0x07;
private const byte DS1287_MONTH = 0x08;
private const byte DS1287_YEAR = 0x09;
private const byte DS1287_USERDATA = 0x32; // Randomly chosen user reg.
// Control registers and masks
private const byte DS1287_A = 0x0a;
private const byte DS1287_A_UIP = 0x80; // Update Cycle Status
private const byte DS1287_A_DIVON = 0x20; // Osc and Freq Div on
private const byte DS1287_A_8KHZ = 0x03;
private const byte DS1287_A_4KHZ = 0x04;
private const byte DS1287_A_2KHZ = 0x05;
private const byte DS1287_A_1KHZ = 0x06;
private const byte DS1287_A_64HZ = 0x0a;
private const byte DS1287_A_2HZ = 0x0f;
private const byte DS1287_B = 0x0b;
private const byte DS1287_B_UTI = 0x80; // Update Transfer Inhibit
private const byte DS1287_B_PIE = 0x40; // Periodic Interrupt Enable
private const byte DS1287_B_24H = 0x20; // Alarm Interrupt Enable
private const byte DS1287_B_UIE = 0x10; // Update Cycle Inter. Enable
private const byte DS1287_B_SQWE = 0x08; // Square-Wave Enable
private const byte DS1287_B_DF = 0x04; // Data Format
private const byte DS1287_B_DF_BINARY = 0x04; // Binary
private const byte DS1287_B_DF_BCD = 0x00; // Binary Coded Decimal (BCD)
private const byte DS1287_B_HF = 0x02; // Hour Format
private const byte DS1287_B_HF_24H = 0x02; // 24-hour format
private const byte DS1287_B_HF_12H = 0x00; // 12-hour format
private const byte DS1287_B_DSE = 0x01; // Daylight Saving Enable
private const byte DS1287_C = 0x0c;
private const byte DS1287_C_UF = 0x10; // Update Event Flag
private const byte DS1287_C_AF = 0x20; // Alarm Event Flag
private const byte DS1287_C_PF = 0x40; // Periodic Event Flag
private const byte DS1287_C_INTF = 0x80; // Interrupt Request Flag
private const byte DS1287_D = 0x0d;
private const byte DS1287_D_VRT = 0x80; // Valid RAM and Time
private PnpConfig config;
private IoPort rtcadd;
private IoPort rtcdat;
private Apic apic;
private byte irq;
private byte interrupt;
internal volatile uint irqCount = 0;
private SpinLock rtcSpinLock; // Protects rtcBootTime and rtc update
private long rtcBootTime;
// Constructor
internal RTClock(PnpConfig config, Apic apic)
{
DebugStub.Print("RTClock: create\n");
// /pnp/08/03/01/PNP0B00 0003 cfg dis : ISA RTC Controller : AT RTC
// IRQ mask=0100 type=47
// I/O Port inf=01 min=0070 max=0070 aln=01 len=02 0070..0071
this.config = config;
this.irq = ((IoIrqRange)config.DynamicRanges[1]).Irq;
this.apic = apic;
this.rtcSpinLock = new SpinLock();
rtcadd = ((IoPortRange)config.DynamicRanges[0])
.PortAtOffset(0, 1, Access.ReadWrite);
rtcdat = ((IoPortRange)config.DynamicRanges[0])
.PortAtOffset(1, 1, Access.ReadWrite);
this.interrupt = Initialize();
}
[NoHeapAllocation]
private byte ReadRtc(byte addr)
{
IoResult result;
result = rtcadd.Write8NoThrow(addr);
DebugStub.Assert(IoResult.Success == result);
byte value;
result = rtcdat.Read8NoThrow(out value);
DebugStub.Assert(IoResult.Success == result);
return value;
}
[NoHeapAllocation]
private void WriteRtc(byte addr, byte val)
{
IoResult result;
result = rtcadd.Write8NoThrow(addr);
DebugStub.Assert(IoResult.Success == result);
result = rtcdat.Write8NoThrow(val);
DebugStub.Assert(IoResult.Success == result);
}
private byte Initialize()
{
// Disable and clear interrupts
if (Processor.SamplingEnabled()) {
WriteRtc(DS1287_A, DS1287_A_DIVON | DS1287_A_8KHZ);
}
else {
WriteRtc(DS1287_A, DS1287_A_DIVON | DS1287_A_64HZ);
}
WriteRtc(DS1287_B, DS1287_B_HF_24H | DS1287_B_DF_BCD);
ReadRtc(DS1287_C); // Clear any update bits
if ((ReadRtc(DS1287_D) & DS1287_D_VRT) == 0)
{
DebugStub.Print("RTClock weak or defective power source.\n");
}
rtcBootTime = PullRtcTime();
DebugStub.Print("RTClock::Start()\n");
// Enable and clear interrupts
// NB it may take 500ms for first interrupt to be generated.
WriteRtc(DS1287_A, DS1287_A_DIVON | DS1287_A_2HZ);
WriteRtc(DS1287_B, DS1287_B_HF_24H | DS1287_B_DF_BCD | DS1287_B_PIE | DS1287_B_SQWE);
ReadRtc(DS1287_C);
apic.EnableIrq(irq);
return apic.IrqToInterrupt(irq);
}
private int Log2(uint value)
{
int l = 0;
if ((value & 0xffff0000u) != 0) { l += 16; value >>= 16; }
if ((value & 0x0000ff00u) != 0) { l += 8; value >>= 8; }
if ((value & 0x000000f0u) != 0) { l += 4; value >>= 4; }
if ((value & 0x0000000cu) != 0) { l += 2; value >>= 2; }
if ((value & 0x0000000au) != 0) { l += 1; value >>= 1; }
return l;
}
internal bool SetFrequency(uint frequency)
{
if (frequency > 8192 || frequency < 2)
{
return false;
}
if (Processor.SamplingEnabled()) {
frequency = 8192;
}
int a = 0x20;
a |= (0xf - (Log2(frequency) - 1)) & 0xf;
WriteRtc(DS1287_A, (byte) a);
WriteRtc(DS1287_B, DS1287_B_HF_24H | DS1287_B_DF_BCD | DS1287_B_PIE | DS1287_B_SQWE);
ReadRtc(DS1287_C);
return true;
}
public void Finalize()
{
apic.DisableIrq(irq);
}
[NoHeapAllocation]
public void ClearInterrupt()
{
byte status = ReadRtc(DS1287_C);
if ((status & DS1287_C_PF) != 0)
{
irqCount++;
}
apic.AckIrq(irq);
}
public byte Interrupt
{
[NoHeapAllocation]
get { return interrupt; }
}
///////////////////////////////////////////////////////////////////////
[ System.Diagnostics.Conditional("SINGULARITY_MP") ]
[NoHeapAllocation]
private void AcquireLock()
{
rtcSpinLock.Acquire();
}
[ System.Diagnostics.Conditional("SINGULARITY_MP") ]
[NoHeapAllocation]
private void ReleaseLock()
{
rtcSpinLock.Release();
}
[NoHeapAllocation]
public long GetBootTime()
{
bool en = Processor.DisableInterrupts();
try {
AcquireLock();
long bootTime = rtcBootTime;
ReleaseLock();
return bootTime;
}
finally {
Processor.RestoreInterrupts(en);
}
}
public void SetRtcTime(long newRtcTime, long kernelTicks)
{
bool en = Processor.DisableInterrupts();
try {
AcquireLock();
long currentRtcTime = rtcBootTime + kernelTicks;
long delta = newRtcTime - currentRtcTime;
rtcBootTime = rtcBootTime + delta;
PushRtcTime(newRtcTime);
ReleaseLock();
}
finally {
Processor.RestoreInterrupts(en);
}
}
[NoHeapAllocation]
internal static byte BcdToHex(byte bcd)
{
return (byte) (10 * (bcd >> 4) + (bcd & 0x0f));
}
[NoHeapAllocation]
internal static byte HexToBcd(int hex)
{
int h = hex / 10;
return (byte) ((h << 4) | (hex - h * 10));
}
private long PullRtcTime()
{
for (uint iters = 0; iters < 1000000; iters++) {
bool iflag = Processor.DisableInterrupts();
try {
ReadRtc(DS1287_C); // Clear update flag if set
if ((ReadRtc(DS1287_A) & DS1287_A_UIP) != 0) {
// Update is in progress, try again
continue;
}
// There is no update
// pending. The specs suggest at least
// ~244us to read values (T_BUC)...
byte second = BcdToHex(ReadRtc(DS1287_SECONDS));
byte minute = BcdToHex(ReadRtc(DS1287_MINUTES));
byte hour = BcdToHex(ReadRtc(DS1287_HOURS));
byte day = BcdToHex(ReadRtc(DS1287_DAY_OF_MONTH));
byte month = BcdToHex(ReadRtc(DS1287_MONTH));
byte year = BcdToHex(ReadRtc(DS1287_YEAR));
byte century = BcdToHex(ReadRtc(DS1287_USERDATA));
// ...but we don't
// trust the specs, particularly as we may
// be running on emulated hardware. Retry
// if an update is pending or appears to
// have completed whilst reading the time.
if ((ReadRtc(DS1287_A) & DS1287_A_UIP) != 0 ||
(ReadRtc(DS1287_C) & DS1287_C_UF) != 0) {
continue;
}
DebugStub.Print("PullRtcTime: " +
"{0:d4}-{1:d2}-{2:d} {3:2}:{4:d2}:{5:d2}" +
"\n",
__arglist(100*century+year, month, day,
hour, minute, second));
DateTime d = new DateTime(century * 100 + year, month, day,
hour, minute, second);
return d.Ticks;
}
catch (ArgumentOutOfRangeException e) {
DebugStub.Print("PullRtcTime failed: {0}\n",
__arglist(e));
}
finally {
Processor.RestoreInterrupts(iflag);
}
}
return (new DateTime(2005, 1, 1, 0, 0, 0)).Ticks;
}
private void PushRtcTime(long dtTicks)
{
DateTime dt = new DateTime(dtTicks);
bool iflag = Processor.DisableInterrupts();
try {
byte saved = ReadRtc(DS1287_B);
// Set UTI bit to stop transfers between RTC
// bytes and user buffer.
WriteRtc(DS1287_B, (byte)(saved | DS1287_B_UTI));
// Write new values
WriteRtc(DS1287_SECONDS, HexToBcd(dt.Second));
WriteRtc(DS1287_MINUTES, HexToBcd(dt.Minute));
WriteRtc(DS1287_HOURS, HexToBcd(dt.Hour));
WriteRtc(DS1287_DAY_OF_MONTH, HexToBcd(dt.Day));
WriteRtc(DS1287_MONTH, HexToBcd(dt.Month));
int century = dt.Year / 100;
WriteRtc(DS1287_YEAR, HexToBcd(dt.Year - century*100));
WriteRtc(DS1287_USERDATA, HexToBcd(century));
// Clear UTI bit to enable transfers again
WriteRtc(DS1287_B, saved);
DebugStub.Print("PushRtcTime {3:2}:{4:d2}:{5:d2} {0}/{1}-{2:d4}\n",
__arglist(dt.Month, dt.Day,
100*century + dt.Year, dt.Hour,
dt.Minute, dt.Second));
}
finally {
Processor.RestoreInterrupts(iflag);
}
}
}
}