123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632 |
- /*++
- Copyright (c) 2012 Minoca Corp. All Rights Reserved
- Module Name:
- omap3tmr.c
- Abstract:
- This module implements support for the GP Timers on the TI OMAP3.
- Author:
- Evan Green 3-Sep-2012
- Environment:
- Kernel
- --*/
- //
- // ------------------------------------------------------------------- Includes
- //
- //
- // Include kernel.h, but be cautious about which APIs are used. Most of the
- // system depends on the hardware modules. Limit use to HL, RTL and AR routines.
- //
- #include <minoca/kernel/kernel.h>
- #include "omap3.h"
- //
- // --------------------------------------------------------------------- Macros
- //
- //
- // This macro reads from an OMAP3 timer. _Base should be a pointer, and
- // _Register should be a GP_TIMER_REGISTER value.
- //
- #define READ_TIMER_REGISTER(_Base, _Register) \
- HlReadRegister32((PULONG)(_Base) + (_Register))
- //
- // This macro writes to an OMAP3 timer. _Base should be a pointer,
- // _Register should be GP_TIMER_REGISTER value, and _Value should be a ULONG.
- //
- #define WRITE_TIMER_REGISTER(_Base, _Register, _Value) \
- HlWriteRegister32((PULONG)(_Base) + (_Register), (_Value))
- //
- // ---------------------------------------------------------------- Definitions
- //
- #define GPTIMER_IDLEMODE_NOIDLE 0x00000080
- //
- // Mode bits.
- //
- #define GPTIMER_STARTED 0x00000001
- #define GPTIMER_OVERFLOW_TRIGGER 0x00000400
- #define GPTIMER_OVERFLOW_AND_MATCH_TRIGGER 0x00000800
- #define GPTIMER_COMPARE_ENABLED 0x00000040
- #define GPTIMER_AUTORELOAD 0x00000002
- //
- // Interrupt enable bits.
- //
- #define GPTIMER_MATCH_INTERRUPT 0x00000001
- #define GPTIMER_OVERFLOW_INTERRUPT 0x00000002
- //
- // ----------------------------------------------- Internal Function Prototypes
- //
- KSTATUS
- HlpOmap3TimerInitialize (
- PVOID Context
- );
- ULONGLONG
- HlpOmap3TimerRead (
- PVOID Context
- );
- VOID
- HlpOmap3TimerWrite (
- PVOID Context,
- ULONGLONG NewCount
- );
- KSTATUS
- HlpOmap3TimerArm (
- PVOID Context,
- TIMER_MODE Mode,
- ULONGLONG TickCount
- );
- VOID
- HlpOmap3TimerDisarm (
- PVOID Context
- );
- VOID
- HlpOmap3TimerAcknowledgeInterrupt (
- PVOID Context
- );
- //
- // ------------------------------------------------------ Data Type Definitions
- //
- //
- // Define the GP timer register set, with offsets in ULONGs.
- //
- typedef enum _GP_TIMER_REGISTER {
- GpTimerReserved0,
- GpTimerReserved1,
- GpTimerReserved2,
- GpTimerReserved3,
- GpTimerInterfaceConfiguration1, // TIOCP_CFG
- GpTimerStatus, // TISTAT
- GpTimerInterruptStatus, // TISR
- GpTimerInterruptEnable, // TIER
- GpTimerWakeup, // TWER
- GpTimerMode, // TCLR
- GpTimerCurrentCount, // TCRR
- GpTimerLoadCount, // TLDR
- GpTimerTriggerReload, // TTGR
- GpTimerWritePending, // TWPS
- GpTimerMatchCount, // TMAR
- GpTimerCapture1, // TCAR1
- GpTimerInterfaceConfiguration2, // TSICR
- GpTimerCapture2, // TCAR2
- GpTimerPositive1msIncrement, // TPIR
- GpTimerNegative1msIncrement, // TNIR
- GpTimerCurrentRounding1ms, // TCVR
- GpTimerOverflowValue, // TOCR
- GpTimerMaskedOverflowCount, // TOWR
- } GP_TIMER_REGISTER, *PGP_TIMER_REGISTER;
- /*++
- Structure Description:
- This structure stores the internal state associated with an OMAP3 GP
- timer.
- Members:
- Base - Stores the virtual address of the timer.
- PhysicalAddress - Stores the physical address of the timer.
- Index - Stores the zero-based index of this timer within the timer block.
- --*/
- typedef struct _GP_TIMER_DATA {
- PVOID Base;
- PHYSICAL_ADDRESS PhysicalAddress;
- ULONG Index;
- } GP_TIMER_DATA, *PGP_TIMER_DATA;
- //
- // -------------------------------------------------------------------- Globals
- //
- //
- // Store a pointer to the OMAP ACPI table.
- //
- POMAP3_TABLE HlOmap3Table = NULL;
- //
- // ------------------------------------------------------------------ Functions
- //
- VOID
- HlpOmap3TimerModuleEntry (
- VOID
- )
- /*++
- Routine Description:
- This routine is the entry point for the OMAP3 GP Timer hardware module.
- Its role is to detect and report the prescense of OMAP3 Timers.
- Arguments:
- Services - Supplies a pointer to the services/APIs made available by the
- kernel to the hardware module.
- Return Value:
- None.
- --*/
- {
- KSTATUS Status;
- TIMER_DESCRIPTION Timer;
- PGP_TIMER_DATA TimerData;
- ULONG TimerIndex;
- //
- // Interrupt controllers are always initialized before timers, so the
- // OMAP3 ACPI table should already be set up.
- //
- if (HlOmap3Table == NULL) {
- goto GpTimerModuleEntryEnd;
- }
- //
- // Fire up the timer's power.
- //
- Status = HlpOmap3InitializePowerAndClocks();
- if (!KSUCCESS(Status)) {
- goto GpTimerModuleEntryEnd;
- }
- //
- // Register each of the independent timers in the timer block.
- //
- for (TimerIndex = 0; TimerIndex < OMAP3_TIMER_COUNT; TimerIndex += 1) {
- //
- // Skip the timer if it has no address.
- //
- if (HlOmap3Table->TimerPhysicalAddress[TimerIndex] == (UINTN)NULL) {
- continue;
- }
- RtlZeroMemory(&Timer, sizeof(TIMER_DESCRIPTION));
- Timer.TableVersion = TIMER_DESCRIPTION_VERSION;
- Timer.FunctionTable.Initialize = HlpOmap3TimerInitialize;
- Timer.FunctionTable.ReadCounter = HlpOmap3TimerRead;
- Timer.FunctionTable.WriteCounter = HlpOmap3TimerWrite;
- Timer.FunctionTable.Arm = HlpOmap3TimerArm;
- Timer.FunctionTable.Disarm = HlpOmap3TimerDisarm;
- Timer.FunctionTable.AcknowledgeInterrupt =
- HlpOmap3TimerAcknowledgeInterrupt;
- TimerData = HlAllocateMemory(sizeof(GP_TIMER_DATA),
- OMAP3_ALLOCATION_TAG,
- FALSE,
- NULL);
- if (TimerData == NULL) {
- Status = STATUS_INSUFFICIENT_RESOURCES;
- goto GpTimerModuleEntryEnd;
- }
- RtlZeroMemory(TimerData, sizeof(GP_TIMER_DATA));
- TimerData->PhysicalAddress =
- HlOmap3Table->TimerPhysicalAddress[TimerIndex];
- TimerData->Index = TimerIndex;
- Timer.Context = TimerData;
- Timer.Features = TIMER_FEATURE_READABLE |
- TIMER_FEATURE_WRITABLE |
- TIMER_FEATURE_PERIODIC |
- TIMER_FEATURE_ONE_SHOT;
- Timer.CounterBitWidth = OMAP3_TIMER_BIT_WIDTH;
- //
- // The first timer runs at the bus clock speed, but the rest run at
- // a fixed frequency.
- //
- if (TimerIndex == 0) {
- Timer.CounterFrequency = 0;
- } else {
- Timer.CounterFrequency = OMAP3_TIMER_FIXED_FREQUENCY;
- }
- Timer.Interrupt.Line.Type = InterruptLineControllerSpecified;
- Timer.Interrupt.Line.U.Local.Controller = 0;
- Timer.Interrupt.Line.U.Local.Line = HlOmap3Table->TimerGsi[TimerIndex];
- Timer.Interrupt.TriggerMode = InterruptModeUnknown;
- Timer.Interrupt.ActiveLevel = InterruptActiveLevelUnknown;
- //
- // Register the timer with the system.
- //
- Status = HlRegisterHardware(HardwareModuleTimer, &Timer);
- if (!KSUCCESS(Status)) {
- goto GpTimerModuleEntryEnd;
- }
- }
- GpTimerModuleEntryEnd:
- return;
- }
- //
- // --------------------------------------------------------- Internal Functions
- //
- KSTATUS
- HlpOmap3TimerInitialize (
- PVOID Context
- )
- /*++
- Routine Description:
- This routine initializes an OMAP3 timer.
- Arguments:
- Context - Supplies the pointer to the timer's context, provided by the
- hardware module upon initialization.
- Return Value:
- Status code.
- --*/
- {
- KSTATUS Status;
- PGP_TIMER_DATA Timer;
- ULONG Value;
- Timer = (PGP_TIMER_DATA)Context;
- //
- // Map the hardware if that has not been done.
- //
- if (Timer->Base == NULL) {
- Timer->Base = HlMapPhysicalAddress(Timer->PhysicalAddress,
- OMAP3_TIMER_CONTROLLER_SIZE,
- TRUE);
- if (Timer->Base == NULL) {
- Status = STATUS_INSUFFICIENT_RESOURCES;
- goto GpTimerInitializeEnd;
- }
- }
- //
- // Program the timer in free running mode with no interrupt. Set the
- // interface configuration to a state that disables going idle.
- //
- WRITE_TIMER_REGISTER(Timer->Base,
- GpTimerInterfaceConfiguration1,
- GPTIMER_IDLEMODE_NOIDLE);
- //
- // Disable wakeup functionality.
- //
- WRITE_TIMER_REGISTER(Timer->Base, GpTimerWakeup, 0);
- //
- // Set the second interface configuration register to non-posted mode,
- // which means that writes don't return until they complete. Posted mode
- // is faster for writes but requires polling a bit for reads.
- //
- WRITE_TIMER_REGISTER(Timer->Base, GpTimerInterfaceConfiguration2, 0);
- //
- // Disable all interrupts for now.
- //
- WRITE_TIMER_REGISTER(Timer->Base, GpTimerInterruptEnable, 0);
- //
- // Set the load value to zero to create a free-running timer, and reset the
- // current counter now too.
- //
- WRITE_TIMER_REGISTER(Timer->Base, GpTimerLoadCount, 0x00000000);
- WRITE_TIMER_REGISTER(Timer->Base, GpTimerCurrentCount, 0x00000000);
- //
- // Set the mode register to auto-reload, and start the timer.
- //
- Value = GPTIMER_OVERFLOW_TRIGGER | GPTIMER_STARTED | GPTIMER_AUTORELOAD;
- WRITE_TIMER_REGISTER(Timer->Base, GpTimerMode, Value);
- //
- // Reset all interrupt-pending bits.
- //
- WRITE_TIMER_REGISTER(Timer->Base, GpTimerInterruptStatus, 0x7);
- Status = STATUS_SUCCESS;
- GpTimerInitializeEnd:
- return Status;
- }
- ULONGLONG
- HlpOmap3TimerRead (
- PVOID Context
- )
- /*++
- Routine Description:
- This routine returns the hardware counter's raw value.
- Arguments:
- Context - Supplies the pointer to the timer's context.
- Return Value:
- Returns the timer's current count.
- --*/
- {
- PGP_TIMER_DATA Timer;
- Timer = (PGP_TIMER_DATA)Context;
- return READ_TIMER_REGISTER(Timer->Base, GpTimerCurrentCount);
- }
- VOID
- HlpOmap3TimerWrite (
- PVOID Context,
- ULONGLONG NewCount
- )
- /*++
- Routine Description:
- This routine writes to the timer's hardware counter. This routine will
- only be called for timers that have the writable counter feature bit set.
- Arguments:
- Context - Supplies the pointer to the timer's context, provided by the
- hardware module upon initialization.
- NewCount - Supplies the value to write into the counter. It is expected that
- the counter will not stop after the write.
- Return Value:
- None.
- --*/
- {
- PGP_TIMER_DATA Timer;
- Timer = (PGP_TIMER_DATA)Context;
- WRITE_TIMER_REGISTER(Timer->Base, GpTimerCurrentCount, (ULONG)NewCount);
- return;
- }
- KSTATUS
- HlpOmap3TimerArm (
- PVOID Context,
- TIMER_MODE Mode,
- ULONGLONG TickCount
- )
- /*++
- Routine Description:
- This routine arms the timer to fire an interrupt after the specified number
- of ticks.
- Arguments:
- Context - Supplies the pointer to the timer's context, provided by the
- hardware module upon initialization.
- Mode - Supplies the mode to arm the timer in. The system will never request
- a mode not supported by the timer's feature bits. The mode dictates
- how the tick count argument is interpreted.
- TickCount - Supplies the number of timer ticks from now for the timer to
- fire in. In absolute mode, this supplies the time in timer ticks at
- which to fire an interrupt.
- Return Value:
- STATUS_SUCCESS always.
- --*/
- {
- PGP_TIMER_DATA Timer;
- ULONG Value;
- Timer = (PGP_TIMER_DATA)Context;
- if (TickCount >= MAX_ULONG) {
- TickCount = MAX_ULONG - 1;
- }
- //
- // Start the timer ticking.
- //
- WRITE_TIMER_REGISTER(Timer->Base, GpTimerMode, 0);
- WRITE_TIMER_REGISTER(Timer->Base,
- GpTimerLoadCount,
- 0xFFFFFFFF - (ULONG)TickCount);
- WRITE_TIMER_REGISTER(Timer->Base,
- GpTimerCurrentCount,
- 0xFFFFFFFF - (ULONG)TickCount);
- Value = GPTIMER_STARTED;
- if (Mode == TimerModePeriodic) {
- Value |= GPTIMER_AUTORELOAD;
- }
- WRITE_TIMER_REGISTER(Timer->Base, GpTimerMode, Value);
- WRITE_TIMER_REGISTER(Timer->Base,
- GpTimerInterruptEnable,
- GPTIMER_OVERFLOW_INTERRUPT);
- return STATUS_SUCCESS;
- }
- VOID
- HlpOmap3TimerDisarm (
- PVOID Context
- )
- /*++
- Routine Description:
- This routine disarms the timer, stopping interrupts from firing.
- Arguments:
- Context - Supplies the pointer to the timer's context, provided by the
- hardware module upon initialization.
- Return Value:
- None.
- --*/
- {
- PGP_TIMER_DATA Timer;
- //
- // Disable all interrupts on this timer.
- //
- Timer = (PGP_TIMER_DATA)Context;
- WRITE_TIMER_REGISTER(Timer->Base, GpTimerInterruptEnable, 0);
- //
- // Reset all interrupt-pending bits.
- //
- WRITE_TIMER_REGISTER(Timer->Base, GpTimerInterruptStatus, 0x7);
- return;
- }
- VOID
- HlpOmap3TimerAcknowledgeInterrupt (
- PVOID Context
- )
- /*++
- Routine Description:
- This routine performs any actions necessary upon reciept of a timer's
- interrupt. This may involve writing to an acknowledge register to re-enable
- the timer to fire again, or other hardware specific actions.
- Arguments:
- Context - Supplies the pointer to the timer's context, provided by the
- hardware module upon initialization.
- Return Value:
- None.
- --*/
- {
- PGP_TIMER_DATA Timer;
- Timer = (PGP_TIMER_DATA)Context;
- //
- // Clear the overflow interrupt by writing a 1 to the status bit.
- //
- WRITE_TIMER_REGISTER(Timer->Base,
- GpTimerInterruptStatus,
- GPTIMER_OVERFLOW_INTERRUPT);
- return;
- }
|