Files
system76-edk2/ArmPkg/Drivers/ArmGic/GicV3/ArmGicV3Dxe.c
Ard Biesheuvel 8659306a2e 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>
2017-10-05 21:23:39 +01:00

498 lines
14 KiB
C

/** @file
*
* Copyright (c) 2011-2017, 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 <Library/ArmGicLib.h>
#include "ArmGicDxe.h"
#define ARM_GIC_DEFAULT_PRIORITY 0x80
extern EFI_HARDWARE_INTERRUPT_PROTOCOL gHardwareInterruptV3Protocol;
extern EFI_HARDWARE_INTERRUPT2_PROTOCOL gHardwareInterrupt2V3Protocol;
STATIC UINTN mGicDistributorBase;
STATIC UINTN mGicRedistributorsBase;
/**
Enable interrupt source Source.
@param This Instance pointer for this protocol
@param Source Hardware source of the interrupt
@retval EFI_SUCCESS Source interrupt enabled.
@retval EFI_DEVICE_ERROR Hardware could not be programmed.
**/
STATIC
EFI_STATUS
EFIAPI
GicV3EnableInterruptSource (
IN EFI_HARDWARE_INTERRUPT_PROTOCOL *This,
IN HARDWARE_INTERRUPT_SOURCE Source
)
{
if (Source >= mGicNumInterrupts) {
ASSERT(FALSE);
return EFI_UNSUPPORTED;
}
ArmGicEnableInterrupt (mGicDistributorBase, mGicRedistributorsBase, Source);
return EFI_SUCCESS;
}
/**
Disable interrupt source Source.
@param This Instance pointer for this protocol
@param Source Hardware source of the interrupt
@retval EFI_SUCCESS Source interrupt disabled.
@retval EFI_DEVICE_ERROR Hardware could not be programmed.
**/
STATIC
EFI_STATUS
EFIAPI
GicV3DisableInterruptSource (
IN EFI_HARDWARE_INTERRUPT_PROTOCOL *This,
IN HARDWARE_INTERRUPT_SOURCE Source
)
{
if (Source >= mGicNumInterrupts) {
ASSERT(FALSE);
return EFI_UNSUPPORTED;
}
ArmGicDisableInterrupt (mGicDistributorBase, mGicRedistributorsBase, Source);
return EFI_SUCCESS;
}
/**
Return current state of interrupt source Source.
@param This Instance pointer for this protocol
@param Source Hardware source of the interrupt
@param InterruptState TRUE: source enabled, FALSE: source disabled.
@retval EFI_SUCCESS InterruptState is valid
@retval EFI_DEVICE_ERROR InterruptState is not valid
**/
STATIC
EFI_STATUS
EFIAPI
GicV3GetInterruptSourceState (
IN EFI_HARDWARE_INTERRUPT_PROTOCOL *This,
IN HARDWARE_INTERRUPT_SOURCE Source,
IN BOOLEAN *InterruptState
)
{
if (Source >= mGicNumInterrupts) {
ASSERT(FALSE);
return EFI_UNSUPPORTED;
}
*InterruptState = ArmGicIsInterruptEnabled (
mGicDistributorBase,
mGicRedistributorsBase,
Source
);
return EFI_SUCCESS;
}
/**
Signal to the hardware that the End Of Interrupt state
has been reached.
@param This Instance pointer for this protocol
@param Source Hardware source of the interrupt
@retval EFI_SUCCESS Source interrupt EOI'ed.
@retval EFI_DEVICE_ERROR Hardware could not be programmed.
**/
STATIC
EFI_STATUS
EFIAPI
GicV3EndOfInterrupt (
IN EFI_HARDWARE_INTERRUPT_PROTOCOL *This,
IN HARDWARE_INTERRUPT_SOURCE Source
)
{
if (Source >= mGicNumInterrupts) {
ASSERT(FALSE);
return EFI_UNSUPPORTED;
}
ArmGicV3EndOfInterrupt (Source);
return EFI_SUCCESS;
}
/**
EFI_CPU_INTERRUPT_HANDLER that is called when a processor interrupt occurs.
@param InterruptType Defines the type of interrupt or exception that
occurred on the processor. This parameter is
processor architecture specific.
@param SystemContext A pointer to the processor context when
the interrupt occurred on the processor.
@return None
**/
STATIC
VOID
EFIAPI
GicV3IrqInterruptHandler (
IN EFI_EXCEPTION_TYPE InterruptType,
IN EFI_SYSTEM_CONTEXT SystemContext
)
{
UINT32 GicInterrupt;
HARDWARE_INTERRUPT_HANDLER InterruptHandler;
GicInterrupt = ArmGicV3AcknowledgeInterrupt ();
// Special Interrupts (ID1020-ID1023) have an Interrupt ID greater than the
// number of interrupt (ie: Spurious interrupt).
if ((GicInterrupt & ARM_GIC_ICCIAR_ACKINTID) >= mGicNumInterrupts) {
// The special interrupt do not need to be acknowledge
return;
}
InterruptHandler = gRegisteredInterruptHandlers[GicInterrupt];
if (InterruptHandler != NULL) {
// Call the registered interrupt handler.
InterruptHandler (GicInterrupt, SystemContext);
} else {
DEBUG ((DEBUG_ERROR, "Spurious GIC interrupt: 0x%x\n", GicInterrupt));
GicV3EndOfInterrupt (&gHardwareInterruptV3Protocol, GicInterrupt);
}
}
// The protocol instance produced by this driver
EFI_HARDWARE_INTERRUPT_PROTOCOL gHardwareInterruptV3Protocol = {
RegisterInterruptSource,
GicV3EnableInterruptSource,
GicV3DisableInterruptSource,
GicV3GetInterruptSourceState,
GicV3EndOfInterrupt
};
/**
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
after all the event handlers have run.
@param[in] Event The Event that is being processed
@param[in] Context Event Context
**/
VOID
EFIAPI
GicV3ExitBootServicesEvent (
IN EFI_EVENT Event,
IN VOID *Context
)
{
UINTN Index;
// Acknowledge all pending interrupts
for (Index = 0; Index < mGicNumInterrupts; Index++) {
GicV3DisableInterruptSource (&gHardwareInterruptV3Protocol, Index);
}
for (Index = 0; Index < mGicNumInterrupts; Index++) {
GicV3EndOfInterrupt (&gHardwareInterruptV3Protocol, Index);
}
// Disable Gic Interface
ArmGicV3DisableInterruptInterface ();
// Disable Gic Distributor
ArmGicDisableDistributor (mGicDistributorBase);
}
/**
Initialize the state information for the CPU Architectural Protocol
@param ImageHandle of the loaded driver
@param SystemTable Pointer to the System Table
@retval EFI_SUCCESS Protocol registered
@retval EFI_OUT_OF_RESOURCES Cannot allocate protocol data structure
@retval EFI_DEVICE_ERROR Hardware problems
**/
EFI_STATUS
GicV3DxeInitialize (
IN EFI_HANDLE ImageHandle,
IN EFI_SYSTEM_TABLE *SystemTable
)
{
EFI_STATUS Status;
UINTN Index;
UINT32 RegOffset;
UINTN RegShift;
UINT64 CpuTarget;
UINT64 MpId;
// Make sure the Interrupt Controller Protocol is not already installed in
// the system.
ASSERT_PROTOCOL_ALREADY_INSTALLED (NULL, &gHardwareInterruptProtocolGuid);
mGicDistributorBase = PcdGet64 (PcdGicDistributorBase);
mGicRedistributorsBase = PcdGet64 (PcdGicRedistributorsBase);
mGicNumInterrupts = ArmGicGetMaxNumInterrupts (mGicDistributorBase);
// We will be driving this GIC in native v3 mode, i.e., with Affinity
// Routing enabled. So ensure that the ARE bit is set.
if (!FeaturePcdGet (PcdArmGicV3WithV2Legacy)) {
MmioOr32 (mGicDistributorBase + ARM_GIC_ICDDCR, ARM_GIC_ICDDCR_ARE);
}
for (Index = 0; Index < mGicNumInterrupts; Index++) {
GicV3DisableInterruptSource (&gHardwareInterruptV3Protocol, Index);
// Set Priority
RegOffset = Index / 4;
RegShift = (Index % 4) * 8;
MmioAndThenOr32 (
mGicDistributorBase + ARM_GIC_ICDIPR + (4 * RegOffset),
~(0xff << RegShift),
ARM_GIC_DEFAULT_PRIORITY << RegShift
);
}
// Targets the interrupts to the Primary Cpu
if (FeaturePcdGet (PcdArmGicV3WithV2Legacy)) {
// Only Primary CPU will run this code. We can identify our GIC CPU ID by
// reading the GIC Distributor Target register. The 8 first
// GICD_ITARGETSRn are banked to each connected CPU. These 8 registers
// hold the CPU targets fields for interrupts 0-31. More Info in the GIC
// Specification about "Interrupt Processor Targets Registers"
// Read the first Interrupt Processor Targets Register (that corresponds
// to the 4 first SGIs)
CpuTarget = MmioRead32 (mGicDistributorBase + ARM_GIC_ICDIPTR);
// The CPU target is a bit field mapping each CPU to a GIC CPU Interface.
// This value is 0 when we run on a uniprocessor platform.
if (CpuTarget != 0) {
// The 8 first Interrupt Processor Targets Registers are read-only
for (Index = 8; Index < (mGicNumInterrupts / 4); Index++) {
MmioWrite32 (
mGicDistributorBase + ARM_GIC_ICDIPTR + (Index * 4),
CpuTarget
);
}
}
} else {
MpId = ArmReadMpidr ();
CpuTarget = MpId &
(ARM_CORE_AFF0 | ARM_CORE_AFF1 | ARM_CORE_AFF2 | ARM_CORE_AFF3);
if ((MmioRead32 (
mGicDistributorBase + ARM_GIC_ICDDCR
) & ARM_GIC_ICDDCR_DS) != 0) {
// If the Disable Security (DS) control bit is set, we are dealing with a
// GIC that has only one security state. In this case, let's assume we are
// executing in non-secure state (which is appropriate for DXE modules)
// and that no other firmware has performed any configuration on the GIC.
// This means we need to reconfigure all interrupts to non-secure Group 1
// first.
MmioWrite32 (
mGicRedistributorsBase + ARM_GICR_CTLR_FRAME_SIZE + ARM_GIC_ICDISR,
0xffffffff
);
for (Index = 32; Index < mGicNumInterrupts; Index += 32) {
MmioWrite32 (
mGicDistributorBase + ARM_GIC_ICDISR + Index / 8,
0xffffffff
);
}
}
// Route the SPIs to the primary CPU. SPIs start at the INTID 32
for (Index = 0; Index < (mGicNumInterrupts - 32); Index++) {
MmioWrite32 (
mGicDistributorBase + ARM_GICD_IROUTER + (Index * 8),
CpuTarget | ARM_GICD_IROUTER_IRM
);
}
}
// Set binary point reg to 0x7 (no preemption)
ArmGicV3SetBinaryPointer (0x7);
// Set priority mask reg to 0xff to allow all priorities through
ArmGicV3SetPriorityMask (0xff);
// Enable gic cpu interface
ArmGicV3EnableInterruptInterface ();
// Enable gic distributor
ArmGicEnableDistributor (mGicDistributorBase);
Status = InstallAndRegisterInterruptService (
&gHardwareInterruptV3Protocol,
&gHardwareInterrupt2V3Protocol,
GicV3IrqInterruptHandler,
GicV3ExitBootServicesEvent
);
return Status;
}