/** @file\r
Timer Architectural Protocol module using High Precesion Event Timer (HPET)\r
\r
- Copyright (c) 2011, Intel Corporation. All rights reserved.<BR>\r
+ Copyright (c) 2011 - 2018, 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
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
@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
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
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
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
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
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
+ 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
- mTimerAccumulator += HpetRead (HPET_MAIN_COUNTER_OFFSET);\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
- // Reset HPET main counter to 0\r
+ // Enable the HPET counter once the new COMPARATOR value has been set.\r
//\r
- HpetWrite (HPET_MAIN_COUNTER_OFFSET, 0);\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
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
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.MsiInterruptCapablity != 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
+ 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.MsiInterruptCapablity != 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
} 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
@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
\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
\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
// 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
//\r
MsiTimerIndex = TimerIndex;\r
}\r
}\r
- \r
+\r
//\r
// Check to see if this HPET Timer supports I/O APIC interrupts\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 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
// Show state of enabled HPET timer\r
//\r
DEBUG_CODE (\r
- if (mTimerConfiguration.Bits.MsiInterruptCapablity != 0) {\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
+ 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