2008-03-05 09:52:00 -05:00
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
|
|
//
|
|
|
|
// Microsoft Research Singularity
|
|
|
|
//
|
|
|
|
// Copyright (c) Microsoft Corporation. All rights reserved.
|
|
|
|
//
|
|
|
|
// Note:
|
|
|
|
// Based on IA-PC HPET (High Precision Event Timers) Specification
|
|
|
|
// Revision 1.0, June 2004.
|
|
|
|
//
|
|
|
|
// The HPET code includes unchecked I/O memory operations so
|
|
|
|
// methods can be marked as NoHeapAllocation. CreateDevice()
|
|
|
|
// must verify that there is at least enough memory for the
|
|
|
|
// MainTimer.
|
|
|
|
|
|
|
|
namespace Microsoft.Singularity.Hal
|
|
|
|
{
|
|
|
|
using Microsoft.Singularity.Io;
|
|
|
|
using Microsoft.Singularity.Configuration;
|
|
|
|
|
|
|
|
using System;
|
|
|
|
using System.Runtime.CompilerServices;
|
|
|
|
|
|
|
|
internal struct HpetTimerCaps
|
|
|
|
{
|
|
|
|
internal const uint FsbDelivery = 1u << 15;
|
|
|
|
internal const uint Width64 = 1u << 5;
|
|
|
|
internal const uint PeriodicSupport = 1u << 4;
|
|
|
|
|
|
|
|
internal const uint Mask = FsbDelivery | Width64 | PeriodicSupport;
|
|
|
|
}
|
|
|
|
|
|
|
|
internal struct HpetTimerConf
|
|
|
|
{
|
|
|
|
internal const uint LevelTriggered = 1u << 1;
|
|
|
|
internal const uint InterruptEnable = 1u << 2;
|
|
|
|
internal const uint PeriodicMode = 1u << 3;
|
|
|
|
internal const uint Force32Bit = 1u << 8;
|
|
|
|
}
|
|
|
|
|
|
|
|
[DriverCategory]
|
|
|
|
[Signature("/pnp/PNP0103")]
|
|
|
|
public sealed class HpetResources : DriverCategoryDeclaration
|
|
|
|
{
|
|
|
|
[IoMemoryRange(0, Default = 0, Length = 0x400)]
|
|
|
|
public IoMemoryRange mem1;
|
|
|
|
}
|
|
|
|
|
|
|
|
internal sealed class Hpet : IDevice
|
|
|
|
{
|
|
|
|
private PnpConfig config;
|
|
|
|
private IoMemory region;
|
|
|
|
|
|
|
|
private const int MainCounterOffset = 0xf0;
|
|
|
|
|
|
|
|
internal const int MinRegionBytes = 0xf8;
|
|
|
|
|
|
|
|
uint periodFs; // femtoseconds
|
|
|
|
uint capabilities; // bits 0-31 at offset 0
|
|
|
|
|
|
|
|
internal Hpet(PnpConfig config)
|
|
|
|
{
|
2008-11-17 18:29:00 -05:00
|
|
|
int cpuId;
|
|
|
|
Microsoft.Singularity.Hal.Platform p = Microsoft.Singularity.Hal.Platform.ThePlatform;
|
|
|
|
cpuId = p.ApicId;
|
|
|
|
|
2008-03-05 09:52:00 -05:00
|
|
|
this.config = config;
|
|
|
|
IoMemoryRange imr = (IoMemoryRange) config.DynamicRanges[0];
|
|
|
|
this.region = imr.MemoryAtOffset(0, (int) imr.Length.ToUInt32(),
|
|
|
|
Access.ReadWrite);
|
|
|
|
|
|
|
|
capabilities = region.Read32(0x00);
|
|
|
|
periodFs = region.Read32(0x04);
|
|
|
|
|
2008-11-17 18:29:00 -05:00
|
|
|
if (cpuId == 0) {
|
|
|
|
DebugStub.Print("new Hpet writing regions\n");
|
|
|
|
// Disable interrupts on timers
|
|
|
|
for (uint i = 0; i <= MaxCounterIndex; i++) {
|
|
|
|
DisableInterrupt(i);
|
|
|
|
}
|
|
|
|
|
|
|
|
uint gc = region.Read32(0x10) & ~3u; // Clear legacy bits
|
|
|
|
region.Write32(0x10, gc | 1); // Enable main counter
|
2008-03-05 09:52:00 -05:00
|
|
|
}
|
2008-11-17 18:29:00 -05:00
|
|
|
}
|
2008-03-05 09:52:00 -05:00
|
|
|
|
2008-11-17 18:29:00 -05:00
|
|
|
private bool MainCounterWorks()
|
|
|
|
{
|
|
|
|
uint first = this.MainCounterValue32;
|
|
|
|
for (int i = 0; i < 50; i++) {
|
|
|
|
if (this.MainCounterValue32 != first) {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return false;
|
2008-03-05 09:52:00 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
void IDevice.Initialize()
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
void IDevice.Finalize()
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
/// <summary> Period in femptoseconds (10^-15) </summary>
|
|
|
|
internal uint PeriodFs
|
|
|
|
{
|
|
|
|
[NoHeapAllocation]
|
|
|
|
get { return periodFs; }
|
|
|
|
}
|
|
|
|
|
|
|
|
internal ushort VendorId
|
|
|
|
{
|
|
|
|
[NoHeapAllocation]
|
|
|
|
get { return (ushort) (capabilities >> 16); }
|
|
|
|
}
|
|
|
|
|
|
|
|
internal uint Width
|
|
|
|
{
|
|
|
|
[NoHeapAllocation]
|
|
|
|
get { return ((capabilities & 0x2000) != 0) ? 64u : 32u; }
|
|
|
|
}
|
|
|
|
|
|
|
|
internal uint MaxCounterIndex
|
|
|
|
{
|
|
|
|
[NoHeapAllocation]
|
|
|
|
get { return ((capabilities >> 8) & 0xf); }
|
|
|
|
}
|
|
|
|
|
|
|
|
internal uint AvailableCounters
|
|
|
|
{
|
|
|
|
[NoHeapAllocation]
|
|
|
|
get { return MaxCounterIndex + 1; }
|
|
|
|
}
|
|
|
|
|
|
|
|
internal uint Revision
|
|
|
|
{
|
|
|
|
[NoHeapAllocation]
|
|
|
|
get { return capabilities & 0xff; }
|
|
|
|
}
|
|
|
|
|
|
|
|
internal ulong MainCounterValue
|
|
|
|
{
|
|
|
|
[NoHeapAllocation]
|
|
|
|
get {
|
|
|
|
ulong value;
|
|
|
|
uint hi;
|
|
|
|
do {
|
|
|
|
IoResult r;
|
|
|
|
r = region.Read64NoThrow(MainCounterOffset, out value);
|
|
|
|
DebugStub.Assert(IoResult.Success == r);
|
|
|
|
r = region.Read32NoThrow(MainCounterOffset + 4, out hi);
|
|
|
|
DebugStub.Assert(IoResult.Success == r);
|
|
|
|
} while ((uint)(value >> 32) != hi);
|
|
|
|
return value;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
internal uint MainCounterValue32
|
|
|
|
{
|
|
|
|
[NoHeapAllocation]
|
|
|
|
get {
|
|
|
|
uint value;
|
|
|
|
IoResult r = region.Read32NoThrow(MainCounterOffset,
|
|
|
|
out value);
|
|
|
|
DebugStub.Assert(IoResult.Success == r);
|
|
|
|
return value;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
[NoHeapAllocation]
|
|
|
|
internal void ClearInterrupt(uint timer)
|
|
|
|
{
|
|
|
|
if (timer <= MaxCounterIndex) {
|
|
|
|
|
|
|
|
uint value;
|
|
|
|
IoResult result = region.Read32NoThrow(0x20, out value);
|
|
|
|
DebugStub.Assert(IoResult.Success == result);
|
|
|
|
value = value & ~(1u << (int)timer);
|
|
|
|
|
|
|
|
result = region.Write32NoThrow(0x20, value);
|
|
|
|
DebugStub.Assert(IoResult.Success == result);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
[NoHeapAllocation]
|
|
|
|
private int TimerOffset(uint timer)
|
|
|
|
{
|
|
|
|
return 0x100 + 0x20 * (int)timer;
|
|
|
|
}
|
|
|
|
|
|
|
|
internal void DisableInterrupt(uint timer)
|
|
|
|
{
|
|
|
|
if (timer <= MaxCounterIndex) {
|
|
|
|
uint m = region.Read32(TimerOffset(timer));
|
|
|
|
m &= ~HpetTimerConf.InterruptEnable;
|
|
|
|
region.Write32(TimerOffset(timer), m);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
internal static IDevice CreateDevice(IoConfig config, string name)
|
|
|
|
{
|
|
|
|
PnpConfig pnpConfig = config as PnpConfig;
|
|
|
|
if (pnpConfig == null) {
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
|
|
|
|
IoMemoryRange imr = config.DynamicRanges[0] as IoMemoryRange;
|
|
|
|
if (imr == null) {
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
|
|
|
|
int imrBytes = (int)imr.Length.ToUInt32();
|
|
|
|
if (imrBytes < Hpet.MinRegionBytes) {
|
|
|
|
DebugStub.Write(
|
|
|
|
"HPET failed as region too small ({0} bytes).\n",
|
|
|
|
__arglist(imrBytes));
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
|
|
|
|
Hpet hpet = new Hpet(pnpConfig);
|
2008-11-17 18:29:00 -05:00
|
|
|
|
|
|
|
if (hpet.MainCounterWorks()) {
|
|
|
|
HalDevicesApic.SwitchToHpetClock(hpet);
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
DebugStub.Print("WARNING: HPET main counter does not work!\n");
|
|
|
|
}
|
|
|
|
|
2008-03-05 09:52:00 -05:00
|
|
|
return hpet;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|