| /** @file | |
| * | |
| * Copyright (c) 2011-2023, Arm Limited. All rights reserved. | |
| * | |
| * SPDX-License-Identifier: BSD-2-Clause-Patent | |
| * | |
| **/ | |
| #include <Base.h> | |
| #include <Library/ArmGicLib.h> | |
| #include <Library/ArmLib.h> | |
| #include <Library/DebugLib.h> | |
| #include <Library/IoLib.h> | |
| #include <Library/PcdLib.h> | |
| // In GICv3, there are 2 x 64KB frames: | |
| // Redistributor control frame + SGI Control & Generation frame | |
| #define GIC_V3_REDISTRIBUTOR_GRANULARITY (ARM_GICR_CTLR_FRAME_SIZE \ | |
| + ARM_GICR_SGI_PPI_FRAME_SIZE) | |
| // In GICv4, there are 2 additional 64KB frames: | |
| // VLPI frame + Reserved page frame | |
| #define GIC_V4_REDISTRIBUTOR_GRANULARITY (GIC_V3_REDISTRIBUTOR_GRANULARITY \ | |
| + ARM_GICR_SGI_VLPI_FRAME_SIZE \ | |
| + ARM_GICR_SGI_RESERVED_FRAME_SIZE) | |
| #define ISENABLER_ADDRESS(base, offset) ((base) +\ | |
| ARM_GICR_CTLR_FRAME_SIZE + ARM_GICR_ISENABLER + 4 * (offset)) | |
| #define ICENABLER_ADDRESS(base, offset) ((base) +\ | |
| ARM_GICR_CTLR_FRAME_SIZE + ARM_GICR_ICENABLER + 4 * (offset)) | |
| #define IPRIORITY_ADDRESS(base, offset) ((base) +\ | |
| ARM_GICR_CTLR_FRAME_SIZE + ARM_GIC_ICDIPR + 4 * (offset)) | |
| /** | |
| * | |
| * Return whether the Source interrupt index refers to a shared interrupt (SPI) | |
| */ | |
| STATIC | |
| BOOLEAN | |
| SourceIsSpi ( | |
| IN UINTN Source | |
| ) | |
| { | |
| return Source >= 32 && Source < 1020; | |
| } | |
| /** | |
| * Return the base address of the GIC redistributor for the current CPU | |
| * | |
| * @param Revision GIC Revision. The GIC redistributor might have a different | |
| * granularity following the GIC revision. | |
| * | |
| * @retval Base address of the associated GIC Redistributor | |
| */ | |
| STATIC | |
| UINTN | |
| GicGetCpuRedistributorBase ( | |
| IN UINTN GicRedistributorBase, | |
| IN ARM_GIC_ARCH_REVISION Revision | |
| ) | |
| { | |
| UINTN MpId; | |
| UINTN CpuAffinity; | |
| UINTN Affinity; | |
| UINTN GicCpuRedistributorBase; | |
| UINT64 TypeRegister; | |
| MpId = ArmReadMpidr (); | |
| // Define CPU affinity as: | |
| // Affinity0[0:8], Affinity1[9:15], Affinity2[16:23], Affinity3[24:32] | |
| // whereas Affinity3 is defined at [32:39] in MPIDR | |
| CpuAffinity = (MpId & (ARM_CORE_AFF0 | ARM_CORE_AFF1 | ARM_CORE_AFF2)) | | |
| ((MpId & ARM_CORE_AFF3) >> 8); | |
| if (Revision < ARM_GIC_ARCH_REVISION_3) { | |
| ASSERT_EFI_ERROR (EFI_UNSUPPORTED); | |
| return 0; | |
| } | |
| GicCpuRedistributorBase = GicRedistributorBase; | |
| do { | |
| TypeRegister = MmioRead64 (GicCpuRedistributorBase + ARM_GICR_TYPER); | |
| Affinity = ARM_GICR_TYPER_GET_AFFINITY (TypeRegister); | |
| if (Affinity == CpuAffinity) { | |
| return GicCpuRedistributorBase; | |
| } | |
| // Move to the next GIC Redistributor frame. | |
| // The GIC specification does not forbid a mixture of redistributors | |
| // with or without support for virtual LPIs, so we test Virtual LPIs | |
| // Support (VLPIS) bit for each frame to decide the granularity. | |
| // Note: The assumption here is that the redistributors are adjacent | |
| // for all CPUs. However this may not be the case for NUMA systems. | |
| GicCpuRedistributorBase += (((ARM_GICR_TYPER_VLPIS & TypeRegister) != 0) | |
| ? GIC_V4_REDISTRIBUTOR_GRANULARITY | |
| : GIC_V3_REDISTRIBUTOR_GRANULARITY); | |
| } while ((TypeRegister & ARM_GICR_TYPER_LAST) == 0); | |
| // The Redistributor has not been found for the current CPU | |
| ASSERT_EFI_ERROR (EFI_NOT_FOUND); | |
| return 0; | |
| } | |
| /** | |
| 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 | |
| ArmGicSendSgiTo ( | |
| IN UINTN GicDistributorBase, | |
| IN UINT8 TargetListFilter, | |
| IN UINT8 CPUTargetList, | |
| IN UINT8 SgiId | |
| ) | |
| { | |
| MmioWrite32 ( | |
| GicDistributorBase + ARM_GIC_ICDSGIR, | |
| ((TargetListFilter & 0x3) << 24) | | |
| ((CPUTargetList & 0xFF) << 16) | | |
| (SgiId & 0xF) | |
| ); | |
| } | |
| /* | |
| * Acknowledge and return the value of the Interrupt Acknowledge Register | |
| * | |
| * InterruptId is returned separately from the register value because in | |
| * the GICv2 the register value contains the CpuId and InterruptId while | |
| * in the GICv3 the register value is only the InterruptId. | |
| * | |
| * @param GicInterruptInterfaceBase Base Address of the GIC CPU Interface | |
| * @param InterruptId InterruptId read from the Interrupt | |
| * Acknowledge Register | |
| * | |
| * @retval value returned by the Interrupt Acknowledge Register | |
| * | |
| */ | |
| UINTN | |
| EFIAPI | |
| ArmGicAcknowledgeInterrupt ( | |
| IN UINTN GicInterruptInterfaceBase, | |
| OUT UINTN *InterruptId | |
| ) | |
| { | |
| UINTN Value; | |
| UINTN IntId; | |
| ARM_GIC_ARCH_REVISION Revision; | |
| ASSERT (InterruptId != NULL); | |
| Revision = ArmGicGetSupportedArchRevision (); | |
| if (Revision == ARM_GIC_ARCH_REVISION_2) { | |
| Value = ArmGicV2AcknowledgeInterrupt (GicInterruptInterfaceBase); | |
| IntId = Value & ARM_GIC_ICCIAR_ACKINTID; | |
| } else if (Revision == ARM_GIC_ARCH_REVISION_3) { | |
| Value = ArmGicV3AcknowledgeInterrupt (); | |
| IntId = Value; | |
| } else { | |
| ASSERT_EFI_ERROR (EFI_UNSUPPORTED); | |
| // Report Spurious interrupt which is what the above controllers would | |
| // return if no interrupt was available | |
| Value = 1023; | |
| } | |
| if (InterruptId != NULL) { | |
| // InterruptId is required for the caller to know if a valid or spurious | |
| // interrupt has been read | |
| *InterruptId = IntId; | |
| } | |
| return Value; | |
| } | |
| VOID | |
| EFIAPI | |
| ArmGicEndOfInterrupt ( | |
| IN UINTN GicInterruptInterfaceBase, | |
| IN UINTN Source | |
| ) | |
| { | |
| ARM_GIC_ARCH_REVISION Revision; | |
| Revision = ArmGicGetSupportedArchRevision (); | |
| if (Revision == ARM_GIC_ARCH_REVISION_2) { | |
| ArmGicV2EndOfInterrupt (GicInterruptInterfaceBase, Source); | |
| } else if (Revision == ARM_GIC_ARCH_REVISION_3) { | |
| ArmGicV3EndOfInterrupt (Source); | |
| } else { | |
| ASSERT_EFI_ERROR (EFI_UNSUPPORTED); | |
| } | |
| } | |
| VOID | |
| EFIAPI | |
| ArmGicSetInterruptPriority ( | |
| IN UINTN GicDistributorBase, | |
| IN UINTN GicRedistributorBase, | |
| IN UINTN Source, | |
| IN UINTN Priority | |
| ) | |
| { | |
| UINT32 RegOffset; | |
| UINT8 RegShift; | |
| ARM_GIC_ARCH_REVISION Revision; | |
| UINTN GicCpuRedistributorBase; | |
| // Calculate register offset and bit position | |
| RegOffset = (UINT32)(Source / 4); | |
| RegShift = (UINT8)((Source % 4) * 8); | |
| Revision = ArmGicGetSupportedArchRevision (); | |
| if ((Revision == ARM_GIC_ARCH_REVISION_2) || | |
| FeaturePcdGet (PcdArmGicV3WithV2Legacy) || | |
| SourceIsSpi (Source)) | |
| { | |
| MmioAndThenOr32 ( | |
| GicDistributorBase + ARM_GIC_ICDIPR + (4 * RegOffset), | |
| ~(0xff << RegShift), | |
| Priority << RegShift | |
| ); | |
| } else { | |
| GicCpuRedistributorBase = GicGetCpuRedistributorBase ( | |
| GicRedistributorBase, | |
| Revision | |
| ); | |
| if (GicCpuRedistributorBase == 0) { | |
| return; | |
| } | |
| MmioAndThenOr32 ( | |
| IPRIORITY_ADDRESS (GicCpuRedistributorBase, RegOffset), | |
| ~(0xff << RegShift), | |
| Priority << RegShift | |
| ); | |
| } | |
| } | |
| VOID | |
| EFIAPI | |
| ArmGicEnableInterrupt ( | |
| IN UINTN GicDistributorBase, | |
| IN UINTN GicRedistributorBase, | |
| IN UINTN Source | |
| ) | |
| { | |
| UINT32 RegOffset; | |
| UINT8 RegShift; | |
| ARM_GIC_ARCH_REVISION Revision; | |
| UINTN GicCpuRedistributorBase; | |
| // Calculate enable register offset and bit position | |
| RegOffset = (UINT32)(Source / 32); | |
| RegShift = (UINT8)(Source % 32); | |
| Revision = ArmGicGetSupportedArchRevision (); | |
| if ((Revision == ARM_GIC_ARCH_REVISION_2) || | |
| FeaturePcdGet (PcdArmGicV3WithV2Legacy) || | |
| SourceIsSpi (Source)) | |
| { | |
| // Write set-enable register | |
| MmioWrite32 ( | |
| GicDistributorBase + ARM_GIC_ICDISER + (4 * RegOffset), | |
| 1 << RegShift | |
| ); | |
| } else { | |
| GicCpuRedistributorBase = GicGetCpuRedistributorBase ( | |
| GicRedistributorBase, | |
| Revision | |
| ); | |
| if (GicCpuRedistributorBase == 0) { | |
| ASSERT_EFI_ERROR (EFI_NOT_FOUND); | |
| return; | |
| } | |
| // Write set-enable register | |
| MmioWrite32 ( | |
| ISENABLER_ADDRESS (GicCpuRedistributorBase, RegOffset), | |
| 1 << RegShift | |
| ); | |
| } | |
| } | |
| VOID | |
| EFIAPI | |
| ArmGicDisableInterrupt ( | |
| IN UINTN GicDistributorBase, | |
| IN UINTN GicRedistributorBase, | |
| IN UINTN Source | |
| ) | |
| { | |
| UINT32 RegOffset; | |
| UINT8 RegShift; | |
| ARM_GIC_ARCH_REVISION Revision; | |
| UINTN GicCpuRedistributorBase; | |
| // Calculate enable register offset and bit position | |
| RegOffset = (UINT32)(Source / 32); | |
| RegShift = (UINT8)(Source % 32); | |
| Revision = ArmGicGetSupportedArchRevision (); | |
| if ((Revision == ARM_GIC_ARCH_REVISION_2) || | |
| FeaturePcdGet (PcdArmGicV3WithV2Legacy) || | |
| SourceIsSpi (Source)) | |
| { | |
| // Write clear-enable register | |
| MmioWrite32 ( | |
| GicDistributorBase + ARM_GIC_ICDICER + (4 * RegOffset), | |
| 1 << RegShift | |
| ); | |
| } else { | |
| GicCpuRedistributorBase = GicGetCpuRedistributorBase ( | |
| GicRedistributorBase, | |
| Revision | |
| ); | |
| if (GicCpuRedistributorBase == 0) { | |
| return; | |
| } | |
| // Write clear-enable register | |
| MmioWrite32 ( | |
| ICENABLER_ADDRESS (GicCpuRedistributorBase, RegOffset), | |
| 1 << RegShift | |
| ); | |
| } | |
| } | |
| BOOLEAN | |
| EFIAPI | |
| ArmGicIsInterruptEnabled ( | |
| IN UINTN GicDistributorBase, | |
| IN UINTN GicRedistributorBase, | |
| IN UINTN Source | |
| ) | |
| { | |
| UINT32 RegOffset; | |
| UINT8 RegShift; | |
| ARM_GIC_ARCH_REVISION Revision; | |
| UINTN GicCpuRedistributorBase; | |
| UINT32 Interrupts; | |
| // Calculate enable register offset and bit position | |
| RegOffset = (UINT32)(Source / 32); | |
| RegShift = (UINT8)(Source % 32); | |
| Revision = ArmGicGetSupportedArchRevision (); | |
| if ((Revision == ARM_GIC_ARCH_REVISION_2) || | |
| FeaturePcdGet (PcdArmGicV3WithV2Legacy) || | |
| SourceIsSpi (Source)) | |
| { | |
| Interrupts = MmioRead32 ( | |
| GicDistributorBase + ARM_GIC_ICDISER + (4 * RegOffset) | |
| ); | |
| } else { | |
| GicCpuRedistributorBase = GicGetCpuRedistributorBase ( | |
| GicRedistributorBase, | |
| Revision | |
| ); | |
| if (GicCpuRedistributorBase == 0) { | |
| return 0; | |
| } | |
| // Read set-enable register | |
| Interrupts = MmioRead32 ( | |
| ISENABLER_ADDRESS (GicCpuRedistributorBase, RegOffset) | |
| ); | |
| } | |
| return ((Interrupts & (1 << RegShift)) != 0); | |
| } | |
| VOID | |
| EFIAPI | |
| ArmGicDisableDistributor ( | |
| IN UINTN GicDistributorBase | |
| ) | |
| { | |
| // Disable Gic Distributor | |
| MmioWrite32 (GicDistributorBase + ARM_GIC_ICDDCR, 0x0); | |
| } | |
| VOID | |
| EFIAPI | |
| ArmGicEnableInterruptInterface ( | |
| IN UINTN GicInterruptInterfaceBase | |
| ) | |
| { | |
| ARM_GIC_ARCH_REVISION Revision; | |
| Revision = ArmGicGetSupportedArchRevision (); | |
| if (Revision == ARM_GIC_ARCH_REVISION_2) { | |
| ArmGicV2EnableInterruptInterface (GicInterruptInterfaceBase); | |
| } else if (Revision == ARM_GIC_ARCH_REVISION_3) { | |
| ArmGicV3EnableInterruptInterface (); | |
| } else { | |
| ASSERT_EFI_ERROR (EFI_UNSUPPORTED); | |
| } | |
| } | |
| VOID | |
| EFIAPI | |
| ArmGicDisableInterruptInterface ( | |
| IN UINTN GicInterruptInterfaceBase | |
| ) | |
| { | |
| ARM_GIC_ARCH_REVISION Revision; | |
| Revision = ArmGicGetSupportedArchRevision (); | |
| if (Revision == ARM_GIC_ARCH_REVISION_2) { | |
| ArmGicV2DisableInterruptInterface (GicInterruptInterfaceBase); | |
| } else if (Revision == ARM_GIC_ARCH_REVISION_3) { | |
| ArmGicV3DisableInterruptInterface (); | |
| } else { | |
| ASSERT_EFI_ERROR (EFI_UNSUPPORTED); | |
| } | |
| } |