///////////////////////////////////////////////////////////////////////////////
//
//  Microsoft Research Singularity
//
//  Copyright (c) Microsoft Corporation.  All rights reserved.
//
//  File:   HalMpClock.cs
//
//  Note:
//
//  This file is an implementation of Interfaces/Hal/HalClock.csi
//
//    For now we just read the time from the HPET or PMTimer.  Both of these
//  use a spinlock to protect the relevant state.  The PMTimer is incredibly
//  expensive.


#if !SINGULARITY_MP
#error "This file is only for MP builds."
#endif

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

    using System;
    using System.Diagnostics;
    using System.Runtime.CompilerServices;
    using System.Threading;

    public class HalClockApic : HalClock
    {
        Apic      apic;
        PMClock   pmClock;
        RTClockApic   rtClock;
        HpetClock hpetClock;
        SpinLock  spinLock;

        internal HalClockApic(Apic apic, RTClockApic rtClock, PMClock pmClock)
        {
            this.apic      = apic;
            this.rtClock   = rtClock;
            this.pmClock   = pmClock;
            this.hpetClock = null;
            this.spinLock  = new SpinLock(SpinLock.Types.MpHalClock);
        }

        [Conditional("SINGULARITY_MP")]
        [NoHeapAllocation]
        private void AcquireLock()
        {
            this.spinLock.Acquire();
        }

        [Conditional("SINGULARITY_MP")]
        [NoHeapAllocation]
        private void ReleaseLock()
        {
            this.spinLock.Release();
        }

        [NoHeapAllocation][HalLock]
        public override long GetKernelTicks()
        {
            bool en = Processor.DisableInterrupts();
            this.AcquireLock();
            try {
                if (this.hpetClock == null) {
                    return (long) pmClock.GetKernelTicks();
                }
                else {
                    return (long) hpetClock.GetKernelTicks();
                }
            }
            finally {
                this.ReleaseLock();
                Processor.RestoreInterrupts(en);
            }
        }

        internal byte Interrupt
        {
            [NoHeapAllocation]
            get { return rtClock.Interrupt; }
        }

        [NoHeapAllocation][HalLock]
        public override void ClearInterrupt()
        {
            bool en = Processor.DisableInterrupts();
            this.AcquireLock();
            try {
                Microsoft.Singularity.Hal.Platform p = Microsoft.Singularity.Hal.Platform.ThePlatform;
                if (hpetClock == null) {
                    pmClock.Update();
                }
                else {
                    hpetClock.Update();
                }
                rtClock.ClearInterrupt();
            }
            finally {
                this.ReleaseLock();
                Processor.RestoreInterrupts(en);
            }
        }

        [HalLock]
        internal void SwitchToHpetClock(HpetClock hc)
        {
            // Change rt clock interrupt frequency to appropriate
            // rate for HPET main clock.
            bool en = Processor.DisableInterrupts();
            this.AcquireLock();
            try {
                rtClock.SetFrequency(HpetClock.UpdateFrequency(hc.Hpet));
                hpetClock = hc;
            }
            finally {
                this.ReleaseLock();
                Processor.RestoreInterrupts(en);
            }
            DebugStub.Print("Hal switching to HpetClock.\n");
        }

        [NoHeapAllocation]
        public override void CpuResumeFromHaltEvent()
        {
        }

        [NoHeapAllocation]
        public override long GetRtcTime()
        {
            return rtClock.GetBootTime() + GetKernelTicks();
        }

        public override void SetRtcTime(long newRtcTime)
        {
            rtClock.SetRtcTime(newRtcTime, GetKernelTicks());
        }
    }
}
