REF: https://bugzilla.tianocore.org/show_bug.cgi?id=3737 Apply uncrustify changes to .c/.h files in the PcAtChipsetPkg package Cc: Andrew Fish <afish@apple.com> Cc: Leif Lindholm <leif@nuviainc.com> Cc: Michael D Kinney <michael.d.kinney@intel.com> Signed-off-by: Michael Kubacki <michael.kubacki@microsoft.com> Reviewed-by: Ray Ni <ray.ni@intel.com>
		
			
				
	
	
		
			1005 lines
		
	
	
		
			36 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			1005 lines
		
	
	
		
			36 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
| /** @file
 | |
|   Timer Architectural Protocol module using High Precision Event Timer (HPET)
 | |
| 
 | |
|   Copyright (c) 2011 - 2018, Intel Corporation. All rights reserved.<BR>
 | |
|   SPDX-License-Identifier: BSD-2-Clause-Patent
 | |
| 
 | |
| **/
 | |
| 
 | |
| #include <PiDxe.h>
 | |
| 
 | |
| #include <Protocol/Cpu.h>
 | |
| #include <Protocol/Timer.h>
 | |
| 
 | |
| #include <Library/IoLib.h>
 | |
| #include <Library/PcdLib.h>
 | |
| #include <Library/BaseLib.h>
 | |
| #include <Library/DebugLib.h>
 | |
| #include <Library/UefiBootServicesTableLib.h>
 | |
| #include <Library/LocalApicLib.h>
 | |
| #include <Library/IoApicLib.h>
 | |
| 
 | |
| #include <Register/LocalApic.h>
 | |
| #include <Register/IoApic.h>
 | |
| #include <Register/Hpet.h>
 | |
| 
 | |
| ///
 | |
| /// Define value for an invalid HPET Timer index.
 | |
| ///
 | |
| #define HPET_INVALID_TIMER_INDEX  0xff
 | |
| 
 | |
| ///
 | |
| /// Timer Architectural Protocol function prototypes.
 | |
| ///
 | |
| 
 | |
| /**
 | |
|   This function registers the handler NotifyFunction so it is called every time
 | |
|   the timer interrupt fires.  It also passes the amount of time since the last
 | |
|   handler call to the NotifyFunction.  If NotifyFunction is NULL, then the
 | |
|   handler is unregistered.  If the handler is registered, then EFI_SUCCESS is
 | |
|   returned.  If the CPU does not support registering a timer interrupt handler,
 | |
|   then EFI_UNSUPPORTED is returned.  If an attempt is made to register a handler
 | |
|   when a handler is already registered, then EFI_ALREADY_STARTED is returned.
 | |
|   If an attempt is made to unregister a handler when a handler is not registered,
 | |
|   then EFI_INVALID_PARAMETER is returned.  If an error occurs attempting to
 | |
|   register the NotifyFunction with the timer interrupt, then EFI_DEVICE_ERROR
 | |
|   is returned.
 | |
| 
 | |
|   @param  This            The EFI_TIMER_ARCH_PROTOCOL instance.
 | |
|   @param  NotifyFunction  The function to call when a timer interrupt fires.
 | |
|                           This function executes at TPL_HIGH_LEVEL.  The DXE
 | |
|                           Core will register a handler for the timer interrupt,
 | |
|                           so it can know how much time has passed.  This
 | |
|                           information is used to signal timer based events.
 | |
|                           NULL will unregister the handler.
 | |
| 
 | |
|   @retval  EFI_SUCCESS            The timer handler was registered.
 | |
|   @retval  EFI_UNSUPPORTED        The platform does not support timer interrupts.
 | |
|   @retval  EFI_ALREADY_STARTED    NotifyFunction is not NULL, and a handler is already
 | |
|                                   registered.
 | |
|   @retval  EFI_INVALID_PARAMETER  NotifyFunction is NULL, and a handler was not
 | |
|                                   previously registered.
 | |
|   @retval  EFI_DEVICE_ERROR       The timer handler could not be registered.
 | |
| 
 | |
| **/
 | |
| EFI_STATUS
 | |
| EFIAPI
 | |
| TimerDriverRegisterHandler (
 | |
|   IN EFI_TIMER_ARCH_PROTOCOL  *This,
 | |
|   IN EFI_TIMER_NOTIFY         NotifyFunction
 | |
|   );
 | |
| 
 | |
| /**
 | |
|   This function adjusts the period of timer interrupts to the value specified
 | |
|   by TimerPeriod.  If the timer period is updated, then the selected timer
 | |
|   period is stored in EFI_TIMER.TimerPeriod, and EFI_SUCCESS is returned.  If
 | |
|   the timer hardware is not programmable, then EFI_UNSUPPORTED is returned.
 | |
|   If an error occurs while attempting to update the timer period, then the
 | |
|   timer hardware will be put back in its state prior to this call, and
 | |
|   EFI_DEVICE_ERROR is returned.  If TimerPeriod is 0, then the timer interrupt
 | |
|   is disabled.  This is not the same as disabling the CPU's interrupts.
 | |
|   Instead, it must either turn off the timer hardware, or it must adjust the
 | |
|   interrupt controller so that a CPU interrupt is not generated when the timer
 | |
|   interrupt fires.
 | |
| 
 | |
|   @param  This         The EFI_TIMER_ARCH_PROTOCOL instance.
 | |
|   @param  TimerPeriod  The rate to program the timer interrupt in 100 nS units.
 | |
|                        If the timer hardware is not programmable, then
 | |
|                        EFI_UNSUPPORTED is returned.  If the timer is programmable,
 | |
|                        then the timer period will be rounded up to the nearest
 | |
|                        timer period that is supported by the timer hardware.
 | |
|                        If TimerPeriod is set to 0, then the timer interrupts
 | |
|                        will be disabled.
 | |
| 
 | |
|   @retval  EFI_SUCCESS       The timer period was changed.
 | |
|   @retval  EFI_UNSUPPORTED   The platform cannot change the period of the timer interrupt.
 | |
|   @retval  EFI_DEVICE_ERROR  The timer period could not be changed due to a device error.
 | |
| 
 | |
| **/
 | |
| EFI_STATUS
 | |
| EFIAPI
 | |
| TimerDriverSetTimerPeriod (
 | |
|   IN EFI_TIMER_ARCH_PROTOCOL  *This,
 | |
|   IN UINT64                   TimerPeriod
 | |
|   );
 | |
| 
 | |
| /**
 | |
|   This function retrieves the period of timer interrupts in 100 ns units,
 | |
|   returns that value in TimerPeriod, and returns EFI_SUCCESS.  If TimerPeriod
 | |
|   is NULL, then EFI_INVALID_PARAMETER is returned.  If a TimerPeriod of 0 is
 | |
|   returned, then the timer is currently disabled.
 | |
| 
 | |
|   @param  This         The EFI_TIMER_ARCH_PROTOCOL instance.
 | |
|   @param  TimerPeriod  A pointer to the timer period to retrieve in 100 ns units.
 | |
|                        If 0 is returned, then the timer is currently disabled.
 | |
| 
 | |
|   @retval  EFI_SUCCESS            The timer period was returned in TimerPeriod.
 | |
|   @retval  EFI_INVALID_PARAMETER  TimerPeriod is NULL.
 | |
| 
 | |
| **/
 | |
