summaryrefslogtreecommitdiff
path: root/PcAtChipsetPkg
diff options
context:
space:
mode:
authormdkinney <mdkinney@6f19259b-4bc3-4df7-8a09-765794883524>2011-09-02 02:43:51 +0000
committermdkinney <mdkinney@6f19259b-4bc3-4df7-8a09-765794883524>2011-09-02 02:43:51 +0000
commit986d1dfb0813d6a7623531e85c2e2a7e1f956cf8 (patch)
tree089ccd87cb8053f4836453c8fdfa30164d2c2496 /PcAtChipsetPkg
parent5f867ad00d499debbf22dee044b22e01b63e1ade (diff)
downloadedk2-platforms-986d1dfb0813d6a7623531e85c2e2a7e1f956cf8.tar.xz
Add generic HPET Timer DXE Driver and support libraries
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
Diffstat (limited to 'PcAtChipsetPkg')
-rw-r--r--PcAtChipsetPkg/HpetTimerDxe/HpetTimer.c860
-rw-r--r--PcAtChipsetPkg/HpetTimerDxe/HpetTimerDxe.inf60
-rw-r--r--PcAtChipsetPkg/Include/Library/IoApicLib.h102
-rw-r--r--PcAtChipsetPkg/Include/Register/Hpet.h106
-rw-r--r--PcAtChipsetPkg/Include/Register/IoApic.h86
-rw-r--r--PcAtChipsetPkg/Library/BaseIoApicLib/BaseIoApicLib.inf38
-rw-r--r--PcAtChipsetPkg/Library/BaseIoApicLib/IoApicLib.c158
-rw-r--r--PcAtChipsetPkg/PcAtChipsetPkg.dec27
-rw-r--r--PcAtChipsetPkg/PcAtChipsetPkg.dsc4
9 files changed, 1441 insertions, 0 deletions
diff --git a/PcAtChipsetPkg/HpetTimerDxe/HpetTimer.c b/PcAtChipsetPkg/HpetTimerDxe/HpetTimer.c
new file mode 100644
index 0000000000..025d504fc4
--- /dev/null
+++ b/PcAtChipsetPkg/HpetTimerDxe/HpetTimer.c
@@ -0,0 +1,860 @@
+/** @file
+ Timer Architectural Protocol module using High Precesion Event Timer (HPET)
+
+ Copyright (c) 2011, 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;
+
+///
+/// Accumulates HPET timer ticks to account for time passed when the
+/// HPET timer is disabled or when there is no timer notification function
+/// registered.
+///
+volatile UINT64 mTimerAccumulator = 0;
+
+///
+/// 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 TimerPeriod;
+
+ //
+ // 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 ();
+
+ //
+ // Accumulate time from the HPET main counter value
+ //
+ mTimerAccumulator += HpetRead (HPET_MAIN_COUNTER_OFFSET);
+
+ //
+ // Reset HPET main counter to 0
+ //
+ HpetWrite (HPET_MAIN_COUNTER_OFFSET, 0);
+
+ //
+ // Check to see if there is a registered notification function
+ //
+ if (mTimerNotifyFunction != NULL) {
+ //
+ // Compute time since last notification in 100 ns units (10 ^ -7)
+ //
+ TimerPeriod = DivU64x32 (
+ MultU64x32 (
+ mTimerAccumulator,
+ mHpetGeneralCapabilities.Bits.CounterClockPeriod
+ ),
+ 100000000
+ );
+ mTimerAccumulator = 0;
+
+ //
+ // Call registered notification function passing in the time since the last
+ // interrupt in 100 ns units.
+ //
+ mTimerNotifyFunction (TimerPeriod);
+ }
+}
+
+/**
+ 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 TimerCount;
+
+ //
+ // Disable HPET timer when adjusting the timer period
+ //
+ HpetEnable (FALSE);
+
+ if (TimerPeriod == 0) {
+ //
+ // If TimerPeriod is 0, then mask HPET Timer interrupts
+ //
+
+ if (mTimerConfiguration.Bits.MsiInterruptCapablity != 0) {
+ //
+ // 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.
+ //
+ TimerCount = DivU64x32 (
+ MultU64x32 (TimerPeriod, 100000000),
+ mHpetGeneralCapabilities.Bits.CounterClockPeriod
+ );
+
+ //
+ // Program the HPET Comparator with the number of ticks till the next interrupt
+ //
+ HpetWrite (HPET_TIMER_COMPARATOR_OFFSET + mTimerIndex * HPET_TIMER_STRIDE, TimerCount);
+
+ //
+ // Capture the number of ticks since the last HPET Timer interrupt before
+ // clearing the main counter. This value will be used in the next HPET
+ // timer interrupt handler to compute the total amount of time since the
+ // last HPET timer interrupt
+ //
+ mTimerAccumulator = HpetRead (HPET_MAIN_COUNTER_OFFSET);
+
+ //
+ // If the number of ticks since the last timer interrupt is greater than the
+ // timer period, reduce the number of ticks till the next interrupt to 1, so
+ // a timer interrupt will be generated as soon as the HPET counter is enabled.
+ //
+ if (mTimerAccumulator >= TimerCount) {
+ HpetWrite (HPET_MAIN_COUNTER_OFFSET, TimerCount - 1);
+ //
+ // Adjust the accumulator down by TimerCount ticks because TimerCount
+ // ticks will be added to the accumulator on the next interrupt
+ //
+ mTimerAccumulator -= TimerCount;
+ }
+
+ //
+ // Enable HPET Timer interrupt generation
+ //
+ if (mTimerConfiguration.Bits.MsiInterruptCapablity != 0) {
+ //
+ // Enable HPET MSI Interrupt
+ //
+ mTimerConfiguration.Bits.MsiInterruptEnable = 1;
+ } else {
+ //
+ // Enable timer interrupt through I/O APIC
+ //
+ 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
+ )
+{
+ EFI_TPL Tpl;
+ UINT64 TimerPeriod;
+
+ //
+ // Disable interrupts
+ //
+ Tpl = gBS->RaiseTPL (TPL_HIGH_LEVEL);
+
+ //
+ // Read the current HPET main counter value
+ //
+ mTimerAccumulator += HpetRead (HPET_MAIN_COUNTER_OFFSET);
+
+ //
+ // Reset HPET main counter to 0
+ //
+ HpetWrite (HPET_MAIN_COUNTER_OFFSET, 0);
+
+ //
+ // Check to see if there is a registered notification function
+ //
+ if (mTimerNotifyFunction != NULL) {
+ //
+ // Compute time since last interrupt in 100 ns units (10 ^ -7)
+ //
+ TimerPeriod = DivU64x32 (
+ MultU64x32 (
+ mTimerAccumulator,
+ mHpetGeneralCapabilities.Bits.CounterClockPeriod
+ ),
+ 100000000
+ );
+ mTimerAccumulator = 0;
+
+ //
+ // Call registered notification function passing in the time since the last
+ // interrupt in 100 ns units.
+ //
+ mTimerNotifyFunction (TimerPeriod);
+ }
+
+ //
+ // 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 = %08x\n", PcdGet32 (PcdHpetBaseAddress)));
+ DEBUG ((DEBUG_INFO, " HPET_GENERAL_CAPABILITIES_ID = %016lx\n", mHpetGeneralCapabilities));
+ DEBUG ((DEBUG_INFO, " HPET_GENERAL_CONFIGURATION = %016lx\n", mHpetGeneralConfiguration.Uint64));
+ DEBUG ((DEBUG_INFO, " HPET_GENERAL_INTERRUPT_STATUS = %016lx\n", HpetRead (HPET_GENERAL_INTERRUPT_STATUS_OFFSET)));
+ DEBUG ((DEBUG_INFO, " HPET_MAIN_COUNTER = %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 = %016lx\n", TimerIndex, HpetRead (HPET_TIMER_CONFIGURATION_OFFSET + TimerIndex * HPET_TIMER_STRIDE)));
+ DEBUG ((DEBUG_INFO, " HPET_TIMER%d_COMPARATOR = %016lx\n", TimerIndex, HpetRead (HPET_TIMER_COMPARATOR_OFFSET + TimerIndex * HPET_TIMER_STRIDE)));
+ DEBUG ((DEBUG_INFO, " HPET_TIMER%d_MSI_ROUTE = %016lx\n", TimerIndex, HpetRead (HPET_TIMER_MSI_ROUTE_OFFSET + TimerIndex * HPET_TIMER_STRIDE)));
+ }
+ );
+
+ //
+ // 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.MsiInterruptCapablity = 0;
+ 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 = 0;
+ HpetWrite (HPET_TIMER_CONFIGURATION_OFFSET + mTimerIndex * HPET_TIMER_STRIDE, mTimerConfiguration.Uint64);
+
+ //
+ // 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) {
+ 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 = %02x\n", mTimerIrq));
+ }
+ DEBUG ((DEBUG_INFO, "HPET Interrupt Vector = %02x\n", PcdGet8 (PcdHpetLocalApicVector)));
+ DEBUG ((DEBUG_INFO, "HPET_TIMER%d_CONFIGURATION = %016lx\n", mTimerIndex, HpetRead (HPET_TIMER_CONFIGURATION_OFFSET + mTimerIndex * HPET_TIMER_STRIDE)));
+ DEBUG ((DEBUG_INFO, "HPET_TIMER%d_COMPARATOR = %016lx\n", mTimerIndex, HpetRead (HPET_TIMER_COMPARATOR_OFFSET + mTimerIndex * HPET_TIMER_STRIDE)));
+ DEBUG ((DEBUG_INFO, "HPET_TIMER%d_MSI_ROUTE = %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;
+}
diff --git a/PcAtChipsetPkg/HpetTimerDxe/HpetTimerDxe.inf b/PcAtChipsetPkg/HpetTimerDxe/HpetTimerDxe.inf
new file mode 100644
index 0000000000..c22c3817e8
--- /dev/null
+++ b/PcAtChipsetPkg/HpetTimerDxe/HpetTimerDxe.inf
@@ -0,0 +1,60 @@
+## @file
+#
+# Copyright (c) 2011, 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.
+#
+##
+
+[Defines]
+ INF_VERSION = 0x00010005
+ BASE_NAME = HpetTimerDxe
+ FILE_GUID = 6CE6B0DE-781C-4f6c-B42D-98346C614BEC
+ MODULE_TYPE = DXE_DRIVER
+ VERSION_STRING = 1.0
+ ENTRY_POINT = TimerDriverInitialize
+
+#
+# The following information is for reference only and not required by the build tools.
+#
+# VALID_ARCHITECTURES = IA32 X64 IPF EBC
+#
+#
+
+[Sources]
+ HpetTimer.c
+
+[Packages]
+ MdePkg/MdePkg.dec
+ UefiCpuPkg/UefiCpuPkg.dec
+ PcAtChipsetPkg/PcAtChipsetPkg.dec
+
+[LibraryClasses]
+ PcdLib
+ IoLib
+ DebugLib
+ UefiDriverEntryPoint
+ UefiBootServicesTableLib
+ BaseLib
+ LocalApicLib
+ IoApicLib
+
+[Protocols]
+ gEfiTimerArchProtocolGuid # PROTOCOL ALWAYS_PRODUCED
+ gEfiCpuArchProtocolGuid # PROTOCOL ALWAYS_CONSUMED
+
+[FeaturePcd]
+ gPcAtChipsetPkgTokenSpaceGuid.PcdHpetMsiEnable
+
+[Pcd]
+ gPcAtChipsetPkgTokenSpaceGuid.PcdHpetBaseAddress
+ gPcAtChipsetPkgTokenSpaceGuid.PcdHpetLocalApicVector
+ gPcAtChipsetPkgTokenSpaceGuid.PcdHpetDefaultTimerPeriod
+
+[Depex]
+ gEfiCpuArchProtocolGuid
diff --git a/PcAtChipsetPkg/Include/Library/IoApicLib.h b/PcAtChipsetPkg/Include/Library/IoApicLib.h
new file mode 100644
index 0000000000..bd9054be53
--- /dev/null
+++ b/PcAtChipsetPkg/Include/Library/IoApicLib.h
@@ -0,0 +1,102 @@
+/** @file
+ Public include file for I/O APIC library.
+
+ I/O APIC library assumes I/O APIC is enabled. It does not
+ handles cases where I/O APIC is disabled.
+
+ Copyright (c) 2011, 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.
+
+**/
+
+/**
+ Read a 32-bit I/O APIC register.
+
+ If Index is >= 0x100, then ASSERT().
+
+ @param Index Specifies the I/O APIC register to read.
+
+ @return The 32-bit value read from the I/O APIC register specified by Index.
+**/
+UINT32
+EFIAPI
+IoApicRead (
+ IN UINTN Index
+ );
+
+/**
+ Write a 32-bit I/O APIC register.
+
+ If Index is >= 0x100, then ASSERT().
+
+ @param Index Specifies the I/O APIC register to write.
+ @param Value Specifies the value to write to the I/O APIC register specified by Index.
+
+ @return The 32-bit value written to I/O APIC register specified by Index.
+**/
+UINT32
+EFIAPI
+IoApicWrite (
+ IN UINTN Index,
+ IN UINT32 Value
+ );
+
+/**
+ Set the interrupt mask of an I/O APIC interrupt.
+
+ If Irq is larger than the maximum number I/O APIC redirection entries, then ASSERT().
+
+ @param Irq Specifies the I/O APIC interrupt to enable or disable.
+ @param Enable If TRUE, then enable the I/O APIC interrupt specified by Irq.
+ If FALSE, then disable the I/O APIC interrupt specified by Irq.
+**/
+VOID
+EFIAPI
+IoApicEnableInterrupt (
+ IN UINTN Irq,
+ IN BOOLEAN Enable
+ );
+
+/**
+ Configures an I/O APIC interrupt.
+
+ Configure an I/O APIC Redirection Table Entry to deliver an interrupt in physical
+ mode to the Local APIC of the currntly executing CPU. The default state of the
+ entry is for the interrupt to be disabled (masked). IoApicEnableInterrupts() must
+ be used to enable(unmask) the I/O APIC Interrupt.
+
+ If Irq is larger than the maximum number I/O APIC redirection entries, then ASSERT().
+ If Vector >= 0x100, then ASSERT().
+ If DeliveryMode is not supported, then ASSERT().
+
+ @param Irq Specifies the I/O APIC interrupt to initialize.
+ @param Vector The 8-bit interrupt vector associated with the I/O APIC
+ Interrupt. Must be in the range 0x10..0xFE.
+ @param DeliveryMode A 3-bit value that specifies how the recept of the I/O APIC
+ interrupt is handled. The only supported values are:
+ 0: IO_APIC_DELIVERY_MODE_FIXED
+ 1: IO_APIC_DELIVERY_MODE_LOWEST_PRIORITY
+ 2: IO_APIC_DELIVERY_MODE_SMI
+ 4: IO_APIC_DELIVERY_MODE_NMI
+ 5: IO_APIC_DELIVERY_MODE_INIT
+ 7: IO_APIC_DELIVERY_MODE_EXTINT
+ @param LevelTriggered TRUE specifies a level triggered interrupt.
+ FALSE specifies an edge triggered interrupt.
+ @param AssertionLevel TRUE specified an active high interrupt.
+ FALSE specifies an active low interrupt.
+**/
+VOID
+EFIAPI
+IoApicConfigureInterrupt (
+ IN UINTN Irq,
+ IN UINTN Vector,
+ IN UINTN DeliveryMode,
+ IN BOOLEAN LevelTriggered,
+ IN BOOLEAN AssertionLevel
+ );
diff --git a/PcAtChipsetPkg/Include/Register/Hpet.h b/PcAtChipsetPkg/Include/Register/Hpet.h
new file mode 100644
index 0000000000..2933767b5e
--- /dev/null
+++ b/PcAtChipsetPkg/Include/Register/Hpet.h
@@ -0,0 +1,106 @@
+/** @file
+ HPET register definitions from the IA-PC HPET (High Precision Event Timers)
+ Specification, Revision 1.0a, October 2004.
+
+ Copyright (c) 2011, 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.
+
+**/
+
+#ifndef __HPET_REGISTER_H__
+#define __HPET_REGISTER_H__
+
+///
+/// HPET General Register Offsets
+///
+#define HPET_GENERAL_CAPABILITIES_ID_OFFSET 0x000
+#define HPET_GENERAL_CONFIGURATION_OFFSET 0x010
+#define HPET_GENERAL_INTERRUPT_STATUS_OFFSET 0x020
+
+///
+/// HPET Timer Register Offsets
+///
+#define HPET_MAIN_COUNTER_OFFSET 0x0F0
+#define HPET_TIMER_CONFIGURATION_OFFSET 0x100
+#define HPET_TIMER_COMPARATOR_OFFSET 0x108
+#define HPET_TIMER_MSI_ROUTE_OFFSET 0x110
+
+///
+/// Stride between sets of HPET Timer Registers
+///
+#define HPET_TIMER_STRIDE 0x20
+
+#pragma pack(1)
+
+///
+/// HPET General Capabilities and ID Register
+///
+typedef union {
+ struct {
+ UINT32 Revision:8;
+ UINT32 NumberOfTimers:5;
+ UINT32 CounterSize:1;
+ UINT32 Reserved0:1;
+ UINT32 LegacyRoute:1;
+ UINT32 VendorId:16;
+ UINT32 CounterClockPeriod:32;
+ } Bits;
+ UINT64 Uint64;
+} HPET_GENERAL_CAPABILITIES_ID_REGISTER;
+
+///
+/// HPET General Configuration Register
+///
+typedef union {
+ struct {
+ UINT32 MainCounterEnable:1;
+ UINT32 LegacyRouteEnable:1;
+ UINT32 Reserved0:30;
+ UINT32 Reserved1:32;
+ } Bits;
+ UINT64 Uint64;
+} HPET_GENERAL_CONFIGURATION_REGISTER;
+
+///
+/// HPET Timer Configuration Register
+///
+typedef union {
+ struct {
+ UINT32 Reserved0:1;
+ UINT32 LevelTriggeredInterrupt:1;
+ UINT32 InterruptEnable:1;
+ UINT32 PeriodicInterruptEnable:1;
+ UINT32 PeriodicInterruptCapablity:1;
+ UINT32 CounterSizeCapablity:1;
+ UINT32 ValueSetEnable:1;
+ UINT32 Reserved1:1;
+ UINT32 CounterSizeEnable:1;
+ UINT32 InterruptRoute:5;
+ UINT32 MsiInterruptEnable:1;
+ UINT32 MsiInterruptCapablity:1;
+ UINT32 Reserved2:16;
+ UINT32 InterruptRouteCapability;
+ } Bits;
+ UINT64 Uint64;
+} HPET_TIMER_CONFIGURATION_REGISTER;
+
+///
+/// HPET Timer MSI Route Register
+///
+typedef union {
+ struct {
+ UINT32 Value:32;
+ UINT32 Address:32;
+ } Bits;
+ UINT64 Uint64;
+} HPET_TIMER_MSI_ROUTE_REGISTER;
+
+#pragma pack()
+
+#endif
diff --git a/PcAtChipsetPkg/Include/Register/IoApic.h b/PcAtChipsetPkg/Include/Register/IoApic.h
new file mode 100644
index 0000000000..d6e73be92b
--- /dev/null
+++ b/PcAtChipsetPkg/Include/Register/IoApic.h
@@ -0,0 +1,86 @@
+/** @file
+ I/O APIC Register Definitions from 82093AA I/O Advanced Programmable Interrupt
+ Controller (IOAPIC), 1996.
+
+ Copyright (c) 2011, 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.
+
+**/
+
+#ifndef __IO_APIC_H__
+#define __IO_APIC_H__
+
+///
+/// I/O APIC Register Offsets
+///
+#define IOAPIC_INDEX_OFFSET 0x00
+#define IOAPIC_DATA_OFFSET 0x10
+
+///
+/// I/O APIC Indirect Register Indexes
+///
+#define IO_APIC_IDENTIFICATION_REGISTER_INDEX 0x00
+#define IO_APIC_VERSION_REGISTER_INDEX 0x01
+#define IO_APIC_REDIRECTION_TABLE_ENTRY_INDEX 0x10
+
+///
+/// I/O APIC Interrupt Deliver Modes
+///
+#define IO_APIC_DELIVERY_MODE_FIXED 0
+#define IO_APIC_DELIVERY_MODE_LOWEST_PRIORITY 1
+#define IO_APIC_DELIVERY_MODE_SMI 2
+#define IO_APIC_DELIVERY_MODE_NMI 4
+#define IO_APIC_DELIVERY_MODE_INIT 5
+#define IO_APIC_DELIVERY_MODE_EXTINT 7
+
+#pragma pack(1)
+
+typedef union {
+ struct {
+ UINT32 Reserved0:24;
+ UINT32 Identification:4;
+ UINT32 Reserved1:4;
+ } Bits;
+ UINT32 Uint32;
+} IO_APIC_IDENTIFICATION_REGISTER;
+
+typedef union {
+ struct {
+ UINT32 Version:8;
+ UINT32 Reserved0:8;
+ UINT32 MaximumRedirectionEntry:8;
+ UINT32 Reserved1:8;
+ } Bits;
+ UINT32 Uint32;
+} IO_APIC_VERSION_REGISTER;
+
+typedef union {
+ struct {
+ UINT32 Vector: 8;
+ UINT32 DeliveryMode: 3;
+ UINT32 DestinationMode: 1;
+ UINT32 DeliveryStatus: 1;
+ UINT32 Polarity: 1;
+ UINT32 RemoteIRR: 1;
+ UINT32 TriggerMode: 1;
+ UINT32 Mask: 1;
+ UINT32 Reserved0: 15;
+ UINT32 Reserved1: 24;
+ UINT32 DestinationID: 8;
+ } Bits;
+ struct {
+ UINT32 Low;
+ UINT32 High;
+ } Uint32;
+ UINT64 Uint64;
+} IO_APIC_REDIRECTION_TABLE_ENTRY;
+
+#pragma pack()
+
+#endif
diff --git a/PcAtChipsetPkg/Library/BaseIoApicLib/BaseIoApicLib.inf b/PcAtChipsetPkg/Library/BaseIoApicLib/BaseIoApicLib.inf
new file mode 100644
index 0000000000..10d3977ff3
--- /dev/null
+++ b/PcAtChipsetPkg/Library/BaseIoApicLib/BaseIoApicLib.inf
@@ -0,0 +1,38 @@
+## @file
+# Library instance for I/O APIC library class
+#
+# Copyright (c) 2011, 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.
+#
+##
+
+[Defines]
+ INF_VERSION = 0x00010005
+ BASE_NAME = BaseIoApicLib
+ FILE_GUID = 58ED6E5A-E36A-462a-9ED6-6E62C9A26DF8
+ MODULE_TYPE = BASE
+ VERSION_STRING = 1.0
+ LIBRARY_CLASS = IoApicLib
+
+[Packages]
+ MdePkg/MdePkg.dec
+ UefiCpuPkg/UefiCpuPkg.dec
+ PcAtChipsetPkg/PcAtChipsetPkg.dec
+
+[LibraryClasses]
+ DebugLib
+ IoLib
+ PcdLib
+ LocalApicLib
+
+[Sources]
+ IoApicLib.c
+
+[Pcd]
+ gPcAtChipsetPkgTokenSpaceGuid.PcdIoApicBaseAddress
diff --git a/PcAtChipsetPkg/Library/BaseIoApicLib/IoApicLib.c b/PcAtChipsetPkg/Library/BaseIoApicLib/IoApicLib.c
new file mode 100644
index 0000000000..42b3f21ec1
--- /dev/null
+++ b/PcAtChipsetPkg/Library/BaseIoApicLib/IoApicLib.c
@@ -0,0 +1,158 @@
+/** @file
+ I/O APIC library.
+
+ I/O APIC library assumes I/O APIC is enabled. It does not
+ handles cases where I/O APIC is disabled.
+
+ Copyright (c) 2011, 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 <Base.h>
+
+#include <Library/IoApicLib.h>
+
+#include <Library/DebugLib.h>
+#include <Library/PcdLib.h>
+#include <Library/IoLib.h>
+#include <Library/LocalApicLib.h>
+
+#include <Register/IoApic.h>
+
+/**
+ Read a 32-bit I/O APIC register.
+
+ If Index is >= 0x100, then ASSERT().
+
+ @param Index Specifies the I/O APIC register to read.
+
+ @return The 32-bit value read from the I/O APIC register specified by Index.
+**/
+UINT32
+EFIAPI
+IoApicRead (
+ IN UINTN Index
+ )
+{
+ ASSERT (Index < 0x100);
+ MmioWrite8 (PcdGet32 (PcdIoApicBaseAddress) + IOAPIC_INDEX_OFFSET, (UINT8)Index);
+ return MmioRead32 (PcdGet32 (PcdIoApicBaseAddress) + IOAPIC_DATA_OFFSET);
+}
+
+/**
+ Write a 32-bit I/O APIC register.
+
+ If Index is >= 0x100, then ASSERT().
+
+ @param Index Specifies the I/O APIC register to write.
+ @param Value Specifies the value to write to the I/O APIC register specified by Index.
+
+ @return The 32-bit value written to I/O APIC register specified by Index.
+**/
+UINT32
+EFIAPI
+IoApicWrite (
+ IN UINTN Index,
+ IN UINT32 Value
+ )
+{
+ ASSERT (Index < 0x100);
+ MmioWrite8 (PcdGet32 (PcdIoApicBaseAddress) + IOAPIC_INDEX_OFFSET, (UINT8)Index);
+ return MmioWrite32 (PcdGet32 (PcdIoApicBaseAddress) + IOAPIC_DATA_OFFSET, Value);
+}
+
+/**
+ Set the interrupt mask of an I/O APIC interrupt.
+
+ If Irq is larger than the maximum number I/O APIC redirection entries, then ASSERT().
+
+ @param Irq Specifies the I/O APIC interrupt to enable or disable.
+ @param Enable If TRUE, then enable the I/O APIC interrupt specified by Irq.
+ If FALSE, then disable the I/O APIC interrupt specified by Irq.
+**/
+VOID
+EFIAPI
+IoApicEnableInterrupt (
+ IN UINTN Irq,
+ IN BOOLEAN Enable
+ )
+{
+ IO_APIC_VERSION_REGISTER Version;
+ IO_APIC_REDIRECTION_TABLE_ENTRY Entry;
+
+ Version.Uint32 = IoApicRead (IO_APIC_VERSION_REGISTER_INDEX);
+ ASSERT (Version.Bits.MaximumRedirectionEntry < 0xF0);
+ ASSERT (Irq <= Version.Bits.MaximumRedirectionEntry);
+
+ Entry.Uint32.Low = IoApicRead (IO_APIC_REDIRECTION_TABLE_ENTRY_INDEX + Irq * 2);
+ Entry.Bits.Mask = Enable ? 0 : 1;
+ IoApicWrite (IO_APIC_REDIRECTION_TABLE_ENTRY_INDEX + Irq * 2, Entry.Uint32.Low);
+}
+
+/**
+ Configures an I/O APIC interrupt.
+
+ Configure an I/O APIC Redirection Table Entry to deliver an interrupt in physical
+ mode to the Local APIC of the currntly executing CPU. The default state of the
+ entry is for the interrupt to be disabled (masked). IoApicEnableInterrupts() must
+ be used to enable(unmask) the I/O APIC Interrupt.
+
+ If Irq is larger than the maximum number I/O APIC redirection entries, then ASSERT().
+ If Vector >= 0x100, then ASSERT().
+ If DeliveryMode is not supported, then ASSERT().
+
+ @param Irq Specifies the I/O APIC interrupt to initialize.
+ @param Vector The 8-bit interrupt vector associated with the I/O APIC
+ Interrupt. Must be in the range 0x10..0xFE.
+ @param DeliveryMode A 3-bit value that specifies how the recept of the I/O APIC
+ interrupt is handled. The only supported values are:
+ 0: IO_APIC_DELIVERY_MODE_FIXED
+ 1: IO_APIC_DELIVERY_MODE_LOWEST_PRIORITY
+ 2: IO_APIC_DELIVERY_MODE_SMI
+ 4: IO_APIC_DELIVERY_MODE_NMI
+ 5: IO_APIC_DELIVERY_MODE_INIT
+ 7: IO_APIC_DELIVERY_MODE_EXTINT
+ @param LevelTriggered TRUE specifies a level triggered interrupt.
+ FALSE specifies an edge triggered interrupt.
+ @param AssertionLevel TRUE specified an active high interrupt.
+ FALSE specifies an active low interrupt.
+**/
+VOID
+EFIAPI
+IoApicConfigureInterrupt (
+ IN UINTN Irq,
+ IN UINTN Vector,
+ IN UINTN DeliveryMode,
+ IN BOOLEAN LevelTriggered,
+ IN BOOLEAN AssertionLevel
+ )
+{
+ IO_APIC_VERSION_REGISTER Version;
+ IO_APIC_REDIRECTION_TABLE_ENTRY Entry;
+
+ Version.Uint32 = IoApicRead (IO_APIC_VERSION_REGISTER_INDEX);
+ ASSERT (Version.Bits.MaximumRedirectionEntry < 0xF0);
+ ASSERT (Irq <= Version.Bits.MaximumRedirectionEntry);
+ ASSERT (Vector <= 0xFF);
+ ASSERT (DeliveryMode < 8 && DeliveryMode != 6 && DeliveryMode != 3);
+
+ Entry.Uint32.Low = IoApicRead (IO_APIC_REDIRECTION_TABLE_ENTRY_INDEX + Irq * 2);
+ Entry.Bits.Vector = (UINT8)Vector;
+ Entry.Bits.DeliveryMode = (UINT32)DeliveryMode;
+ Entry.Bits.DestinationMode = 0;
+ Entry.Bits.Polarity = AssertionLevel ? 0 : 1;
+ Entry.Bits.TriggerMode = LevelTriggered ? 1 : 0;
+ Entry.Bits.Mask = 1;
+ IoApicWrite (IO_APIC_REDIRECTION_TABLE_ENTRY_INDEX + Irq * 2, Entry.Uint32.Low);
+
+ Entry.Uint32.High = IoApicRead (IO_APIC_REDIRECTION_TABLE_ENTRY_INDEX + Irq * 2 + 1);
+ Entry.Bits.DestinationID = GetApicId ();
+ IoApicWrite (IO_APIC_REDIRECTION_TABLE_ENTRY_INDEX + Irq * 2 + 1, Entry.Uint32.High);
+}
diff --git a/PcAtChipsetPkg/PcAtChipsetPkg.dec b/PcAtChipsetPkg/PcAtChipsetPkg.dec
index 93beb0d76c..6c6e714f93 100644
--- a/PcAtChipsetPkg/PcAtChipsetPkg.dec
+++ b/PcAtChipsetPkg/PcAtChipsetPkg.dec
@@ -22,9 +22,22 @@
PACKAGE_GUID = B728689A-52D3-4b8c-AE89-2CE5514CC6DC
PACKAGE_VERSION = 0.1
+[Includes]
+ Include
+
+[LibraryClasses]
+ ## @libraryclass Provides functions to manage I/O APIC Redirection Table Entries.
+ #
+ IoApicLib|Include/Library/IoApicLib.h
+
[Guids]
gPcAtChipsetPkgTokenSpaceGuid = { 0x326ae723, 0xae32, 0x4589, { 0x98, 0xb8, 0xca, 0xc2, 0x3c, 0xdc, 0xc1, 0xb1 } }
+[PcdsFeatureFlag]
+ ## If TRUE, then the HPET Timer will be configured to use MSI interrupts if the HPET timer supports them.
+ # If FALSE, then the HPET Timer will be configued to use I/O APIC interrupts.
+ gPcAtChipsetPkgTokenSpaceGuid.PcdHpetMsiEnable|TRUE|BOOLEAN|0x00001000
+
[PcdsFixedAtBuild, PcdsDynamic, PcdsDynamicEx, PcdsPatchableInModule]
## Pcd8259LegacyModeMask defines the default mask value for platform. This value is determined
# 1) If platform only support pure UEFI, value should be set to 0xFFFF or 0xFFFE;
@@ -63,3 +76,17 @@
## This PCD specifies whether we need enable IsaAcpiFloppyB device.
gPcAtChipsetPkgTokenSpaceGuid.PcdIsaAcpiFloppyBEnable|TRUE|BOOLEAN|0x00000008
+
+ ## This PCD specifies the base address of the HPET timer.
+ gPcAtChipsetPkgTokenSpaceGuid.PcdHpetBaseAddress|0xFED00000|UINT32|0x00000009
+
+ ## This PCD specifies the Local APIC Interrupt Vector for the HPET Timer.
+ gPcAtChipsetPkgTokenSpaceGuid.PcdHpetLocalApicVector|0x40|UINT8|0x0000000A
+
+ ## This PCD specifies the defaut period of the HPET Timer in 100 ns units.
+ # The default value of 100000 100 ns units is the same as 10 ms.
+ gPcAtChipsetPkgTokenSpaceGuid.PcdHpetDefaultTimerPeriod|100000|UINT64|0x0000000B
+
+ ## This PCD specifies the base address of the HPET timer.
+ gPcAtChipsetPkgTokenSpaceGuid.PcdIoApicBaseAddress|0xFEC00000|UINT32|0x0000000C
+ \ No newline at end of file
diff --git a/PcAtChipsetPkg/PcAtChipsetPkg.dsc b/PcAtChipsetPkg/PcAtChipsetPkg.dsc
index 576ad859dc..68a644a2cb 100644
--- a/PcAtChipsetPkg/PcAtChipsetPkg.dsc
+++ b/PcAtChipsetPkg/PcAtChipsetPkg.dsc
@@ -43,13 +43,17 @@
PciLib|MdePkg/Library/BasePciLibCf8/BasePciLibCf8.inf
PciCf8Lib|MdePkg/Library/BasePciCf8Lib/BasePciCf8Lib.inf
ResetSystemLib|PcAtChipsetPkg/Library/ResetSystemLib/ResetSystemLib.inf
+ IoApicLib|PcAtChipsetPkg/Library/BaseIoApicLib/BaseIoApicLib.inf
+ LocalApicLib|UefiCpuPkg/Library/BaseXApicLib/BaseXApicLib.inf
[Components]
PcAtChipsetPkg/8254TimerDxe/8254Timer.inf
+ PcAtChipsetPkg/HpetTimerDxe/HpetTimerDxe.inf
PcAtChipsetPkg/8259InterruptControllerDxe/8259.inf
PcAtChipsetPkg/IsaAcpiDxe/IsaAcpi.inf
PcAtChipsetPkg/KbcResetDxe/Reset.inf
PcAtChipsetPkg/Library/SerialIoLib/SerialIoLib.inf
PcAtChipsetPkg/Library/ResetSystemLib/ResetSystemLib.inf
+ PcAtChipsetPkg/Library/BaseIoApicLib/BaseIoApicLib.inf
PcAtChipsetPkg/PcatRealTimeClockRuntimeDxe/PcatRealTimeClockRuntimeDxe.inf
PcAtChipsetPkg/PciHostBridgeDxe/PciHostBridgeDxe.inf