diff options
author | Ard Biesheuvel <ard.biesheuvel@linaro.org> | 2016-09-01 17:21:56 +0100 |
---|---|---|
committer | Leif Lindholm <leif.lindholm@linaro.org> | 2017-10-05 21:23:39 +0100 |
commit | 8659306a2e8f7dfceebce2be358b564f7aff6cbb (patch) | |
tree | 324bf9dee90853f664fa16d58e5407d3f53fdb7e /ArmPkg/Drivers | |
parent | e40c72893669dd5246d53288d13301192e23c670 (diff) | |
download | edk2-8659306a2e8f7dfceebce2be358b564f7aff6cbb.tar.gz edk2-8659306a2e8f7dfceebce2be358b564f7aff6cbb.tar.bz2 edk2-8659306a2e8f7dfceebce2be358b564f7aff6cbb.zip |
ArmPkg/ArmGicDxe: Expose HardwareInterrupt2 protocol
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>
Diffstat (limited to 'ArmPkg/Drivers')
-rw-r--r-- | ArmPkg/Drivers/ArmGic/ArmGicCommonDxe.c | 43 | ||||
-rw-r--r-- | ArmPkg/Drivers/ArmGic/ArmGicDxe.h | 23 | ||||
-rw-r--r-- | ArmPkg/Drivers/ArmGic/ArmGicDxe.inf | 3 | ||||
-rw-r--r-- | ArmPkg/Drivers/ArmGic/GicV2/ArmGicV2Dxe.c | 137 | ||||
-rw-r--r-- | ArmPkg/Drivers/ArmGic/GicV3/ArmGicV3Dxe.c | 136 |
5 files changed, 341 insertions, 1 deletions
diff --git a/ArmPkg/Drivers/ArmGic/ArmGicCommonDxe.c b/ArmPkg/Drivers/ArmGic/ArmGicCommonDxe.c index 7ca3ca8f52..bff8d983cf 100644 --- a/ArmPkg/Drivers/ArmGic/ArmGicCommonDxe.c +++ b/ArmPkg/Drivers/ArmGic/ArmGicCommonDxe.c @@ -39,6 +39,46 @@ 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.
@@ -84,6 +124,7 @@ RegisterInterruptSource ( 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
)
@@ -103,6 +144,8 @@ InstallAndRegisterInterruptService ( &gHardwareInterruptHandle,
&gHardwareInterruptProtocolGuid,
InterruptProtocol,
+ &gHardwareInterrupt2ProtocolGuid,
+ Interrupt2Protocol,
NULL
);
if (EFI_ERROR (Status)) {
diff --git a/ArmPkg/Drivers/ArmGic/ArmGicDxe.h b/ArmPkg/Drivers/ArmGic/ArmGicDxe.h index 76945282d9..610ffacc7e 100644 --- a/ArmPkg/Drivers/ArmGic/ArmGicDxe.h +++ b/ArmPkg/Drivers/ArmGic/ArmGicDxe.h @@ -24,6 +24,7 @@ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. #include <Protocol/Cpu.h>
#include <Protocol/HardwareInterrupt.h>
+#include <Protocol/HardwareInterrupt2.h>
extern UINTN mGicNumInterrupts;
extern HARDWARE_INTERRUPT_HANDLER *gRegisteredInterruptHandlers;
@@ -32,6 +33,7 @@ extern HARDWARE_INTERRUPT_HANDLER *gRegisteredInterruptHandlers; 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
);
@@ -58,4 +60,25 @@ GicV3DxeInitialize ( IN EFI_SYSTEM_TABLE *SystemTable
);
+
+// Shared code
+
+/**
+ 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
+ );
+
#endif
diff --git a/ArmPkg/Drivers/ArmGic/ArmGicDxe.inf b/ArmPkg/Drivers/ArmGic/ArmGicDxe.inf index e554301c4b..d5921533fb 100644 --- a/ArmPkg/Drivers/ArmGic/ArmGicDxe.inf +++ b/ArmPkg/Drivers/ArmGic/ArmGicDxe.inf @@ -1,7 +1,7 @@ #/** @file
#
# Copyright (c) 2008 - 2010, Apple Inc. All rights reserved.<BR>
-# Copyright (c) 2012 - 2015, ARM Ltd. All rights reserved.<BR>
+# Copyright (c) 2012 - 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
@@ -48,6 +48,7 @@ [Protocols]
gHardwareInterruptProtocolGuid
+ gHardwareInterrupt2ProtocolGuid
gEfiCpuArchProtocolGuid
[Pcd.common]
diff --git a/ArmPkg/Drivers/ArmGic/GicV2/ArmGicV2Dxe.c b/ArmPkg/Drivers/ArmGic/GicV2/ArmGicV2Dxe.c index 3d5d034efb..7717ebe753 100644 --- a/ArmPkg/Drivers/ArmGic/GicV2/ArmGicV2Dxe.c +++ b/ArmPkg/Drivers/ArmGic/GicV2/ArmGicV2Dxe.c @@ -29,6 +29,7 @@ Abstract: #define ARM_GIC_DEFAULT_PRIORITY 0x80
extern EFI_HARDWARE_INTERRUPT_PROTOCOL gHardwareInterruptV2Protocol;
+extern EFI_HARDWARE_INTERRUPT2_PROTOCOL gHardwareInterrupt2V2Protocol;
STATIC UINT32 mGicInterruptInterfaceBase;
STATIC UINT32 mGicDistributorBase;
@@ -199,6 +200,141 @@ EFI_HARDWARE_INTERRUPT_PROTOCOL gHardwareInterruptV2Protocol = { };
/**
+ Get interrupt trigger type of an interrupt
+
+ @param This Instance pointer for this protocol
+ @param Source Hardware source of the interrupt.
+ @param TriggerType Returns interrupt trigger type.
+
+ @retval EFI_SUCCESS Source interrupt supported.
+ @retval EFI_UNSUPPORTED Source interrupt is not supported.
+**/
+STATIC
+EFI_STATUS
+EFIAPI
+GicV2GetTriggerType (
+ IN EFI_HARDWARE_INTERRUPT2_PROTOCOL *This,
+ IN HARDWARE_INTERRUPT_SOURCE Source,
+ OUT EFI_HARDWARE_INTERRUPT2_TRIGGER_TYPE *TriggerType
+ )
+{
+ UINTN RegAddress;
+ UINTN Config1Bit;
+ EFI_STATUS Status;
+
+ Status = GicGetDistributorIcfgBaseAndBit (
+ Source,
+ &RegAddress,
+ &Config1Bit
+ );
+
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ if ((MmioRead32 (RegAddress) & (1 << Config1Bit)) == 0) {
+ *TriggerType = EFI_HARDWARE_INTERRUPT2_TRIGGER_LEVEL_HIGH;
+ } else {
+ *TriggerType = EFI_HARDWARE_INTERRUPT2_TRIGGER_EDGE_RISING;
+ }
+
+ return EFI_SUCCESS;
+}
+
+/**
+ Set interrupt trigger type of an interrupt
+
+ @param This Instance pointer for this protocol
+ @param Source Hardware source of the interrupt.
+ @param TriggerType Interrupt trigger type.
+
+ @retval EFI_SUCCESS Source interrupt supported.
+ @retval EFI_UNSUPPORTED Source interrupt is not supported.
+**/
+STATIC
+EFI_STATUS
+EFIAPI
+GicV2SetTriggerType (
+ IN EFI_HARDWARE_INTERRUPT2_PROTOCOL *This,
+ IN HARDWARE_INTERRUPT_SOURCE Source,
+ IN EFI_HARDWARE_INTERRUPT2_TRIGGER_TYPE TriggerType
+ )
+{
+ UINTN RegAddress;
+ UINTN Config1Bit;
+ UINT32 Value;
+ EFI_STATUS Status;
+ BOOLEAN SourceEnabled;
+
+ if ( (TriggerType != EFI_HARDWARE_INTERRUPT2_TRIGGER_EDGE_RISING)
+ && (TriggerType != EFI_HARDWARE_INTERRUPT2_TRIGGER_LEVEL_HIGH)) {
+ DEBUG ((DEBUG_ERROR, "Invalid interrupt trigger type: %d\n", \
+ TriggerType));
+ ASSERT (FALSE);
+ return EFI_UNSUPPORTED;
+ }
+
+ Status = GicGetDistributorIcfgBaseAndBit (
+ Source,
+ &RegAddress,
+ &Config1Bit
+ );
+
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ Status = GicV2GetInterruptSourceState (
+ (EFI_HARDWARE_INTERRUPT_PROTOCOL*)This,
+ Source,
+ &SourceEnabled
+ );
+
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ Value = (TriggerType == EFI_HARDWARE_INTERRUPT2_TRIGGER_EDGE_RISING)
+ ? ARM_GIC_ICDICFR_EDGE_TRIGGERED
+ : ARM_GIC_ICDICFR_LEVEL_TRIGGERED;
+
+ // Before changing the value, we must disable the interrupt,
+ // otherwise GIC behavior is UNPREDICTABLE.
+ if (SourceEnabled) {
+ GicV2DisableInterruptSource (
+ (EFI_HARDWARE_INTERRUPT_PROTOCOL*)This,
+ Source
+ );
+ }
+
+ MmioAndThenOr32 (
+ RegAddress,
+ ~(0x1 << Config1Bit),
+ Value << Config1Bit
+ );
+
+ // Restore interrupt state
+ if (SourceEnabled) {
+ GicV2EnableInterruptSource (
+ (EFI_HARDWARE_INTERRUPT_PROTOCOL*)This,
+ Source
+ );
+ }
+
+ return EFI_SUCCESS;
+}
+
+EFI_HARDWARE_INTERRUPT2_PROTOCOL gHardwareInterrupt2V2Protocol = {
+ (HARDWARE_INTERRUPT2_REGISTER)RegisterInterruptSource,
+ (HARDWARE_INTERRUPT2_ENABLE)GicV2EnableInterruptSource,
+ (HARDWARE_INTERRUPT2_DISABLE)GicV2DisableInterruptSource,
+ (HARDWARE_INTERRUPT2_INTERRUPT_STATE)GicV2GetInterruptSourceState,
+ (HARDWARE_INTERRUPT2_END_OF_INTERRUPT)GicV2EndOfInterrupt,
+ GicV2GetTriggerType,
+ GicV2SetTriggerType
+};
+
+/**
Shutdown our hardware
DXE Core will disable interrupts and turn off the timer and disable
@@ -321,6 +457,7 @@ GicV2DxeInitialize ( Status = InstallAndRegisterInterruptService (
&gHardwareInterruptV2Protocol,
+ &gHardwareInterrupt2V2Protocol,
GicV2IrqInterruptHandler,
GicV2ExitBootServicesEvent
);
diff --git a/ArmPkg/Drivers/ArmGic/GicV3/ArmGicV3Dxe.c b/ArmPkg/Drivers/ArmGic/GicV3/ArmGicV3Dxe.c index 50b51ffcf5..01154848f4 100644 --- a/ArmPkg/Drivers/ArmGic/GicV3/ArmGicV3Dxe.c +++ b/ArmPkg/Drivers/ArmGic/GicV3/ArmGicV3Dxe.c @@ -19,6 +19,7 @@ #define ARM_GIC_DEFAULT_PRIORITY 0x80
extern EFI_HARDWARE_INTERRUPT_PROTOCOL gHardwareInterruptV3Protocol;
+extern EFI_HARDWARE_INTERRUPT2_PROTOCOL gHardwareInterrupt2V3Protocol;
STATIC UINTN mGicDistributorBase;
STATIC UINTN mGicRedistributorsBase;
@@ -193,6 +194,140 @@ EFI_HARDWARE_INTERRUPT_PROTOCOL gHardwareInterruptV3Protocol = { };
/**
+ Get interrupt trigger type of an interrupt
+
+ @param This Instance pointer for this protocol
+ @param Source Hardware source of the interrupt.
+ @param TriggerType Returns interrupt trigger type.
+
+ @retval EFI_SUCCESS Source interrupt supported.
+ @retval EFI_UNSUPPORTED Source interrupt is not supported.
+**/
+STATIC
+EFI_STATUS
+EFIAPI
+GicV3GetTriggerType (
+ IN EFI_HARDWARE_INTERRUPT2_PROTOCOL *This,
+ IN HARDWARE_INTERRUPT_SOURCE Source,
+ OUT EFI_HARDWARE_INTERRUPT2_TRIGGER_TYPE *TriggerType
+ )
+{
+ UINTN RegAddress;
+ UINTN Config1Bit;
+ EFI_STATUS Status;
+
+ Status = GicGetDistributorIcfgBaseAndBit (
+ Source,
+ &RegAddress,
+ &Config1Bit
+ );
+
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ if ((MmioRead32 (RegAddress) & (1 << Config1Bit)) == 0) {
+ *TriggerType = EFI_HARDWARE_INTERRUPT2_TRIGGER_LEVEL_HIGH;
+ } else {
+ *TriggerType = EFI_HARDWARE_INTERRUPT2_TRIGGER_EDGE_RISING;
+ }
+
+ return EFI_SUCCESS;
+}
+
+/**
+ Set interrupt trigger type of an interrupt
+
+ @param This Instance pointer for this protocol
+ @param Source Hardware source of the interrupt.
+ @param TriggerType Interrupt trigger type.
+
+ @retval EFI_SUCCESS Source interrupt supported.
+ @retval EFI_UNSUPPORTED Source interrupt is not supported.
+**/
+STATIC
+EFI_STATUS
+EFIAPI
+GicV3SetTriggerType (
+ IN EFI_HARDWARE_INTERRUPT2_PROTOCOL *This,
+ IN HARDWARE_INTERRUPT_SOURCE Source,
+ IN EFI_HARDWARE_INTERRUPT2_TRIGGER_TYPE TriggerType
+ )
+{
+ UINTN RegAddress;
+ UINTN Config1Bit;
+ UINT32 Value;
+ EFI_STATUS Status;
+ BOOLEAN SourceEnabled;
+
+ if ( (TriggerType != EFI_HARDWARE_INTERRUPT2_TRIGGER_EDGE_RISING)
+ && (TriggerType != EFI_HARDWARE_INTERRUPT2_TRIGGER_LEVEL_HIGH)) {
+ DEBUG ((DEBUG_ERROR, "Invalid interrupt trigger type: %d\n", \
+ TriggerType));
+ ASSERT (FALSE);
+ return EFI_UNSUPPORTED;
+ }
+
+ Status = GicGetDistributorIcfgBaseAndBit (
+ Source,
+ &RegAddress,
+ &Config1Bit
+ );
+
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ Status = GicV3GetInterruptSourceState (
+ (EFI_HARDWARE_INTERRUPT_PROTOCOL*)This,
+ Source,
+ &SourceEnabled
+ );
+
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ Value = (TriggerType == EFI_HARDWARE_INTERRUPT2_TRIGGER_EDGE_RISING)
+ ? ARM_GIC_ICDICFR_EDGE_TRIGGERED
+ : ARM_GIC_ICDICFR_LEVEL_TRIGGERED;
+
+ // Before changing the value, we must disable the interrupt,
+ // otherwise GIC behavior is UNPREDICTABLE.
+ if (SourceEnabled) {
+ GicV3DisableInterruptSource (
+ (EFI_HARDWARE_INTERRUPT_PROTOCOL*)This,
+ Source
+ );
+ }
+
+ MmioAndThenOr32 (
+ RegAddress,
+ ~(0x1 << Config1Bit),
+ Value << Config1Bit
+ );
+ // Restore interrupt state
+ if (SourceEnabled) {
+ GicV3EnableInterruptSource (
+ (EFI_HARDWARE_INTERRUPT_PROTOCOL*)This,
+ Source
+ );
+ }
+
+ return EFI_SUCCESS;
+}
+
+EFI_HARDWARE_INTERRUPT2_PROTOCOL gHardwareInterrupt2V3Protocol = {
+ (HARDWARE_INTERRUPT2_REGISTER)RegisterInterruptSource,
+ (HARDWARE_INTERRUPT2_ENABLE)GicV3EnableInterruptSource,
+ (HARDWARE_INTERRUPT2_DISABLE)GicV3DisableInterruptSource,
+ (HARDWARE_INTERRUPT2_INTERRUPT_STATE)GicV3GetInterruptSourceState,
+ (HARDWARE_INTERRUPT2_END_OF_INTERRUPT)GicV3EndOfInterrupt,
+ GicV3GetTriggerType,
+ GicV3SetTriggerType
+};
+
+/**
Shutdown our hardware
DXE Core will disable interrupts and turn off the timer and disable interrupts
@@ -353,6 +488,7 @@ GicV3DxeInitialize ( Status = InstallAndRegisterInterruptService (
&gHardwareInterruptV3Protocol,
+ &gHardwareInterrupt2V3Protocol,
GicV3IrqInterruptHandler,
GicV3ExitBootServicesEvent
);
|