hikey: Add UEFI sources for reference

UEFI needs to be built outside Android build system.
Please follow the instructions in README.

The sources correspond to:
https://github.com/96boards/edk2/commit/14eae0c12e71fd33c4c0fc51e4475e8db02566cf
https://github.com/96boards/arm-trusted-firmware/commit/e9b4909dcd75fc4ae7041cfb83d28ab9adb7afdf
https://github.com/96boards/l-loader/commit/6b784ad5c4ab00e2b1c6f53cd5f74054e5d00a78
https://git.linaro.org/uefi/uefi-tools.git/commit/abe618f8ab72034fff1ce46c9c006a2c6bd40a7e

Change-Id: Ieeefdb63e673e0c8e64e0a1f02c7bddc63b2c7fb
Signed-off-by: Vishal Bhoj <vishal.bhoj@linaro.org>
diff --git a/uefi/linaro-edk2/PcAtChipsetPkg/HpetTimerDxe/HpetTimer.c b/uefi/linaro-edk2/PcAtChipsetPkg/HpetTimerDxe/HpetTimer.c
new file mode 100644
index 0000000..e23a2c8
--- /dev/null
+++ b/uefi/linaro-edk2/PcAtChipsetPkg/HpetTimerDxe/HpetTimer.c
@@ -0,0 +1,988 @@
+/** @file

+  Timer Architectural Protocol module using High Precesion Event Timer (HPET)

+

+  Copyright (c) 2011 - 2014, Intel Corporation. All rights reserved.<BR>

+  This program and the accompanying materials

+  are licensed and made available under the terms and conditions of the BSD License

+  which accompanies this distribution.  The full text of the license may be found at

+  http://opensource.org/licenses/bsd-license.php

+

+  THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,

+  WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.

+

+**/

+

+#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_UNSUPPORTEDT  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 ofsfert 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 occured.

+  @param  SystemContext  A pointer to the system context when the interrupt occured.

+**/

+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

+  )

+{

+  UINT64                         MainCounter;

+  UINT64                         Delta;

+  UINT64                         CurrentComparator;

+  HPET_TIMER_MSI_ROUTE_REGISTER  HpetTimerMsiRoute;

+  

+  //

+  // 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.MsiInterruptCapablity != 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.MsiInterruptCapablity != 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);

+  

+  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_UNSUPPORTEDT  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 occured 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 (

+    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)));

+    }

+  );

+  

+  //

+  // 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.MsiInterruptCapablity != 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 (

+    if (mTimerConfiguration.Bits.MsiInterruptCapablity != 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);

+  );

+ 

+  //

+  // Install the Timer Architectural Protocol onto a new handle

+  //

+  Status = gBS->InstallMultipleProtocolInterfaces (

+                  &mTimerHandle,

+                  &gEfiTimerArchProtocolGuid, &mTimer,

+                  NULL

+                  );

+  ASSERT_EFI_ERROR (Status);

+

+  return Status;

+}