]> git.proxmox.com Git - mirror_edk2.git/blobdiff - PcAtChipsetPkg/HpetTimerDxe/HpetTimer.c
OvmfPkg: use generic QEMU image loader for secure boot enabled builds
[mirror_edk2.git] / PcAtChipsetPkg / HpetTimerDxe / HpetTimer.c
index 025d504fc4eb33354fc779790be5bd9aff36d1bd..cbe986ebfd9f0ec9074039728edac902e6a8b388 100644 (file)
@@ -1,14 +1,8 @@
 /** @file\r
-  Timer Architectural Protocol module using High Precesion Event Timer (HPET)\r
+  Timer Architectural Protocol module using High Precision Event Timer (HPET)\r
 \r
-  Copyright (c) 2011, Intel Corporation. All rights reserved.<BR>\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
+  Copyright (c) 2011 - 2018, Intel Corporation. All rights reserved.<BR>\r
+  SPDX-License-Identifier: BSD-2-Clause-Patent\r
 \r
 **/\r
 \r
   is returned.\r
 \r
   @param  This            The EFI_TIMER_ARCH_PROTOCOL instance.\r
-  @param  NotifyFunction  The function to call when a timer interrupt fires.  \r
-                          This function executes at TPL_HIGH_LEVEL.  The DXE \r
-                          Core will register a handler for the timer interrupt, \r
-                          so it can know how much time has passed.  This \r
-                          information is used to signal timer based events.  \r
+  @param  NotifyFunction  The function to call when a timer interrupt fires.\r
+                          This function executes at TPL_HIGH_LEVEL.  The DXE\r
+                          Core will register a handler for the timer interrupt,\r
+                          so it can know how much time has passed.  This\r
+                          information is used to signal timer based events.\r
                           NULL will unregister the handler.\r
 \r
   @retval  EFI_SUCCESS            The timer handler was registered.\r
