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
-volatile UINT64 mTimerAccumulator = 0;\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 mPreviousMainCounter;\r
+\r
+volatile UINT64 mPreviousComparator;\r
\r
///\r
/// The index of the HPET timer being managed by this driver.\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
- 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
//\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
100000000\r
);\r
- mTimerAccumulator = 0;\r
\r
//\r
// Call registered notification function passing in the time since the last\r
// \r
mTimerNotifyFunction (TimerPeriod);\r
}\r
+ \r
+ //\r
+ // Save main counter value\r
+ //\r
+ mPreviousMainCounter = MainCounter;\r
}\r
\r
/**\r
IN UINT64 TimerPeriod\r
)\r
{\r
- UINT64 TimerCount;\r
-\r
+ UINT64 MainCounter;\r
+ UINT64 Delta;\r
+ UINT64 CurrentComparator;\r
+ HPET_TIMER_MSI_ROUTE_REGISTER HpetTimerMsiRoute;\r
+ \r
//\r
// Disable HPET timer when adjusting the timer period\r
//\r
HpetEnable (FALSE);\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
// 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
+ 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
-\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
+ 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
// Enable HPET Timer interrupt generation\r
//\r
if (mTimerConfiguration.Bits.MsiInterruptCapablity != 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
- IoApicEnableInterrupt (mTimerIrq, TRUE);\r
+ IoApicConfigureInterrupt (mTimerIrq, PcdGet8 (PcdHpetLocalApicVector), IO_APIC_DELIVERY_MODE_LOWEST_PRIORITY, TRUE, FALSE);\r
}\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
-\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
// 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
100000000\r
);\r
- mTimerAccumulator = 0;\r
\r
//\r
// Call registered notification function passing in the time since the last\r
mTimerNotifyFunction (TimerPeriod);\r
}\r
\r
+ //\r
+ // Save main counter value\r
+ //\r
+ mPreviousMainCounter = MainCounter;\r
+ \r
//\r
// Restore interrupts\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 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
+ // 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
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
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
+ DEBUG ((DEBUG_INFO, "HPET I/O APIC IRQ = 0x%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 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