///////////////////////////////////////////////////////////////////////////////
//
//  Microsoft Research Singularity
//
//  Copyright (c) Microsoft Corporation.  All rights reserved.
//
//  File:   TimerOmap3430.cs
//
//
// The OMAP platform has a plethora of timers to choose from,
// including 12 general-purpose timers, three watchdog timers,
// a 32-kHz synchronised timer and a frame adjustment counter
// for USB applications.
//
// Each of the general-purpose timers are free-running upward
// counters which can be run from either a 32-kHz clock or the
// system clock (12, 13, 19.2, 24, 26, or 38.4 MHz), with the
// option of passing the system clock through a pre-scaler to
// divide it by 2^0 -> 2^8 as desired.
//
// Each of the general purpose timers also provides the option
// of operating in three modes: timer, capture and compare.
//
// The compare mode allows an interrupt and counter value reload
// to happen at an arbitrary counter value. The capture  mode
// allows the value of the timer to be captured and an interrupt
// possibly generated the next time the counter reaches that
// value. The timer mode allows standard periodic and one-shot
// timer behaviour, with value reload and interrupts happening
// when the counter value transitions from 0xffffffff -> 0x0.
//
// The 32-bit width of the timer admits a wide degree of
// flexibility in the use of general-purpose timers, and so for
// our present purposes it is sufficient to use the GP timer in
// the basic timer mode with the 32 kHz clock, giving a minimum
// period of 1/32,768 s = 30.5us and a maximum period of
// (2^32 - 1) / 2^15 s ~= 131,072s = 36hrs 24mins 32s.
//
// As the maximum interval is expressed as a long in units of
// 100ns, the maximum interval for a timer which can be
// expressed is comfortably larger than this (0x7fffffffffffffff
// / 365 / 24 / 60 / 60 / 1000 / 1000 / 10) = ~29,247 years
//
#define VERBOSE

using Microsoft.Singularity.Io;

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

using Microsoft.Singularity.Configuration;

namespace Microsoft.Singularity.Hal
{
    // declare resources for the kernel manifest
    [DriverCategory]
    [Signature("/arm/ti/3430/GPTIMER1")]
    public sealed class TimerResources : DriverCategoryDeclaration
    {
        // register block
        [IoMemoryRange(0, Default = 0x48318000, Length = 0x1000)]
        internal readonly IoMemoryRange registers;

        // interrupt
        [IoIrqRange(1, Default = 37)]
        internal readonly IoIrqRange irq;

        // CTR will create the rest of this class:
        public TimerResources(IoConfig config)
        {
            // dynamic resources
            registers = (IoMemoryRange)config.DynamicRanges[0];
            irq = (IoIrqRange)config.DynamicRanges[1];
        }
    }

    [CLSCompliant(false)]
    public sealed class TimerOmap3430 : HalTimer
    {
        // size of registers in block
        const int Omap3430_TIMER1_RegisterSize      = 0x0000005c; // size of registers in block

        // clock frequency
        const uint Omap3430_TIMER1_Freq             = 32768;

        // registers
        const uint Omap3430_TISR                    = 0x00000018; // interrupt status
        const uint Omap3430_TIER                    = 0x0000001c; // interrupt enable
        const uint Omap3430_TCLR                    = 0x00000024; // control
        const uint Omap3430_TCRR                    = 0x00000028; // timer counter
        const uint Omap3430_TLDR                    = 0x0000002c; // timer load

        // interrupt status register fields
        const uint Omap3430_TISR_TCAR_IT_FLAG       = 0x00000001; // capture interrupt
        const uint Omap3430_TISR_OVF_IT_FLAG        = 0x00000002; // overflow interrupt
        const uint Omap3430_TISR_MAT_IT_FLAG        = 0x00000004; // match interrupt

        // interrupt enable register fields
        const uint Omap3430_TIER_TCAR_IT_ENA        = 0x00000001; // capture interrupt
        const uint Omap3430_TIER_OVF_IT_ENA         = 0x00000002; // overflow interrupt
        const uint Omap3430_TIER_MAT_IT_ENA         = 0x00000004; // match interrupt