@@ -90,11 +84,11 @@ TimerDriverRegisterHandler (
 \r
   @param  This         The EFI_TIMER_ARCH_PROTOCOL instance.\r
   @param  TimerPeriod  The rate to program the timer interrupt in 100 nS units.\r
-                       If the timer hardware is not programmable, then \r
-                       EFI_UNSUPPORTED is returned.  If the timer is programmable, \r
-                       then the timer period will be rounded up to the nearest \r
-                       timer period that is supported by the timer hardware.  \r
-                       If TimerPeriod is set to 0, then the timer interrupts \r
+                       If the timer hardware is not programmable, then\r
+                       EFI_UNSUPPORTED is returned.  If the timer is programmable,\r
+                       then the timer period will be rounded up to the nearest\r
+                       timer period that is supported by the timer hardware.\r
+                       If TimerPeriod is set to 0, then the timer interrupts\r
                        will be disabled.\r
 \r
   @retval  EFI_SUCCESS       The timer period was changed.\r
@@ -142,7 +136,7 @@ TimerDriverGetTimerPeriod (
   @param  This  The EFI_TIMER_ARCH_PROTOCOL instance.\r
 \r
   @retval  EFI_SUCCESS       The soft timer interrupt was generated.\r
-  @retval  EFI_UNSUPPORTEDT  The platform does not support the generation of soft \r
+  @retval  EFI_UNSUPPORTED   The platform does not support the generation of soft\r
                              timer interrupts.\r
 \r
 **/\r
@@ -151,7 +145,7 @@ EFIAPI
 TimerDriverGenerateSoftInterrupt (\r
   IN EFI_TIMER_ARCH_PROTOCOL  *This\r
   );\r
-  \r
+\r
 ///\r
 /// The handle onto which the Timer Architectural Protocol will be installed.\r
 ///\r
@@ -183,11 +177,21 @@ EFI_TIMER_NOTIFY  mTimerNotifyFunction = NULL;
 UINT64  mTimerPeriod = 0;\r
 \r
 ///\r
-/// Accumulates HPET timer ticks to account for time passed when the \r
-/// HPET timer is disabled or when there is no timer notification function\r
-/// registered.\r
+/// The number of HPET timer ticks required for the current HPET rate specified by mTimerPeriod.\r
+///\r
+UINT64  mTimerCount;\r
+\r
+///\r
+/// Mask used for counter and comparator calculations to adjust for a 32-bit or 64-bit counter.\r
 ///\r
-volatile UINT64  mTimerAccumulator = 0;\r
+UINT64  mCounterMask;\r
+\r
+///\r
+/// The HPET main counter value from the most recent HPET timer interrupt.\r
+///\r
+volatile UINT64  mPreviousMainCounter;\r
+\r
+volatile UINT64  mPreviousComparator;\r
 \r
 ///\r
 /// The index of the HPET timer being managed by this driver.\r
@@ -212,7 +216,7 @@ HPET_GENERAL_CAPABILITIES_ID_REGISTER  mHpetGeneralCapabilities;
 HPET_GENERAL_CONFIGURATION_REGISTER  mHpetGeneralConfiguration;\r
 \r
 ///\r
-/// Cached state of the Configuration register for the HPET Timer managed by \r
+/// Cached state of the Configuration register for the HPET Timer managed by\r
 /// this driver.  Caching the state reduces the number of times the configuration\r
 /// register is read.\r
 ///\r
@@ -242,7 +246,7 @@ HpetRead (
 /**\r
   Write a 64-bit HPET register.\r
 \r
-  @param  Offset  Specifies the ofsfert of the HPET register to write.\r
+  @param  Offset  Specifies the offset of the HPET register to write.\r
   @param  Value   Specifies the value to write to the HPET register specified by Offset.\r
 \r
   @return  The 64-bit value written to HPET register specified by Offset.\r
@@ -267,7 +271,7 @@ HpetEnable (
   IN BOOLEAN  Enable\r
   )\r
 {\r
-  mHpetGeneralConfiguration.Bits.MainCounterEnable = Enable ? 1 : 0;  \r
+  mHpetGeneralConfiguration.Bits.MainCounterEnable = Enable ? 1 : 0;\r
   HpetWrite (HPET_GENERAL_CONFIGURATION_OFFSET, mHpetGeneralConfiguration.Uint64);\r
 }\r
 \r
@@ -276,10 +280,10 @@ HpetEnable (
   and computes the amount of time that has passed since the last HPET timer interrupt.\r
   If a notification function is registered, then the amount of time since the last\r
   HPET interrupt is passed to that notification function in 100 ns units.  The HPET\r
-  time is updated to generate another interrupt in the required time period. \r
+  time is updated to generate another interrupt in the required time period.\r
 \r
-  @param  InterruptType  The type of interrupt that occured.\r
-  @param  SystemContext  A pointer to the system context when the interrupt occured.\r
+  @param  InterruptType  The type of interrupt that occurred.\r
+  @param  SystemContext  A pointer to the system context when the interrupt occurred.\r
 **/\r
 VOID\r
 EFIAPI\r
@@ -288,8 +292,11 @@ TimerInterruptHandler (
   IN EFI_SYSTEM_CONTEXT   SystemContext\r
   )\r
 {\r
+  UINT64  MainCounter;\r
+  UINT64  Comparator;\r
   UINT64  TimerPeriod;\r
-  \r
+  UINT64  Delta;\r
+\r
   //\r
   // Count number of ticks\r
   //\r
@@ -306,37 +313,88 @@ TimerInterruptHandler (
   SendApicEoi ();\r
 \r
   //\r
-  // Accumulate time from the HPET main counter value\r
+  // Disable HPET timer when adjusting the COMPARATOR value to prevent a missed interrupt\r
+  //\r
+  HpetEnable (FALSE);\r
+\r
+  //\r
+  // Capture main counter value\r
+  //\r
+  MainCounter = HpetRead (HPET_MAIN_COUNTER_OFFSET);\r
+\r
+  //\r
+  // Get the previous comparator counter\r
   //\r
-  mTimerAccumulator += HpetRead (HPET_MAIN_COUNTER_OFFSET);\r
+  mPreviousComparator = HpetRead (HPET_TIMER_COMPARATOR_OFFSET + mTimerIndex * HPET_TIMER_STRIDE);\r
 \r
   //\r
-  // Reset HPET main counter to 0\r
+  // Set HPET COMPARATOR to the value required for the next timer tick\r
   //\r
-  HpetWrite (HPET_MAIN_COUNTER_OFFSET, 0);\r
+  Comparator = (mPreviousComparator + mTimerCount) & mCounterMask;\r
+\r
+  if ((mPreviousMainCounter < MainCounter) && (mPreviousComparator > Comparator)) {\r
+    //\r
+    // When comparator overflows\r
+    //\r
+    HpetWrite (HPET_TIMER_COMPARATOR_OFFSET + mTimerIndex * HPET_TIMER_STRIDE, Comparator);\r
+  } else if ((mPreviousMainCounter > MainCounter) && (mPreviousComparator < Comparator)) {\r
+    //\r
+    // When main counter overflows\r
+    //\r
+    HpetWrite (HPET_TIMER_COMPARATOR_OFFSET + mTimerIndex * HPET_TIMER_STRIDE, (MainCounter + mTimerCount) & mCounterMask);\r
+  } else {\r
+    //\r
+    // When both main counter and comparator do not overflow or both do overflow\r
+    //\r
+    if (Comparator > MainCounter) {\r
+      HpetWrite (HPET_TIMER_COMPARATOR_OFFSET + mTimerIndex * HPET_TIMER_STRIDE, Comparator);\r
+    } else {\r
+      HpetWrite (HPET_TIMER_COMPARATOR_OFFSET + mTimerIndex * HPET_TIMER_STRIDE, (MainCounter + mTimerCount) & mCounterMask);\r
+    }\r
+  }\r
+\r
+  //\r
+  // Enable the HPET counter once the new COMPARATOR value has been set.\r
+  //\r
+  HpetEnable (TRUE);\r
 \r
   //\r
   // Check to see if there is a registered notification function\r
   //\r
   if (mTimerNotifyFunction != NULL) {\r
     //\r
-    // Compute time since last notification in 100 ns units (10 ^ -7) \r
+    // Compute time since last notification in 100 ns units (10 ^ -7)\r
     //\r
+    if (MainCounter > mPreviousMainCounter) {\r
+      //\r
+      // Main counter does not overflow\r
+      //\r
+      Delta = MainCounter - mPreviousMainCounter;\r
+    } else {\r
+      //\r
+      // Main counter overflows, first usb, then add\r
+      //\r
+      Delta = (mCounterMask - mPreviousMainCounter) + MainCounter;\r
+    }\r
     TimerPeriod = DivU64x32 (\r
                     MultU64x32 (\r
-                      mTimerAccumulator, \r
+                      Delta & mCounterMask,\r
                       mHpetGeneralCapabilities.Bits.CounterClockPeriod\r
-                      ), \r
+                      ),\r
                     100000000\r
                     );\r
-    mTimerAccumulator = 0;\r
-                    \r
+\r
     //\r
     // Call registered notification function passing in the time since the last\r
     // interrupt in 100 ns units.\r
-    //    \r
+    //\r
     mTimerNotifyFunction (TimerPeriod);\r
   }\r
+\r
+  //\r
+  // Save main counter value\r
+  //\r
+  mPreviousMainCounter = MainCounter;\r
 }\r
 \r
 /**\r
@@ -353,11 +411,11 @@ TimerInterruptHandler (
   is returned.\r
 \r
   @param  This            The EFI_TIMER_ARCH_PROTOCOL instance.\r
-  @param  NotifyFunction  The function to call when a timer interrupt fires.  \r
-                          This function executes at TPL_HIGH_LEVEL.  The DXE \r
-                          Core will register a handler for the timer interrupt, \r
-                          so it can know how much time has passed.  This \r
-                          information is used to signal timer based events.  \r
+  @param  NotifyFunction  The function to call when a timer interrupt fires.\r
+                          This function executes at TPL_HIGH_LEVEL.  The DXE\r
+                          Core will register a handler for the timer interrupt,\r
+                          so it can know how much time has passed.  This\r
+                          information is used to signal timer based events.\r
                           NULL will unregister the handler.\r
 \r
   @retval  EFI_SUCCESS            The timer handler was registered.\r
@@ -409,11 +467,11 @@ TimerDriverRegisterHandler (
 \r
   @param  This         The EFI_TIMER_ARCH_PROTOCOL instance.\r
   @param  TimerPeriod  The rate to program the timer interrupt in 100 nS units.\r
-                       If the timer hardware is not programmable, then \r
-                       EFI_UNSUPPORTED is returned.  If the timer is programmable, \r
-                       then the timer period will be rounded up to the nearest \r
-                       timer period that is supported by the timer hardware.  \r
-                       If TimerPeriod is set to 0, then the timer interrupts \r
+                       If the timer hardware is not programmable, then\r
+                       EFI_UNSUPPORTED is returned.  If the timer is programmable,\r
+                       then the timer period will be rounded up to the nearest\r
+                       timer period that is supported by the timer hardware.\r
+                       If TimerPeriod is set to 0, then the timer interrupts\r
                        will be disabled.\r
 \r
   @retval  EFI_SUCCESS       The timer period was changed.\r
@@ -428,19 +486,51 @@ TimerDriverSetTimerPeriod (
   IN UINT64                   TimerPeriod\r
   )\r
 {\r
-  UINT64  TimerCount;\r
+  EFI_TPL                        Tpl;\r
+  UINT64                         MainCounter;\r
+  UINT64                         Delta;\r
+  UINT64                         CurrentComparator;\r
+  HPET_TIMER_MSI_ROUTE_REGISTER  HpetTimerMsiRoute;\r
+\r
+  //\r
+  // Disable interrupts\r
+  //\r
+  Tpl = gBS->RaiseTPL (TPL_HIGH_LEVEL);\r
 \r
   //\r
   // Disable HPET timer when adjusting the timer period\r
   //\r
   HpetEnable (FALSE);\r
-  \r
+\r
   if (TimerPeriod == 0) {\r
+    if (mTimerPeriod != 0) {\r
+      //\r
+      // Check if there is possibly a pending interrupt\r
+      //\r
+      MainCounter = HpetRead (HPET_MAIN_COUNTER_OFFSET);\r
+      if (MainCounter < mPreviousMainCounter) {\r
+        Delta = (mCounterMask - mPreviousMainCounter) + MainCounter;\r
+      } else {\r
+        Delta = MainCounter - mPreviousMainCounter;\r
+      }\r
+      if ((Delta & mCounterMask) >= mTimerCount) {\r
+        //\r
+        // Interrupt still happens after disable HPET, wait to be processed\r
+        // Wait until interrupt is processed and comparator is increased\r
+        //\r
+        CurrentComparator = HpetRead (HPET_TIMER_COMPARATOR_OFFSET + mTimerIndex * HPET_TIMER_STRIDE);\r
+        while (CurrentComparator == mPreviousComparator) {\r
+          CurrentComparator = HpetRead (HPET_TIMER_COMPARATOR_OFFSET + mTimerIndex * HPET_TIMER_STRIDE);\r
+          CpuPause();\r
+        }\r
+      }\r
+    }\r
+\r
     //\r
     // If TimerPeriod is 0, then mask HPET Timer interrupts\r
     //\r
-    \r
-    if (mTimerConfiguration.Bits.MsiInterruptCapablity != 0) {\r
+\r
+    if (mTimerConfiguration.Bits.MsiInterruptCapability != 0 && FeaturePcdGet (PcdHpetMsiEnable)) {\r
       //\r
       // Disable HPET MSI interrupt generation\r
       //\r
@@ -451,54 +541,49 @@ TimerDriverSetTimerPeriod (
       //\r
       IoApicEnableInterrupt (mTimerIrq, FALSE);\r
     }\r
-    \r
+\r
     //\r
-    // Disable HPET timer interrupt \r
+    // Disable HPET timer interrupt\r
     //\r
     mTimerConfiguration.Bits.InterruptEnable = 0;\r
     HpetWrite (HPET_TIMER_CONFIGURATION_OFFSET + mTimerIndex * HPET_TIMER_STRIDE, mTimerConfiguration.Uint64);\r
   } else {\r
     //\r
-    // Convert TimerPeriod to femtoseconds and divide by the number if femtoseconds \r
+    // Convert TimerPeriod to femtoseconds and divide by the number if femtoseconds\r
     // per tick of the HPET counter to determine the number of HPET counter ticks\r
     // in TimerPeriod 100 ns units.\r
-    // \r
-    TimerCount = DivU64x32 (\r
-                   MultU64x32 (TimerPeriod, 100000000),\r
-                   mHpetGeneralCapabilities.Bits.CounterClockPeriod\r
-                   );\r
+    //\r
+    mTimerCount = DivU64x32 (\r
+                    MultU64x32 (TimerPeriod, 100000000),\r
+                    mHpetGeneralCapabilities.Bits.CounterClockPeriod\r
+                    );\r
 \r
     //\r
     // Program the HPET Comparator with the number of ticks till the next interrupt\r
     //\r
-    HpetWrite (HPET_TIMER_COMPARATOR_OFFSET + mTimerIndex * HPET_TIMER_STRIDE, TimerCount);\r
+    MainCounter = HpetRead (HPET_MAIN_COUNTER_OFFSET);\r
+    if (MainCounter > mPreviousMainCounter) {\r
+      Delta = MainCounter - mPreviousMainCounter;\r
+    } else {\r
+      Delta = (mCounterMask - mPreviousMainCounter) + MainCounter;\r
+    }\r
+    if ((Delta & mCounterMask) >= mTimerCount) {\r
+      HpetWrite (HPET_TIMER_COMPARATOR_OFFSET + mTimerIndex * HPET_TIMER_STRIDE, (MainCounter + 1) & mCounterMask);\r
+    } else {\r
+      HpetWrite (HPET_TIMER_COMPARATOR_OFFSET + mTimerIndex * HPET_TIMER_STRIDE, (mPreviousMainCounter + mTimerCount) & mCounterMask);\r
+    }\r
 \r
     //\r
-    // Capture the number of ticks since the last HPET Timer interrupt before \r
-    // clearing the main counter.  This value will be used in the next HPET\r
-    // timer interrupt handler to compute the total amount of time since the\r
-    // last HPET timer interrupt\r
-    //    \r
-    mTimerAccumulator = HpetRead (HPET_MAIN_COUNTER_OFFSET);\r
-    \r
+    // Enable HPET Timer interrupt generation\r
     //\r
-    // If the number of ticks since the last timer interrupt is greater than the\r
-    // timer period, reduce the number of ticks till the next interrupt to 1, so \r
-    // a timer interrupt will be generated as soon as the HPET counter is enabled.\r
-    //    \r
-    if (mTimerAccumulator >= TimerCount) {\r
-      HpetWrite (HPET_MAIN_COUNTER_OFFSET, TimerCount - 1);\r
+    if (mTimerConfiguration.Bits.MsiInterruptCapability != 0 && FeaturePcdGet (PcdHpetMsiEnable)) {\r
       //\r
-      // Adjust the accumulator down by TimerCount ticks because TimerCount\r
-      // ticks will be added to the accumulator on the next interrupt\r
+      // Program MSI Address and MSI Data values in the selected HPET Timer\r
+      // Program HPET register with APIC ID of current BSP in case BSP has been switched\r
       //\r
-      mTimerAccumulator -= TimerCount;\r
-    }\r
-    \r
-    //\r
-    // Enable HPET Timer interrupt generation\r
-    //\r
-    if (mTimerConfiguration.Bits.MsiInterruptCapablity != 0) {\r
+      HpetTimerMsiRoute.Bits.Address = GetApicMsiAddress ();\r
+      HpetTimerMsiRoute.Bits.Value   = (UINT32)GetApicMsiValue (PcdGet8 (PcdHpetLocalApicVector), LOCAL_APIC_DELIVERY_MODE_LOWEST_PRIORITY, FALSE, FALSE);\r
+      HpetWrite (HPET_TIMER_MSI_ROUTE_OFFSET + mTimerIndex * HPET_TIMER_STRIDE, HpetTimerMsiRoute.Uint64);\r
       //\r
       // Enable HPET MSI Interrupt\r
       //\r
@@ -506,7 +591,9 @@ TimerDriverSetTimerPeriod (
     } else {\r
       //\r
       // Enable timer interrupt through I/O APIC\r
+      // Program IOAPIC register with APIC ID of current BSP in case BSP has been switched\r
       //\r
+      IoApicConfigureInterrupt (mTimerIrq, PcdGet8 (PcdHpetLocalApicVector), IO_APIC_DELIVERY_MODE_LOWEST_PRIORITY, TRUE, FALSE);\r
       IoApicEnableInterrupt (mTimerIrq, TRUE);\r
     }\r
 \r
@@ -516,7 +603,7 @@ TimerDriverSetTimerPeriod (
     mTimerConfiguration.Bits.InterruptEnable = 1;\r
     HpetWrite (HPET_TIMER_CONFIGURATION_OFFSET + mTimerIndex * HPET_TIMER_STRIDE, mTimerConfiguration.Uint64);\r
   }\r
-    \r
+\r
   //\r
   // Save the new timer period\r
   //\r
@@ -529,7 +616,12 @@ TimerDriverSetTimerPeriod (
   // is disabled.\r
   //\r
   HpetEnable (TRUE);\r
-  \r
+\r
+  //\r
+  // Restore interrupts\r
+  //\r
+  gBS->RestoreTPL (Tpl);\r
+\r
   return EFI_SUCCESS;\r
 }\r
 \r
@@ -575,7 +667,7 @@ TimerDriverGetTimerPeriod (
   @param  This  The EFI_TIMER_ARCH_PROTOCOL instance.\r
 \r
   @retval  EFI_SUCCESS       The soft timer interrupt was generated.\r
-  @retval  EFI_UNSUPPORTEDT  The platform does not support the generation of soft \r
+  @retval  EFI_UNSUPPORTED   The platform does not support the generation of soft\r
                              timer interrupts.\r
 \r
 **/\r
@@ -585,52 +677,65 @@ TimerDriverGenerateSoftInterrupt (
   IN EFI_TIMER_ARCH_PROTOCOL  *This\r
   )\r
 {\r
+  UINT64   MainCounter;\r
   EFI_TPL  Tpl;\r
   UINT64   TimerPeriod;\r
+  UINT64   Delta;\r
 \r
   //\r
   // Disable interrupts\r
-  //  \r
-  Tpl = gBS->RaiseTPL (TPL_HIGH_LEVEL);\r
-\r
-  //\r
-  // Read the current HPET main counter value\r
   //\r
-  mTimerAccumulator += HpetRead (HPET_MAIN_COUNTER_OFFSET);\r
+  Tpl = gBS->RaiseTPL (TPL_HIGH_LEVEL);\r
 \r
   //\r
-  // Reset HPET main counter to 0\r
+  // Capture main counter value\r
   //\r
-  HpetWrite (HPET_MAIN_COUNTER_OFFSET, 0);\r
+  MainCounter = HpetRead (HPET_MAIN_COUNTER_OFFSET);\r
 \r
   //\r
   // Check to see if there is a registered notification function\r
   //\r
   if (mTimerNotifyFunction != NULL) {\r
     //\r
-    // Compute time since last interrupt in 100 ns units (10 ^ -7) \r
+    // Compute time since last interrupt in 100 ns units (10 ^ -7)\r
     //\r
+    if (MainCounter > mPreviousMainCounter) {\r
+      //\r
+      // Main counter does not overflow\r
+      //\r
+      Delta = MainCounter - mPreviousMainCounter;\r
+    } else {\r
+      //\r
+      // Main counter overflows, first usb, then add\r
+      //\r
+      Delta = (mCounterMask - mPreviousMainCounter) + MainCounter;\r
+    }\r
+\r
     TimerPeriod = DivU64x32 (\r
                     MultU64x32 (\r
-                      mTimerAccumulator, \r
+                      Delta & mCounterMask,\r
                       mHpetGeneralCapabilities.Bits.CounterClockPeriod\r
-                      ), \r
+                      ),\r
                     100000000\r
                     );\r
-    mTimerAccumulator = 0;\r
-                    \r
+\r
     //\r
     // Call registered notification function passing in the time since the last\r
     // interrupt in 100 ns units.\r
-    //    \r
+    //\r
     mTimerNotifyFunction (TimerPeriod);\r
   }\r
 \r
+  //\r
+  // Save main counter value\r
+  //\r
+  mPreviousMainCounter = MainCounter;\r
+\r
   //\r
   // Restore interrupts\r
-  //  \r
+  //\r
   gBS->RestoreTPL (Tpl);\r
-  \r
+\r
   return EFI_SUCCESS;\r
 }\r
 \r
@@ -642,7 +747,7 @@ TimerDriverGenerateSoftInterrupt (
 \r
   @retval  EFI_SUCCESS           Timer Architectural Protocol created\r
   @retval  EFI_OUT_OF_RESOURCES  Not enough resources available to initialize driver.\r
-  @retval  EFI_DEVICE_ERROR      A device error occured attempting to initialize the driver.\r
+  @retval  EFI_DEVICE_ERROR      A device error occurred attempting to initialize the driver.\r
 \r
 **/\r
 EFI_STATUS\r
@@ -672,14 +777,14 @@ TimerDriverInitialize (
 \r
   //\r
   // Retrieve HPET Capabilities and Configuration Information\r
-  //  \r
+  //\r
   mHpetGeneralCapabilities.Uint64  = HpetRead (HPET_GENERAL_CAPABILITIES_ID_OFFSET);\r
   mHpetGeneralConfiguration.Uint64 = HpetRead (HPET_GENERAL_CONFIGURATION_OFFSET);\r
\r
+\r
   //\r
-  // If Revision is not valid, then ASSERT() and unload the driver because the HPET \r
+  // If Revision is not valid, then ASSERT() and unload the driver because the HPET\r
   // device is not present.\r
-  //  \r
+  //\r
   ASSERT (mHpetGeneralCapabilities.Uint64 != 0);\r
   ASSERT (mHpetGeneralCapabilities.Uint64 != 0xFFFFFFFFFFFFFFFFULL);\r
   if (mHpetGeneralCapabilities.Uint64 == 0 || mHpetGeneralCapabilities.Uint64 == 0xFFFFFFFFFFFFFFFFULL) {\r
@@ -694,25 +799,30 @@ TimerDriverInitialize (
 \r
   //\r
   // Dump HPET Configuration Information\r
-  //  \r
+  //\r
   DEBUG_CODE (\r
-    DEBUG ((DEBUG_INFO, "HPET Base Address = %08x\n", PcdGet32 (PcdHpetBaseAddress)));\r
-    DEBUG ((DEBUG_INFO, "  HPET_GENERAL_CAPABILITIES_ID  = %016lx\n", mHpetGeneralCapabilities));\r
-    DEBUG ((DEBUG_INFO, "  HPET_GENERAL_CONFIGURATION    = %016lx\n", mHpetGeneralConfiguration.Uint64));\r
-    DEBUG ((DEBUG_INFO, "  HPET_GENERAL_INTERRUPT_STATUS = %016lx\n", HpetRead (HPET_GENERAL_INTERRUPT_STATUS_OFFSET)));\r
-    DEBUG ((DEBUG_INFO, "  HPET_MAIN_COUNTER             = %016lx\n", HpetRead (HPET_MAIN_COUNTER_OFFSET)));\r
+    DEBUG ((DEBUG_INFO, "HPET Base Address = 0x%08x\n", PcdGet32 (PcdHpetBaseAddress)));\r
+    DEBUG ((DEBUG_INFO, "  HPET_GENERAL_CAPABILITIES_ID  = 0x%016lx\n", mHpetGeneralCapabilities));\r
+    DEBUG ((DEBUG_INFO, "  HPET_GENERAL_CONFIGURATION    = 0x%016lx\n", mHpetGeneralConfiguration.Uint64));\r
+    DEBUG ((DEBUG_INFO, "  HPET_GENERAL_INTERRUPT_STATUS = 0x%016lx\n", HpetRead (HPET_GENERAL_INTERRUPT_STATUS_OFFSET)));\r
+    DEBUG ((DEBUG_INFO, "  HPET_MAIN_COUNTER             = 0x%016lx\n", HpetRead (HPET_MAIN_COUNTER_OFFSET)));\r
     DEBUG ((DEBUG_INFO, "  HPET Main Counter Period      = %d (fs)\n", mHpetGeneralCapabilities.Bits.CounterClockPeriod));\r
     for (TimerIndex = 0; TimerIndex <= mHpetGeneralCapabilities.Bits.NumberOfTimers; TimerIndex++) {\r
-      DEBUG ((DEBUG_INFO, "  HPET_TIMER%d_CONFIGURATION     = %016lx\n", TimerIndex, HpetRead (HPET_TIMER_CONFIGURATION_OFFSET + TimerIndex * HPET_TIMER_STRIDE)));\r
-      DEBUG ((DEBUG_INFO, "  HPET_TIMER%d_COMPARATOR        = %016lx\n", TimerIndex, HpetRead (HPET_TIMER_COMPARATOR_OFFSET    + TimerIndex * HPET_TIMER_STRIDE)));\r
-      DEBUG ((DEBUG_INFO, "  HPET_TIMER%d_MSI_ROUTE         = %016lx\n", TimerIndex, HpetRead (HPET_TIMER_MSI_ROUTE_OFFSET     + TimerIndex * HPET_TIMER_STRIDE)));\r
+      DEBUG ((DEBUG_INFO, "  HPET_TIMER%d_CONFIGURATION     = 0x%016lx\n", TimerIndex, HpetRead (HPET_TIMER_CONFIGURATION_OFFSET + TimerIndex * HPET_TIMER_STRIDE)));\r
+      DEBUG ((DEBUG_INFO, "  HPET_TIMER%d_COMPARATOR        = 0x%016lx\n", TimerIndex, HpetRead (HPET_TIMER_COMPARATOR_OFFSET    + TimerIndex * HPET_TIMER_STRIDE)));\r
+      DEBUG ((DEBUG_INFO, "  HPET_TIMER%d_MSI_ROUTE         = 0x%016lx\n", TimerIndex, HpetRead (HPET_TIMER_MSI_ROUTE_OFFSET     + TimerIndex * HPET_TIMER_STRIDE)));\r
     }\r
   );\r
-  \r
+\r
+  //\r
+  // Capture the current HPET main counter value.\r
+  //\r
+  mPreviousMainCounter = HpetRead (HPET_MAIN_COUNTER_OFFSET);\r
+\r
   //\r
-  // Determine the interrupt mode to use for the HPET Timer.  \r
+  // Determine the interrupt mode to use for the HPET Timer.\r
   // Look for MSI first, then unused PIC mode interrupt, then I/O APIC mode interrupt\r
-  //  \r
+  //\r
   MsiTimerIndex = HPET_INVALID_TIMER_INDEX;\r
   mTimerIndex   = HPET_INVALID_TIMER_INDEX;\r
   for (TimerIndex = 0; TimerIndex <= mHpetGeneralCapabilities.Bits.NumberOfTimers; TimerIndex++) {\r
@@ -720,11 +830,11 @@ TimerDriverInitialize (
     // Read the HPET Timer Capabilities and Configuration register\r
     //\r
     mTimerConfiguration.Uint64 = HpetRead (HPET_TIMER_CONFIGURATION_OFFSET + TimerIndex * HPET_TIMER_STRIDE);\r
-    \r
+\r
     //\r
-    // Check to see if this HPET Timer supports MSI \r
+    // Check to see if this HPET Timer supports MSI\r
     //\r
-    if (mTimerConfiguration.Bits.MsiInterruptCapablity != 0) {\r
+    if (mTimerConfiguration.Bits.MsiInterruptCapability != 0) {\r
       //\r
       // Save the index of the first HPET Timer that supports MSI interrupts\r
       //\r
@@ -732,7 +842,7 @@ TimerDriverInitialize (
         MsiTimerIndex = TimerIndex;\r
       }\r
     }\r
-    \r
+\r
     //\r
     // Check to see if this HPET Timer supports I/O APIC interrupts\r
     //\r
@@ -775,7 +885,7 @@ TimerDriverInitialize (
       DEBUG ((DEBUG_ERROR, "No HPET timers support MSI or I/O APIC mode.  Unload HPET driver.\n"));\r
       return EFI_DEVICE_ERROR;\r
     }\r
-    \r
+\r
     //\r
     // Initialize I/O APIC entry for HPET Timer Interrupt\r
     //   Fixed Delivery Mode, Level Triggered, Asserted Low\r
@@ -789,22 +899,42 @@ TimerDriverInitialize (
     //   Set InterruptRoute field based in mTimerIrq\r
     //\r
     mTimerConfiguration.Uint64 = HpetRead (HPET_TIMER_CONFIGURATION_OFFSET + mTimerIndex * HPET_TIMER_STRIDE);\r
-    mTimerConfiguration.Bits.MsiInterruptCapablity   = 0;\r
     mTimerConfiguration.Bits.LevelTriggeredInterrupt = 1;\r
     mTimerConfiguration.Bits.InterruptRoute          = mTimerIrq;\r
   }\r
 \r
   //\r
   // Configure the selected HPET Timer with settings common to both MSI mode and I/O APIC mode\r
-  //   Clear InterruptEnable to keep interrupts disabled until full init is complete \r
-  //   Clear PeriodicInterruptEnable to use one-shot mode \r
-  //   Configure as a 32-bit counter  \r
+  //   Clear InterruptEnable to keep interrupts disabled until full init is complete\r
+  //   Clear PeriodicInterruptEnable to use one-shot mode\r
+  //   Configure as a 32-bit counter\r
   //\r
   mTimerConfiguration.Bits.InterruptEnable         = 0;\r
   mTimerConfiguration.Bits.PeriodicInterruptEnable = 0;\r
-  mTimerConfiguration.Bits.CounterSizeEnable       = 0;\r
+  mTimerConfiguration.Bits.CounterSizeEnable       = 1;\r
   HpetWrite (HPET_TIMER_CONFIGURATION_OFFSET + mTimerIndex * HPET_TIMER_STRIDE, mTimerConfiguration.Uint64);\r
 \r
+  //\r
+  // Read the HPET Timer Capabilities and Configuration register back again.\r
+  // CounterSizeEnable will be read back as a 0 if it is a 32-bit only timer\r
+  //\r
+  mTimerConfiguration.Uint64 = HpetRead (HPET_TIMER_CONFIGURATION_OFFSET + mTimerIndex * HPET_TIMER_STRIDE);\r
+  if ((mTimerConfiguration.Bits.CounterSizeEnable == 1) && (sizeof (UINTN) == sizeof (UINT64))) {\r
+    DEBUG ((DEBUG_INFO, "Choose 64-bit HPET timer.\n"));\r
+    //\r
+    // 64-bit BIOS can use 64-bit HPET timer\r
+    //\r
+    mCounterMask = 0xffffffffffffffffULL;\r
+    //\r
+    // Set timer back to 64-bit\r
+    //\r
+    mTimerConfiguration.Bits.CounterSizeEnable = 0;\r
+    HpetWrite (HPET_TIMER_CONFIGURATION_OFFSET + mTimerIndex * HPET_TIMER_STRIDE, mTimerConfiguration.Uint64);\r
+  } else {\r
+    DEBUG ((DEBUG_INFO, "Choose 32-bit HPET timer.\n"));\r
+    mCounterMask = 0x00000000ffffffffULL;\r
+  }\r
+\r
   //\r
   // Install interrupt handler for selected HPET Timer\r
   //\r
@@ -829,23 +959,26 @@ TimerDriverInitialize (
   // Show state of enabled HPET timer\r
   //\r
   DEBUG_CODE (\r
-    if (mTimerConfiguration.Bits.MsiInterruptCapablity != 0) {\r
+    if (mTimerConfiguration.Bits.MsiInterruptCapability != 0 && FeaturePcdGet (PcdHpetMsiEnable)) {\r
       DEBUG ((DEBUG_INFO, "HPET Interrupt Mode MSI\n"));\r
     } else {\r
       DEBUG ((DEBUG_INFO, "HPET Interrupt Mode I/O APIC\n"));\r
-      DEBUG ((DEBUG_INFO, "HPET I/O APIC IRQ         = %02x\n",   mTimerIrq));\r
-    }  \r
-    DEBUG ((DEBUG_INFO, "HPET Interrupt Vector     = %02x\n",   PcdGet8 (PcdHpetLocalApicVector)));\r
-    DEBUG ((DEBUG_INFO, "HPET_TIMER%d_CONFIGURATION = %016lx\n", mTimerIndex, HpetRead (HPET_TIMER_CONFIGURATION_OFFSET + mTimerIndex * HPET_TIMER_STRIDE)));\r
-    DEBUG ((DEBUG_INFO, "HPET_TIMER%d_COMPARATOR    = %016lx\n", mTimerIndex, HpetRead (HPET_TIMER_COMPARATOR_OFFSET    + mTimerIndex * HPET_TIMER_STRIDE)));\r
-    DEBUG ((DEBUG_INFO, "HPET_TIMER%d_MSI_ROUTE     = %016lx\n", mTimerIndex, HpetRead (HPET_TIMER_MSI_ROUTE_OFFSET     + mTimerIndex * HPET_TIMER_STRIDE)));\r
+      DEBUG ((DEBUG_INFO, "HPET I/O APIC IRQ         = 0x%02x\n",  mTimerIrq));\r
+    }\r
+    DEBUG ((DEBUG_INFO, "HPET Interrupt Vector     = 0x%02x\n",    PcdGet8 (PcdHpetLocalApicVector)));\r
+    DEBUG ((DEBUG_INFO, "HPET Counter Mask         = 0x%016lx\n",  mCounterMask));\r
+    DEBUG ((DEBUG_INFO, "HPET Timer Period         = %d\n",        mTimerPeriod));\r
+    DEBUG ((DEBUG_INFO, "HPET Timer Count          = 0x%016lx\n",  mTimerCount));\r
+    DEBUG ((DEBUG_INFO, "HPET_TIMER%d_CONFIGURATION = 0x%016lx\n", mTimerIndex, HpetRead (HPET_TIMER_CONFIGURATION_OFFSET + mTimerIndex * HPET_TIMER_STRIDE)));\r
+    DEBUG ((DEBUG_INFO, "HPET_TIMER%d_COMPARATOR    = 0x%016lx\n", mTimerIndex, HpetRead (HPET_TIMER_COMPARATOR_OFFSET    + mTimerIndex * HPET_TIMER_STRIDE)));\r
+    DEBUG ((DEBUG_INFO, "HPET_TIMER%d_MSI_ROUTE     = 0x%016lx\n", mTimerIndex, HpetRead (HPET_TIMER_MSI_ROUTE_OFFSET     + mTimerIndex * HPET_TIMER_STRIDE)));\r
 \r
     //\r
     // Wait for a few timer interrupts to fire before continuing\r
-    // \r
+    //\r
     while (mNumTicks < 10);\r
   );\r
\r
+\r
   //\r
   // Install the Timer Architectural Protocol onto a new handle\r
   //\r