/** @file\r
Timer Architecture Protocol driver of the ARM flavor\r
\r
- Copyright (c) 2011 ARM Ltd. All rights reserved.<BR>\r
- \r
- This program and the accompanying materials \r
- are licensed and made available under the terms and conditions of the BSD License \r
- which accompanies this distribution. The full text of the license may be found at \r
- http://opensource.org/licenses/bsd-license.php \r
+ Copyright (c) 2011-2013 ARM Ltd. All rights reserved.<BR>\r
\r
- THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, \r
- WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. \r
+ This program and the accompanying materials\r
+ are licensed and made available under the terms and conditions of the BSD License\r
+ which accompanies this distribution. The full text of the license may be found at\r
+ http://opensource.org/licenses/bsd-license.php\r
+\r
+ THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,\r
+ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.\r
\r
**/\r
\r
#include <Library/UefiLib.h>\r
#include <Library/PcdLib.h>\r
#include <Library/IoLib.h>\r
-#include <Library/ArmV7ArchTimerLib.h>\r
+#include <Library/ArmGenericTimerCounterLib.h>\r
\r
#include <Protocol/Timer.h>\r
#include <Protocol/HardwareInterrupt.h>\r
\r
// The current period of the timer interrupt\r
UINT64 mTimerPeriod = 0;\r
+// The latest Timer Tick calculated for mTimerPeriod\r
+UINT64 mTimerTicks = 0;\r
+// Number of elapsed period since the last Timer interrupt\r
+UINT64 mElapsedPeriod = 1;\r
\r
// Cached copy of the Hardware Interrupt protocol instance\r
EFI_HARDWARE_INTERRUPT_PROTOCOL *gInterrupt = NULL;\r
\r
/**\r
- This function registers the handler NotifyFunction so it is called every time \r
- the timer interrupt fires. It also passes the amount of time since the last \r
- handler call to the NotifyFunction. If NotifyFunction is NULL, then the \r
- handler is unregistered. If the handler is registered, then EFI_SUCCESS is \r
- returned. If the CPU does not support registering a timer interrupt handler, \r
- then EFI_UNSUPPORTED is returned. If an attempt is made to register a handler \r
- when a handler is already registered, then EFI_ALREADY_STARTED is returned. \r
- If an attempt is made to unregister a handler when a handler is not registered, \r
- then EFI_INVALID_PARAMETER is returned. If an error occurs attempting to \r
- register the NotifyFunction with the timer interrupt, then EFI_DEVICE_ERROR \r
+ This function registers the handler NotifyFunction so it is called every time\r
+ the timer interrupt fires. It also passes the amount of time since the last\r
+ handler call to the NotifyFunction. If NotifyFunction is NULL, then the\r
+ handler is unregistered. If the handler is registered, then EFI_SUCCESS is\r
+ returned. If the CPU does not support registering a timer interrupt handler,\r
+ then EFI_UNSUPPORTED is returned. If an attempt is made to register a handler\r
+ when a handler is already registered, then EFI_ALREADY_STARTED is returned.\r
+ If an attempt is made to unregister a handler when a handler is not registered,\r
+ then EFI_INVALID_PARAMETER is returned. If an error occurs attempting to\r
+ register the NotifyFunction with the timer interrupt, then EFI_DEVICE_ERROR\r
is returned.\r
\r
@param This The EFI_TIMER_ARCH_PROTOCOL instance.\r
IN VOID *Context\r
)\r
{\r
- ArmArchTimerDisableTimer ();\r
+ ArmGenericTimerDisableTimer ();\r
}\r
\r
/**\r
\r
- This function adjusts the period of timer interrupts to the value specified \r
- by TimerPeriod. If the timer period is updated, then the selected timer \r
- period is stored in EFI_TIMER.TimerPeriod, and EFI_SUCCESS is returned. If \r
- the timer hardware is not programmable, then EFI_UNSUPPORTED is returned. \r
- If an error occurs while attempting to update the timer period, then the \r
- timer hardware will be put back in its state prior to this call, and \r
- EFI_DEVICE_ERROR is returned. If TimerPeriod is 0, then the timer interrupt \r
- is disabled. This is not the same as disabling the CPU's interrupts. \r
- Instead, it must either turn off the timer hardware, or it must adjust the \r
- interrupt controller so that a CPU interrupt is not generated when the timer \r
- interrupt fires. \r
+ This function adjusts the period of timer interrupts to the value specified\r
+ by TimerPeriod. If the timer period is updated, then the selected timer\r
+ period is stored in EFI_TIMER.TimerPeriod, and EFI_SUCCESS is returned. If\r
+ the timer hardware is not programmable, then EFI_UNSUPPORTED is returned.\r
+ If an error occurs while attempting to update the timer period, then the\r
+ timer hardware will be put back in its state prior to this call, and\r
+ EFI_DEVICE_ERROR is returned. If TimerPeriod is 0, then the timer interrupt\r
+ is disabled. This is not the same as disabling the CPU's interrupts.\r
+ Instead, it must either turn off the timer hardware, or it must adjust the\r
+ interrupt controller so that a CPU interrupt is not generated when the timer\r
+ interrupt fires.\r
\r
@param This The EFI_TIMER_ARCH_PROTOCOL instance.\r
@param TimerPeriod The rate to program the timer interrupt in 100 nS units. If\r
IN UINT64 TimerPeriod\r
)\r
{\r
+ UINT64 CounterValue;\r
UINT64 TimerTicks;\r
- \r
+ EFI_TPL OriginalTPL;\r
+\r
// Always disable the timer\r
- ArmArchTimerDisableTimer ();\r
+ ArmGenericTimerDisableTimer ();\r
\r
if (TimerPeriod != 0) {\r
- // Convert TimerPeriod to micro sec units\r
- TimerTicks = DivU64x32 (TimerPeriod, 10);\r
+ // mTimerTicks = TimerPeriod in 1ms unit x Frequency.10^-3\r
+ // = TimerPeriod.10^-4 x Frequency.10^-3\r
+ // = (TimerPeriod x Frequency) x 10^-7\r
+ TimerTicks = MultU64x32 (TimerPeriod, ArmGenericTimerGetTimerFreq ());\r
+ TimerTicks = DivU64x32 (TimerTicks, 10000000U);\r
+\r
+ // Raise TPL to update the mTimerTicks and mTimerPeriod to ensure these values\r
+ // are coherent in the interrupt handler\r
+ OriginalTPL = gBS->RaiseTPL (TPL_HIGH_LEVEL);\r
+\r
+ mTimerTicks = TimerTicks;\r
+ mTimerPeriod = TimerPeriod;\r
+ mElapsedPeriod = 1;\r
\r
- TimerTicks = MultU64x32 (TimerTicks, (PcdGet32(PcdArmArchTimerFreqInHz)/1000000));\r
+ gBS->RestoreTPL (OriginalTPL);\r
\r
- ArmArchTimerSetTimerVal((UINTN)TimerTicks);\r
+ // Get value of the current timer\r
+ CounterValue = ArmGenericTimerGetSystemCount ();\r
+ // Set the interrupt in Current Time + mTimerTick\r
+ ArmGenericTimerSetCompareVal (CounterValue + mTimerTicks);\r
\r
// Enable the timer\r
- ArmArchTimerEnableTimer ();\r
+ ArmGenericTimerEnableTimer ();\r
+ } else {\r
+ // Save the new timer period\r
+ mTimerPeriod = TimerPeriod;\r
+ // Reset the elapsed period\r
+ mElapsedPeriod = 1;\r
}\r
\r
- // Save the new timer period\r
- mTimerPeriod = TimerPeriod;\r
return EFI_SUCCESS;\r
}\r
\r
/**\r
- This function retrieves the period of timer interrupts in 100 ns units, \r
- returns that value in TimerPeriod, and returns EFI_SUCCESS. If TimerPeriod \r
- is NULL, then EFI_INVALID_PARAMETER is returned. If a TimerPeriod of 0 is \r
+ This function retrieves the period of timer interrupts in 100 ns units,\r
+ returns that value in TimerPeriod, and returns EFI_SUCCESS. If TimerPeriod\r
+ is NULL, then EFI_INVALID_PARAMETER is returned. If a TimerPeriod of 0 is\r
returned, then the timer is currently disabled.\r
\r
@param This The EFI_TIMER_ARCH_PROTOCOL instance.\r
}\r
\r
/**\r
- This function generates a soft timer interrupt. If the platform does not support soft \r
- timer interrupts, then EFI_UNSUPPORTED is returned. Otherwise, EFI_SUCCESS is returned. \r
- If a handler has been registered through the EFI_TIMER_ARCH_PROTOCOL.RegisterHandler() \r
- service, then a soft timer interrupt will be generated. If the timer interrupt is \r
- enabled when this service is called, then the registered handler will be invoked. The \r
- registered handler should not be able to distinguish a hardware-generated timer \r
+ This function generates a soft timer interrupt. If the platform does not support soft\r
+ timer interrupts, then EFI_UNSUPPORTED is returned. Otherwise, EFI_SUCCESS is returned.\r
+ If a handler has been registered through the EFI_TIMER_ARCH_PROTOCOL.RegisterHandler()\r
+ service, then a soft timer interrupt will be generated. If the timer interrupt is\r
+ enabled when this service is called, then the registered handler will be invoked. The\r
+ registered handler should not be able to distinguish a hardware-generated timer\r
interrupt from a software-generated timer interrupt.\r
\r
@param This The EFI_TIMER_ARCH_PROTOCOL instance.\r
)\r
{\r
EFI_TPL OriginalTPL;\r
+ UINT64 CurrentValue;\r
+ UINT64 CompareValue;\r
\r
//\r
// DXE core uses this callback for the EFI timer tick. The DXE core uses locks\r
//\r
OriginalTPL = gBS->RaiseTPL (TPL_HIGH_LEVEL);\r
\r
- // Check if the timer interrupt is active\r
- if ((ArmArchTimerGetTimerCtrlReg () ) & ARM_ARCH_TIMER_ISTATUS) {\r
+ // Signal end of interrupt early to help avoid losing subsequent ticks\r
+ // from long duration handlers\r
+ gInterrupt->EndOfInterrupt (gInterrupt, Source);\r
\r
- // Signal end of interrupt early to help avoid losing subsequent ticks from long duration handlers\r
- gInterrupt->EndOfInterrupt (gInterrupt, Source);\r
+ // Check if the timer interrupt is active\r
+ if ((ArmGenericTimerGetTimerCtrlReg () ) & ARM_ARCH_TIMER_ISTATUS) {\r
\r
if (mTimerNotifyFunction) {\r
- mTimerNotifyFunction (mTimerPeriod);\r
+ mTimerNotifyFunction (mTimerPeriod * mElapsedPeriod);\r
}\r
\r
+ //\r
// Reload the Timer\r
- TimerDriverSetTimerPeriod (&gTimer, FixedPcdGet32(PcdTimerPeriod));\r
+ //\r
+\r
+ // Get current counter value\r
+ CurrentValue = ArmGenericTimerGetSystemCount ();\r
+ // Get the counter value to compare with\r
+ CompareValue = ArmGenericTimerGetCompareVal ();\r
+\r
+ // This loop is needed in case we missed interrupts (eg: case when the interrupt handling\r
+ // has taken longer than mTickPeriod).\r
+ // Note: Physical Counter is counting up\r
+ mElapsedPeriod = 0;\r
+ do {\r
+ CompareValue += mTimerTicks;\r
+ mElapsedPeriod++;\r
+ } while (CompareValue < CurrentValue);\r
+\r
+ // Set next compare value\r
+ ArmGenericTimerSetCompareVal (CompareValue);\r
+ ArmGenericTimerEnableTimer ();\r
+ ArmInstructionSynchronizationBarrier ();\r
}\r
\r
- // Enable timer interrupts\r
- gInterrupt->EnableInterruptSource (gInterrupt, Source);\r
-\r
gBS->RestoreTPL (OriginalTPL);\r
}\r
\r
{\r
EFI_HANDLE Handle = NULL;\r
EFI_STATUS Status;\r
- UINTN TimerCtrlReg;\r
+ UINTN TimerCtrlReg;\r
+ UINT32 TimerHypIntrNum;\r
\r
if (ArmIsArchTimerImplemented () == 0) {\r
DEBUG ((EFI_D_ERROR, "ARM Architectural Timer is not available in the CPU, hence cann't use this Driver \n"));\r
ASSERT_EFI_ERROR (Status);\r
\r
// Disable the timer\r
+ TimerCtrlReg = ArmGenericTimerGetTimerCtrlReg ();\r
+ TimerCtrlReg |= ARM_ARCH_TIMER_IMASK;\r
+ TimerCtrlReg &= ~ARM_ARCH_TIMER_ENABLE;\r
+ ArmGenericTimerSetTimerCtrlReg (TimerCtrlReg);\r
Status = TimerDriverSetTimerPeriod (&gTimer, 0);\r
ASSERT_EFI_ERROR (Status);\r
\r
// Note: Because it is not possible to determine the security state of the\r
// CPU dynamically, we just install interrupt handler for both sec and non-sec\r
// timer PPI\r
+ Status = gInterrupt->RegisterInterruptSource (gInterrupt, PcdGet32 (PcdArmArchTimerVirtIntrNum), TimerInterruptHandler);\r
+ ASSERT_EFI_ERROR (Status);\r
+\r
+ //\r
+ // The hypervisor timer interrupt may be omitted by implementations that\r
+ // execute under virtualization.\r
+ //\r
+ TimerHypIntrNum = PcdGet32 (PcdArmArchTimerHypIntrNum);\r
+ if (TimerHypIntrNum != 0) {\r
+ Status = gInterrupt->RegisterInterruptSource (gInterrupt, TimerHypIntrNum, TimerInterruptHandler);\r
+ ASSERT_EFI_ERROR (Status);\r
+ }\r
+\r
Status = gInterrupt->RegisterInterruptSource (gInterrupt, PcdGet32 (PcdArmArchTimerSecIntrNum), TimerInterruptHandler);\r
ASSERT_EFI_ERROR (Status);\r
\r
Status = gInterrupt->RegisterInterruptSource (gInterrupt, PcdGet32 (PcdArmArchTimerIntrNum), TimerInterruptHandler);\r
ASSERT_EFI_ERROR (Status);\r
\r
- // Unmask timer interrupts\r
- TimerCtrlReg = ArmArchTimerGetTimerCtrlReg ();\r
- TimerCtrlReg &= ~ARM_ARCH_TIMER_IMASK;\r
- ArmArchTimerSetTimerCtrlReg (TimerCtrlReg);\r
-\r
// Set up default timer\r
Status = TimerDriverSetTimerPeriod (&gTimer, FixedPcdGet32(PcdTimerPeriod)); // TIMER_DEFAULT_PERIOD\r
ASSERT_EFI_ERROR (Status);\r
);\r
ASSERT_EFI_ERROR(Status);\r
\r
- // enable Secure timer interrupts\r
- Status = gInterrupt->EnableInterruptSource (gInterrupt, PcdGet32 (PcdArmArchTimerSecIntrNum));\r
-\r
- // enable NonSecure timer interrupts\r
- Status = gInterrupt->EnableInterruptSource (gInterrupt, PcdGet32 (PcdArmArchTimerIntrNum));\r
+ // Everything is ready, unmask and enable timer interrupts\r
+ TimerCtrlReg = ARM_ARCH_TIMER_ENABLE;\r
+ ArmGenericTimerSetTimerCtrlReg (TimerCtrlReg);\r
\r
// Register for an ExitBootServicesEvent\r
Status = gBS->CreateEvent (EVT_SIGNAL_EXIT_BOOT_SERVICES, TPL_NOTIFY, ExitBootServicesEvent, NULL, &EfiExitBootServicesEvent);\r