| EFI_STATUS
 | |
| EFIAPI
 | |
| TimerDriverGetTimerPeriod (
 | |
|   IN EFI_TIMER_ARCH_PROTOCOL  *This,
 | |
|   OUT UINT64                  *TimerPeriod
 | |
|   );
 | |
| 
 | |
| /**
 | |
|   This function generates a soft timer interrupt. If the platform does not support soft
 | |
|   timer interrupts, then EFI_UNSUPPORTED is returned. Otherwise, EFI_SUCCESS is returned.
 | |
|   If a handler has been registered through the EFI_TIMER_ARCH_PROTOCOL.RegisterHandler()
 | |
|   service, then a soft timer interrupt will be generated. If the timer interrupt is
 | |
|   enabled when this service is called, then the registered handler will be invoked. The
 | |
|   registered handler should not be able to distinguish a hardware-generated timer
 | |
|   interrupt from a software-generated timer interrupt.
 | |
| 
 | |
|   @param  This  The EFI_TIMER_ARCH_PROTOCOL instance.
 | |
| 
 | |
|   @retval  EFI_SUCCESS       The soft timer interrupt was generated.
 | |
|   @retval  EFI_UNSUPPORTED   The platform does not support the generation of soft
 | |
|                              timer interrupts.
 | |
| 
 | |
| **/
 | |
| EFI_STATUS
 | |
| EFIAPI
 | |
| TimerDriverGenerateSoftInterrupt (
 | |
|   IN EFI_TIMER_ARCH_PROTOCOL  *This
 | |
|   );
 | |
| 
 | |
| ///
 | |
| /// The handle onto which the Timer Architectural Protocol will be installed.
 | |
| ///
 | |
| EFI_HANDLE  mTimerHandle = NULL;
 | |
| 
 | |
| ///
 | |
| /// The Timer Architectural Protocol that this driver produces.
 | |
| ///
 | |
| EFI_TIMER_ARCH_PROTOCOL  mTimer = {
 | |
|   TimerDriverRegisterHandler,
 | |
|   TimerDriverSetTimerPeriod,
 | |
|   TimerDriverGetTimerPeriod,
 | |
|   TimerDriverGenerateSoftInterrupt
 | |
| };
 | |
| 
 | |
| ///
 | |
| /// Pointer to the CPU Architectural Protocol instance.
 | |
| ///
 | |
| EFI_CPU_ARCH_PROTOCOL  *mCpu = NULL;
 | |
| 
 | |
| ///
 | |
| /// The notification function to call on every timer interrupt.
 | |
| ///
 | |
| EFI_TIMER_NOTIFY  mTimerNotifyFunction = NULL;
 | |
| 
 | |
| ///
 | |
| /// The current period of the HPET timer interrupt in 100 ns units.
 | |
| ///
 | |
| UINT64  mTimerPeriod = 0;
 | |
| 
 | |
| ///
 | |
| /// The number of HPET timer ticks required for the current HPET rate specified by mTimerPeriod.
 | |
| ///
 | |
| UINT64  mTimerCount;
 | |
| 
 | |
| ///
 | |
| /// Mask used for counter and comparator calculations to adjust for a 32-bit or 64-bit counter.
 | |
| ///
 | |
| UINT64  mCounterMask;
 | |
| 
 | |
| ///
 | |
| /// The HPET main counter value from the most recent HPET timer interrupt.
 | |
| ///
 | |
| volatile UINT64  mPreviousMainCounter;
 | |
| 
 | |
| volatile UINT64  mPreviousComparator;
 | |
| 
 | |
| ///
 | |
| /// The index of the HPET timer being managed by this driver.
 | |
| ///
 | |
| UINTN  mTimerIndex;
 | |
| 
 | |
| ///
 | |
| /// The I/O APIC IRQ that the HPET Timer is mapped if I/O APIC mode is used.
 | |
| ///
 | |
| UINT32  mTimerIrq;
 | |
| 
 | |
| ///
 | |
| /// Cached state of the HPET General Capabilities register managed by this driver.
 | |
| /// Caching the state reduces the number of times the configuration register is read.
 | |
| ///
 | |
| HPET_GENERAL_CAPABILITIES_ID_REGISTER  mHpetGeneralCapabilities;
 | |
| 
 | |
| ///
 | |
| /// Cached state of the HPET General Configuration register managed by this driver.
 | |
| /// Caching the state reduces the number of times the configuration register is read.
 | |
| ///
 | |
| HPET_GENERAL_CONFIGURATION_REGISTER  mHpetGeneralConfiguration;
 | |
| 
 | |
| ///
 | |
| /// Cached state of the Configuration register for the HPET Timer managed by
 | |
| /// this driver.  Caching the state reduces the number of times the configuration
 | |
| /// register is read.
 | |
| ///
 | |
| HPET_TIMER_CONFIGURATION_REGISTER  mTimerConfiguration;
 | |
| 
 | |
| ///
 | |
| /// Counts the number of HPET Timer interrupts processed by this driver.
 | |
| /// Only required for debug.
 | |
| ///
 | |
| volatile UINTN  mNumTicks;
 | |
| 
 | |
| /**
 | |
|   Read a 64-bit register from the HPET
 | |
| 
 | |
|   @param  Offset  Specifies the offset of the HPET register to read.
 | |
| 
 | |
|   @return  The 64-bit value read from the HPET register specified by Offset.
 | |
| **/
 | |
| UINT64
 | |
| HpetRead (
 | |
|   IN UINTN  Offset
 | |
|   )
 | |
| {
 | |
|   return MmioRead64 (PcdGet32 (PcdHpetBaseAddress) + Offset);
 | |
| }
 | |
| 
 | |
| /**
 | |
|   Write a 64-bit HPET register.
 | |
| 
 | |
|   @param  Offset  Specifies the offset of the HPET register to write.
 | |
|   @param  Value   Specifies the value to write to the HPET register specified by Offset.
 | |
| 
 | |
|   @return  The 64-bit value written to HPET register specified by Offset.
 | |
| **/
 | |
| UINT64
 | |
| HpetWrite (
 | |
|   IN UINTN   Offset,
 | |
|   IN UINT64  Value
 | |
|   )
 | |
| {
 | |
|   return MmioWrite64 (PcdGet32 (PcdHpetBaseAddress) + Offset, Value);
 | |
| }
 | |
| 
 | |
| /**
 | |
|   Enable or disable the main counter in the HPET Timer.
 | |
| 
 | |
|   @param  Enable  If TRUE, then enable the main counter in the HPET Timer.
 | |
|                   If FALSE, then disable the main counter in the HPET Timer.
 | |
| **/
 | |
