audk/ArmPkg/Drivers/ArmGic/ArmGicLib.c

331 lines
9.5 KiB
C
Raw Normal View History

/** @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>
/**
*
* 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 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
GicCpuRedistributorBase += 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) ||
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 (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) ||
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 (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) ||
SourceIsSpi (Source)) {
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);
}
}