        // timer control register fields
        const uint Omap3430_TCLR_ST                 = 0x00000001; // start timer
        const uint Omap3430_TCLR_AR                 = 0x00000002; // auto-reload
        const uint Omap3430_TCLR_PTV_Mask           = 0x0000001c; // prescaler trigger value
        const uint Omap3430_TCLR_PTV_Shift          = 2;          // prescaler trigger value
        const uint Omap3430_TCLR_PRE                = 0x00000020; // prescale enable
        const uint Omap3430_TCLR_CE                 = 0x00000040; // compare enable
        const uint Omap3430_TCLR_SCPWM              = 0x00000080; // PWM value when stopped
        const uint Omap3430_TCLR_TCM_Rising         = 0x00000100; // trigger capture mode
        const uint Omap3430_TCLR_TCM_Falling        = 0x00000200; // trigger capture mode
        const uint Omap3430_TCLR_TCM_Both =                       // trigger capture mode
            (Omap3430_TCLR_TCM_Rising | Omap3430_TCLR_TCM_Falling);
        const uint Omap3430_TCLR_TRG_Overflow       = 0x00000002; // trigger output mode
        const uint Omap3430_TCLR_TRG_OvrMatch       = 0x00000002; // trigger output mode
        const uint Omap3430_TCLR_PT                 = 0x00000004; // PWM toggle select
        const uint Omap3430_TCLR_CAPT_MODE          = 0x00000001; // capture mode
        const uint Omap3430_TCLR_GPO_CFG            = 0x00000002; // PWM config

        // resource allocation
        private PnpConfig   config;
        private Pic         pic;
        private byte        irq;

        // registers
        private IoMappedPort tisr;
        private IoMappedPort tier;
        private IoMappedPort tclr;
        private IoMappedPort tcrr;
        private IoMappedPort tldr;

        // timer state
        private volatile bool interruptPending = false;
        internal volatile uint irqCount = 0;

        int ticksPerSecond = 0;

        // Constructor
        internal TimerOmap3430(PnpConfig config, Pic pic)
        {
#if VERBOSE
            DebugStub.WriteLine("TimerOmap3430: create");
#endif
            TimerResources tr = new TimerResources(config);

            this.config = config;
            this.pic    = pic;
            this.irq    = tr.irq.Irq;

            IoMemory timerRegisters = tr.registers
                .MemoryAtOffset(0, Omap3430_TIMER1_RegisterSize, Access.ReadWrite);

            tisr = timerRegisters.MappedPortAtOffset(Omap3430_TISR, 4, Access.ReadWrite);
            tier = timerRegisters.MappedPortAtOffset(Omap3430_TIER, 4, Access.ReadWrite);
            tclr = timerRegisters.MappedPortAtOffset(Omap3430_TCLR, 4, Access.ReadWrite);
            tcrr = timerRegisters.MappedPortAtOffset(Omap3430_TCRR, 4, Access.ReadWrite);
            tldr = timerRegisters.MappedPortAtOffset(Omap3430_TLDR, 4, Access.ReadWrite);

            // _ARM_ERRATA problem with how functional clock is unstuck
            tcrr.Write32(0, 0);
        }

        public byte Initialize()
        {
#if VERBOSE
            DebugStub.WriteLine("Timer.Initialize()");
#endif

            SetOneShot();
            SetInterruptEnabled(false);
            Stop();

            pic.EnableIrq(irq);
            return pic.IrqToInterrupt(irq);
        }

        public void Finalize()
        {
            pic.DisableIrq(irq);
            Stop();
        }

        internal void SetTicksPerSecond(int count)
        {
#if FALSE
            ticksPerSecond = count;
#endif
#if VERBOSE
            DebugStub.WriteLine("gptimer1 frequency {0} Hz.", __arglist(count));
#endif
        }

        [NoHeapAllocation]
        internal uint Read(IoMappedPort register)
        {
            uint outValue;
            IoResult result = register.Read32NoThrow(0, out outValue);
            DebugStub.Assert(IoResult.Success == result);
            return outValue;
        }

        [NoHeapAllocation]
        internal void Write(IoMappedPort register, uint value)
        {
            IoResult result = register.Write32NoThrow(0, value);
            DebugStub.Assert(IoResult.Success == result);
        }

        [NoHeapAllocation]
        internal uint GetCurrentCount()
        {
            return Read(tcrr);
        }

