Add generic HPET Timer DXE Driver and support libraries
authormdkinney <mdkinney@6f19259b-4bc3-4df7-8a09-765794883524>
Fri, 2 Sep 2011 02:43:51 +0000 (02:43 +0000)
committermdkinney <mdkinney@6f19259b-4bc3-4df7-8a09-765794883524>
Fri, 2 Sep 2011 02:43:51 +0000 (02:43 +0000)
Signed-off-by: mdkinney
Reviewed-by: li-elvin
git-svn-id: https://edk2.svn.sourceforge.net/svnroot/edk2/trunk/edk2@12260 6f19259b-4bc3-4df7-8a09-765794883524

PcAtChipsetPkg/HpetTimerDxe/HpetTimer.c [new file with mode: 0644]
PcAtChipsetPkg/HpetTimerDxe/HpetTimerDxe.inf [new file with mode: 0644]
PcAtChipsetPkg/Include/Library/IoApicLib.h [new file with mode: 0644]
PcAtChipsetPkg/Include/Register/Hpet.h [new file with mode: 0644]
PcAtChipsetPkg/Include/Register/IoApic.h [new file with mode: 0644]
PcAtChipsetPkg/Library/BaseIoApicLib/BaseIoApicLib.inf [new file with mode: 0644]
PcAtChipsetPkg/Library/BaseIoApicLib/IoApicLib.c [new file with mode: 0644]
PcAtChipsetPkg/PcAtChipsetPkg.dec
PcAtChipsetPkg/PcAtChipsetPkg.dsc

