The existing HardwareInterrupt protocol lacked a means to configure the level/edge properties of an interrupt. The new HardwareInterrupt2 protocol introduced this capability. This patch updates the GIC drivers to provide the new interfaces. The changes comprise: Update to use HardwareInterrupt2 protocol Additions to register info in ArmGicLib.h Added new functionality (GetTriggerType and SetTriggerType) The requirement for this change derives from a problem detected on ARM Juno boards, but the change is of generic (ARM) relevance. This commit is in response to review on the mailing list and, as suggested there, rolls Girish's updates onto Ard's original example. Contributed-under: TianoCore Contribution Agreement 1.1 Signed-off-by: Ard Biesheuvel <ard.biesheuvel@linaro.org> Signed-off-by: Girish Pathak <girish.pathak@arm.com> Signed-off-by: Evan Lloyd <evan.lloyd@arm.com> Tested-by: Girish Pathak <girish.pathak@arm.com> Reviewed-by: Leif Lindholm <leif.lindholm@linaro.org>
188 lines
5.4 KiB
C
188 lines
5.4 KiB
C
/*++
|
|
|
|
Copyright (c) 2013-2017, ARM Ltd. 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 "ArmGicDxe.h"
|
|
|
|
VOID
|
|
EFIAPI
|
|
IrqInterruptHandler (
|
|
IN EFI_EXCEPTION_TYPE InterruptType,
|
|
IN EFI_SYSTEM_CONTEXT SystemContext
|
|
);
|
|
|
|
VOID
|
|
EFIAPI
|
|
ExitBootServicesEvent (
|
|
IN EFI_EVENT Event,
|
|
IN VOID *Context
|
|
);
|
|
|
|
// Making this global saves a few bytes in image size
|
|
EFI_HANDLE gHardwareInterruptHandle = NULL;
|
|
|
|
// Notifications
|
|
EFI_EVENT EfiExitBootServicesEvent = (EFI_EVENT)NULL;
|
|
|
|
// Maximum Number of Interrupts
|
|
UINTN mGicNumInterrupts = 0;
|
|
|
|
HARDWARE_INTERRUPT_HANDLER *gRegisteredInterruptHandlers = NULL;
|
|
|
|
|
|
/**
|
|
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
|
|
GicGetDistributorIcfgBaseAndBit (
|
|
IN HARDWARE_INTERRUPT_SOURCE Source,
|
|
OUT UINTN *RegAddress,
|
|
OUT UINTN *Config1Bit
|
|
)
|
|
{
|
|
UINTN RegIndex;
|
|
UINTN Field;
|
|
|
|
if (Source >= mGicNumInterrupts) {
|
|
ASSERT(Source < mGicNumInterrupts);
|
|
return EFI_UNSUPPORTED;
|
|
}
|
|
|
|
RegIndex = Source / ARM_GIC_ICDICFR_F_STRIDE; // NOTE: truncation is significant
|
|
Field = Source % ARM_GIC_ICDICFR_F_STRIDE;
|
|
*RegAddress = PcdGet64 (PcdGicDistributorBase)
|
|
+ ARM_GIC_ICDICFR
|
|
+ (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
|
|
|
|
@retval EFI_SUCCESS Source was updated to support Handler.
|
|
@retval EFI_DEVICE_ERROR Hardware could not be programmed.
|
|
|
|
**/
|
|
EFI_STATUS
|
|
EFIAPI
|
|
RegisterInterruptSource (
|
|
IN EFI_HARDWARE_INTERRUPT_PROTOCOL *This,
|
|
IN HARDWARE_INTERRUPT_SOURCE Source,
|
|
IN HARDWARE_INTERRUPT_HANDLER Handler
|
|
)
|
|
{
|
|
if (Source >= mGicNumInterrupts) {
|
|
ASSERT(FALSE);
|
|
return EFI_UNSUPPORTED;
|
|
}
|
|
|
|
if ((Handler == NULL) && (gRegisteredInterruptHandlers[Source] == NULL)) {
|
|
return EFI_INVALID_PARAMETER;
|
|
}
|
|
|
|
if ((Handler != NULL) && (gRegisteredInterruptHandlers[Source] != NULL)) {
|
|
return EFI_ALREADY_STARTED;
|
|
}
|
|
|
|
gRegisteredInterruptHandlers[Source] = Handler;
|
|
|
|
// If the interrupt handler is unregistered then disable the interrupt
|
|
if (NULL == Handler){
|
|
return This->DisableInterruptSource (This, Source);
|
|
} else {
|
|
return This->EnableInterruptSource (This, Source);
|
|
}
|
|
}
|
|
|
|
EFI_STATUS
|
|
InstallAndRegisterInterruptService (
|
|
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;
|
|
EFI_CPU_ARCH_PROTOCOL *Cpu;
|
|
CONST UINTN RihArraySize =
|
|
(sizeof(HARDWARE_INTERRUPT_HANDLER) * mGicNumInterrupts);
|
|
|
|
// Initialize the array for the Interrupt Handlers
|
|
gRegisteredInterruptHandlers = AllocateZeroPool (RihArraySize);
|
|
if (gRegisteredInterruptHandlers == NULL) {
|
|
return EFI_OUT_OF_RESOURCES;
|
|
}
|
|
|
|
Status = gBS->InstallMultipleProtocolInterfaces (
|
|
&gHardwareInterruptHandle,
|
|
&gHardwareInterruptProtocolGuid,
|
|
InterruptProtocol,
|
|
&gHardwareInterrupt2ProtocolGuid,
|
|
Interrupt2Protocol,
|
|
NULL
|
|
);
|
|
if (EFI_ERROR (Status)) {
|
|
return Status;
|
|
}
|
|
|
|
// Get the CPU protocol that this driver requires.
|
|
Status = gBS->LocateProtocol (&gEfiCpuArchProtocolGuid, NULL, (VOID **)&Cpu);
|
|
if (EFI_ERROR (Status)) {
|
|
return Status;
|
|
}
|
|
|
|
// Unregister the default exception handler.
|
|
Status = Cpu->RegisterInterruptHandler (Cpu, ARM_ARCH_EXCEPTION_IRQ, NULL);
|
|
if (EFI_ERROR (Status)) {
|
|
return Status;
|
|
}
|
|
|
|
// Register to receive interrupts
|
|
Status = Cpu->RegisterInterruptHandler (
|
|
Cpu,
|
|
ARM_ARCH_EXCEPTION_IRQ,
|
|
InterruptHandler
|
|
);
|
|
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;
|
|
}
|