123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578 |
- /*++
- Copyright (c) 2012 Minoca Corp. All Rights Reserved
- Module Name:
- sp804tmr.c
- Abstract:
- This module implements support for the ARM SP804 dual timer.
- Author:
- Evan Green 22-Aug-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 "realview.h"
- //
- // ---------------------------------------------------------------- Definitions
- //
- #define SP804_ALLOCATION_TAG 0x34385053 // '48PS'
- //
- // Control register bits.
- //
- #define SP804_CONTROL_ENABLED 0x80
- #define SP804_CONTROL_MODE_FREE_RUNNING 0x00
- #define SP804_CONTROL_MODE_PERIODIC 0x40
- #define SP804_CONTROL_INTERRUPT_ENABLE 0x20
- #define SP804_CONTROL_DIVIDE_BY_1 0x00
- #define SP804_CONTROL_DIVIDE_BY_16 0x04
- #define SP804_CONTROL_DIVIDE_BY_256 0x08
- #define SP804_CONTROL_32_BIT 0x02
- #define SP804_CONTROL_16_BIT 0x00
- #define SP804_CONTROL_MODE_ONE_SHOT 0x01
- //
- // --------------------------------------------------------------------- Macros
- //
- //
- // This macro reads from an SP804 timer. _Base should be a pointer, and
- // _Register should be a SP804_TIMER_REGISTER value.
- //
- #define READ_TIMER_REGISTER(_Base, _Register) \
- HlReadRegister32((PULONG)(_Base) + (_Register))
- //
- // This macro writes to a SP804 timer. _Base should be a pointer,
- // _Register should be SP804_TIMER_REGISTER value, and _Value should be a ULONG.
- //
- #define WRITE_TIMER_REGISTER(_Base, _Register, _Value) \
- HlWriteRegister32((PULONG)(_Base) + (_Register), (_Value))
- //
- // ----------------------------------------------- Internal Function Prototypes
- //
- KSTATUS
- HlpSp804TimerInitialize (
- PVOID Context
- );
- ULONGLONG
- HlpSp804TimerRead (
- PVOID Context
- );
- KSTATUS
- HlpSp804TimerArm (
- PVOID Context,
- TIMER_MODE Mode,
- ULONGLONG TickCount
- );
- VOID
- HlpSp804TimerDisarm (
- PVOID Context
- );
- VOID
- HlpSp804TimerAcknowledgeInterrupt (
- PVOID Context
- );
- //
- // ------------------------------------------------------ Data Type Definitions
- //
- //
- // Define the registers for one timer, in ULONGs.
- //
- typedef enum _SP804_TIMER_REGISTER {
- Sp804LoadValue = 0,
- Sp804CurrentValue = 1,
- Sp804Control = 2,
- Sp804InterruptClear = 3,
- Sp804InterruptRawStatus = 4,
- Sp804InterruptStatus = 5,
- Sp804BackgroundLoadValue = 6,
- Sp804RegisterSize = 0x20
- } SP804_TIMER_REGISTER, *PSP804_TIMER_REGISTER;
- /*++
- Structure Description:
- This structure stores the internal state associated with a SP804 timer.
- Members:
- PhysicalAddress - Stores the physical address of the timer base.
- BaseAddress - Stores the virtual address of the beginning of this timer
- block.
- Index - Stores the zero-based index of this timer within the timer block.
- --*/
- typedef struct _SP804_TIMER_DATA {
- PHYSICAL_ADDRESS PhysicalAddress;
- PVOID BaseAddress;
- ULONG Index;
- } SP804_TIMER_DATA, *PSP804_TIMER_DATA;
- //
- // -------------------------------------------------------------------- Globals
- //
- //
- // Store a pointer to the RealView table.
- //
- PREALVIEW_TABLE HlRealViewTable;
- //
- // ------------------------------------------------------------------ Functions
- //
- VOID
- HlpSp804TimerModuleEntry (
- VOID
- )
- /*++
- Routine Description:
- This routine is the entry point for the SP804 timer hardware module.
- Its role is to detect and report the prescense of an SP804 timer.
- Arguments:
- None.
- Return Value:
- None.
- --*/
- {
- PULONGLONG Frequencies;
- PULONGLONG PhysicalAddresses;
- KSTATUS Status;
- TIMER_DESCRIPTION Timer;
- ULONG TimerCount;
- PSP804_TIMER_DATA TimerData;
- PULONG TimerGsi;
- ULONG TimerIndex;
- if (HlRealViewTable == NULL) {
- HlRealViewTable = HlGetAcpiTable(REALVIEW_SIGNATURE, NULL);
- }
- if (HlRealViewTable != NULL) {
- Frequencies = HlRealViewTable->TimerFrequency;
- PhysicalAddresses = HlRealViewTable->TimerPhysicalAddress;
- TimerCount = REALVIEW_TIMER_COUNT;
- TimerGsi = HlRealViewTable->TimerGsi;
- } else {
- goto Sp804TimerModuleEntryEnd;
- }
- //
- // Register each of the independent timers in the timer block.
- //
- for (TimerIndex = 0; TimerIndex < TimerCount; TimerIndex += 1) {
- RtlZeroMemory(&Timer, sizeof(TIMER_DESCRIPTION));
- Timer.TableVersion = TIMER_DESCRIPTION_VERSION;
- Timer.FunctionTable.Initialize = HlpSp804TimerInitialize;
- Timer.FunctionTable.ReadCounter = HlpSp804TimerRead;
- Timer.FunctionTable.WriteCounter = NULL;
- Timer.FunctionTable.Arm = HlpSp804TimerArm;
- Timer.FunctionTable.Disarm = HlpSp804TimerDisarm;
- Timer.FunctionTable.AcknowledgeInterrupt =
- HlpSp804TimerAcknowledgeInterrupt;
- //
- // Each timer block is actually two timers. Allocate a single structure
- // for both, and know that if the index is one, go backwards to get
- // to the main information.
- //
- TimerData = HlAllocateMemory(sizeof(SP804_TIMER_DATA) * 2,
- SP804_ALLOCATION_TAG,
- FALSE,
- NULL);
- if (TimerData == NULL) {
- Status = STATUS_INSUFFICIENT_RESOURCES;
- goto Sp804TimerModuleEntryEnd;
- }
- RtlZeroMemory(TimerData, sizeof(SP804_TIMER_DATA) * 2);
- TimerData->PhysicalAddress = PhysicalAddresses[TimerIndex];
- TimerData->Index = 0;
- TimerData[1].Index = 1;
- Timer.Context = TimerData;
- Timer.Features = TIMER_FEATURE_READABLE |
- TIMER_FEATURE_PERIODIC |
- TIMER_FEATURE_ONE_SHOT;
- Timer.CounterBitWidth = 32;
- Timer.CounterFrequency = Frequencies[TimerIndex];
- Timer.Interrupt.Line.Type = InterruptLineGsi;
- Timer.Interrupt.Line.U.Gsi = TimerGsi[TimerIndex];
- Timer.Interrupt.TriggerMode = InterruptModeUnknown;
- Timer.Interrupt.ActiveLevel = InterruptActiveLevelUnknown;
- //
- // Register the timer with the system.
- //
- Status = HlRegisterHardware(HardwareModuleTimer, &Timer);
- if (!KSUCCESS(Status)) {
- goto Sp804TimerModuleEntryEnd;
- }
- //
- // Register the second one. Report it as not having interrupt
- // capabilities as there's no way to disambiguate between the two
- // timers when the interrupt comes in.
- //
- Timer.Context = TimerData + 1;
- Timer.Features = TIMER_FEATURE_READABLE;
- Status = HlRegisterHardware(HardwareModuleTimer, &Timer);
- if (!KSUCCESS(Status)) {
- goto Sp804TimerModuleEntryEnd;
- }
- }
- Sp804TimerModuleEntryEnd:
- return;
- }
- //
- // --------------------------------------------------------- Internal Functions
- //
- KSTATUS
- HlpSp804TimerInitialize (
- PVOID Context
- )
- /*++
- Routine Description:
- This routine initializes an SP804 timer.
- Arguments:
- Context - Supplies the pointer to the timer's context, provided by the
- hardware module upon initialization.
- Return Value:
- Status code.
- --*/
- {
- PVOID Base;
- ULONG ControlValue;
- BOOL SecondTimer;
- KSTATUS Status;
- PSP804_TIMER_DATA Timer;
- //
- // If this is the second timer, go back a structure to get the real one.
- //
- SecondTimer = FALSE;
- Timer = (PSP804_TIMER_DATA)Context;
- if (Timer->Index == 1) {
- Timer -= 1;
- SecondTimer = TRUE;
- }
- //
- // Map the hardware if that has not been done.
- //
- if (Timer->BaseAddress == NULL) {
- Timer->BaseAddress = HlMapPhysicalAddress(
- Timer->PhysicalAddress,
- 2 * Sp804RegisterSize * sizeof(ULONG),
- TRUE);
- if (Timer->BaseAddress == NULL) {
- Status = STATUS_INSUFFICIENT_RESOURCES;
- goto Sp804TimerInitializeEnd;
- }
- }
- Base = Timer->BaseAddress;
- if (SecondTimer != FALSE) {
- Base += Sp804RegisterSize;
- }
- //
- // Program the timer in free running mode with no interrupt generation.
- //
- ControlValue = SP804_CONTROL_ENABLED |
- SP804_CONTROL_DIVIDE_BY_1 |
- SP804_CONTROL_32_BIT |
- SP804_CONTROL_MODE_FREE_RUNNING;
- WRITE_TIMER_REGISTER(Base, Sp804Control, ControlValue);
- WRITE_TIMER_REGISTER(Base, Sp804InterruptClear, 1);
- Status = STATUS_SUCCESS;
- Sp804TimerInitializeEnd:
- return Status;
- }
- ULONGLONG
- HlpSp804TimerRead (
- 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.
- --*/
- {
- PVOID Base;
- PSP804_TIMER_DATA Timer;
- ULONG Value;
- //
- // If this is the second timer, go back a structure to get the real one.
- //
- Timer = (PSP804_TIMER_DATA)Context;
- Base = Timer->BaseAddress;
- if (Timer->Index == 1) {
- Timer -= 1;
- Base = Timer->BaseAddress + Sp804RegisterSize;
- }
- Value = 0xFFFFFFFF - READ_TIMER_REGISTER(Base, Sp804CurrentValue);
- return Value;
- }
- KSTATUS
- HlpSp804TimerArm (
- 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.
- --*/
- {
- PVOID Base;
- ULONG ControlValue;
- PSP804_TIMER_DATA Timer;
- //
- // If this is the second timer, go back a structure to get the real one.
- //
- Timer = (PSP804_TIMER_DATA)Context;
- Base = Timer->BaseAddress;
- if (Timer->Index == 1) {
- Timer -= 1;
- Base = Timer->BaseAddress + Sp804RegisterSize;
- }
- if (TickCount >= MAX_ULONG) {
- TickCount = MAX_ULONG - 1;
- }
- //
- // Set up the control value to program.
- //
- ControlValue = SP804_CONTROL_ENABLED |
- SP804_CONTROL_DIVIDE_BY_1 |
- SP804_CONTROL_32_BIT |
- SP804_CONTROL_INTERRUPT_ENABLE;
- if (Mode == TimerModePeriodic) {
- ControlValue |= SP804_CONTROL_MODE_PERIODIC;
- } else {
- ControlValue |= SP804_CONTROL_MODE_ONE_SHOT;
- }
- //
- // Set the timer to its maximum value, set the configuration, clear the
- // interrupt, then set the value.
- //
- WRITE_TIMER_REGISTER(Base, Sp804LoadValue, 0xFFFFFFFF);
- WRITE_TIMER_REGISTER(Base, Sp804Control, ControlValue);
- WRITE_TIMER_REGISTER(Base, Sp804InterruptClear, 1);
- WRITE_TIMER_REGISTER(Base, Sp804LoadValue, TickCount);
- return STATUS_SUCCESS;
- }
- VOID
- HlpSp804TimerDisarm (
- 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.
- --*/
- {
- ULONG ControlValue;
- PSP804_TIMER_DATA Timer;
- Timer = (PSP804_TIMER_DATA)Context;
- //
- // Disable the timer by programming it in free running mode with no
- // interrupt generation.
- //
- ControlValue = SP804_CONTROL_ENABLED |
- SP804_CONTROL_DIVIDE_BY_1 |
- SP804_CONTROL_32_BIT |
- SP804_CONTROL_MODE_FREE_RUNNING;
- WRITE_TIMER_REGISTER(Timer->BaseAddress, Sp804Control, ControlValue);
- WRITE_TIMER_REGISTER(Timer->BaseAddress, Sp804InterruptClear, 1);
- return;
- }
- VOID
- HlpSp804TimerAcknowledgeInterrupt (
- 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.
- --*/
- {
- PVOID Base;
- PSP804_TIMER_DATA Timer;
- //
- // If this is the second timer, go back a structure to get the real one.
- //
- Timer = (PSP804_TIMER_DATA)Context;
- Base = Timer->BaseAddress;
- if (Timer->Index == 1) {
- Timer -= 1;
- Base = Timer->BaseAddress + Sp804RegisterSize;
- }
- WRITE_TIMER_REGISTER(Base, Sp804InterruptClear, 1);
- return;
- }
|