From 986d1dfb0813d6a7623531e85c2e2a7e1f956cf8 Mon Sep 17 00:00:00 2001 From: mdkinney Date: Fri, 2 Sep 2011 02:43:51 +0000 Subject: [PATCH] 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 --- PcAtChipsetPkg/HpetTimerDxe/HpetTimer.c | 860 ++++++++++++++++++ PcAtChipsetPkg/HpetTimerDxe/HpetTimerDxe.inf | 60 ++ PcAtChipsetPkg/Include/Library/IoApicLib.h | 102 +++ PcAtChipsetPkg/Include/Register/Hpet.h | 106 +++ PcAtChipsetPkg/Include/Register/IoApic.h | 86 ++ .../Library/BaseIoApicLib/BaseIoApicLib.inf | 38 + .../Library/BaseIoApicLib/IoApicLib.c | 158 ++++ PcAtChipsetPkg/PcAtChipsetPkg.dec | 27 + PcAtChipsetPkg/PcAtChipsetPkg.dsc | 4 + 9 files changed, 1441 insertions(+) create mode 100644 PcAtChipsetPkg/HpetTimerDxe/HpetTimer.c create mode 100644 PcAtChipsetPkg/HpetTimerDxe/HpetTimerDxe.inf create mode 100644 PcAtChipsetPkg/Include/Library/IoApicLib.h create mode 100644 PcAtChipsetPkg/Include/Register/Hpet.h create mode 100644 PcAtChipsetPkg/Include/Register/IoApic.h create mode 100644 PcAtChipsetPkg/Library/BaseIoApicLib/BaseIoApicLib.inf create mode 100644 PcAtChipsetPkg/Library/BaseIoApicLib/IoApicLib.c 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.
+ 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 + +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +/// +/// 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.
+# 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.
+ 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.
+ 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.
+ 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.
+# 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.
+ 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 + +#include + +#include +#include +#include +#include + +#include + +/** + 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 -- 2.39.2