/** @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
\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
EFI_STATUS\r
EFIAPI\r
TimerDriverGetTimerPeriod (\r
- IN EFI_TIMER_ARCH_PROTOCOL *This,\r
- OUT UINT64 *TimerPeriod\r
+ IN EFI_TIMER_ARCH_PROTOCOL *This,\r
+ OUT UINT64 *TimerPeriod\r
);\r
\r
/**\r
@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
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
-EFI_HANDLE mTimerHandle = NULL;\r
+EFI_HANDLE mTimerHandle = NULL;\r
\r
///\r
/// The Timer Architectural Protocol that this driver produces.\r
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
+UINT64 mCounterMask;\r
+\r
+///\r
+/// The HPET main counter value from the most recent HPET timer interrupt.\r
///\r
-volatile UINT64 mTimerAccumulator = 0;\r
+volatile UINT64 mPreviousMainCounter;\r
+\r
+volatile UINT64 mPreviousComparator;\r
\r
///\r
/// The index of the HPET timer being managed by this driver.\r
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
/**\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
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
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
TimerInterruptHandler (\r
- IN EFI_EXCEPTION_TYPE InterruptType,\r
- IN EFI_SYSTEM_CONTEXT SystemContext\r
+ IN EFI_EXCEPTION_TYPE InterruptType,\r
+ 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
- DEBUG_CODE (mNumTicks++;);\r
+ DEBUG_CODE (\r
+ mNumTicks++;\r
+ );\r
\r
//\r
// Clear HPET timer interrupt status\r
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
- mTimerAccumulator += HpetRead (HPET_MAIN_COUNTER_OFFSET);\r
+ HpetEnable (FALSE);\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
+ // Get the previous comparator counter\r
+ //\r
+ mPreviousComparator = HpetRead (HPET_TIMER_COMPARATOR_OFFSET + mTimerIndex * HPET_TIMER_STRIDE);\r
+\r
+ //\r
+ // Set HPET COMPARATOR to the value required for the next timer tick\r
+ //\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
+\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
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
//\r
// Check for invalid parameters\r
//\r
- if (NotifyFunction == NULL && mTimerNotifyFunction == NULL) {\r
+ if ((NotifyFunction == NULL) && (mTimerNotifyFunction == NULL)) {\r
return EFI_INVALID_PARAMETER;\r
}\r
- if (NotifyFunction != NULL && mTimerNotifyFunction != NULL) {\r
+\r
+ if ((NotifyFunction != NULL) && (mTimerNotifyFunction != NULL)) {\r
return EFI_ALREADY_STARTED;\r
}\r
\r
\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
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
+\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 && FeaturePcdGet (PcdHpetMsiEnable)) {\r
+\r
+ if ((mTimerConfiguration.Bits.MsiInterruptCapability != 0) && FeaturePcdGet (PcdHpetMsiEnable)) {\r
//\r
// Disable HPET MSI interrupt generation\r
//\r
//\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
\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
- //\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
- //\r
- // Adjust the accumulator down by TimerCount ticks because TimerCount\r
- // ticks will be added to the accumulator on the next interrupt\r
- //\r
- mTimerAccumulator -= TimerCount;\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
//\r
// Enable HPET Timer interrupt generation\r
//\r
- if (mTimerConfiguration.Bits.MsiInterruptCapablity != 0 && FeaturePcdGet (PcdHpetMsiEnable)) {\r
+ if ((mTimerConfiguration.Bits.MsiInterruptCapability != 0) && FeaturePcdGet (PcdHpetMsiEnable)) {\r
+ //\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
+ 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
} 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
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
// 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
EFI_STATUS\r
EFIAPI\r
TimerDriverGetTimerPeriod (\r
- IN EFI_TIMER_ARCH_PROTOCOL *This,\r
- OUT UINT64 *TimerPeriod\r
+ IN EFI_TIMER_ARCH_PROTOCOL *This,\r
+ OUT UINT64 *TimerPeriod\r
)\r
{\r
if (TimerPeriod == NULL) {\r
@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
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
\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
IN EFI_SYSTEM_TABLE *SystemTable\r
)\r
{\r
- EFI_STATUS Status;\r
- UINTN TimerIndex;\r
- UINTN MsiTimerIndex;\r
- HPET_TIMER_MSI_ROUTE_REGISTER HpetTimerMsiRoute;\r
+ EFI_STATUS Status;\r
+ UINTN TimerIndex;\r
+ UINTN MsiTimerIndex;\r
+ HPET_TIMER_MSI_ROUTE_REGISTER HpetTimerMsiRoute;\r
\r
DEBUG ((DEBUG_INFO, "Init HPET Timer Driver\n"));\r
\r
//\r
// Find the CPU architectural protocol.\r
//\r
- Status = gBS->LocateProtocol (&gEfiCpuArchProtocolGuid, NULL, (VOID **) &mCpu);\r
+ Status = gBS->LocateProtocol (&gEfiCpuArchProtocolGuid, NULL, (VOID **)&mCpu);\r
ASSERT_EFI_ERROR (Status);\r
\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
+ if ((mHpetGeneralCapabilities.Uint64 == 0) || (mHpetGeneralCapabilities.Uint64 == 0xFFFFFFFFFFFFFFFFULL)) {\r
DEBUG ((DEBUG_ERROR, "HPET device is not present. Unload HPET driver.\n"));\r
return EFI_DEVICE_ERROR;\r
}\r
\r
//\r
// Dump HPET Configuration Information\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 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
- }\r
- );\r
- \r
//\r
- // Determine the interrupt mode to use for the HPET Timer. \r
+ DEBUG_CODE_BEGIN ();\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 = 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
+ DEBUG_CODE_END ();\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
// 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
// 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
MsiTimerIndex = TimerIndex;\r
}\r
}\r
- \r
+\r
//\r
// Check to see if this HPET Timer supports I/O APIC interrupts\r
//\r
}\r
}\r
\r
- if (FeaturePcdGet (PcdHpetMsiEnable) && MsiTimerIndex != HPET_INVALID_TIMER_INDEX) {\r
+ if (FeaturePcdGet (PcdHpetMsiEnable) && (MsiTimerIndex != HPET_INVALID_TIMER_INDEX)) {\r
//\r
// Use MSI interrupt if supported\r
//\r
- mTimerIndex = MsiTimerIndex;\r
+ mTimerIndex = MsiTimerIndex;\r
\r
//\r
// Program MSI Address and MSI Data values in the selected HPET Timer\r
// Read the HPET Timer Capabilities and Configuration register and initialize for MSI mode\r
// Clear LevelTriggeredInterrupt to use edge triggered interrupts when in MSI mode\r
//\r
- mTimerConfiguration.Uint64 = HpetRead (HPET_TIMER_CONFIGURATION_OFFSET + mTimerIndex * HPET_TIMER_STRIDE);\r
+ mTimerConfiguration.Uint64 = HpetRead (HPET_TIMER_CONFIGURATION_OFFSET + mTimerIndex * HPET_TIMER_STRIDE);\r
mTimerConfiguration.Bits.LevelTriggeredInterrupt = 0;\r
} else {\r
//\r
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
// Set LevelTriggeredInterrupt to use level triggered interrupts when in I/O APIC mode\r
// Set InterruptRoute field based in mTimerIrq\r
//\r
- mTimerConfiguration.Uint64 = HpetRead (HPET_TIMER_CONFIGURATION_OFFSET + mTimerIndex * HPET_TIMER_STRIDE);\r
+ mTimerConfiguration.Uint64 = HpetRead (HPET_TIMER_CONFIGURATION_OFFSET + mTimerIndex * HPET_TIMER_STRIDE);\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
//\r
// Show state of enabled HPET timer\r
//\r
- DEBUG_CODE (\r
- if (mTimerConfiguration.Bits.MsiInterruptCapablity != 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
-\r
- //\r
- // Wait for a few timer interrupts to fire before continuing\r
- // \r
- while (mNumTicks < 10);\r
- );\r
- \r
+ DEBUG_CODE_BEGIN ();\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 = 0x%02x\n", mTimerIrq));\r
+ }\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
+ while (mNumTicks < 10) {\r
+ }\r
+\r
+ DEBUG_CODE_END ();\r
+\r
//\r
// Install the Timer Architectural Protocol onto a new handle\r
//\r
Status = gBS->InstallMultipleProtocolInterfaces (\r
&mTimerHandle,\r
- &gEfiTimerArchProtocolGuid, &mTimer,\r
+ &gEfiTimerArchProtocolGuid,\r
+ &mTimer,\r
NULL\r
);\r
ASSERT_EFI_ERROR (Status);\r