singrdk/base/Kernel/Singularity.Hal.Omap3430/TimerOmap3430.cs

434 lines
16 KiB
C#
Raw Permalink Normal View History

2008-11-17 18:29:00 -05:00
///////////////////////////////////////////////////////////////////////////////
//
// 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