///////////////////////////////////////////////////////////////////////////////
//
//  Microsoft Research Singularity
//
//  Copyright (c) Microsoft Corporation.  All rights reserved.
//
//  Note:
//

namespace Microsoft.Singularity.Hal
{
    using Microsoft.Singularity.Hal.Acpi;

    internal class Calibrate
    {
        private static uint PmDelta(uint newValue, uint oldValue)
        {
            // PmTimer may be 24 or 32 bits depending on system.
            // Simplest to treat as 24 bits.
            return (newValue - oldValue) & 0x00ffffff;
        }

        internal static void CpuCycleCounter(PMTimer pmTimer)
        {
            uint  pmLast  = pmTimer.Value;
            ulong tscLast = Processor.CycleCount;

            uint  pmLimit = PMTimer.FrequencyHz / 15;
            uint  pmAccum = 0;
            ulong tscNow  = 0;

            // Initial measurements
            tscLast = Processor.CycleCount;
            pmLast  = pmTimer.Value;

            // Measurement loop
            do
            {
                tscNow      = Processor.CycleCount;
                uint pmNow  = pmTimer.Value;
                pmAccum    += PmDelta(pmNow, pmLast);
                pmLast      = pmNow;
            } while (pmAccum < pmLimit);

            ulong tscHz = PMTimer.FrequencyHz * (tscNow - tscLast) / pmAccum;
            DebugStub.Print("Cpu{0}: TSC frequency {1} Hz\n",
                            __arglist(Processor.CurrentProcessor.Id, tscHz));
            Processor.CyclesPerSecond = tscHz;
        }

        internal static void ApicTimer(PMTimer pmTimer, ApicTimer apicTimer)
        {
            apicTimer.SetDivisor(1);
            apicTimer.SetOneShot();
            apicTimer.SetInterruptEnabled(false);
            apicTimer.SetInitialCount(~0u);

            uint apicLast = apicTimer.Value;
            uint pmLast   = pmTimer.Value;

            uint  pmLimit = PMTimer.FrequencyHz / 15;
            uint  pmAccum = 0;
            uint  apicNow = 0;

            // Initial measurements
            apicLast = apicTimer.Value;
            pmLast   = pmTimer.Value;
            do
            {
                apicNow     = apicTimer.Value;
                uint pmNow  = pmTimer.Value | 0xff000000;
                pmAccum    += PmDelta(pmNow, pmLast);
                pmLast      = pmNow;
            } while (pmAccum < pmLimit);

            ulong apicHz = PMTimer.FrequencyHz * (ulong)(apicLast - apicNow) / pmAccum;
            DebugStub.Print("Cpu{0}: APIC timer frequency {1} Hz\n",
                            __arglist(Processor.CurrentProcessor.Id, apicHz));

            apicTimer.SetBusFrequency((uint)apicHz);
        }
    }
}