diff --git a/PcAtChipsetPkg/HpetTimerDxe/HpetTimer.c b/PcAtChipsetPkg/HpetTimerDxe/HpetTimer.c
new file mode 100644 (file)
index 0000000..025d504
--- /dev/null
@@ -0,0 +1,860 @@
+/** @file\r
+  Timer Architectural Protocol module using High Precesion Event Timer (HPET)\r
+\r
+  Copyright (c) 2011, Intel Corporation. All rights reserved.<BR>\r
+  This program and the accompanying materials\r
+  are licensed and made available under the terms and conditions of the BSD License\r
+  which accompanies this distribution.  The full text of the license may be found at\r
+  http://opensource.org/licenses/bsd-license.php\r
+\r
+  THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,\r
+  WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.\r
+\r
+**/\r
+\r
+#include <PiDxe.h>\r
+\r
+#include <Protocol/Cpu.h>\r
+#include <Protocol/Timer.h>\r
+\r
+#include <Library/IoLib.h>\r
+#include <Library/PcdLib.h>\r
+#include <Library/BaseLib.h>\r
+#include <Library/DebugLib.h>\r
+#include <Library/UefiBootServicesTableLib.h>\r
+#include <Library/LocalApicLib.h>\r
+#include <Library/IoApicLib.h>\r
+\r
+#include <Register/LocalApic.h>\r
+#include <Register/IoApic.h>\r
+#include <Register/Hpet.h>\r
+\r
+///\r
+/// Define value for an invalid HPET Timer index.\r
+///\r
+#define HPET_INVALID_TIMER_INDEX  0xff\r
+\r
+///\r
+/// Timer Architectural Protocol function prototypes.\r
+///\r
+\r
+/**\r
+  This function registers the handler NotifyFunction so it is called every time\r
+  the timer interrupt fires.  It also passes the amount of time since the last\r
+  handler call to the NotifyFunction.  If NotifyFunction is NULL, then the\r
+  handler is unregistered.  If the handler is registered, then EFI_SUCCESS is\r
+  returned.  If the CPU does not support registering a timer interrupt handler,\r
+  then EFI_UNSUPPORTED is returned.  If an attempt is made to register a handler\r
+  when a handler is already registered, then EFI_ALREADY_STARTED is returned.\r
+  If an attempt is made to unregister a handler when a handler is not registered,\r
+  then EFI_INVALID_PARAMETER is returned.  If an error occurs attempting to\r
+  register the NotifyFunction with the timer interrupt, then EFI_DEVICE_ERROR\r
+  is returned.\r
+\r
+  @param  This            The EFI_TIMER_ARCH_PROTOCOL instance.\r
+  @param  NotifyFunction  The function to call when a timer interrupt fires.  \r
+                          This function executes at TPL_HIGH_LEVEL.  The DXE \r
+                          Core will register a handler for the timer interrupt, \r
+                          so it can know how much time has passed.  This \r
+                          information is used to signal timer based events.  \r
+                          NULL will unregister the handler.\r
+\r
+  @retval  EFI_SUCCESS            The timer handler was registered.\r
+  @retval  EFI_UNSUPPORTED        The platform does not support timer interrupts.\r
+  @retval  EFI_ALREADY_STARTED    NotifyFunction is not NULL, and a handler is already\r
+                                  registered.\r
+  @retval  EFI_INVALID_PARAMETER  NotifyFunction is NULL, and a handler was not\r
+                                  previously registered.\r
+  @retval  EFI_DEVICE_ERROR       The timer handler could not be registered.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+TimerDriverRegisterHandler (\r
+  IN EFI_TIMER_ARCH_PROTOCOL  *This,\r
+  IN EFI_TIMER_NOTIFY         NotifyFunction\r
+  );\r
+\r
+/**\r
+  This function adjusts the period of timer interrupts to the value specified\r
+  by TimerPeriod.  If the timer period is updated, then the selected timer\r
+  period is stored in EFI_TIMER.TimerPeriod, and EFI_SUCCESS is returned.  If\r
+  the timer hardware is not programmable, then EFI_UNSUPPORTED is returned.\r
+  If an error occurs while attempting to update the timer period, then the\r
+  timer hardware will be put back in its state prior to this call, and\r
+  EFI_DEVICE_ERROR is returned.  If TimerPeriod is 0, then the timer interrupt\r
+  is disabled.  This is not the same as disabling the CPU's interrupts.\r
+  Instead, it must either turn off the timer hardware, or it must adjust the\r
+  interrupt controller so that a CPU interrupt is not generated when the timer\r
+  interrupt fires.\r
+\r
+  @param  This         The EFI_TIMER_ARCH_PROTOCOL instance.\r
+  @param  TimerPeriod  The rate to program the timer interrupt in 100 nS units.\r
+                       If the timer hardware is not programmable, then \r
+                       EFI_UNSUPPORTED is returned.  If the timer is programmable, \r
+                       then the timer period will be rounded up to the nearest \r
+                       timer period that is supported by the timer hardware.  \r
+                       If TimerPeriod is set to 0, then the timer interrupts \r
+                       will be disabled.\r
+\r
+  @retval  EFI_SUCCESS       The timer period was changed.\r
+  @retval  EFI_UNSUPPORTED   The platform cannot change the period of the timer interrupt.\r
+  @retval  EFI_DEVICE_ERROR  The timer period could not be changed due to a device error.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+TimerDriverSetTimerPeriod (\r
+  IN EFI_TIMER_ARCH_PROTOCOL  *This,\r
+  IN UINT64                   TimerPeriod\r
+  );\r
+\r
+/**\r
+  This function retrieves the period of timer interrupts in 100 ns units,\r
+  returns that value in TimerPeriod, and returns EFI_SUCCESS.  If TimerPeriod\r
+  is NULL, then EFI_INVALID_PARAMETER is returned.  If a TimerPeriod of 0 is\r
+  returned, then the timer is currently disabled.\r
+\r
+  @param  This         The EFI_TIMER_ARCH_PROTOCOL instance.\r
+  @param  TimerPeriod  A pointer to the timer period to retrieve in 100 ns units.\r
+                       If 0 is returned, then the timer is currently disabled.\r
+\r
+  @retval  EFI_SUCCESS            The timer period was returned in TimerPeriod.\r
+  @retval  EFI_INVALID_PARAMETER  TimerPeriod is NULL.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+TimerDriverGetTimerPeriod (\r
+  IN EFI_TIMER_ARCH_PROTOCOL   *This,\r
+  OUT UINT64                   *TimerPeriod\r
+  );\r
+\r
+/**\r
+  This function generates a soft timer interrupt. If the platform does not support soft\r
+  timer interrupts, then EFI_UNSUPPORTED is returned. Otherwise, EFI_SUCCESS is returned.\r
+  If a handler has been registered through the EFI_TIMER_ARCH_PROTOCOL.RegisterHandler()\r
+  service, then a soft timer interrupt will be generated. If the timer interrupt is\r
+  enabled when this service is called, then the registered handler will be invoked. The\r
+  registered handler should not be able to distinguish a hardware-generated timer\r
+  interrupt from a software-generated timer interrupt.\r
+\r
+  @param  This  The EFI_TIMER_ARCH_PROTOCOL instance.\r
+\r
+  @retval  EFI_SUCCESS       The soft timer interrupt was generated.\r
+  @retval  EFI_UNSUPPORTEDT  The platform does not support the generation of soft \r
+                             timer interrupts.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+TimerDriverGenerateSoftInterrupt (\r
+  IN EFI_TIMER_ARCH_PROTOCOL  *This\r
+  );\r
+  \r
+///\r
+/// The handle onto which the Timer Architectural Protocol will be installed.\r
+///\r
+EFI_HANDLE   mTimerHandle = NULL;\r
+\r
+///\r
+/// The Timer Architectural Protocol that this driver produces.\r
+///\r
+EFI_TIMER_ARCH_PROTOCOL  mTimer = {\r
+  TimerDriverRegisterHandler,\r
+  TimerDriverSetTimerPeriod,\r
+  TimerDriverGetTimerPeriod,\r
+  TimerDriverGenerateSoftInterrupt\r
+};\r
+\r
+///\r
+/// Pointer to the CPU Architectural Protocol instance.\r
+///\r
+EFI_CPU_ARCH_PROTOCOL  *mCpu = NULL;\r
+\r
+///\r
+/// The notification function to call on every timer interrupt.\r
+///\r
+EFI_TIMER_NOTIFY  mTimerNotifyFunction = NULL;\r
+\r
+///\r
+/// The current period of the HPET timer interrupt in 100 ns units.\r
+///\r
+UINT64  mTimerPeriod = 0;\r
+\r
+///\r
+/// Accumulates HPET timer ticks to account for time passed when the \r
+/// HPET timer is disabled or when there is no timer notification function\r
+/// registered.\r
+///\r
+volatile UINT64  mTimerAccumulator = 0;\r
+\r
+///\r
+/// The index of the HPET timer being managed by this driver.\r
+///\r
+UINTN  mTimerIndex;\r
+\r
+///\r
+/// The I/O APIC IRQ that the HPET Timer is mapped if I/O APIC mode is used.\r
+///\r
+UINT32  mTimerIrq;\r
+\r
+///\r
+/// Cached state of the HPET General Capabilities register managed by this driver.\r
+/// Caching the state reduces the number of times the configuration register is read.\r
+///\r
+HPET_GENERAL_CAPABILITIES_ID_REGISTER  mHpetGeneralCapabilities;\r
+\r
+///\r
+/// Cached state of the HPET General Configuration register managed by this driver.\r
+/// Caching the state reduces the number of times the configuration register is read.\r
+///\r
+HPET_GENERAL_CONFIGURATION_REGISTER  mHpetGeneralConfiguration;\r
+\r
+///\r
+/// Cached state of the Configuration register for the HPET Timer managed by \r
+/// this driver.  Caching the state reduces the number of times the configuration\r
+/// register is read.\r
+///\r
+HPET_TIMER_CONFIGURATION_REGISTER  mTimerConfiguration;\r
+\r
+///\r
+/// Counts the number of HPET Timer interrupts processed by this driver.\r
+/// Only required for debug.\r
+///\r
+volatile UINTN  mNumTicks;\r
+\r
+/**\r
+  Read a 64-bit register from the HPET\r
+\r
+  @param  Offset  Specifies the offset of the HPET register to read.\r
+\r
+  @return  The 64-bit value read from the HPET register specified by Offset.\r
+**/\r
+UINT64\r
+HpetRead (\r
+  IN UINTN  Offset\r
+  )\r
+{\r
+  return MmioRead64 (PcdGet32 (PcdHpetBaseAddress) + Offset);\r
+}\r
+\r
+/**\r
+  Write a 64-bit HPET register.\r
+\r
+  @param  Offset  Specifies the ofsfert of the HPET register to write.\r
+  @param  Value   Specifies the value to write to the HPET register specified by Offset.\r
+\r
+  @return  The 64-bit value written to HPET register specified by Offset.\r
+**/\r
+UINT64\r
+HpetWrite (\r
+  IN UINTN   Offset,\r
+  IN UINT64  Value\r
+  )\r
+{\r
+  return MmioWrite64 (PcdGet32 (PcdHpetBaseAddress) + Offset, Value);\r
+}\r
+\r
+/**\r
+  Enable or disable the main counter in the HPET Timer.\r
+\r
+  @param  Enable  If TRUE, then enable the main counter in the HPET Timer.\r
+                  If FALSE, then disable the main counter in the HPET Timer.\r
+**/\r
+VOID\r
+HpetEnable (\r
+  IN BOOLEAN  Enable\r
+  )\r
+{\r
+  mHpetGeneralConfiguration.Bits.MainCounterEnable = Enable ? 1 : 0;  \r
+  HpetWrite (HPET_GENERAL_CONFIGURATION_OFFSET, mHpetGeneralConfiguration.Uint64);\r
+}\r
+\r
+/**\r
+  The interrupt handler for the HPET timer.  This handler clears the HPET interrupt\r
+  and computes the amount of time that has passed since the last HPET timer interrupt.\r
+  If a notification function is registered, then the amount of time since the last\r
+  HPET interrupt is passed to that notification function in 100 ns units.  The HPET\r
+  time is updated to generate another interrupt in the required time period. \r
+\r
+  @param  InterruptType  The type of interrupt that occured.\r
+  @param  SystemContext  A pointer to the system context when the interrupt occured.\r
+**/\r
+VOID\r
+EFIAPI\r
+TimerInterruptHandler (\r
+  IN EFI_EXCEPTION_TYPE   InterruptType,\r
+  IN EFI_SYSTEM_CONTEXT   SystemContext\r
+  )\r
+{\r
+  UINT64  TimerPeriod;\r
+  \r
+  //\r
+  // Count number of ticks\r
+  //\r
+  DEBUG_CODE (mNumTicks++;);\r
+\r
+  //\r
+  // Clear HPET timer interrupt status\r
+  //\r
+  HpetWrite (HPET_GENERAL_INTERRUPT_STATUS_OFFSET, LShiftU64 (1, mTimerIndex));\r
+\r
+  //\r
+  // Local APIC EOI\r
+  //\r
+  SendApicEoi ();\r
+\r
+  //\r
+  // Accumulate time from the HPET main counter value\r
+  //\r
+  mTimerAccumulator += HpetRead (HPET_MAIN_COUNTER_OFFSET);\r
+\r
+  //\r
+  // Reset HPET main counter to 0\r
+  //\r
+  HpetWrite (HPET_MAIN_COUNTER_OFFSET, 0);\r
+\r
+  //\r
+  // Check to see if there is a registered notification function\r
+  //\r
+  if (mTimerNotifyFunction != NULL) {\r
+    //\r
+    // Compute time since last notification in 100 ns units (10 ^ -7) \r
+    //\r
+    TimerPeriod = DivU64x32 (\r
+                    MultU64x32 (\r
+                      mTimerAccumulator, \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
+    // interrupt in 100 ns units.\r
+    //    \r
+    mTimerNotifyFunction (TimerPeriod);\r
+  }\r
+}\r
+\r
+/**\r
+  This function registers the handler NotifyFunction so it is called every time\r
+  the timer interrupt fires.  It also passes the amount of time since the last\r
+  handler call to the NotifyFunction.  If NotifyFunction is NULL, then the\r
+  handler is unregistered.  If the handler is registered, then EFI_SUCCESS is\r
+  returned.  If the CPU does not support registering a timer interrupt handler,\r
+  then EFI_UNSUPPORTED is returned.  If an attempt is made to register a handler\r
+  when a handler is already registered, then EFI_ALREADY_STARTED is returned.\r
+  If an attempt is made to unregister a handler when a handler is not registered,\r
+  then EFI_INVALID_PARAMETER is returned.  If an error occurs attempting to\r
+  register the NotifyFunction with the timer interrupt, then EFI_DEVICE_ERROR\r
+  is returned.\r
+\r
+  @param  This            The EFI_TIMER_ARCH_PROTOCOL instance.\r
+  @param  NotifyFunction  The function to call when a timer interrupt fires.  \r
+                          This function executes at TPL_HIGH_LEVEL.  The DXE \r
+                          Core will register a handler for the timer interrupt, \r
+                          so it can know how much time has passed.  This \r
+                          information is used to signal timer based events.  \r
+                          NULL will unregister the handler.\r
+\r
+  @retval  EFI_SUCCESS            The timer handler was registered.\r
+  @retval  EFI_UNSUPPORTED        The platform does not support timer interrupts.\r
+  @retval  EFI_ALREADY_STARTED    NotifyFunction is not NULL, and a handler is already\r
+                                  registered.\r
+  @retval  EFI_INVALID_PARAMETER  NotifyFunction is NULL, and a handler was not\r
+                                  previously registered.\r
+  @retval  EFI_DEVICE_ERROR       The timer handler could not be registered.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+TimerDriverRegisterHandler (\r
+  IN EFI_TIMER_ARCH_PROTOCOL  *This,\r
+  IN EFI_TIMER_NOTIFY         NotifyFunction\r
+  )\r
+{\r
+  //\r
+  // Check for invalid parameters\r
+  //\r
+  if (NotifyFunction == NULL && mTimerNotifyFunction == NULL) {\r
+    return EFI_INVALID_PARAMETER;\r
+  }\r
+  if (NotifyFunction != NULL && mTimerNotifyFunction != NULL) {\r
+    return EFI_ALREADY_STARTED;\r
+  }\r
+\r
+  //\r
+  // Cache the registered notification function\r
+  //\r
+  mTimerNotifyFunction = NotifyFunction;\r
+\r
+  return EFI_SUCCESS;\r
+}\r
+\r
+/**\r
+  This function adjusts the period of timer interrupts to the value specified\r
+  by TimerPeriod.  If the timer period is updated, then the selected timer\r
+  period is stored in EFI_TIMER.TimerPeriod, and EFI_SUCCESS is returned.  If\r
+  the timer hardware is not programmable, then EFI_UNSUPPORTED is returned.\r
+  If an error occurs while attempting to update the timer period, then the\r
+  timer hardware will be put back in its state prior to this call, and\r
+  EFI_DEVICE_ERROR is returned.  If TimerPeriod is 0, then the timer interrupt\r
+  is disabled.  This is not the same as disabling the CPU's interrupts.\r
+  Instead, it must either turn off the timer hardware, or it must adjust the\r
+  interrupt controller so that a CPU interrupt is not generated when the timer\r
+  interrupt fires.\r
+\r
+  @param  This         The EFI_TIMER_ARCH_PROTOCOL instance.\r
+  @param  TimerPeriod  The rate to program the timer interrupt in 100 nS units.\r
+                       If the timer hardware is not programmable, then \r
+                       EFI_UNSUPPORTED is returned.  If the timer is programmable, \r
+                       then the timer period will be rounded up to the nearest \r
+                       timer period that is supported by the timer hardware.  \r
+                       If TimerPeriod is set to 0, then the timer interrupts \r
+                       will be disabled.\r
+\r
+  @retval  EFI_SUCCESS       The timer period was changed.\r
+  @retval  EFI_UNSUPPORTED   The platform cannot change the period of the timer interrupt.\r
+  @retval  EFI_DEVICE_ERROR  The timer period could not be changed due to a device error.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+TimerDriverSetTimerPeriod (\r
+  IN EFI_TIMER_ARCH_PROTOCOL  *This,\r
+  IN UINT64                   TimerPeriod\r
+  )\r
+{\r
+  UINT64  TimerCount;\r
+\r
+  //\r
+  // Disable HPET timer when adjusting the timer period\r
+  //\r
+  HpetEnable (FALSE);\r
+  \r
+  if (TimerPeriod == 0) {\r
+    //\r
+    // If TimerPeriod is 0, then mask HPET Timer interrupts\r
+    //\r
+    \r
+    if (mTimerConfiguration.Bits.MsiInterruptCapablity != 0) {\r
+      //\r
+      // Disable HPET MSI interrupt generation\r
+      //\r
+      mTimerConfiguration.Bits.MsiInterruptEnable = 0;\r
+    } else {\r
+      //\r
+      // Disable I/O APIC Interrupt\r
+      //\r
+      IoApicEnableInterrupt (mTimerIrq, FALSE);\r
+    }\r
+    \r
+    //\r
+    // Disable HPET timer interrupt \r
+    //\r
+    mTimerConfiguration.Bits.InterruptEnable = 0;\r
+    HpetWrite (HPET_TIMER_CONFIGURATION_OFFSET + mTimerIndex * HPET_TIMER_STRIDE, mTimerConfiguration.Uint64);\r
+  } else {\r
+    //\r
+    // Convert TimerPeriod to femtoseconds and divide by the number if femtoseconds \r
+    // per tick of the HPET counter to determine the number of HPET counter ticks\r
+    // in TimerPeriod 100 ns units.\r
+    // \r
+    TimerCount = DivU64x32 (\r
+                   MultU64x32 (TimerPeriod, 100000000),\r
+                   mHpetGeneralCapabilities.Bits.CounterClockPeriod\r
+                   );\r
+\r
+    //\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
+    }\r
+    \r
+    //\r
+    // Enable HPET Timer interrupt generation\r
+    //\r
+    if (mTimerConfiguration.Bits.MsiInterruptCapablity != 0) {\r
+      //\r
+      // Enable HPET MSI Interrupt\r
+      //\r
+      mTimerConfiguration.Bits.MsiInterruptEnable = 1;\r
+    } else {\r
+      //\r
+      // Enable timer interrupt through I/O APIC\r
+      //\r
+      IoApicEnableInterrupt (mTimerIrq, TRUE);\r
+    }\r
+\r
+    //\r
+    // Enable HPET Interrupt Generation\r
+    //\r
+    mTimerConfiguration.Bits.InterruptEnable = 1;\r
+    HpetWrite (HPET_TIMER_CONFIGURATION_OFFSET + mTimerIndex * HPET_TIMER_STRIDE, mTimerConfiguration.Uint64);\r
+  }\r
+    \r
+  //\r
+  // Save the new timer period\r
+  //\r
+  mTimerPeriod = TimerPeriod;\r
+\r
+  //\r
+  // Enable the HPET counter once new timer period has been established\r
+  // The HPET counter should run even if the HPET Timer interrupts are\r
+  // disabled.  This is used to account for time passed while the interrupt\r
+  // is disabled.\r
+  //\r
+  HpetEnable (TRUE);\r
+  \r
+  return EFI_SUCCESS;\r
+}\r
+\r
+/**\r
+  This function retrieves the period of timer interrupts in 100 ns units,\r
+  returns that value in TimerPeriod, and returns EFI_SUCCESS.  If TimerPeriod\r
+  is NULL, then EFI_INVALID_PARAMETER is returned.  If a TimerPeriod of 0 is\r
+  returned, then the timer is currently disabled.\r
+\r
+  @param  This         The EFI_TIMER_ARCH_PROTOCOL instance.\r
+  @param  TimerPeriod  A pointer to the timer period to retrieve in 100 ns units.\r
+                       If 0 is returned, then the timer is currently disabled.\r
+\r
+  @retval  EFI_SUCCESS            The timer period was returned in TimerPeriod.\r
+  @retval  EFI_INVALID_PARAMETER  TimerPeriod is NULL.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+TimerDriverGetTimerPeriod (\r
+  IN EFI_TIMER_ARCH_PROTOCOL   *This,\r
+  OUT UINT64                   *TimerPeriod\r
+  )\r
+{\r
+  if (TimerPeriod == NULL) {\r
+    return EFI_INVALID_PARAMETER;\r
+  }\r
+\r
+  *TimerPeriod = mTimerPeriod;\r
+\r
+  return EFI_SUCCESS;\r
+}\r
+\r
+/**\r
+  This function generates a soft timer interrupt. If the platform does not support soft\r
+  timer interrupts, then EFI_UNSUPPORTED is returned. Otherwise, EFI_SUCCESS is returned.\r
+  If a handler has been registered through the EFI_TIMER_ARCH_PROTOCOL.RegisterHandler()\r
+  service, then a soft timer interrupt will be generated. If the timer interrupt is\r
+  enabled when this service is called, then the registered handler will be invoked. The\r
+  registered handler should not be able to distinguish a hardware-generated timer\r
+  interrupt from a software-generated timer interrupt.\r
+\r
+  @param  This  The EFI_TIMER_ARCH_PROTOCOL instance.\r
+\r
+  @retval  EFI_SUCCESS       The soft timer interrupt was generated.\r
+  @retval  EFI_UNSUPPORTEDT  The platform does not support the generation of soft \r
+                             timer interrupts.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+TimerDriverGenerateSoftInterrupt (\r
+  IN EFI_TIMER_ARCH_PROTOCOL  *This\r
+  )\r
+{\r
+  EFI_TPL  Tpl;\r
+  UINT64   TimerPeriod;\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
+  // Reset HPET main counter to 0\r
+  //\r
+  HpetWrite (HPET_MAIN_COUNTER_OFFSET, 0);\r
+\r
+  //\r
+  // Check to see if there is a registered notification function\r
+  //\r
+  if (mTimerNotifyFunction != NULL) {\r
+    //\r
+    // Compute time since last interrupt in 100 ns units (10 ^ -7) \r
+    //\r
+    TimerPeriod = DivU64x32 (\r
+                    MultU64x32 (\r
+                      mTimerAccumulator, \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
+    // interrupt in 100 ns units.\r
+    //    \r
+    mTimerNotifyFunction (TimerPeriod);\r
+  }\r
+\r
+  //\r
+  // Restore interrupts\r
+  //  \r
+  gBS->RestoreTPL (Tpl);\r
+  \r
+  return EFI_SUCCESS;\r
+}\r
+\r
+/**\r
+  Initialize the Timer Architectural Protocol driver\r
+\r
+  @param  ImageHandle  ImageHandle of the loaded driver\r
+  @param  SystemTable  Pointer to the System Table\r
+\r
+  @retval  EFI_SUCCESS           Timer Architectural Protocol created\r
+  @retval  EFI_OUT_OF_RESOURCES  Not enough resources available to initialize driver.\r
+  @retval  EFI_DEVICE_ERROR      A device error occured attempting to initialize the driver.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+TimerDriverInitialize (\r
+  IN EFI_HANDLE        ImageHandle,\r
+  IN EFI_SYSTEM_TABLE  *SystemTable\r
+  )\r
+{\r
+  EFI_STATUS                             Status;\r
+  UINTN                                  TimerIndex;\r
+  UINTN                                  MsiTimerIndex;\r
+  HPET_TIMER_MSI_ROUTE_REGISTER          HpetTimerMsiRoute;\r
+\r
+  DEBUG ((DEBUG_INFO, "Init HPET Timer Driver\n"));\r
+\r
+  //\r
+  // Make sure the Timer Architectural Protocol is not already installed in the system\r
+  //\r
+  ASSERT_PROTOCOL_ALREADY_INSTALLED (NULL, &gEfiTimerArchProtocolGuid);\r
+\r
+  //\r
+  // Find the CPU architectural protocol.\r
+  //\r
+  Status = gBS->LocateProtocol (&gEfiCpuArchProtocolGuid, NULL, (VOID **) &mCpu);\r
+  ASSERT_EFI_ERROR (Status);\r
+\r
+  //\r
+  // Retrieve HPET Capabilities and Configuration Information\r
+  //  \r
+  mHpetGeneralCapabilities.Uint64  = HpetRead (HPET_GENERAL_CAPABILITIES_ID_OFFSET);\r
+  mHpetGeneralConfiguration.Uint64 = HpetRead (HPET_GENERAL_CONFIGURATION_OFFSET);\r
\r
+  //\r
+  // If Revision is not valid, then ASSERT() and unload the driver because the HPET \r
+  // device is not present.\r
+  //  \r
+  ASSERT (mHpetGeneralCapabilities.Uint64 != 0);\r
+  ASSERT (mHpetGeneralCapabilities.Uint64 != 0xFFFFFFFFFFFFFFFFULL);\r
+  if (mHpetGeneralCapabilities.Uint64 == 0 || mHpetGeneralCapabilities.Uint64 == 0xFFFFFFFFFFFFFFFFULL) {\r
+    DEBUG ((DEBUG_ERROR, "HPET device is not present.  Unload HPET driver.\n"));\r
+    return EFI_DEVICE_ERROR;\r
+  }\r
+\r
+  //\r
+  // Force the HPET timer to be disabled while setting everything up\r
+  //\r
+  HpetEnable (FALSE);\r
+\r
+  //\r
+  // Dump HPET Configuration Information\r
+  //  \r
+  DEBUG_CODE (\r
+    DEBUG ((DEBUG_INFO, "HPET Base Address = %08x\n", PcdGet32 (PcdHpetBaseAddress)));\r
+    DEBUG ((DEBUG_INFO, "  HPET_GENERAL_CAPABILITIES_ID  = %016lx\n", mHpetGeneralCapabilities));\r
+    DEBUG ((DEBUG_INFO, "  HPET_GENERAL_CONFIGURATION    = %016lx\n", mHpetGeneralConfiguration.Uint64));\r
+    DEBUG ((DEBUG_INFO, "  HPET_GENERAL_INTERRUPT_STATUS = %016lx\n", HpetRead (HPET_GENERAL_INTERRUPT_STATUS_OFFSET)));\r
+    DEBUG ((DEBUG_INFO, "  HPET_MAIN_COUNTER             = %016lx\n", HpetRead (HPET_MAIN_COUNTER_OFFSET)));\r
+    DEBUG ((DEBUG_INFO, "  HPET Main Counter Period      = %d (fs)\n", mHpetGeneralCapabilities.Bits.CounterClockPeriod));\r
+    for (TimerIndex = 0; TimerIndex <= mHpetGeneralCapabilities.Bits.NumberOfTimers; TimerIndex++) {\r
+      DEBUG ((DEBUG_INFO, "  HPET_TIMER%d_CONFIGURATION     = %016lx\n", TimerIndex, HpetRead (HPET_TIMER_CONFIGURATION_OFFSET + TimerIndex * HPET_TIMER_STRIDE)));\r
+      DEBUG ((DEBUG_INFO, "  HPET_TIMER%d_COMPARATOR        = %016lx\n", TimerIndex, HpetRead (HPET_TIMER_COMPARATOR_OFFSET    + TimerIndex * HPET_TIMER_STRIDE)));\r
+      DEBUG ((DEBUG_INFO, "  HPET_TIMER%d_MSI_ROUTE         = %016lx\n", TimerIndex, HpetRead (HPET_TIMER_MSI_ROUTE_OFFSET     + TimerIndex * HPET_TIMER_STRIDE)));\r
+    }\r
+  );\r
+  \r
+  //\r
+  // Determine the interrupt mode to use for the HPET Timer.  \r
+  // Look for MSI first, then unused PIC mode interrupt, then I/O APIC mode interrupt\r
+  //  \r
+  MsiTimerIndex = HPET_INVALID_TIMER_INDEX;\r
+  mTimerIndex   = HPET_INVALID_TIMER_INDEX;\r
+  for (TimerIndex = 0; TimerIndex <= mHpetGeneralCapabilities.Bits.NumberOfTimers; TimerIndex++) {\r
+    //\r
+    // Read the HPET Timer Capabilities and Configuration register\r
+    //\r
+    mTimerConfiguration.Uint64 = HpetRead (HPET_TIMER_CONFIGURATION_OFFSET + TimerIndex * HPET_TIMER_STRIDE);\r
+    \r
+    //\r
+    // Check to see if this HPET Timer supports MSI \r
+    //\r
+    if (mTimerConfiguration.Bits.MsiInterruptCapablity != 0) {\r
+      //\r
+      // Save the index of the first HPET Timer that supports MSI interrupts\r
+      //\r
+      if (MsiTimerIndex == HPET_INVALID_TIMER_INDEX) {\r
+        MsiTimerIndex = TimerIndex;\r
+      }\r
+    }\r
+    \r
+    //\r
+    // Check to see if this HPET Timer supports I/O APIC interrupts\r
+    //\r
+    if (mTimerConfiguration.Bits.InterruptRouteCapability != 0) {\r
+      //\r
+      // Save the index of the first HPET Timer that supports I/O APIC interrupts\r
+      //\r
+      if (mTimerIndex == HPET_INVALID_TIMER_INDEX) {\r
+        mTimerIndex = TimerIndex;\r
+        mTimerIrq   = (UINT32)LowBitSet32 (mTimerConfiguration.Bits.InterruptRouteCapability);\r
+      }\r
+    }\r
+  }\r
+\r
+  if (FeaturePcdGet (PcdHpetMsiEnable) && MsiTimerIndex != HPET_INVALID_TIMER_INDEX) {\r
+    //\r
+    // Use MSI interrupt if supported\r
+    //\r
+    mTimerIndex  = MsiTimerIndex;\r
+\r
+    //\r
+    // Program MSI Address and MSI Data values in the selected HPET Timer\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
+    //\r
+    // Read the HPET Timer Capabilities and Configuration register and initialize for MSI mode\r
+    //   Clear LevelTriggeredInterrupt to use edge triggered interrupts when in MSI mode\r
+    //\r
+    mTimerConfiguration.Uint64 = HpetRead (HPET_TIMER_CONFIGURATION_OFFSET + mTimerIndex * HPET_TIMER_STRIDE);\r
+    mTimerConfiguration.Bits.LevelTriggeredInterrupt = 0;\r
+  } else {\r
+    //\r
+    // If no HPET timers support MSI or I/O APIC modes, then ASSERT() and unload the driver.\r
+    //\r
+    ASSERT (mTimerIndex != HPET_INVALID_TIMER_INDEX);\r
+    if (mTimerIndex == HPET_INVALID_TIMER_INDEX) {\r
+      DEBUG ((DEBUG_ERROR, "No HPET timers support MSI or I/O APIC mode.  Unload HPET driver.\n"));\r
+      return EFI_DEVICE_ERROR;\r
+    }\r
+    \r
+    //\r
+    // Initialize I/O APIC entry for HPET Timer Interrupt\r
+    //   Fixed Delivery Mode, Level Triggered, Asserted Low\r
+    //\r
+    IoApicConfigureInterrupt (mTimerIrq, PcdGet8 (PcdHpetLocalApicVector), IO_APIC_DELIVERY_MODE_LOWEST_PRIORITY, TRUE, FALSE);\r
+\r
+    //\r
+    // Read the HPET Timer Capabilities and Configuration register and initialize for I/O APIC mode\r
+    //   Clear MsiInterruptCapability to force rest of driver to use I/O APIC mode\r
+    //   Set LevelTriggeredInterrupt to use level triggered interrupts when in I/O APIC mode\r
+    //   Set InterruptRoute field based in mTimerIrq\r
+    //\r
+    mTimerConfiguration.Uint64 = HpetRead (HPET_TIMER_CONFIGURATION_OFFSET + mTimerIndex * HPET_TIMER_STRIDE);\r
+    mTimerConfiguration.Bits.MsiInterruptCapablity   = 0;\r
+    mTimerConfiguration.Bits.LevelTriggeredInterrupt = 1;\r
+    mTimerConfiguration.Bits.InterruptRoute          = mTimerIrq;\r
+  }\r
+\r
+  //\r
+  // Configure the selected HPET Timer with settings common to both MSI mode and I/O APIC mode\r
+  //   Clear InterruptEnable to keep interrupts disabled until full init is complete \r
+  //   Clear PeriodicInterruptEnable to use one-shot mode \r
+  //   Configure as a 32-bit counter  \r
+  //\r
+  mTimerConfiguration.Bits.InterruptEnable         = 0;\r
+  mTimerConfiguration.Bits.PeriodicInterruptEnable = 0;\r
+  mTimerConfiguration.Bits.CounterSizeEnable       = 0;\r
+  HpetWrite (HPET_TIMER_CONFIGURATION_OFFSET + mTimerIndex * HPET_TIMER_STRIDE, mTimerConfiguration.Uint64);\r
+\r
+  //\r
+  // Install interrupt handler for selected HPET Timer\r
+  //\r
+  Status = mCpu->RegisterInterruptHandler (mCpu, PcdGet8 (PcdHpetLocalApicVector), TimerInterruptHandler);\r
+  ASSERT_EFI_ERROR (Status);\r
+  if (EFI_ERROR (Status)) {\r
+    DEBUG ((DEBUG_ERROR, "Unable to register HPET interrupt with CPU Arch Protocol.  Unload HPET driver.\n"));\r
+    return EFI_DEVICE_ERROR;\r
+  }\r
+\r
+  //\r
+  // Force the HPET Timer to be enabled at its default period\r
+  //\r
+  Status = TimerDriverSetTimerPeriod (&mTimer, PcdGet64 (PcdHpetDefaultTimerPeriod));\r
+  ASSERT_EFI_ERROR (Status);\r
+  if (EFI_ERROR (Status)) {\r
+    DEBUG ((DEBUG_ERROR, "Unable to set HPET default timer rate.  Unload HPET driver.\n"));\r
+    return EFI_DEVICE_ERROR;\r
+  }\r
+\r
+  //\r
+  // Show state of enabled HPET timer\r
+  //\r
+  DEBUG_CODE (\r
+    if (mTimerConfiguration.Bits.MsiInterruptCapablity != 0) {\r
+      DEBUG ((DEBUG_INFO, "HPET Interrupt Mode MSI\n"));\r
+    } else {\r
+      DEBUG ((DEBUG_INFO, "HPET Interrupt Mode I/O APIC\n"));\r
+      DEBUG ((DEBUG_INFO, "HPET I/O APIC IRQ         = %02x\n",   mTimerIrq));\r
+    }  \r
+    DEBUG ((DEBUG_INFO, "HPET Interrupt Vector     = %02x\n",   PcdGet8 (PcdHpetLocalApicVector)));\r
+    DEBUG ((DEBUG_INFO, "HPET_TIMER%d_CONFIGURATION = %016lx\n", mTimerIndex, HpetRead (HPET_TIMER_CONFIGURATION_OFFSET + mTimerIndex * HPET_TIMER_STRIDE)));\r
+    DEBUG ((DEBUG_INFO, "HPET_TIMER%d_COMPARATOR    = %016lx\n", mTimerIndex, HpetRead (HPET_TIMER_COMPARATOR_OFFSET    + mTimerIndex * HPET_TIMER_STRIDE)));\r
+    DEBUG ((DEBUG_INFO, "HPET_TIMER%d_MSI_ROUTE     = %016lx\n", mTimerIndex, HpetRead (HPET_TIMER_MSI_ROUTE_OFFSET     + mTimerIndex * HPET_TIMER_STRIDE)));\r
+\r
+    //\r
+    // Wait for a few timer interrupts to fire before continuing\r
+    // \r
+    while (mNumTicks < 10);\r
+  );\r
\r
+  //\r
+  // Install the Timer Architectural Protocol onto a new handle\r
+  //\r
+  Status = gBS->InstallMultipleProtocolInterfaces (\r
+                  &mTimerHandle,\r
+                  &gEfiTimerArchProtocolGuid, &mTimer,\r
+                  NULL\r
+                  );\r
+  ASSERT_EFI_ERROR (Status);\r
+\r
+  return Status;\r
+}\r
diff --git a/PcAtChipsetPkg/HpetTimerDxe/HpetTimerDxe.inf b/PcAtChipsetPkg/HpetTimerDxe/HpetTimerDxe.inf
new file mode 100644 (file)
index 0000000..c22c381
--- /dev/null
@@ -0,0 +1,60 @@
+## @file\r
+# \r
+# Copyright (c) 2011, Intel Corporation. All rights reserved.<BR>\r
+# This program and the accompanying materials                          \r
+# are licensed and made available under the terms and conditions of the BSD License         \r
+# which accompanies this distribution.  The full text of the license may be found at        \r
+# http://opensource.org/licenses/bsd-license.php                                            \r
+#                                                                                           \r
+# THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,                     \r
+# WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.             \r
+#\r
+##\r
+\r
+[Defines]\r
+  INF_VERSION                    = 0x00010005\r
+  BASE_NAME                      = HpetTimerDxe\r
+  FILE_GUID                      = 6CE6B0DE-781C-4f6c-B42D-98346C614BEC\r
+  MODULE_TYPE                    = DXE_DRIVER\r
+  VERSION_STRING                 = 1.0\r
+  ENTRY_POINT                    = TimerDriverInitialize\r
+\r
+#\r
+# The following information is for reference only and not required by the build tools.\r
+#\r
+#  VALID_ARCHITECTURES           = IA32 X64 IPF EBC\r
+#\r
+#\r
+\r
+[Sources]\r
+  HpetTimer.c\r
+  \r
+[Packages]\r
+  MdePkg/MdePkg.dec\r
+  UefiCpuPkg/UefiCpuPkg.dec\r
+  PcAtChipsetPkg/PcAtChipsetPkg.dec\r
+  \r
+[LibraryClasses]\r
+  PcdLib\r
+  IoLib\r
+  DebugLib\r
+  UefiDriverEntryPoint\r
+  UefiBootServicesTableLib\r
+  BaseLib\r
+  LocalApicLib\r
+  IoApicLib\r
+\r
+[Protocols]\r
+  gEfiTimerArchProtocolGuid                     # PROTOCOL ALWAYS_PRODUCED\r
+  gEfiCpuArchProtocolGuid                       # PROTOCOL ALWAYS_CONSUMED\r
+\r
+[FeaturePcd]\r
+  gPcAtChipsetPkgTokenSpaceGuid.PcdHpetMsiEnable\r
+  \r
+[Pcd]\r
+  gPcAtChipsetPkgTokenSpaceGuid.PcdHpetBaseAddress\r
+  gPcAtChipsetPkgTokenSpaceGuid.PcdHpetLocalApicVector\r
+  gPcAtChipsetPkgTokenSpaceGuid.PcdHpetDefaultTimerPeriod\r
+\r
+[Depex]\r
+  gEfiCpuArchProtocolGuid\r
diff --git a/PcAtChipsetPkg/Include/Library/IoApicLib.h b/PcAtChipsetPkg/Include/Library/IoApicLib.h
new file mode 100644 (file)
index 0000000..bd9054b
--- /dev/null
@@ -0,0 +1,102 @@
+/** @file\r
+  Public include file for I/O APIC library.\r
+\r
+  I/O APIC library assumes I/O APIC is enabled. It does not\r
+  handles cases where I/O APIC is disabled.\r
+\r
+  Copyright (c) 2011, Intel Corporation. All rights reserved.<BR>\r
+  This program and the accompanying materials\r
+  are licensed and made available under the terms and conditions of the BSD License\r
+  which accompanies this distribution.  The full text of the license may be found at\r
+  http://opensource.org/licenses/bsd-license.php\r
+\r
+  THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,\r
+  WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.\r
+\r
+**/\r
+\r
+/**\r
+  Read a 32-bit I/O APIC register.\r
+\r
+  If Index is >= 0x100, then ASSERT().\r
+  \r
+  @param  Index  Specifies the I/O APIC register to read.\r
+\r
+  @return  The 32-bit value read from the I/O APIC register specified by Index.\r
+**/\r
+UINT32\r
+EFIAPI\r
+IoApicRead (\r
+  IN UINTN  Index\r
+  );\r
+\r
+/**\r
+  Write a 32-bit I/O APIC register.\r
+\r
+  If Index is >= 0x100, then ASSERT().\r
+  \r
+  @param  Index  Specifies the I/O APIC register to write.\r
+  @param  Value  Specifies the value to write to the I/O APIC register specified by Index.\r
+\r
+  @return  The 32-bit value written to I/O APIC register specified by Index.\r
+**/\r
+UINT32\r
+EFIAPI\r
+IoApicWrite (\r
+  IN UINTN   Index,\r
+  IN UINT32  Value\r
+  );\r
+\r
+/**\r
+  Set the interrupt mask of an I/O APIC interrupt.\r
+\r
+  If Irq is larger than the maximum number I/O APIC redirection entries, then ASSERT(). \r
+  \r
+  @param  Irq     Specifies the I/O APIC interrupt to enable or disable.\r
+  @param  Enable  If TRUE, then enable the I/O APIC interrupt specified by Irq.\r
+                  If FALSE, then disable the I/O APIC interrupt specified by Irq.\r
+**/\r
+VOID\r
+EFIAPI\r
+IoApicEnableInterrupt (\r
+  IN UINTN    Irq,\r
+  IN BOOLEAN  Enable\r
+  );\r
+\r
+/**\r
+  Configures an I/O APIC interrupt.\r
+  \r
+  Configure an I/O APIC Redirection Table Entry to deliver an interrupt in physical\r
+  mode to the Local APIC of the currntly executing CPU.  The default state of the \r
+  entry is for the interrupt to be disabled (masked).  IoApicEnableInterrupts() must\r
+  be used to enable(unmask) the I/O APIC Interrupt.\r
+\r
+  If Irq is larger than the maximum number I/O APIC redirection entries, then ASSERT(). \r
+  If Vector >= 0x100, then ASSERT().\r
+  If DeliveryMode is not supported, then ASSERT().\r
+\r
+  @param  Irq             Specifies the I/O APIC interrupt to initialize.\r
+  @param  Vector          The 8-bit interrupt vector associated with the I/O APIC\r
+                          Interrupt.  Must be in the range 0x10..0xFE.\r
+  @param  DeliveryMode    A 3-bit value that specifies how the recept of the I/O APIC\r
+                          interrupt is handled.  The only supported values are:\r
+                            0: IO_APIC_DELIVERY_MODE_FIXED\r
+                            1: IO_APIC_DELIVERY_MODE_LOWEST_PRIORITY\r
+                            2: IO_APIC_DELIVERY_MODE_SMI\r
+                            4: IO_APIC_DELIVERY_MODE_NMI\r
+                            5: IO_APIC_DELIVERY_MODE_INIT\r
+                            7: IO_APIC_DELIVERY_MODE_EXTINT\r
+  @param  LevelTriggered  TRUE specifies a level triggered interrupt.\r
+                          FALSE specifies an edge triggered interrupt.\r
+  @param  AssertionLevel  TRUE specified an active high interrupt.\r
+                          FALSE specifies an active low interrupt.\r
+**/\r
+VOID\r
+EFIAPI\r
+IoApicConfigureInterrupt (\r
+  IN UINTN    Irq,\r
+  IN UINTN    Vector,\r
+  IN UINTN    DeliveryMode,\r
+  IN BOOLEAN  LevelTriggered,\r
+  IN BOOLEAN  AssertionLevel\r
+  );\r
diff --git a/PcAtChipsetPkg/Include/Register/Hpet.h b/PcAtChipsetPkg/Include/Register/Hpet.h
new file mode 100644 (file)
index 0000000..2933767
--- /dev/null
@@ -0,0 +1,106 @@
+/** @file\r
+  HPET register definitions from the IA-PC HPET (High Precision Event Timers) \r
+  Specification, Revision 1.0a, October 2004.\r
+\r
+  Copyright (c) 2011, Intel Corporation. All rights reserved.<BR>\r
+  This program and the accompanying materials\r
+  are licensed and made available under the terms and conditions of the BSD License\r
+  which accompanies this distribution.  The full text of the license may be found at\r
+  http://opensource.org/licenses/bsd-license.php\r
+\r
+  THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,\r
+  WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.\r
+\r
+**/\r
+\r
+#ifndef __HPET_REGISTER_H__\r
+#define __HPET_REGISTER_H__\r
+\r
+///\r
+/// HPET General Register Offsets\r
+///\r
+#define HPET_GENERAL_CAPABILITIES_ID_OFFSET   0x000\r
+#define HPET_GENERAL_CONFIGURATION_OFFSET     0x010\r
+#define HPET_GENERAL_INTERRUPT_STATUS_OFFSET  0x020\r
+\r
+///\r
+/// HPET Timer Register Offsets\r
+///\r
+#define HPET_MAIN_COUNTER_OFFSET              0x0F0\r
+#define HPET_TIMER_CONFIGURATION_OFFSET       0x100\r
+#define HPET_TIMER_COMPARATOR_OFFSET          0x108\r
+#define HPET_TIMER_MSI_ROUTE_OFFSET           0x110\r
+\r
+///\r
+/// Stride between sets of HPET Timer Registers\r
+///\r
+#define HPET_TIMER_STRIDE         0x20\r
+\r
+#pragma pack(1)\r
+\r
+///\r
+/// HPET General Capabilities and ID Register\r
+///\r
+typedef union {\r
+  struct {\r
+    UINT32  Revision:8;\r
+    UINT32  NumberOfTimers:5;\r
+    UINT32  CounterSize:1;\r
+    UINT32  Reserved0:1;\r
+    UINT32  LegacyRoute:1;\r
+    UINT32  VendorId:16;\r
+    UINT32  CounterClockPeriod:32;\r
+  } Bits;\r
+  UINT64  Uint64;\r
+} HPET_GENERAL_CAPABILITIES_ID_REGISTER;\r
+\r
+///\r
+/// HPET General Configuration Register\r
+///\r
+typedef union {\r
+  struct {\r
+    UINT32  MainCounterEnable:1;\r
+    UINT32  LegacyRouteEnable:1;\r
+    UINT32  Reserved0:30;\r
+    UINT32  Reserved1:32;\r
+  } Bits;\r
+  UINT64  Uint64;\r
+} HPET_GENERAL_CONFIGURATION_REGISTER;\r
+\r
+///\r
+/// HPET Timer Configuration Register\r
+///\r
+typedef union {\r
+  struct {\r
+    UINT32  Reserved0:1;\r
+    UINT32  LevelTriggeredInterrupt:1;\r
+    UINT32  InterruptEnable:1;\r
+    UINT32  PeriodicInterruptEnable:1;\r
+    UINT32  PeriodicInterruptCapablity:1;\r
+    UINT32  CounterSizeCapablity:1;\r
+    UINT32  ValueSetEnable:1;\r
+    UINT32  Reserved1:1;\r
+    UINT32  CounterSizeEnable:1;\r
+    UINT32  InterruptRoute:5;\r
+    UINT32  MsiInterruptEnable:1;\r
+    UINT32  MsiInterruptCapablity:1;\r
+    UINT32  Reserved2:16;\r
+    UINT32  InterruptRouteCapability;\r
+  } Bits;\r
+  UINT64  Uint64;\r
+} HPET_TIMER_CONFIGURATION_REGISTER;\r
+\r
+///\r
+/// HPET Timer MSI Route Register\r
+///\r
+typedef union {\r
+  struct {\r
+    UINT32  Value:32;\r
+    UINT32  Address:32;\r
+  } Bits;\r
+  UINT64  Uint64;\r
+} HPET_TIMER_MSI_ROUTE_REGISTER;\r
+\r
+#pragma pack()\r
+\r
+#endif\r
diff --git a/PcAtChipsetPkg/Include/Register/IoApic.h b/PcAtChipsetPkg/Include/Register/IoApic.h
new file mode 100644 (file)
index 0000000..d6e73be
--- /dev/null
@@ -0,0 +1,86 @@
+/** @file\r
+  I/O APIC Register Definitions from 82093AA I/O Advanced Programmable Interrupt \r
+  Controller (IOAPIC), 1996.\r
+  \r
+  Copyright (c) 2011, Intel Corporation. All rights reserved.<BR>\r
+  This program and the accompanying materials\r
+  are licensed and made available under the terms and conditions of the BSD License\r
+  which accompanies this distribution.  The full text of the license may be found at\r
+  http://opensource.org/licenses/bsd-license.php\r
+\r
+  THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,\r
+  WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.\r
+\r
+**/\r
+\r
+#ifndef __IO_APIC_H__\r
+#define __IO_APIC_H__\r
+\r
+///\r
+/// I/O APIC Register Offsets\r
+///\r
+#define IOAPIC_INDEX_OFFSET  0x00\r
+#define IOAPIC_DATA_OFFSET   0x10\r
+\r
+///\r
+/// I/O APIC Indirect Register Indexes\r
+///\r
+#define IO_APIC_IDENTIFICATION_REGISTER_INDEX  0x00\r
+#define IO_APIC_VERSION_REGISTER_INDEX         0x01\r
+#define IO_APIC_REDIRECTION_TABLE_ENTRY_INDEX  0x10\r
+\r
+///\r
+/// I/O APIC Interrupt Deliver Modes\r
+///\r
+#define IO_APIC_DELIVERY_MODE_FIXED            0\r
+#define IO_APIC_DELIVERY_MODE_LOWEST_PRIORITY  1\r
+#define IO_APIC_DELIVERY_MODE_SMI              2\r
+#define IO_APIC_DELIVERY_MODE_NMI              4\r
+#define IO_APIC_DELIVERY_MODE_INIT             5\r
+#define IO_APIC_DELIVERY_MODE_EXTINT           7\r
+\r
+#pragma pack(1)\r
+\r
+typedef union {\r
+  struct {\r
+    UINT32  Reserved0:24;\r
+    UINT32  Identification:4;\r
+    UINT32  Reserved1:4;\r
+  } Bits;\r
+  UINT32  Uint32;\r
+} IO_APIC_IDENTIFICATION_REGISTER;\r
+\r
+typedef union {\r
+  struct {\r
+    UINT32  Version:8;\r
+    UINT32  Reserved0:8;\r
+    UINT32  MaximumRedirectionEntry:8;\r
+    UINT32  Reserved1:8;\r
+  } Bits;\r
+  UINT32  Uint32;\r
+} IO_APIC_VERSION_REGISTER;\r
+\r
+typedef union {\r
+  struct {\r
+    UINT32  Vector:          8;\r
+    UINT32  DeliveryMode:    3;\r
+    UINT32  DestinationMode: 1;\r
+    UINT32  DeliveryStatus:  1;\r
+    UINT32  Polarity:        1;\r
+    UINT32  RemoteIRR:       1;\r
+    UINT32  TriggerMode:     1;\r
+    UINT32  Mask:            1;\r
+    UINT32  Reserved0:       15;\r
+    UINT32  Reserved1:       24;\r
+    UINT32  DestinationID:   8;\r
+  } Bits;\r
+  struct {\r
+    UINT32  Low;\r
+    UINT32  High;\r
+  } Uint32;\r
+  UINT64  Uint64;\r
+} IO_APIC_REDIRECTION_TABLE_ENTRY;\r
+\r
+#pragma pack()\r
+\r
+#endif\r
diff --git a/PcAtChipsetPkg/Library/BaseIoApicLib/BaseIoApicLib.inf b/PcAtChipsetPkg/Library/BaseIoApicLib/BaseIoApicLib.inf
new file mode 100644 (file)
index 0000000..10d3977
--- /dev/null
@@ -0,0 +1,38 @@
+## @file\r
+#   Library instance for I/O APIC library class\r
+#\r
+#  Copyright (c) 2011, Intel Corporation. All rights reserved.<BR>\r
+#  This program and the accompanying materials\r
+#  are licensed and made available under the terms and conditions of the BSD License\r
+#  which accompanies this distribution.  The full text of the license may be found at\r
+#  http://opensource.org/licenses/bsd-license.php\r
+#\r
+#  THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,\r
+#  WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.\r
+#\r
+##\r
+\r
+[Defines]\r
+  INF_VERSION                    = 0x00010005\r
+  BASE_NAME                      = BaseIoApicLib\r
+  FILE_GUID                      = 58ED6E5A-E36A-462a-9ED6-6E62C9A26DF8\r
+  MODULE_TYPE                    = BASE\r
+  VERSION_STRING                 = 1.0\r
+  LIBRARY_CLASS                  = IoApicLib\r
+\r
+[Packages]\r
+  MdePkg/MdePkg.dec\r
+  UefiCpuPkg/UefiCpuPkg.dec\r
+  PcAtChipsetPkg/PcAtChipsetPkg.dec\r
+\r
+[LibraryClasses]\r
+  DebugLib\r
+  IoLib\r
+  PcdLib\r
+  LocalApicLib\r
+\r
+[Sources]\r
+  IoApicLib.c\r
+\r
+[Pcd]\r
+  gPcAtChipsetPkgTokenSpaceGuid.PcdIoApicBaseAddress\r
diff --git a/PcAtChipsetPkg/Library/BaseIoApicLib/IoApicLib.c b/PcAtChipsetPkg/Library/BaseIoApicLib/IoApicLib.c
new file mode 100644 (file)
index 0000000..42b3f21
--- /dev/null
@@ -0,0 +1,158 @@
+/** @file \r
+  I/O APIC library.\r
+\r
+  I/O APIC library assumes I/O APIC is enabled. It does not\r
+  handles cases where I/O APIC is disabled.\r
+\r
+  Copyright (c) 2011, Intel Corporation. All rights reserved.<BR>\r
+  This program and the accompanying materials\r
+  are licensed and made available under the terms and conditions of the BSD License\r
+  which accompanies this distribution.  The full text of the license may be found at\r
+  http://opensource.org/licenses/bsd-license.php\r
+\r
+  THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,\r
+  WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.\r
+\r
+**/\r
+\r
+#include <Base.h>\r
+\r
+#include <Library/IoApicLib.h>\r
+\r
+#include <Library/DebugLib.h>\r
+#include <Library/PcdLib.h>\r
+#include <Library/IoLib.h>\r
+#include <Library/LocalApicLib.h>\r
+\r
+#include <Register/IoApic.h>\r
+\r
+/**\r
+  Read a 32-bit I/O APIC register.\r
+\r
+  If Index is >= 0x100, then ASSERT().\r
+  \r
+  @param  Index  Specifies the I/O APIC register to read.\r
+\r
+  @return  The 32-bit value read from the I/O APIC register specified by Index.\r
+**/\r
+UINT32\r
+EFIAPI\r
+IoApicRead (\r
+  IN UINTN  Index\r
+  )\r
+{\r
+  ASSERT (Index < 0x100);\r
+  MmioWrite8 (PcdGet32 (PcdIoApicBaseAddress) + IOAPIC_INDEX_OFFSET, (UINT8)Index);\r
+  return MmioRead32 (PcdGet32 (PcdIoApicBaseAddress) + IOAPIC_DATA_OFFSET);\r
+}\r
+\r
+/**\r
+  Write a 32-bit I/O APIC register.\r
+\r
+  If Index is >= 0x100, then ASSERT().\r
+  \r
+  @param  Index  Specifies the I/O APIC register to write.\r
+  @param  Value  Specifies the value to write to the I/O APIC register specified by Index.\r
+\r
+  @return  The 32-bit value written to I/O APIC register specified by Index.\r
+**/\r
+UINT32\r
+EFIAPI\r
+IoApicWrite (\r
+  IN UINTN   Index,\r
+  IN UINT32  Value\r
+  )\r
+{\r
+  ASSERT (Index < 0x100);\r
+  MmioWrite8 (PcdGet32 (PcdIoApicBaseAddress) + IOAPIC_INDEX_OFFSET, (UINT8)Index);\r
+  return MmioWrite32 (PcdGet32 (PcdIoApicBaseAddress) + IOAPIC_DATA_OFFSET, Value);\r
+}\r
+\r
+/**\r
+  Set the interrupt mask of an I/O APIC interrupt.\r
+\r
+  If Irq is larger than the maximum number I/O APIC redirection entries, then ASSERT(). \r
+\r
+  @param  Irq     Specifies the I/O APIC interrupt to enable or disable.\r
+  @param  Enable  If TRUE, then enable the I/O APIC interrupt specified by Irq.\r
+                  If FALSE, then disable the I/O APIC interrupt specified by Irq.\r
+**/\r
+VOID\r
+EFIAPI\r
+IoApicEnableInterrupt (\r
+  IN UINTN    Irq,\r
+  IN BOOLEAN  Enable\r
+  )\r
+{\r
+  IO_APIC_VERSION_REGISTER         Version;\r
+  IO_APIC_REDIRECTION_TABLE_ENTRY  Entry;\r
+\r
+  Version.Uint32 = IoApicRead (IO_APIC_VERSION_REGISTER_INDEX);\r
+  ASSERT (Version.Bits.MaximumRedirectionEntry < 0xF0);\r
+  ASSERT (Irq <= Version.Bits.MaximumRedirectionEntry);\r
+\r
+  Entry.Uint32.Low = IoApicRead (IO_APIC_REDIRECTION_TABLE_ENTRY_INDEX + Irq * 2);\r
+  Entry.Bits.Mask = Enable ? 0 : 1;\r
+  IoApicWrite (IO_APIC_REDIRECTION_TABLE_ENTRY_INDEX + Irq * 2, Entry.Uint32.Low);\r
+}\r
+\r
+/**\r
+  Configures an I/O APIC interrupt.\r
+  \r
+  Configure an I/O APIC Redirection Table Entry to deliver an interrupt in physical\r
+  mode to the Local APIC of the currntly executing CPU.  The default state of the \r
+  entry is for the interrupt to be disabled (masked).  IoApicEnableInterrupts() must\r
+  be used to enable(unmask) the I/O APIC Interrupt.\r
\r
+  If Irq is larger than the maximum number I/O APIC redirection entries, then ASSERT(). \r
+  If Vector >= 0x100, then ASSERT().\r
+  If DeliveryMode is not supported, then ASSERT().\r
+\r
+  @param  Irq             Specifies the I/O APIC interrupt to initialize.\r
+  @param  Vector          The 8-bit interrupt vector associated with the I/O APIC\r
+                          Interrupt.  Must be in the range 0x10..0xFE.\r
+  @param  DeliveryMode    A 3-bit value that specifies how the recept of the I/O APIC\r
+                          interrupt is handled.  The only supported values are:\r
+                            0: IO_APIC_DELIVERY_MODE_FIXED\r
+                            1: IO_APIC_DELIVERY_MODE_LOWEST_PRIORITY\r
+                            2: IO_APIC_DELIVERY_MODE_SMI\r
+                            4: IO_APIC_DELIVERY_MODE_NMI\r
+                            5: IO_APIC_DELIVERY_MODE_INIT\r
+                            7: IO_APIC_DELIVERY_MODE_EXTINT\r
+  @param  LevelTriggered  TRUE specifies a level triggered interrupt.\r
+                          FALSE specifies an edge triggered interrupt.\r
+  @param  AssertionLevel  TRUE specified an active high interrupt.\r
+                          FALSE specifies an active low interrupt.\r
+**/\r
+VOID\r
+EFIAPI\r
+IoApicConfigureInterrupt (\r
+  IN UINTN    Irq,\r
+  IN UINTN    Vector,\r
+  IN UINTN    DeliveryMode,\r
+  IN BOOLEAN  LevelTriggered,\r
+  IN BOOLEAN  AssertionLevel\r
+  )\r
+{\r
+  IO_APIC_VERSION_REGISTER         Version;\r
+  IO_APIC_REDIRECTION_TABLE_ENTRY  Entry;\r
+\r
+  Version.Uint32 = IoApicRead (IO_APIC_VERSION_REGISTER_INDEX);\r
+  ASSERT (Version.Bits.MaximumRedirectionEntry < 0xF0);\r
+  ASSERT (Irq <= Version.Bits.MaximumRedirectionEntry);\r
+  ASSERT (Vector <= 0xFF);\r
+  ASSERT (DeliveryMode < 8 && DeliveryMode != 6 && DeliveryMode != 3);\r
+  \r
+  Entry.Uint32.Low = IoApicRead (IO_APIC_REDIRECTION_TABLE_ENTRY_INDEX + Irq * 2);\r
+  Entry.Bits.Vector          = (UINT8)Vector;\r
+  Entry.Bits.DeliveryMode    = (UINT32)DeliveryMode;\r
+  Entry.Bits.DestinationMode = 0; \r
+  Entry.Bits.Polarity        = AssertionLevel ? 0 : 1;\r
+  Entry.Bits.TriggerMode     = LevelTriggered ? 1 : 0;\r
+  Entry.Bits.Mask            = 1;\r
+  IoApicWrite (IO_APIC_REDIRECTION_TABLE_ENTRY_INDEX + Irq * 2, Entry.Uint32.Low);\r
+\r
+  Entry.Uint32.High = IoApicRead (IO_APIC_REDIRECTION_TABLE_ENTRY_INDEX + Irq * 2 + 1);\r
+  Entry.Bits.DestinationID = GetApicId ();\r
+  IoApicWrite (IO_APIC_REDIRECTION_TABLE_ENTRY_INDEX + Irq * 2 + 1, Entry.Uint32.High);\r
+}\r
index 93beb0d..6c6e714 100644 (file)
   PACKAGE_GUID                   = B728689A-52D3-4b8c-AE89-2CE5514CC6DC\r
   PACKAGE_VERSION                = 0.1\r
 \r
+[Includes]\r
+  Include\r
+\r
+[LibraryClasses]\r
+  ##  @libraryclass  Provides functions to manage I/O APIC Redirection Table Entries.\r
+  #\r
+  IoApicLib|Include/Library/IoApicLib.h\r
+  \r
 [Guids]\r
   gPcAtChipsetPkgTokenSpaceGuid = { 0x326ae723, 0xae32, 0x4589, { 0x98, 0xb8, 0xca, 0xc2, 0x3c, 0xdc, 0xc1, 0xb1 } }\r
 \r
+[PcdsFeatureFlag]\r
+  ## If TRUE, then the HPET Timer will be configured to use MSI interrupts if the HPET timer supports them.\r
+  #  If FALSE, then the HPET Timer will be configued to use I/O APIC interrupts.\r
+  gPcAtChipsetPkgTokenSpaceGuid.PcdHpetMsiEnable|TRUE|BOOLEAN|0x00001000\r
+  \r
 [PcdsFixedAtBuild, PcdsDynamic, PcdsDynamicEx, PcdsPatchableInModule]\r
   ## Pcd8259LegacyModeMask defines the default mask value for platform. This value is determined\r
   #  1) If platform only support pure UEFI, value should be set to 0xFFFF or 0xFFFE;\r
 \r
   ## This PCD specifies whether we need enable IsaAcpiFloppyB device.\r
   gPcAtChipsetPkgTokenSpaceGuid.PcdIsaAcpiFloppyBEnable|TRUE|BOOLEAN|0x00000008\r
+\r
+  ## This PCD specifies the base address of the HPET timer.\r
+  gPcAtChipsetPkgTokenSpaceGuid.PcdHpetBaseAddress|0xFED00000|UINT32|0x00000009\r
+\r
+  ## This PCD specifies the Local APIC Interrupt Vector for the HPET Timer.\r
+  gPcAtChipsetPkgTokenSpaceGuid.PcdHpetLocalApicVector|0x40|UINT8|0x0000000A\r
+\r
+  ## This PCD specifies the defaut period of the HPET Timer in 100 ns units.\r
+  #  The default value of 100000 100 ns units is the same as 10 ms.\r
+  gPcAtChipsetPkgTokenSpaceGuid.PcdHpetDefaultTimerPeriod|100000|UINT64|0x0000000B\r
+  \r
+  ## This PCD specifies the base address of the HPET timer.\r
+  gPcAtChipsetPkgTokenSpaceGuid.PcdIoApicBaseAddress|0xFEC00000|UINT32|0x0000000C\r
+  
\ No newline at end of file
index 576ad85..68a644a 100644 (file)
   PciLib|MdePkg/Library/BasePciLibCf8/BasePciLibCf8.inf\r
   PciCf8Lib|MdePkg/Library/BasePciCf8Lib/BasePciCf8Lib.inf\r
   ResetSystemLib|PcAtChipsetPkg/Library/ResetSystemLib/ResetSystemLib.inf\r
+  IoApicLib|PcAtChipsetPkg/Library/BaseIoApicLib/BaseIoApicLib.inf\r
+  LocalApicLib|UefiCpuPkg/Library/BaseXApicLib/BaseXApicLib.inf\r
 \r
 [Components]\r
   PcAtChipsetPkg/8254TimerDxe/8254Timer.inf\r
+  PcAtChipsetPkg/HpetTimerDxe/HpetTimerDxe.inf\r
   PcAtChipsetPkg/8259InterruptControllerDxe/8259.inf\r
   PcAtChipsetPkg/IsaAcpiDxe/IsaAcpi.inf\r
   PcAtChipsetPkg/KbcResetDxe/Reset.inf\r
   PcAtChipsetPkg/Library/SerialIoLib/SerialIoLib.inf\r
   PcAtChipsetPkg/Library/ResetSystemLib/ResetSystemLib.inf\r
+  PcAtChipsetPkg/Library/BaseIoApicLib/BaseIoApicLib.inf\r
   PcAtChipsetPkg/PcatRealTimeClockRuntimeDxe/PcatRealTimeClockRuntimeDxe.inf\r
   PcAtChipsetPkg/PciHostBridgeDxe/PciHostBridgeDxe.inf\r