]> git.proxmox.com Git - mirror_edk2.git/blobdiff - ArmPkg/Drivers/TimerDxe/TimerDxe.c
ArmPkg/TimerDxe: Fixed real time period
[mirror_edk2.git] / ArmPkg / Drivers / TimerDxe / TimerDxe.c
index 279b50f29048a1cca97755b29e84e24414156245..329b085ec464816ca9be81dc0adeb62862b875f1 100644 (file)
@@ -35,6 +35,10 @@ EFI_EVENT             EfiExitBootServicesEvent = (EFI_EVENT)NULL;
 \r
 // The current period of the timer interrupt\r
 UINT64 mTimerPeriod = 0;\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
 // Cached copy of the Hardware Interrupt protocol instance\r
 EFI_HARDWARE_INTERRUPT_PROTOCOL *gInterrupt = NULL;\r
@@ -135,26 +139,44 @@ TimerDriverSetTimerPeriod (
   IN UINT64                   TimerPeriod\r
   )\r
 {\r
   IN UINT64                   TimerPeriod\r
   )\r
 {\r
+  UINT64      CounterValue;\r
   UINT64      TimerTicks;\r
   UINT64      TimerTicks;\r
+  EFI_TPL     OriginalTPL;\r
 \r
   // Always disable the timer\r
   ArmArchTimerDisableTimer ();\r
 \r
   if (TimerPeriod != 0) {\r
 \r
   // Always disable the timer\r
   ArmArchTimerDisableTimer ();\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
+    // 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, FixedPcdGet32 (PcdArmArchTimerFreqInHz));\r
     TimerTicks = DivU64x32 (TimerTicks, 10000000U);\r
 \r
     TimerTicks = MultU64x32 (TimerPeriod, FixedPcdGet32 (PcdArmArchTimerFreqInHz));\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 physical timer\r
+    CounterValue = ArmReadCntPct ();\r
+    // Set the interrupt in Current Time + mTimerTick\r
+    ArmWriteCntpCval (CounterValue + mTimerTicks);\r
 \r
     // Enable the timer\r
     ArmArchTimerEnableTimer ();\r
 \r
     // Enable the timer\r
     ArmArchTimerEnableTimer ();\r
+  } else {\r
+    // Save the new timer period\r
+    mTimerPeriod   = TimerPeriod;\r
+    // Reset the elapsed period\r
+    mElapsedPeriod = 1;\r
   }\r
 \r
   }\r
 \r
-  // Save the new timer period\r
-  mTimerPeriod = TimerPeriod;\r
   return EFI_SUCCESS;\r
 }\r
 \r
   return EFI_SUCCESS;\r
 }\r
 \r
@@ -274,6 +296,8 @@ TimerInterruptHandler (
   )\r
 {\r
   EFI_TPL      OriginalTPL;\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
   //\r
   // DXE core uses this callback for the EFI timer tick. The DXE core uses locks\r
@@ -289,11 +313,29 @@ TimerInterruptHandler (
     gInterrupt->EndOfInterrupt (gInterrupt, Source);\r
 \r
     if (mTimerNotifyFunction) {\r
     gInterrupt->EndOfInterrupt (gInterrupt, Source);\r
 \r
     if (mTimerNotifyFunction) {\r
-      mTimerNotifyFunction (mTimerPeriod);\r
+      mTimerNotifyFunction (mTimerPeriod * mElapsedPeriod);\r
     }\r
 \r
     }\r
 \r
+    //\r
     // Reload the Timer\r
     // Reload the Timer\r
-    TimerDriverSetTimerPeriod (&gTimer, mTimerPeriod);\r
+    //\r
+\r
+    // Get current counter value\r
+    CurrentValue = ArmReadCntPct ();\r
+    // Get the counter value to compare with\r
+    CompareValue = ArmReadCntpCval ();\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
+    ArmWriteCntpCval (CompareValue);\r
   }\r
 \r
   // Enable timer interrupts\r
   }\r
 \r
   // Enable timer interrupts\r