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
This commit is contained in:
mdkinney 2011-09-02 02:43:51 +00:00
parent 5f867ad00d
commit 986d1dfb08
9 changed files with 1441 additions and 0 deletions

View File

@ -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;
}

View File

@ -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

View File

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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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);
}

View File

@ -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

View File

@ -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