blob: fe0442b4ce12fad5edc0c2f227dfd6bb67b3e22a [file] [log] [blame]
/*++
Copyright (c) 2013-2023, Arm Ltd. All rights reserved.<BR>
SPDX-License-Identifier: BSD-2-Clause-Patent
--*/
#include "ArmGicDxe.h"
#include "Uefi/UefiBaseType.h"
// Making this global saves a few bytes in image size
EFI_HANDLE gHardwareInterruptHandle = NULL;
// Notifications
EFI_EVENT EfiExitBootServicesEvent = (EFI_EVENT)NULL;
EFI_CPU_ARCH_PROTOCOL *gCpuArch;
/**
*
* Return whether the Source interrupt index refers to an extended shared
* interrupt.
*
* @param Source The source intid to test
*
* @return True if Source is an extended SPI intid, false otherwise.
*/
BOOLEAN
GicCommonSourceIsExtSpi (
IN UINTN Source
)
{
return Source >= ARM_GIC_ARCH_EXT_SPI_MIN && Source <= ARM_GIC_ARCH_EXT_SPI_MAX;
}
/**
* Return whether this is a special interrupt.
*
* @param Source The source intid to test
*
* @return True if Source is a special interrupt intid, false otherwise.
*/
BOOLEAN
GicCommonSourceIsSpecialInterrupt (
IN UINTN Source
)
{
return (Source >= 1020) && (Source <= 1023);
}
/**
*
* Return whether the Source interrupt index refers to a shared interrupt (SPI)
*
* @param Source The source intid to test
*
* @return True if Source is a SPI intid, false otherwise.
*/
BOOLEAN
GicCommonSourceIsSpi (
IN UINTN Source
)
{
return (Source >= ARM_GIC_ARCH_SPI_MIN) && (Source <= ARM_GIC_ARCH_SPI_MAX);
}
/**
Calculate GICD_ICFGRn base address and corresponding bit
field Int_config[1] of the GIC distributor register.
@param Source Hardware source of the interrupt.
@param RegAddress Corresponding GICD_ICFGRn base address.
@param Config1Bit Bit number of F Int_config[1] bit in the register.
@retval EFI_SUCCESS Source interrupt supported.
@retval EFI_UNSUPPORTED Source interrupt is not supported.
**/
EFI_STATUS
GicCommonGetDistributorIcfgBaseAndBit (
IN HARDWARE_INTERRUPT_SOURCE Source,
OUT UINTN *RegAddress,
OUT UINTN *Config1Bit
)
{
UINTN RegIndex;
UINTN Field;
UINTN RegOffset;
HARDWARE_INTERRUPT_SOURCE AdjustedSource;
// Translate ESPI sources into the SPI range for indexing purposes.
AdjustedSource = Source & ~(ARM_GIC_ARCH_EXT_SPI_MIN);
RegOffset = (GicCommonSourceIsExtSpi (Source)) ? ARM_GIC_ICDICFR_E : ARM_GIC_ICDICFR;
RegIndex = AdjustedSource / ARM_GIC_ICDICFR_F_STRIDE; // NOTE: truncation is significant
Field = AdjustedSource % ARM_GIC_ICDICFR_F_STRIDE;
*RegAddress = (UINTN)PcdGet64 (PcdGicDistributorBase)
+ RegOffset
+ (ARM_GIC_ICDICFR_BYTES * RegIndex);
*Config1Bit = ((Field * ARM_GIC_ICDICFR_F_WIDTH)
+ ARM_GIC_ICDICFR_F_CONFIG1_BIT);
return EFI_SUCCESS;
}
/**
Register Handler for the specified interrupt source.
@param This Instance pointer for this protocol
@param Source Hardware source of the interrupt
@param Handler Callback for interrupt. NULL to unregister
@param HandlerDest Address of where to store Handler.
@retval EFI_SUCCESS Source was updated to support Handler.
@retval EFI_DEVICE_ERROR Hardware could not be programmed.
**/
EFI_STATUS
EFIAPI
GicCommonRegisterInterruptSource (
IN EFI_HARDWARE_INTERRUPT_PROTOCOL *This,
IN HARDWARE_INTERRUPT_SOURCE Source,
IN HARDWARE_INTERRUPT_HANDLER Handler,
IN HARDWARE_INTERRUPT_HANDLER *HandlerDest
)
{
EFI_STATUS Status;
if (HandlerDest == NULL) {
return EFI_INVALID_PARAMETER;
}
if ((Handler == NULL) && (*HandlerDest == NULL)) {
return EFI_INVALID_PARAMETER;
}
if ((Handler != NULL) && (*HandlerDest != NULL)) {
return EFI_ALREADY_STARTED;
}
if (NULL == Handler) {
// Removing the handler - disable the interrupt first.
Status = This->DisableInterruptSource (This, Source);
*HandlerDest = Handler;
return Status;
} else {
// Registering a handler - set up the handler before enabling.
*HandlerDest = Handler;
return This->EnableInterruptSource (This, Source);
}
}
EFI_STATUS
GicCommonInstallAndRegisterInterruptService (
IN EFI_HARDWARE_INTERRUPT_PROTOCOL *InterruptProtocol,
IN EFI_HARDWARE_INTERRUPT2_PROTOCOL *Interrupt2Protocol,
IN EFI_CPU_INTERRUPT_HANDLER InterruptHandler,
IN EFI_EVENT_NOTIFY ExitBootServicesEvent
)
{
EFI_STATUS Status;
// Register to receive interrupts
Status = gCpuArch->RegisterInterruptHandler (gCpuArch, ARM_ARCH_EXCEPTION_IRQ, InterruptHandler);
if (EFI_ERROR (Status)) {
DEBUG ((DEBUG_ERROR, "%a: Cpu->RegisterInterruptHandler() - %r\n", __func__, Status));
return Status;
}
Status = gBS->InstallMultipleProtocolInterfaces (
&gHardwareInterruptHandle,
&gHardwareInterruptProtocolGuid,
InterruptProtocol,
&gHardwareInterrupt2ProtocolGuid,
Interrupt2Protocol,
NULL
);
if (EFI_ERROR (Status)) {
return Status;
}
// Register for an ExitBootServicesEvent
Status = gBS->CreateEvent (
EVT_SIGNAL_EXIT_BOOT_SERVICES,
TPL_NOTIFY,
ExitBootServicesEvent,
NULL,
&EfiExitBootServicesEvent
);
return Status;
}
/**
Return the GIC CPU Interrupt Interface ID.
@param GicInterruptInterfaceBase Base address of the GIC Interrupt Interface.
@retval CPU Interface Identification information.
**/
UINT32
EFIAPI
ArmGicGetInterfaceIdentification (
IN UINTN GicInterruptInterfaceBase
)
{
// Read the GIC Identification Register
return MmioRead32 (GicInterruptInterfaceBase + ARM_GIC_ICCIIDR);
}
UINTN
EFIAPI
ArmGicGetMaxNumInterrupts (
IN UINTN GicDistributorBase
)
{
UINTN ItLines;
ItLines = MmioRead32 (GicDistributorBase + ARM_GIC_ICDICTR) & 0x1F;
//
// Interrupt ID 1020-1023 are reserved.
//
return (ItLines == 0x1f) ? 1020 : 32 * (ItLines + 1);
}
VOID
EFIAPI
ArmGicDisableDistributor (
IN UINTN GicDistributorBase
)
{
// Disable Gic Distributor
MmioWrite32 (GicDistributorBase + ARM_GIC_ICDDCR, 0x0);
}