        [NoHeapAllocation]
        internal void SetInitialCount(uint value)
        {
            // Write current counter & reload values
            Write(tcrr, value);
            Write(tldr, value);
        }

        [NoHeapAllocation]
        internal void SetInterruptEnabled(bool enabled)
        {
            uint ie = Read(tier) & ~Omap3430_TIER_OVF_IT_ENA;

            if (enabled) {
                ie |= Omap3430_TIER_OVF_IT_ENA;
            }
            Write(tier, ie);
        }

        [NoHeapAllocation]
        internal bool InterruptEnabled()
        {
            return (Read(tier) & Omap3430_TIER_OVF_IT_ENA) != 0;
        }

        [NoHeapAllocation]
        internal void Start()
        {
            bool iflag = Processor.DisableInterrupts();
            try {

                // Start counter running
                Write(tclr, Omap3430_TCLR_ST);

            }
            finally {
                Processor.RestoreInterrupts(iflag);
            }
        }

        [NoHeapAllocation]
        internal void Stop()
        {
            bool iflag = Processor.DisableInterrupts();
            try {

                // Stop counter running
                uint val = Read(tclr) & ~Omap3430_TCLR_ST;
                Write(tclr, val);

            }
            finally {
                Processor.RestoreInterrupts(iflag);
            }
        }

        [NoHeapAllocation]
        internal void SetPeriodic()
        {
            uint val = Read(tclr) | Omap3430_TCLR_AR;
            Write(tclr, val);
        }

        [NoHeapAllocation]
        internal void SetOneShot()
        {
            uint val = Read(tclr) & ~Omap3430_TCLR_AR;
            Write(tclr, val);
        }

        private readonly TimeSpan maxInterruptInterval
        = new TimeSpan((((1 << 32) - 1) * (1000 * 1000 * 10)) / Omap3430_TIMER1_Freq); // 36.4 hours
        private readonly TimeSpan minInterruptInterval
        = new TimeSpan(((1000 * 1000 * 10) + Omap3430_TIMER1_Freq) / Omap3430_TIMER1_Freq); // 30.5us, rounded up
        private readonly TimeSpan interruptGranularity
        = new TimeSpan((1000 * 1000 * 10) / Omap3430_TIMER1_Freq); // 30.5us

        /// <value>
        /// Maximum value accepted by SetNextInterrupt (in units of 100ns).
        /// </value>
        public override TimeSpan MaxInterruptInterval
        {
            [NoHeapAllocation]
            get { return maxInterruptInterval; }
        }

        /// <value>
        /// Minimum value accepted by SetNextInterrupt (in units of 100ns).
        /// </value>
        public override TimeSpan MinInterruptInterval
        {
            [NoHeapAllocation]
            get { return minInterruptInterval; }
        }

        [NoHeapAllocation]
        public override void SetNextInterrupt(TimeSpan delta)
        {
#if VERBOSE
            DebugStub.WriteLine("Timer.SetNextInterrupt({0})", __arglist(delta.Ticks));
#endif
            DebugStub.Assert(Processor.InterruptsDisabled());
            DebugStub.Assert(delta >= minInterruptInterval);
            DebugStub.Assert(delta <= maxInterruptInterval);

            bool iflag = Processor.DisableInterrupts();
            try {
                // NB: cast is safe as (delta <= MaxInterruptInterval)
                uint timerIntervals = (uint)((delta.Ticks * Omap3430_TIMER1_Freq)
                                             / (1000 * 1000 * 10));
                uint count = (0xffffffff - timerIntervals) + 1;

                Write(tldr, count);
                SetPeriodic();
                SetInterruptEnabled(true);
                Start();

                interruptPending = true;
            }
            finally {
                Processor.RestoreInterrupts(iflag);
            }
        }

        internal bool InterruptPending
        {
            [NoHeapAllocation]
            get { return interruptPending; }
        }

        [NoHeapAllocation]
        public override void ClearInterrupt()
        {
#if VERBOSE
            DebugStub.WriteLine("Timer.ClearInterrupt()");
#endif

            interruptPending = false;
            irqCount++;

            Write(tisr, Omap3430_TISR_OVF_IT_FLAG);
        }
    }
}


