]> git.proxmox.com Git - mirror_edk2.git/blobdiff - ArmPkg/Drivers/TimerDxe/TimerDxe.c
ArmPkg/TimerDxe: Add ISB for timer compare value reload
[mirror_edk2.git] / ArmPkg / Drivers / TimerDxe / TimerDxe.c
index ccdb38c79b641c71b46728d558f177cc400a5a6e..a3202fa056f3026cd1c6ab3781649923d848d62c 100644 (file)
@@ -24,7 +24,7 @@
 #include <Library/UefiLib.h>\r
 #include <Library/PcdLib.h>\r
 #include <Library/IoLib.h>\r
-#include <Library/ArmArchTimerLib.h>\r
+#include <Library/ArmGenericTimerCounterLib.h>\r
 \r
 #include <Protocol/Timer.h>\r
 #include <Protocol/HardwareInterrupt.h>\r
@@ -35,6 +35,10 @@ EFI_EVENT             EfiExitBootServicesEvent = (EFI_EVENT)NULL;
 \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
@@ -97,7 +101,7 @@ ExitBootServicesEvent (
   IN VOID       *Context\r
   )\r
 {\r
-  ArmArchTimerDisableTimer ();\r
+  ArmGenericTimerDisableTimer ();\r
 }\r
 \r
 /**\r
@@ -135,26 +139,44 @@ TimerDriverSetTimerPeriod (
   IN UINT64                   TimerPeriod\r
   )\r
 {\r
+  UINT64      CounterValue;\r
   UINT64      TimerTicks;\r
+  EFI_TPL     OriginalTPL;\r
 \r
   // Always disable the timer\r
-  ArmArchTimerDisableTimer ();\r
+  ArmGenericTimerDisableTimer ();\r
 \r
   if (TimerPeriod != 0) {\r
-    // TimerTicks = 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, FixedPcdGet32 (PcdArmArchTimerFreqInHz));\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
-    ArmArchTimerSetTimerVal ((UINTN)TimerTicks);\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
+    gBS->RestoreTPL (OriginalTPL);\r
+\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
@@ -274,6 +296,8 @@ TimerInterruptHandler (
   )\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
@@ -282,23 +306,41 @@ TimerInterruptHandler (
   //\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
@@ -325,7 +367,8 @@ TimerInitialize (
 {\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
@@ -337,10 +380,10 @@ TimerInitialize (
   ASSERT_EFI_ERROR (Status);\r
 \r
   // Disable the timer\r
-  TimerCtrlReg = ArmArchTimerGetTimerCtrlReg ();\r
+  TimerCtrlReg = ArmGenericTimerGetTimerCtrlReg ();\r
   TimerCtrlReg |= ARM_ARCH_TIMER_IMASK;\r
   TimerCtrlReg &= ~ARM_ARCH_TIMER_ENABLE;\r
-  ArmArchTimerSetTimerCtrlReg (TimerCtrlReg);\r
+  ArmGenericTimerSetTimerCtrlReg (TimerCtrlReg);\r
   Status = TimerDriverSetTimerPeriod (&gTimer, 0);\r
   ASSERT_EFI_ERROR (Status);\r
 \r
@@ -348,6 +391,19 @@ TimerInitialize (
   // 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
@@ -368,7 +424,7 @@ TimerInitialize (
 \r
   // Everything is ready, unmask and enable timer interrupts\r
   TimerCtrlReg = ARM_ARCH_TIMER_ENABLE;\r
-  ArmArchTimerSetTimerCtrlReg (TimerCtrlReg);\r
+  ArmGenericTimerSetTimerCtrlReg (TimerCtrlReg);\r
 \r
   // Register for an ExitBootServicesEvent\r
   Status = gBS->CreateEvent (EVT_SIGNAL_EXIT_BOOT_SERVICES, TPL_NOTIFY, ExitBootServicesEvent, NULL, &EfiExitBootServicesEvent);\r