The GICv3 driver must use native mode to drive a GICv3 due to the fact that v2 compatibility is optional in the v3 spec. However, if v2 compatibility is implemented, it is the default and needs to be disabled first by setting the Affinity Routing Enable (ARE) bit. Contributed-under: TianoCore Contribution Agreement 1.0 Signed-off-by: Ard Biesheuvel <ard.biesheuvel@linaro.org> Reviewed-by: Olivier Martin <olivier.martin@arm.com> [added PCD that allows forcing the GICv3 driver to drive the GIC in v2 mode] Signed-off-by: Olivier Martin <olivier.martin@arm.com> Tested-by: Ard Biesheuvel <ard@linaro.org> Reviewed-by: Ard Biesheuvel <ard.biesheuvel@linaro.org> git-svn-id: https://svn.code.sf.net/p/edk2/code/trunk/edk2@16875 6f19259b-4bc3-4df7-8a09-765794883524
315 lines
9.3 KiB
C
315 lines
9.3 KiB
C
/** @file
|
|
*
|
|
* Copyright (c) 2011-2015, ARM Limited. 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 <Base.h>
|
|
#include <Library/ArmGicLib.h>
|
|
#include <Library/ArmLib.h>
|
|
#include <Library/DebugLib.h>
|
|
#include <Library/IoLib.h>
|
|
#include <Library/PcdLib.h>
|
|
|
|
#include "GicV2/ArmGicV2Lib.h"
|
|
#include "GicV3/ArmGicV3Lib.h"
|
|
|
|
/**
|
|
* 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 Index;
|
|
UINTN MpId;
|
|
UINTN CpuAffinity;
|
|
UINTN Affinity;
|
|
UINTN GicRedistributorGranularity;
|
|
UINTN GicCpuRedistributorBase;
|
|
|
|
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) {
|
|
// 2 x 64KB frame: Redistributor control frame + SGI Control & Generation frame
|
|
GicRedistributorGranularity = ARM_GICR_CTLR_FRAME_SIZE + ARM_GICR_SGI_PPI_FRAME_SIZE;
|
|
} else {
|
|
ASSERT_EFI_ERROR (EFI_UNSUPPORTED);
|
|
return 0;
|
|
}
|
|
|
|
GicCpuRedistributorBase = GicRedistributorBase;
|
|
|
|
for (Index = 0; Index < PcdGet32 (PcdCoreCount); Index++) {
|
|
Affinity = MmioRead64 (GicCpuRedistributorBase + ARM_GICR_TYPER) >> 32;
|
|
if (Affinity == CpuAffinity) {
|
|
return GicCpuRedistributorBase;
|
|
}
|
|
|
|
// Move to the next GIC Redistributor frame
|
|
GicRedistributorBase += GicRedistributorGranularity;
|
|
}
|
|
|
|
// The Redistributor has not been found for the current CPU
|
|
ASSERT_EFI_ERROR (EFI_NOT_FOUND);
|
|
return 0;
|
|
}
|
|
|
|
UINTN
|
|
EFIAPI
|
|
ArmGicGetInterfaceIdentification (
|
|
IN INTN GicInterruptInterfaceBase
|
|
)
|
|
{
|
|
// Read the GIC Identification Register
|
|
return MmioRead32 (GicInterruptInterfaceBase + ARM_GIC_ICCIIDR);
|
|
}
|
|
|
|
UINTN
|
|
EFIAPI
|
|
ArmGicGetMaxNumInterrupts (
|
|
IN INTN GicDistributorBase
|
|
)
|
|
{
|
|
return 32 * ((MmioRead32 (GicDistributorBase + ARM_GIC_ICDICTR) & 0x1F) + 1);
|
|
}
|
|
|
|
VOID
|
|
EFIAPI
|
|
ArmGicSendSgiTo (
|
|
IN INTN GicDistributorBase,
|
|
IN INTN TargetListFilter,
|
|
IN INTN CPUTargetList,
|
|
IN INTN SgiId
|
|
)
|
|
{
|
|
MmioWrite32 (GicDistributorBase + ARM_GIC_ICDSGIR, ((TargetListFilter & 0x3) << 24) | ((CPUTargetList & 0xFF) << 16) | SgiId);
|
|
}
|
|
|
|
/*
|
|
* 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;
|
|
ARM_GIC_ARCH_REVISION Revision;
|
|
|
|
Revision = ArmGicGetSupportedArchRevision ();
|
|
if (Revision == ARM_GIC_ARCH_REVISION_2) {
|
|
Value = ArmGicV2AcknowledgeInterrupt (GicInterruptInterfaceBase);
|
|
// InterruptId is required for the caller to know if a valid or spurious
|
|
// interrupt has been read
|
|
ASSERT (InterruptId != NULL);
|
|
if (InterruptId != NULL) {
|
|
*InterruptId = Value & ARM_GIC_ICCIAR_ACKINTID;
|
|
}
|
|
} else if (Revision == ARM_GIC_ARCH_REVISION_3) {
|
|
Value = ArmGicV3AcknowledgeInterrupt ();
|
|
} else {
|
|
ASSERT_EFI_ERROR (EFI_UNSUPPORTED);
|
|
// Report Spurious interrupt which is what the above controllers would
|
|
// return if no interrupt was available
|
|
Value = 1023;
|
|
}
|
|
|
|
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
|
|
ArmGicEnableInterrupt (
|
|
IN UINTN GicDistributorBase,
|
|
IN UINTN GicRedistributorBase,
|
|
IN UINTN Source
|
|
)
|
|
{
|
|
UINT32 RegOffset;
|
|
UINTN RegShift;
|
|
ARM_GIC_ARCH_REVISION Revision;
|
|
UINTN GicCpuRedistributorBase;
|
|
|
|
// Calculate enable register offset and bit position
|
|
RegOffset = Source / 32;
|
|
RegShift = Source % 32;
|
|
|
|
Revision = ArmGicGetSupportedArchRevision ();
|
|
if ((Revision == ARM_GIC_ARCH_REVISION_2) || FeaturePcdGet (PcdArmGicV3WithV2Legacy)) {
|
|
// 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 (GicCpuRedistributorBase + ARM_GICR_CTLR_FRAME_SIZE + ARM_GICR_ISENABLER + (4 * RegOffset), 1 << RegShift);
|
|
}
|
|
}
|
|
|
|
VOID
|
|
EFIAPI
|
|
ArmGicDisableInterrupt (
|
|
IN UINTN GicDistributorBase,
|
|
IN UINTN GicRedistributorBase,
|
|
IN UINTN Source
|
|
)
|
|
{
|
|
UINT32 RegOffset;
|
|
UINTN RegShift;
|
|
ARM_GIC_ARCH_REVISION Revision;
|
|
UINTN GicCpuRedistributorBase;
|
|
|
|
// Calculate enable register offset and bit position
|
|
RegOffset = Source / 32;
|
|
RegShift = Source % 32;
|
|
|
|
Revision = ArmGicGetSupportedArchRevision ();
|
|
if ((Revision == ARM_GIC_ARCH_REVISION_2) || FeaturePcdGet (PcdArmGicV3WithV2Legacy)) {
|
|
// 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 (GicCpuRedistributorBase + ARM_GICR_CTLR_FRAME_SIZE + ARM_GICR_ICENABLER + (4 * RegOffset), 1 << RegShift);
|
|
}
|
|
}
|
|
|
|
BOOLEAN
|
|
EFIAPI
|
|
ArmGicIsInterruptEnabled (
|
|
IN UINTN GicDistributorBase,
|
|
IN UINTN GicRedistributorBase,
|
|
IN UINTN Source
|
|
)
|
|
{
|
|
UINT32 RegOffset;
|
|
UINTN RegShift;
|
|
ARM_GIC_ARCH_REVISION Revision;
|
|
UINTN GicCpuRedistributorBase;
|
|
UINT32 Interrupts;
|
|
|
|
// Calculate enable register offset and bit position
|
|
RegOffset = Source / 32;
|
|
RegShift = Source % 32;
|
|
|
|
Revision = ArmGicGetSupportedArchRevision ();
|
|
if ((Revision == ARM_GIC_ARCH_REVISION_2) || FeaturePcdGet (PcdArmGicV3WithV2Legacy)) {
|
|
Interrupts = ((MmioRead32 (GicDistributorBase + ARM_GIC_ICDISER + (4 * RegOffset)) & (1 << RegShift)) != 0);
|
|
} else {
|
|
GicCpuRedistributorBase = GicGetCpuRedistributorBase (GicRedistributorBase, Revision);
|
|
if (GicCpuRedistributorBase == 0) {
|
|
return 0;
|
|
}
|
|
|
|
// Read set-enable register
|
|
Interrupts = MmioRead32 (GicCpuRedistributorBase + ARM_GICR_CTLR_FRAME_SIZE + ARM_GICR_ISENABLER + (4 * RegOffset));
|
|
}
|
|
|
|
return ((Interrupts & (1 << RegShift)) != 0);
|
|
}
|
|
|
|
VOID
|
|
EFIAPI
|
|
ArmGicDisableDistributor (
|
|
IN INTN GicDistributorBase
|
|
)
|
|
{
|
|
// Disable Gic Distributor
|
|
MmioWrite32 (GicDistributorBase + ARM_GIC_ICDDCR, 0x0);
|
|
}
|
|
|
|
VOID
|
|
EFIAPI
|
|
ArmGicEnableInterruptInterface (
|
|
IN INTN 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 INTN 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);
|
|
}
|
|
}
|