| VOID
 | |
| HpetEnable (
 | |
|   IN BOOLEAN  Enable
 | |
|   )
 | |
| {
 | |
|   mHpetGeneralConfiguration.Bits.MainCounterEnable = Enable ? 1 : 0;
 | |
|   HpetWrite (HPET_GENERAL_CONFIGURATION_OFFSET, mHpetGeneralConfiguration.Uint64);
 | |
| }
 | |
| 
 | |
| /**
 | |
|   The interrupt handler for the HPET timer.  This handler clears the HPET interrupt
 | |
|   and computes the amount of time that has passed since the last HPET timer interrupt.
 | |
|   If a notification function is registered, then the amount of time since the last
 | |
|   HPET interrupt is passed to that notification function in 100 ns units.  The HPET
 | |
|   time is updated to generate another interrupt in the required time period.
 | |
| 
 | |
|   @param  InterruptType  The type of interrupt that occurred.
 | |
|   @param  SystemContext  A pointer to the system context when the interrupt occurred.
 | |
| **/
 | |
| VOID
 | |
| EFIAPI
 | |
| TimerInterruptHandler (
 | |
|   IN EFI_EXCEPTION_TYPE  InterruptType,
 | |
|   IN EFI_SYSTEM_CONTEXT  SystemContext
 | |
|   )
 | |