//
// Definitions for registers in GPTIMERx that we aren't currently using
//

//      // timer register base
//      const uint Omap3430_TIMER1_Base = 0x48318000; // GPTIMER1
//      const uint Omap3430_TIMER2_Base = 0x49032000; // GPTIMER2

//        // registers
//        private IoMemory tidr;
//        private IoMemory tiocp_cfg;
//        private IoMemory tistat;
//        private IoMemory tisr;
//        private IoMemory tier;
//        private IoMemory twer;
//        private IoMemory tclr;
//        private IoMemory tcrr;
//        private IoMemory tldr;
//        private IoMemory ttgr;
//        private IoMemory twps;
//        private IoMemory tmar;
//        private IoMemory tcar1;
//        private IoMemory tsicr;
//        private IoMemory tcar2;
//        private IoMemory tpir;
//        private IoMemory tnir;
//        private IoMemory tcvr;
//        private IoMemory tocr;
//        private IoMemory towr;


        // ID register fields
        //const uint Omap3430_TIDR_TID_REV                  = 0x000000ff; // whole id
        //const uint Omap3430_TIDR_TID_REV_Major            = 0x000000f0; // major revision
        //const uint Omap3430_TIDR_TID_REV_Minor            = 0x0000000f; // minor revision

        // L4 interface config register fields
        //const uint Omap3430_TIOCP_CFG_AUTOIDLE            = 0x00000001; // auto L4 clk gating
        //const uint Omap3430_TIOCP_CFG_SOFTRESET           = 0x00000002; // software reset
        //const uint Omap3430_TIOCP_CFG_ENAWAKEUP           = 0x00000004; // wake-up enable
        //const uint Omap3430_TIOCP_CFG_IDLEMODE_Mask       = 0x00000018; // idle mask
        //const uint Omap3430_TIOCP_CFG_IDLEMODE_Force      = 0x00000000; // force idle
        //const uint Omap3430_TIOCP_CFG_IDLEMODE_None       = 0x00000008; // deny idle
        //const uint Omap3430_TIOCP_CFG_IDLEMODE_Smart      = 0x00000010; // smart idle
        //const uint Omap3430_TIOCP_CFG_EMUFREE             = 0x00000020; // timer is free-running under emulation (debug)
        //const uint Omap3430_TIOCP_CFG_CLOCKACTIVITY_Mask  = 0x00000300; // wake-up mode clock activity
        //const uint Omap3430_TIOCP_CFG_CLOCKACTIVITY_L4    = 0x00000100; // maintain L4 clk in wake-up
        //const uint Omap3430_TIOCP_CFG_CLOCKACTIVITY_Func  = 0x00000200; // maintain functional clks in wake-up

        // Registers
        //const uint Omap3430_TIDR          = 0x00000000; // ID
        //const uint Omap3430_TIOCP_CFG     = 0x00000010; // L4 interface config
        //const uint Omap3430_TISTAT        = 0x00000014; // timer status
        //const uint Omap3430_TISR          = 0x00000018; // interrupt status
        //const uint Omap3430_TIER          = 0x0000001c; // interrupt enable
        //const uint Omap3430_TWER          = 0x00000020; // wake-up enable
        //const uint Omap3430_TCLR          = 0x00000024; // control
        //const uint Omap3430_TCRR          = 0x00000028; // timer counter
        //const uint Omap3430_TLDR          = 0x0000002c; // timer load
        //const uint Omap3430_TTGR          = 0x00000030; // timer trigger
        //const uint Omap3430_TWPS          = 0x00000034; // write-posted status
        //const uint Omap3430_TMAR          = 0x00000038; // match value
        //const uint Omap3430_TCAR1         = 0x0000003c; // counter capture 1
        //const uint Omap3430_TSICR         = 0x00000040; // L4 interface sync control
        //const uint Omap3430_TCAR2         = 0x00000044; // counter capture 2
        //const uint Omap3430_TPIR          = 0x00000048; // +ve increment
        //const uint Omap3430_TNIR          = 0x0000004c; // -ve increment
        //const uint Omap3430_TCVR          = 0x00000050; // counter value
        //const uint Omap3430_TOCR          = 0x00000054; // overflow counter
        //const uint Omap3430_TOWR          = 0x00000058; // overflow wrapping