| {
 | |
|   UINT64  MainCounter;
 | |
|   UINT64  Comparator;
 | |
|   UINT64  TimerPeriod;
 | |
|   UINT64  Delta;
 | |
| 
 | |
|   //
 | |
|   // Count number of ticks
 | |
|   //
 | |
|   DEBUG_CODE (
 | |
|     mNumTicks++;
 | |
|     );
 | |
| 
 | |
|   //
 | |
|   // Clear HPET timer interrupt status
 | |
|   //
 | |
|   HpetWrite (HPET_GENERAL_INTERRUPT_STATUS_OFFSET, LShiftU64 (1, mTimerIndex));
 | |
| 
 | |
|   //
 | |
|   // Local APIC EOI
 | |
|   //
 | |
|   SendApicEoi ();
 | |
| 
 | |
|   //
 | |
|   // Disable HPET timer when adjusting the COMPARATOR value to prevent a missed interrupt
 | |
|   //
 | |
|   HpetEnable (FALSE);
 | |
| 
 | |
|   //
 | |
|   // Capture main counter value
 | |
|   //
 | |
|   MainCounter = HpetRead (HPET_MAIN_COUNTER_OFFSET);
 | |
| 
 | |
|   //
 | |
|   // Get the previous comparator counter
 | |
|   //
 | |
|   mPreviousComparator = HpetRead (HPET_TIMER_COMPARATOR_OFFSET + mTimerIndex * HPET_TIMER_STRIDE);
 | |
| 
 | |
|   //
 | |
|   // Set HPET COMPARATOR to the value required for the next timer tick
 | |
|   //
 | |
|   Comparator = (mPreviousComparator + mTimerCount) & mCounterMask;
 | |
| 
 | |
|   if ((mPreviousMainCounter < MainCounter) && (mPreviousComparator > Comparator)) {
 | |
|     //
 | |
|     // When comparator overflows
 | |
|     //
 | |
|     HpetWrite (HPET_TIMER_COMPARATOR_OFFSET + mTimerIndex * HPET_TIMER_STRIDE, Comparator);
 | |
|   } else if ((mPreviousMainCounter > MainCounter) && (mPreviousComparator < Comparator)) {
 | |
|     //
 | |
|     // When main counter overflows
 | |
|     //
 | |
|     HpetWrite (HPET_TIMER_COMPARATOR_OFFSET + mTimerIndex * HPET_TIMER_STRIDE, (MainCounter + mTimerCount) & mCounterMask);
 | |
|   } else {
 | |
|     //
 | |
|     // When both main counter and comparator do not overflow or both do overflow
 | |
|     //
 | |
|     if (Comparator > MainCounter) {
 | |
|       HpetWrite (HPET_TIMER_COMPARATOR_OFFSET + mTimerIndex * HPET_TIMER_STRIDE, Comparator);
 | |
|     } else {
 | |
|       HpetWrite (HPET_TIMER_COMPARATOR_OFFSET + mTimerIndex * HPET_TIMER_STRIDE, (MainCounter + mTimerCount) & mCounterMask);
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   //
 | |
|   // Enable the HPET counter once the new COMPARATOR value has been set.
 | |
|   //
 | |
|   HpetEnable (TRUE);
 | |
| 
 | |
|   //
 | |
|   // Check to see if there is a registered notification function
 | |
|   //
 | |
|   if (mTimerNotifyFunction != NULL) {
 | |
|     //
 | |
|     // Compute time since last notification in 100 ns units (10 ^ -7)
 | |
|     //
 | |
|     if (MainCounter > mPreviousMainCounter) {
 | |
|       //
 | |
|       // Main counter does not overflow
 | |
|       //
 | |
|       Delta = MainCounter - mPreviousMainCounter;
 | |
|     } else {
 | |
|       //
 | |
|       // Main counter overflows, first usb, then add
 | |
|       //
 | |
|       Delta = (mCounterMask - mPreviousMainCounter) + MainCounter;
 | |
|     }
 | |
| 
 | |
|     TimerPeriod = DivU64x32 (
 | |
|                     MultU64x32 (
 | |
|                       Delta & mCounterMask,
 | |
|                       mHpetGeneralCapabilities.Bits.CounterClockPeriod
 | |
|                       ),
 | |
|                     100000000
 | |
|                     );
 | |
| 
 | |
|     //
 | |
|     // Call registered notification function passing in the time since the last
 | |
|     // interrupt in 100 ns units.
 | |
|     //
 | |
|     mTimerNotifyFunction (TimerPeriod);
 | |
|   }
 | |
| 
 | |
|   //
 | |
|   // Save main counter value
 | |
|   //
 | |
|   mPreviousMainCounter = MainCounter;
 | |
| }
 | |
| 
 | |
| /**
 | |
|   This function registers the handler NotifyFunction so it is called every time
 | |
|   the timer interrupt fires.  It also passes the amount of time since the last
 | |
|   handler call to the NotifyFunction.  If NotifyFunction is NULL, then the
 | |
|   handler is unregistered.  If the handler is registered, then EFI_SUCCESS is
 | |
|   returned.  If the CPU does not support registering a timer interrupt handler,
 | |
|   then EFI_UNSUPPORTED is returned.  If an attempt is made to register a handler
 | |
|   when a handler is already registered, then EFI_ALREADY_STARTED is returned.
 | |
|   If an attempt is made to unregister a handler when a handler is not registered,
 | |
|   then EFI_INVALID_PARAMETER is returned.  If an error occurs attempting to
 | |
|   register the NotifyFunction with the timer interrupt, then EFI_DEVICE_ERROR
 | |
|   is returned.
 | |
| 
 | |
|   @param  This            The EFI_TIMER_ARCH_PROTOCOL instance.
 | |
|   @param  NotifyFunction  The function to call when a timer interrupt fires.
 | |
|                           This function executes at TPL_HIGH_LEVEL.  The DXE
 | |
|                           Core will register a handler for the timer interrupt,
 | |
|                           so it can know how much time has passed.  This
 | |
|                           information is used to signal timer based events.
 | |
|                           NULL will unregister the handler.
 | |
| 
 | |
|   @retval  EFI_SUCCESS            The timer handler was registered.
 | |
|   @retval  EFI_UNSUPPORTED        The platform does not support timer interrupts.
 | |
|   @retval  EFI_ALREADY_STARTED    NotifyFunction is not NULL, and a handler is already
 | |
|                                   registered.
 | |
|   @retval  EFI_INVALID_PARAMETER  NotifyFunction is NULL, and a handler was not
 | |
|                                   previously registered.
 | |
|   @retval  EFI_DEVICE_ERROR       The timer handler could not be registered.
 | |
| 
 | |
| **/
 | |
| EFI_STATUS
 | |
| EFIAPI
 | |
| TimerDriverRegisterHandler (
 | |
|   IN EFI_TIMER_ARCH_PROTOCOL  *This,
 | |
|   IN EFI_TIMER_NOTIFY         NotifyFunction
 | |
|   )
 | |
| {
 | |
|   //
 | |
|   // Check for invalid parameters
 | |
|   //
 | |
|   if ((NotifyFunction == NULL) && (mTimerNotifyFunction == NULL)) {
 | |
|     return EFI_INVALID_PARAMETER;
 | |
|   }
 | |
| 
 | |
|   if ((NotifyFunction != NULL) && (mTimerNotifyFunction != NULL)) {
 | |
|     return EFI_ALREADY_STARTED;
 | |
|   }
 | |
| 
 | |
|   //
 | |
|   // Cache the registered notification function
 | |
|   //
 | |
|   mTimerNotifyFunction = NotifyFunction;
 | |
| 
 | |
|   return EFI_SUCCESS;
 | |
| }
 | |
| 
 | |
| /**
 | |
|   This function adjusts the period of timer interrupts to the value specified
 | |
|   by TimerPeriod.  If the timer period is updated, then the selected timer
 | |
|   period is stored in EFI_TIMER.TimerPeriod, and EFI_SUCCESS is returned.  If
 | |
|   the timer hardware is not programmable, then EFI_UNSUPPORTED is returned.
 | |
|   If an error occurs while attempting to update the timer period, then the
 | |
|   timer hardware will be put back in its state prior to this call, and
 | |
|   EFI_DEVICE_ERROR is returned.  If TimerPeriod is 0, then the timer interrupt
 | |
|   is disabled.  This is not the same as disabling the CPU's interrupts.
 | |
|   Instead, it must either turn off the timer hardware, or it must adjust the
 | |
|   interrupt controller so that a CPU interrupt is not generated when the timer
 | |
|   interrupt fires.
 | |
| 
 | |
|   @param  This         The EFI_TIMER_ARCH_PROTOCOL instance.
 | |
|   @param  TimerPeriod  The rate to program the timer interrupt in 100 nS units.
 | |
|                        If the timer hardware is not programmable, then
 | |
|                        EFI_UNSUPPORTED is returned.  If the timer is programmable,
 | |
|                        then the timer period will be rounded up to the nearest
 | |
|                        timer period that is supported by the timer hardware.
 | |
|                        If TimerPeriod is set to 0, then the timer interrupts
 | |
|                        will be disabled.
 | |
| 
 | |
|   @retval  EFI_SUCCESS       The timer period was changed.
 | |
|   @retval  EFI_UNSUPPORTED   The platform cannot change the period of the timer interrupt.
 | |
|   @retval  EFI_DEVICE_ERROR  The timer period could not be changed due to a device error.
 | |
| 
 | |
| **/
 | |
| EFI_STATUS
 | |
| EFIAPI
 | |
| TimerDriverSetTimerPeriod (
 | |
|   IN EFI_TIMER_ARCH_PROTOCOL  *This,
 | |
|   IN UINT64                   TimerPeriod
 | |
|   )
 | |
| {
 | |
|   EFI_TPL                        Tpl;
 | |
|   UINT64                         MainCounter;
 | |
|   UINT64                         Delta;
 | |
|   UINT64                         CurrentComparator;
 | |
|   HPET_TIMER_MSI_ROUTE_REGISTER  HpetTimerMsiRoute;
 | |
| 
 | |
|   //
 | |
|   // Disable interrupts
 | |
|   //
 | |
|   Tpl = gBS->RaiseTPL (TPL_HIGH_LEVEL);
 | |
| 
 | |
|   //
 | |
|   // Disable HPET timer when adjusting the timer period
 | |
|   //
 | |
|   HpetEnable (FALSE);
 | |
| 
 | |
|   if (TimerPeriod == 0) {
 | |
|     if (mTimerPeriod != 0) {
 | |
|       //
 | |
|       // Check if there is possibly a pending interrupt
 | |
|       //
 | |
|       MainCounter = HpetRead (HPET_MAIN_COUNTER_OFFSET);
 | |
|       if (MainCounter < mPreviousMainCounter) {
 | |
|         Delta = (mCounterMask - mPreviousMainCounter) + MainCounter;
 | |
|       } else {
 | |
|         Delta = MainCounter - mPreviousMainCounter;
 | |
|       }
 | |
| 
 | |
|       if ((Delta & mCounterMask) >= mTimerCount) {
 | |
|         //
 | |
|         // Interrupt still happens after disable HPET, wait to be processed
 | |
|         // Wait until interrupt is processed and comparator is increased
 | |
|         //
 | |
|         CurrentComparator = HpetRead (HPET_TIMER_COMPARATOR_OFFSET + mTimerIndex * HPET_TIMER_STRIDE);
 | |
|         while (CurrentComparator == mPreviousComparator) {
 | |
|           CurrentComparator = HpetRead (HPET_TIMER_COMPARATOR_OFFSET + mTimerIndex * HPET_TIMER_STRIDE);
 | |
|           CpuPause ();
 | |
|         }
 | |
|       }
 | |
|     }
 | |
| 
 | |
|     //
 | |
|     // If TimerPeriod is 0, then mask HPET Timer interrupts
 | |
|     //
 | |
| 
 | |
|     if ((mTimerConfiguration.Bits.MsiInterruptCapability != 0) && FeaturePcdGet (PcdHpetMsiEnable)) {
 | |
|       //
 | |
|       // Disable HPET MSI interrupt generation
 | |
|       //
 | |
|       mTimerConfiguration.Bits.MsiInterruptEnable = 0;
 | |
|     } else {
 | |
|       //
 | |
|       // Disable I/O APIC Interrupt
 | |
|       //
 | |
|       IoApicEnableInterrupt (mTimerIrq, FALSE);
 | |
|     }
 | |
| 
 | |
|     //
 | |
|     // Disable HPET timer interrupt
 | |
|     //
 | |
|     mTimerConfiguration.Bits.InterruptEnable = 0;
 | |
|     HpetWrite (HPET_TIMER_CONFIGURATION_OFFSET + mTimerIndex * HPET_TIMER_STRIDE, mTimerConfiguration.Uint64);
 | |
|   } else {
 | |
|     //
 | |
|     // Convert TimerPeriod to femtoseconds and divide by the number if femtoseconds
 | |
|     // per tick of the HPET counter to determine the number of HPET counter ticks
 | |
|     // in TimerPeriod 100 ns units.
 | |
|     //
 | |
|     mTimerCount = DivU64x32 (
 | |
|                     MultU64x32 (TimerPeriod, 100000000),
 | |
|                     mHpetGeneralCapabilities.Bits.CounterClockPeriod
 | |
|                     );
 | |
| 
 | |
|     //
 | |
|     // Program the HPET Comparator with the number of ticks till the next interrupt
 | |
|     //
 | |
|     MainCounter = HpetRead (HPET_MAIN_COUNTER_OFFSET);
 | |
|     if (MainCounter > mPreviousMainCounter) {
 | |
|       Delta = MainCounter - mPreviousMainCounter;
 | |
|     } else {
 | |
|       Delta = (mCounterMask - mPreviousMainCounter) + MainCounter;
 | |
|     }
 | |
| 
 | |
|     if ((Delta & mCounterMask) >= mTimerCount) {
 | |
|       HpetWrite (HPET_TIMER_COMPARATOR_OFFSET + mTimerIndex * HPET_TIMER_STRIDE, (MainCounter + 1) & mCounterMask);
 | |
|     } else {
 | |
|       HpetWrite (HPET_TIMER_COMPARATOR_OFFSET + mTimerIndex * HPET_TIMER_STRIDE, (mPreviousMainCounter + mTimerCount) & mCounterMask);
 | |
|     }
 | |
| 
 | |
|     //
 | |
|     // Enable HPET Timer interrupt generation
 | |
|     //
 | |
|     if ((mTimerConfiguration.Bits.MsiInterruptCapability != 0) && FeaturePcdGet (PcdHpetMsiEnable)) {
 | |
|       //
 | |
|       // Program MSI Address and MSI Data values in the selected HPET Timer
 | |
|       // Program HPET register with APIC ID of current BSP in case BSP has been switched
 | |
|       //
 | |
|       HpetTimerMsiRoute.Bits.Address = GetApicMsiAddress ();
 | |
|       HpetTimerMsiRoute.Bits.Value   = (UINT32)GetApicMsiValue (PcdGet8 (PcdHpetLocalApicVector), LOCAL_APIC_DELIVERY_MODE_LOWEST_PRIORITY, FALSE, FALSE);
 | |
|       HpetWrite (HPET_TIMER_MSI_ROUTE_OFFSET + mTimerIndex * HPET_TIMER_STRIDE, HpetTimerMsiRoute.Uint64);
 | |
|       //
 | |
|       // Enable HPET MSI Interrupt
 | |
|       //
 | |
|       mTimerConfiguration.Bits.MsiInterruptEnable = 1;
 | |
|     } else {
 | |
|       //
 | |
|       // Enable timer interrupt through I/O APIC
 | |
|       // Program IOAPIC register with APIC ID of current BSP in case BSP has been switched
 | |
|       //
 | |
|       IoApicConfigureInterrupt (mTimerIrq, PcdGet8 (PcdHpetLocalApicVector), IO_APIC_DELIVERY_MODE_LOWEST_PRIORITY, TRUE, FALSE);
 | |
|       IoApicEnableInterrupt (mTimerIrq, TRUE);
 | |
|     }
 | |
| 
 | |
|     //
 | |
|     // Enable HPET Interrupt Generation
 | |
|     //
 | |
|     mTimerConfiguration.Bits.InterruptEnable = 1;
 | |
|     HpetWrite (HPET_TIMER_CONFIGURATION_OFFSET + mTimerIndex * HPET_TIMER_STRIDE, mTimerConfiguration.Uint64);
 | |
|   }
 | |
| 
 | |
|   //
 | |
|   // Save the new timer period
 | |
|   //
 | |
|   mTimerPeriod = TimerPeriod;
 | |
| 
 | |
|   //
 | |
|   // Enable the HPET counter once new timer period has been established
 | |
|   // The HPET counter should run even if the HPET Timer interrupts are
 | |
|   // disabled.  This is used to account for time passed while the interrupt
 | |
|   // is disabled.
 | |
|   //
 | |
|   HpetEnable (TRUE);
 | |
| 
 | |
|   //
 | |
|   // Restore interrupts
 | |
|   //
 | |
|   gBS->RestoreTPL (Tpl);
 | |
| 
 | |
|   return EFI_SUCCESS;
 | |
| }
 | |
| 
 | |
| /**
 | |
|   This function retrieves the period of timer interrupts in 100 ns units,
 | |
|   returns that value in TimerPeriod, and returns EFI_SUCCESS.  If TimerPeriod
 | |
|   is NULL, then EFI_INVALID_PARAMETER is returned.  If a TimerPeriod of 0 is
 | |
|   returned, then the timer is currently disabled.
 | |
| 
 | |
|   @param  This         The EFI_TIMER_ARCH_PROTOCOL instance.
 | |
|   @param  TimerPeriod  A pointer to the timer period to retrieve in 100 ns units.
 | |
|                        If 0 is returned, then the timer is currently disabled.
 | |
| 
 | |
|   @retval  EFI_SUCCESS            The timer period was returned in TimerPeriod.
 | |
|   @retval  EFI_INVALID_PARAMETER  TimerPeriod is NULL.
 | |
| 
 | |
| **/
 | |
| EFI_STATUS
 | |
| EFIAPI
 | |
| TimerDriverGetTimerPeriod (
 | |
|   IN EFI_TIMER_ARCH_PROTOCOL  *This,
 | |
|   OUT UINT64                  *TimerPeriod
 | |
|   )
 | |
| {
 | |
|   if (TimerPeriod == NULL) {
 | |
|     return EFI_INVALID_PARAMETER;
 | |
|   }
 | |
| 
 | |
|   *TimerPeriod = mTimerPeriod;
 | |
| 
 | |
|   return EFI_SUCCESS;
 | |
| }
 | |
| 
 | |
| /**
 | |
|   This function generates a soft timer interrupt. If the platform does not support soft
 | |
|   timer interrupts, then EFI_UNSUPPORTED is returned. Otherwise, EFI_SUCCESS is returned.
 | |
|   If a handler has been registered through the EFI_TIMER_ARCH_PROTOCOL.RegisterHandler()
 | |
|   service, then a soft timer interrupt will be generated. If the timer interrupt is
 | |
|   enabled when this service is called, then the registered handler will be invoked. The
 | |
|   registered handler should not be able to distinguish a hardware-generated timer
 | |
|   interrupt from a software-generated timer interrupt.
 | |
| 
 | |
|   @param  This  The EFI_TIMER_ARCH_PROTOCOL instance.
 | |
| 
 | |
|   @retval  EFI_SUCCESS       The soft timer interrupt was generated.
 | |
|   @retval  EFI_UNSUPPORTED   The platform does not support the generation of soft
 | |
|                              timer interrupts.
 | |
| 
 | |
| **/
 | |
| EFI_STATUS
 | |
| EFIAPI
 | |
| TimerDriverGenerateSoftInterrupt (
 | |
|   IN EFI_TIMER_ARCH_PROTOCOL  *This
 | |
|   )
 | |
| {
 | |
|   UINT64   MainCounter;
 | |
|   EFI_TPL  Tpl;
 | |
|   UINT64   TimerPeriod;
 | |
|   UINT64   Delta;
 | |
| 
 | |
|   //
 | |
|   // Disable interrupts
 | |
|   //
 | |
|   Tpl = gBS->RaiseTPL (TPL_HIGH_LEVEL);
 | |
| 
 | |
|   //
 | |
|   // Capture main counter value
 | |
|   //
 | |
|   MainCounter = HpetRead (HPET_MAIN_COUNTER_OFFSET);
 | |
| 
 | |
|   //
 | |
|   // Check to see if there is a registered notification function
 | |
|   //
 | |
|   if (mTimerNotifyFunction != NULL) {
 | |
|     //
 | |
|     // Compute time since last interrupt in 100 ns units (10 ^ -7)
 | |
|     //
 | |
|     if (MainCounter > mPreviousMainCounter) {
 | |
|       //
 | |
|       // Main counter does not overflow
 | |
|       //
 | |
|       Delta = MainCounter - mPreviousMainCounter;
 | |
|     } else {
 | |
|       //
 | |
|       // Main counter overflows, first usb, then add
 | |
|       //
 | |
|       Delta = (mCounterMask - mPreviousMainCounter) + MainCounter;
 | |
|     }
 | |
| 
 | |
|     TimerPeriod = DivU64x32 (
 | |
|                     MultU64x32 (
 | |
|                       Delta & mCounterMask,
 | |
|                       mHpetGeneralCapabilities.Bits.CounterClockPeriod
 | |
|                       ),
 | |
|                     100000000
 | |
|                     );
 | |
| 
 | |
|     //
 | |
|     // Call registered notification function passing in the time since the last
 | |
|     // interrupt in 100 ns units.
 | |
|     //
 | |
|     mTimerNotifyFunction (TimerPeriod);
 | |
|   }
 | |
| 
 | |
|   //
 | |
|   // Save main counter value
 | |
|   //
 | |
|   mPreviousMainCounter = MainCounter;
 | |
| 
 | |
|   //
 | |
|   // Restore interrupts
 | |
|   //
 | |
|   gBS->RestoreTPL (Tpl);
 | |
| 
 | |
|   return EFI_SUCCESS;
 | |
| }
 | |
| 
 | |
| /**
 | |
|   Initialize the Timer Architectural Protocol driver
 | |
| 
 | |
|   @param  ImageHandle  ImageHandle of the loaded driver
 | |
|   @param  SystemTable  Pointer to the System Table
 | |
| 
 | |
|   @retval  EFI_SUCCESS           Timer Architectural Protocol created
 | |
|   @retval  EFI_OUT_OF_RESOURCES  Not enough resources available to initialize driver.
 | |
|   @retval  EFI_DEVICE_ERROR      A device error occurred attempting to initialize the driver.
 | |
| 
 | |
| **/
 | |
| EFI_STATUS
 | |
| EFIAPI
 | |
| TimerDriverInitialize (
 | |
|   IN EFI_HANDLE        ImageHandle,
 | |
|   IN EFI_SYSTEM_TABLE  *SystemTable
 | |
|   )
 | |
| {
 | |
|   EFI_STATUS                     Status;
 | |
|   UINTN                          TimerIndex;
 | |
|   UINTN                          MsiTimerIndex;
 | |
|   HPET_TIMER_MSI_ROUTE_REGISTER  HpetTimerMsiRoute;
 | |
| 
 | |
|   DEBUG ((DEBUG_INFO, "Init HPET Timer Driver\n"));
 | |
| 
 | |
|   //
 | |
|   // Make sure the Timer Architectural Protocol is not already installed in the system
 | |
|   //
 | |
|   ASSERT_PROTOCOL_ALREADY_INSTALLED (NULL, &gEfiTimerArchProtocolGuid);
 | |
| 
 | |
|   //
 | |
|   // Find the CPU architectural protocol.
 | |
|   //
 | |
|   Status = gBS->LocateProtocol (&gEfiCpuArchProtocolGuid, NULL, (VOID **)&mCpu);
 | |
|   ASSERT_EFI_ERROR (Status);
 | |
| 
 | |
|   //
 | |
|   // Retrieve HPET Capabilities and Configuration Information
 | |
|   //
 | |
|   mHpetGeneralCapabilities.Uint64  = HpetRead (HPET_GENERAL_CAPABILITIES_ID_OFFSET);
 | |
|   mHpetGeneralConfiguration.Uint64 = HpetRead (HPET_GENERAL_CONFIGURATION_OFFSET);
 | |
| 
 | |
|   //
 | |
|   // If Revision is not valid, then ASSERT() and unload the driver because the HPET
 | |
|   // device is not present.
 | |
|   //
 | |
|   ASSERT (mHpetGeneralCapabilities.Uint64 != 0);
 | |
|   ASSERT (mHpetGeneralCapabilities.Uint64 != 0xFFFFFFFFFFFFFFFFULL);
 | |
|   if ((mHpetGeneralCapabilities.Uint64 == 0) || (mHpetGeneralCapabilities.Uint64 == 0xFFFFFFFFFFFFFFFFULL)) {
 | |
|     DEBUG ((DEBUG_ERROR, "HPET device is not present.  Unload HPET driver.\n"));
 | |
|     return EFI_DEVICE_ERROR;
 | |
|   }
 | |
| 
 | |
|   //
 | |
|   // Force the HPET timer to be disabled while setting everything up
 | |
|   //
 | |
|   HpetEnable (FALSE);
 | |
| 
 | |
|   //
 | |
|   // Dump HPET Configuration Information
 | |
|   //
 | |
|   DEBUG_CODE_BEGIN ();
 | |
|   DEBUG ((DEBUG_INFO, "HPET Base Address = 0x%08x\n", PcdGet32 (PcdHpetBaseAddress)));
 | |
|   DEBUG ((DEBUG_INFO, "  HPET_GENERAL_CAPABILITIES_ID  = 0x%016lx\n", mHpetGeneralCapabilities));
 | |
|   DEBUG ((DEBUG_INFO, "  HPET_GENERAL_CONFIGURATION    = 0x%016lx\n", mHpetGeneralConfiguration.Uint64));
 | |
|   DEBUG ((DEBUG_INFO, "  HPET_GENERAL_INTERRUPT_STATUS = 0x%016lx\n", HpetRead (HPET_GENERAL_INTERRUPT_STATUS_OFFSET)));
 | |
|   DEBUG ((DEBUG_INFO, "  HPET_MAIN_COUNTER             = 0x%016lx\n", HpetRead (HPET_MAIN_COUNTER_OFFSET)));
 | |
|   DEBUG ((DEBUG_INFO, "  HPET Main Counter Period      = %d (fs)\n", mHpetGeneralCapabilities.Bits.CounterClockPeriod));
 | |
|   for (TimerIndex = 0; TimerIndex <= mHpetGeneralCapabilities.Bits.NumberOfTimers; TimerIndex++) {
 | |
|     DEBUG ((DEBUG_INFO, "  HPET_TIMER%d_CONFIGURATION     = 0x%016lx\n", TimerIndex, HpetRead (HPET_TIMER_CONFIGURATION_OFFSET + TimerIndex * HPET_TIMER_STRIDE)));
 | |
|     DEBUG ((DEBUG_INFO, "  HPET_TIMER%d_COMPARATOR        = 0x%016lx\n", TimerIndex, HpetRead (HPET_TIMER_COMPARATOR_OFFSET    + TimerIndex * HPET_TIMER_STRIDE)));
 | |
|     DEBUG ((DEBUG_INFO, "  HPET_TIMER%d_MSI_ROUTE         = 0x%016lx\n", TimerIndex, HpetRead (HPET_TIMER_MSI_ROUTE_OFFSET     + TimerIndex * HPET_TIMER_STRIDE)));
 | |
|   }
 | |
| 
 | |
|   DEBUG_CODE_END ();
 | |
| 
 | |
|   //
 | |
|   // Capture the current HPET main counter value.
 | |
|   //
 | |
|   mPreviousMainCounter = HpetRead (HPET_MAIN_COUNTER_OFFSET);
 | |
| 
 | |
|   //
 | |
|   // Determine the interrupt mode to use for the HPET Timer.
 | |
|   // Look for MSI first, then unused PIC mode interrupt, then I/O APIC mode interrupt
 | |
|   //
 | |
|   MsiTimerIndex = HPET_INVALID_TIMER_INDEX;
 | |
|   mTimerIndex   = HPET_INVALID_TIMER_INDEX;
 | |
|   for (TimerIndex = 0; TimerIndex <= mHpetGeneralCapabilities.Bits.NumberOfTimers; TimerIndex++) {
 | |
|     //
 | |
|     // Read the HPET Timer Capabilities and Configuration register
 | |
|     //
 | |
|     mTimerConfiguration.Uint64 = HpetRead (HPET_TIMER_CONFIGURATION_OFFSET + TimerIndex * HPET_TIMER_STRIDE);
 | |
| 
 | |
|     //
 | |
|     // Check to see if this HPET Timer supports MSI
 | |
|     //
 | |
|     if (mTimerConfiguration.Bits.MsiInterruptCapability != 0) {
 | |
|       //
 | |
|       // Save the index of the first HPET Timer that supports MSI interrupts
 | |
|       //
 | |
|       if (MsiTimerIndex == HPET_INVALID_TIMER_INDEX) {
 | |
|         MsiTimerIndex = TimerIndex;
 | |
|       }
 | |
|     }
 | |
| 
 | |
|     //
 | |
|     // Check to see if this HPET Timer supports I/O APIC interrupts
 | |
|     //
 | |
|     if (mTimerConfiguration.Bits.InterruptRouteCapability != 0) {
 | |
|       //
 | |
|       // Save the index of the first HPET Timer that supports I/O APIC interrupts
 | |
|       //
 | |
|       if (mTimerIndex == HPET_INVALID_TIMER_INDEX) {
 | |
|         mTimerIndex = TimerIndex;
 | |
|         mTimerIrq   = (UINT32)LowBitSet32 (mTimerConfiguration.Bits.InterruptRouteCapability);
 | |
|       }
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   if (FeaturePcdGet (PcdHpetMsiEnable) && (MsiTimerIndex != HPET_INVALID_TIMER_INDEX)) {
 | |
|     //
 | |
|     // Use MSI interrupt if supported
 | |
|     //
 | |
|     mTimerIndex = MsiTimerIndex;
 | |
| 
 | |
|     //
 | |
|     // Program MSI Address and MSI Data values in the selected HPET Timer
 | |
|     //
 | |
|     HpetTimerMsiRoute.Bits.Address = GetApicMsiAddress ();
 | |
|     HpetTimerMsiRoute.Bits.Value   = (UINT32)GetApicMsiValue (PcdGet8 (PcdHpetLocalApicVector), LOCAL_APIC_DELIVERY_MODE_LOWEST_PRIORITY, FALSE, FALSE);
 | |
|     HpetWrite (HPET_TIMER_MSI_ROUTE_OFFSET + mTimerIndex * HPET_TIMER_STRIDE, HpetTimerMsiRoute.Uint64);
 | |
| 
 | |
|     //
 | |
|     // Read the HPET Timer Capabilities and Configuration register and initialize for MSI mode
 | |
|     //   Clear LevelTriggeredInterrupt to use edge triggered interrupts when in MSI mode
 | |
|     //
 | |
|     mTimerConfiguration.Uint64                       = HpetRead (HPET_TIMER_CONFIGURATION_OFFSET + mTimerIndex * HPET_TIMER_STRIDE);
 | |
|     mTimerConfiguration.Bits.LevelTriggeredInterrupt = 0;
 | |
|   } else {
 | |
|     //
 | |
|     // If no HPET timers support MSI or I/O APIC modes, then ASSERT() and unload the driver.
 | |
|     //
 | |
|     ASSERT (mTimerIndex != HPET_INVALID_TIMER_INDEX);
 | |
|     if (mTimerIndex == HPET_INVALID_TIMER_INDEX) {
 | |
|       DEBUG ((DEBUG_ERROR, "No HPET timers support MSI or I/O APIC mode.  Unload HPET driver.\n"));
 | |
|       return EFI_DEVICE_ERROR;
 | |
|     }
 | |
| 
 | |
|     //
 | |
|     // Initialize I/O APIC entry for HPET Timer Interrupt
 | |
|     //   Fixed Delivery Mode, Level Triggered, Asserted Low
 | |
|     //
 | |
|     IoApicConfigureInterrupt (mTimerIrq, PcdGet8 (PcdHpetLocalApicVector), IO_APIC_DELIVERY_MODE_LOWEST_PRIORITY, TRUE, FALSE);
 | |
| 
 | |
|     //
 | |
|     // Read the HPET Timer Capabilities and Configuration register and initialize for I/O APIC mode
 | |
|     //   Clear MsiInterruptCapability to force rest of driver to use I/O APIC mode
 | |
|     //   Set LevelTriggeredInterrupt to use level triggered interrupts when in I/O APIC mode
 | |
|     //   Set InterruptRoute field based in mTimerIrq
 | |
|     //
 | |
|     mTimerConfiguration.Uint64                       = HpetRead (HPET_TIMER_CONFIGURATION_OFFSET + mTimerIndex * HPET_TIMER_STRIDE);
 | |
|     mTimerConfiguration.Bits.LevelTriggeredInterrupt = 1;
 | |
|     mTimerConfiguration.Bits.InterruptRoute          = mTimerIrq;
 | |
|   }
 | |
| 
 | |
|   //
 | |
|   // Configure the selected HPET Timer with settings common to both MSI mode and I/O APIC mode
 | |
|   //   Clear InterruptEnable to keep interrupts disabled until full init is complete
 | |
|   //   Clear PeriodicInterruptEnable to use one-shot mode
 | |
|   //   Configure as a 32-bit counter
 | |
|   //
 | |
|   mTimerConfiguration.Bits.InterruptEnable         = 0;
 | |
|   mTimerConfiguration.Bits.PeriodicInterruptEnable = 0;
 | |
|   mTimerConfiguration.Bits.CounterSizeEnable       = 1;
 | |
|   HpetWrite (HPET_TIMER_CONFIGURATION_OFFSET + mTimerIndex * HPET_TIMER_STRIDE, mTimerConfiguration.Uint64);
 | |
| 
 | |
|   //
 | |
|   // Read the HPET Timer Capabilities and Configuration register back again.
 | |
|   // CounterSizeEnable will be read back as a 0 if it is a 32-bit only timer
 | |
|   //
 | |
|   mTimerConfiguration.Uint64 = HpetRead (HPET_TIMER_CONFIGURATION_OFFSET + mTimerIndex * HPET_TIMER_STRIDE);
 | |
|   if ((mTimerConfiguration.Bits.CounterSizeEnable == 1) && (sizeof (UINTN) == sizeof (UINT64))) {
 | |
|     DEBUG ((DEBUG_INFO, "Choose 64-bit HPET timer.\n"));
 | |
|     //
 | |
|     // 64-bit BIOS can use 64-bit HPET timer
 | |
|     //
 | |
|     mCounterMask = 0xffffffffffffffffULL;
 | |
|     //
 | |
|     // Set timer back to 64-bit
 | |
|     //
 | |
|     mTimerConfiguration.Bits.CounterSizeEnable = 0;
 | |
|     HpetWrite (HPET_TIMER_CONFIGURATION_OFFSET + mTimerIndex * HPET_TIMER_STRIDE, mTimerConfiguration.Uint64);
 | |
|   } else {
 | |
|     DEBUG ((DEBUG_INFO, "Choose 32-bit HPET timer.\n"));
 | |
|     mCounterMask = 0x00000000ffffffffULL;
 | |
|   }
 | |
| 
 | |
|   //
 | |
|   // Install interrupt handler for selected HPET Timer
 | |
|   //
 | |
|   Status = mCpu->RegisterInterruptHandler (mCpu, PcdGet8 (PcdHpetLocalApicVector), TimerInterruptHandler);
 | |
|   ASSERT_EFI_ERROR (Status);
 | |
|   if (EFI_ERROR (Status)) {
 | |
|     DEBUG ((DEBUG_ERROR, "Unable to register HPET interrupt with CPU Arch Protocol.  Unload HPET driver.\n"));
 | |
|     return EFI_DEVICE_ERROR;
 | |
|   }
 | |
| 
 | |
|   //
 | |
|   // Force the HPET Timer to be enabled at its default period
 | |
|   //
 | |
|   Status = TimerDriverSetTimerPeriod (&mTimer, PcdGet64 (PcdHpetDefaultTimerPeriod));
 | |
|   ASSERT_EFI_ERROR (Status);
 | |
|   if (EFI_ERROR (Status)) {
 | |
|     DEBUG ((DEBUG_ERROR, "Unable to set HPET default timer rate.  Unload HPET driver.\n"));
 | |
|     return EFI_DEVICE_ERROR;
 | |
|   }
 | |
| 
 | |
|   //
 | |
|   // Show state of enabled HPET timer
 | |
|   //
 | |
|   DEBUG_CODE_BEGIN ();
 | |
|   if ((mTimerConfiguration.Bits.MsiInterruptCapability != 0) && FeaturePcdGet (PcdHpetMsiEnable)) {
 | |
|     DEBUG ((DEBUG_INFO, "HPET Interrupt Mode MSI\n"));
 | |
|   } else {
 | |
|     DEBUG ((DEBUG_INFO, "HPET Interrupt Mode I/O APIC\n"));
 | |
|     DEBUG ((DEBUG_INFO, "HPET I/O APIC IRQ         = 0x%02x\n", mTimerIrq));
 | |
|   }
 | |
| 
 | |
|   DEBUG ((DEBUG_INFO, "HPET Interrupt Vector     = 0x%02x\n", PcdGet8 (PcdHpetLocalApicVector)));
 | |
|   DEBUG ((DEBUG_INFO, "HPET Counter Mask         = 0x%016lx\n", mCounterMask));
 | |
|   DEBUG ((DEBUG_INFO, "HPET Timer Period         = %d\n", mTimerPeriod));
 | |
|   DEBUG ((DEBUG_INFO, "HPET Timer Count          = 0x%016lx\n", mTimerCount));
 | |
|   DEBUG ((DEBUG_INFO, "HPET_TIMER%d_CONFIGURATION = 0x%016lx\n", mTimerIndex, HpetRead (HPET_TIMER_CONFIGURATION_OFFSET + mTimerIndex * HPET_TIMER_STRIDE)));
 | |
|   DEBUG ((DEBUG_INFO, "HPET_TIMER%d_COMPARATOR    = 0x%016lx\n", mTimerIndex, HpetRead (HPET_TIMER_COMPARATOR_OFFSET    + mTimerIndex * HPET_TIMER_STRIDE)));
 | |
|   DEBUG ((DEBUG_INFO, "HPET_TIMER%d_MSI_ROUTE     = 0x%016lx\n", mTimerIndex, HpetRead (HPET_TIMER_MSI_ROUTE_OFFSET     + mTimerIndex * HPET_TIMER_STRIDE)));
 | |
| 
 | |
|   //
 | |
|   // Wait for a few timer interrupts to fire before continuing
 | |
|   //
 | |
|   while (mNumTicks < 10) {
 | |
|   }
 | |
| 
 | |
|   DEBUG_CODE_END ();
 | |
| 
 | |
|   //
 | |
|   // Install the Timer Architectural Protocol onto a new handle
 | |
|   //
 | |
|   Status = gBS->InstallMultipleProtocolInterfaces (
 | |
|                   &mTimerHandle,
 | |
|                   &gEfiTimerArchProtocolGuid,
 | |
|                   &mTimer,
 | |
|                   NULL
 | |
|                   );
 | |
|   ASSERT_EFI_ERROR (Status);
 | |
| 
 | |
|   return Status;
 | |
| }
 |