blob: ee82f7a0b4615ffaec50df123d54867f287bab34 [file] [log] [blame]
/** @file
Arm Gic cpu parser.
Copyright (c) 2021 - 2022, Arm Limited. All rights reserved.<BR>
SPDX-License-Identifier: BSD-2-Clause-Patent
@par Reference(s):
- linux/Documentation/devicetree/bindings/arm/cpus.yaml
- linux/Documentation/devicetree/bindings/interrupt-controller/arm,gic.yaml
- linux/Documentation/devicetree/bindings/interrupt-controller/arm,gic-v3.yaml
- linux/Documentation/devicetree/bindings/arm/pmu.yaml
**/
#include "FdtHwInfoParser.h"
#include "CmObjectDescUtility.h"
#include "Gic/ArmGicCParser.h"
#include "Gic/ArmGicDispatcher.h"
/** List of "compatible" property values for CPU nodes.
Any other "compatible" value is not supported by this module.
*/
STATIC CONST COMPATIBILITY_STR CpuCompatibleStr[] = {
{ "arm,arm-v7" },
{ "arm,arm-v8" },
{ "arm,armv8" },
{ "arm,cortex-a15" },
{ "arm,cortex-a7" },
{ "arm,cortex-a57" }
};
/** COMPATIBILITY_INFO structure for CPU nodes.
*/
STATIC CONST COMPATIBILITY_INFO CpuCompatibleInfo = {
ARRAY_SIZE (CpuCompatibleStr),
CpuCompatibleStr
};
/** Pmu compatible strings.
Any other "compatible" value is not supported by this module.
*/
STATIC CONST COMPATIBILITY_STR PmuCompatibleStr[] = {
{ "arm,armv8-pmuv3" }
};
/** COMPATIBILITY_INFO structure for the PmuCompatibleStr.
*/
CONST COMPATIBILITY_INFO PmuCompatibleInfo = {
ARRAY_SIZE (PmuCompatibleStr),
PmuCompatibleStr
};
/** Parse a "cpu" node.
@param [in] Fdt Pointer to a Flattened Device Tree (Fdt).
@param [in] CpuNode Offset of a cpu node.
@param [in] GicVersion Version of the GIC.
@param [in] AddressCells Number of address cells used for the reg
property.
@param [out] GicCInfo CM_ARM_GICC_INFO structure to populate.
@retval EFI_SUCCESS The function completed successfully.
@retval EFI_ABORTED An error occurred.
@retval EFI_INVALID_PARAMETER Invalid parameter.
@retval EFI_UNSUPPORTED Unsupported.
**/
STATIC
EFI_STATUS
EFIAPI
CpuNodeParser (
IN CONST VOID *Fdt,
IN INT32 CpuNode,
IN UINT32 GicVersion,
IN UINT32 AddressCells,
OUT CM_ARM_GICC_INFO *GicCInfo
)
{
CONST UINT8 *Data;
INT32 DataSize;
UINT32 ProcUid;
UINT64 MpIdr;
UINT64 CheckAffMask;
MpIdr = 0;
CheckAffMask = ARM_CORE_AFF0 | ARM_CORE_AFF1 | ARM_CORE_AFF2;
if (GicCInfo == NULL) {
ASSERT (0);
return EFI_INVALID_PARAMETER;
}
Data = fdt_getprop (Fdt, CpuNode, "reg", &DataSize);
if ((Data == NULL) ||
((DataSize != sizeof (UINT32)) &&
(DataSize != sizeof (UINT64))))
{
ASSERT (0);
return EFI_ABORTED;
}
/* If cpus node's #address-cells property is set to 2
The first reg cell bits [7:0] must be set to
bits [39:32] of MPIDR_EL1.
The second reg cell bits [23:0] must be set to
bits [23:0] of MPIDR_EL1.
*/
if (AddressCells == 2) {
MpIdr = fdt64_to_cpu (*((UINT64 *)Data));
CheckAffMask |= ARM_CORE_AFF3;
} else {
MpIdr = fdt32_to_cpu (*((UINT32 *)Data));
}
if ((MpIdr & ~CheckAffMask) != 0) {
ASSERT (0);
return EFI_INVALID_PARAMETER;
}
// To fit the Affinity [0-3] a 32bits value, place the Aff3 on bits
// [31:24] instead of their original place ([39:32]).
ProcUid = MpIdr | ((MpIdr & ARM_CORE_AFF3) >> 8);
/* ACPI 6.3, s5.2.12.14 GIC CPU Interface (GICC) Structure:
GIC 's CPU Interface Number. In GICv1/v2 implementations,
this value matches the bit index of the associated processor
in the GIC distributor's GICD_ITARGETSR register. For
GICv3/4 implementations this field must be provided by the
platform, if compatibility mode is supported. If it is not supported
by the implementation, then this field must be zero.
Note: We do not support compatibility mode for GicV3
*/
if (GicVersion == 2) {
GicCInfo->CPUInterfaceNumber = ProcUid;
} else {
GicCInfo->CPUInterfaceNumber = 0;
}
GicCInfo->AcpiProcessorUid = ProcUid;
GicCInfo->Flags = EFI_ACPI_6_3_GIC_ENABLED;
GicCInfo->MPIDR = MpIdr;
return EFI_SUCCESS;
}
/** Parse a "cpus" node and its children "cpu" nodes.
Create as many CM_ARM_GICC_INFO structures as "cpu" nodes.
@param [in] Fdt Pointer to a Flattened Device Tree (Fdt).
@param [in] CpusNode Offset of a cpus node.
@param [in] GicVersion Version of the GIC.
@param [out] NewGicCmObjDesc If success, CM_OBJ_DESCRIPTOR containing
all the created CM_ARM_GICC_INFO.
@retval EFI_SUCCESS The function completed successfully.
@retval EFI_ABORTED An error occurred.
@retval EFI_INVALID_PARAMETER Invalid parameter.
@retval EFI_UNSUPPORTED Unsupported.
**/
STATIC
EFI_STATUS
EFIAPI
CpusNodeParser (
IN CONST VOID *Fdt,
IN INT32 CpusNode,
IN UINT32 GicVersion,
OUT CM_OBJ_DESCRIPTOR **NewGicCmObjDesc
)
{
EFI_STATUS Status;
INT32 CpuNode;
UINT32 CpuNodeCount;
INT32 AddressCells;
UINT32 Index;
CM_ARM_GICC_INFO *GicCInfoBuffer;
UINT32 GicCInfoBufferSize;
if (NewGicCmObjDesc == NULL) {
ASSERT (0);
return EFI_INVALID_PARAMETER;
}
AddressCells = fdt_address_cells (Fdt, CpusNode);
if (AddressCells < 0) {
ASSERT (0);
return EFI_ABORTED;
}
// Count the number of "cpu" nodes under the "cpus" node.
Status = FdtCountNamedNodeInBranch (Fdt, CpusNode, "cpu", &CpuNodeCount);
if (EFI_ERROR (Status)) {
ASSERT (0);
return Status;
}
if (CpuNodeCount == 0) {
ASSERT (0);
return EFI_NOT_FOUND;
}
// Allocate memory for CpuNodeCount CM_ARM_GICC_INFO structures.
GicCInfoBufferSize = CpuNodeCount * sizeof (CM_ARM_GICC_INFO);
GicCInfoBuffer = AllocateZeroPool (GicCInfoBufferSize);
if (GicCInfoBuffer == NULL) {
ASSERT (0);
return EFI_OUT_OF_RESOURCES;
}
CpuNode = CpusNode;
for (Index = 0; Index < CpuNodeCount; Index++) {
Status = FdtGetNextNamedNodeInBranch (Fdt, CpusNode, "cpu", &CpuNode);
if (EFI_ERROR (Status)) {
ASSERT (0);
if (Status == EFI_NOT_FOUND) {
// Should have found the node.
Status = EFI_ABORTED;
}
goto exit_handler;
}
// Parse the "cpu" node.
if (!FdtNodeIsCompatible (Fdt, CpuNode, &CpuCompatibleInfo)) {
ASSERT (0);
Status = EFI_UNSUPPORTED;
goto exit_handler;
}
Status = CpuNodeParser (
Fdt,
CpuNode,
GicVersion,
AddressCells,
&GicCInfoBuffer[Index]
);
if (EFI_ERROR (Status)) {
ASSERT (0);
goto exit_handler;
}
} // for
Status = CreateCmObjDesc (
CREATE_CM_ARM_OBJECT_ID (EArmObjGicCInfo),
CpuNodeCount,
GicCInfoBuffer,
GicCInfoBufferSize,
NewGicCmObjDesc
);
ASSERT_EFI_ERROR (Status);
exit_handler:
FreePool (GicCInfoBuffer);
return Status;
}
/** Parse a Gic compatible interrupt-controller node,
extracting GicC information generic to Gic v2 and v3.
This function modifies a CM_OBJ_DESCRIPTOR object.
The following CM_ARM_GICC_INFO fields are patched:
- VGICMaintenanceInterrupt;
- Flags;
@param [in] Fdt Pointer to a Flattened Device Tree (Fdt).
@param [in] GicIntcNode Offset of a Gic compatible
interrupt-controller node.
@param [in, out] GicCCmObjDesc The CM_ARM_GICC_INFO to patch.
@retval EFI_SUCCESS The function completed successfully.
@retval EFI_ABORTED An error occurred.
@retval EFI_INVALID_PARAMETER Invalid parameter.
**/
STATIC
EFI_STATUS
EFIAPI
GicCIntcNodeParser (
IN CONST VOID *Fdt,
IN INT32 GicIntcNode,
IN OUT CM_OBJ_DESCRIPTOR *GicCCmObjDesc
)
{
EFI_STATUS Status;
INT32 IntCells;
CM_ARM_GICC_INFO *GicCInfo;
CONST UINT8 *Data;
INT32 DataSize;
if (GicCCmObjDesc == NULL) {
ASSERT (0);
return EFI_INVALID_PARAMETER;
}
// Get the number of cells used to encode an interrupt.
Status = FdtGetInterruptCellsInfo (Fdt, GicIntcNode, &IntCells);
if (EFI_ERROR (Status)) {
ASSERT (0);
return Status;
}
// Get the GSIV maintenance interrupt.
// According to the DT bindings, this could be the:
// "Interrupt source of the parent interrupt controller on secondary GICs"
// but it is assumed that only one Gic is available.
Data = fdt_getprop (Fdt, GicIntcNode, "interrupts", &DataSize);
if ((Data != NULL) && (DataSize == (IntCells * sizeof (UINT32)))) {
GicCInfo = (CM_ARM_GICC_INFO *)GicCCmObjDesc->Data;
GicCInfo->VGICMaintenanceInterrupt =
FdtGetInterruptId ((CONST UINT32 *)Data);
GicCInfo->Flags = DT_IRQ_IS_EDGE_TRIGGERED (
fdt32_to_cpu (((UINT32 *)Data)[IRQ_FLAGS_OFFSET])
) ?
EFI_ACPI_6_3_VGIC_MAINTENANCE_INTERRUPT_MODE_FLAGS :
0;
return Status;
} else if (DataSize < 0) {
// This property is optional and was not found. Just return.
return Status;
}
// The property exists and its size doesn't match for one interrupt.
ASSERT (0);
return EFI_ABORTED;
}
/** Parse a Gic compatible interrupt-controller node,
extracting GicCv2 information.
This function modifies a CM_OBJ_DESCRIPTOR object.
The following CM_ARM_GICC_INFO fields are patched:
- PhysicalAddress;
- GICH;
- GICV;
@param [in] Fdt Pointer to a Flattened Device Tree (Fdt).
@param [in] Gicv2IntcNode Offset of a Gicv2 compatible
interrupt-controller node.
@param [in, out] GicCCmObjDesc The CM_ARM_GICC_INFO to patch.
@retval EFI_SUCCESS The function completed successfully.
@retval EFI_ABORTED An error occurred.
@retval EFI_INVALID_PARAMETER Invalid parameter.
**/
STATIC
EFI_STATUS
EFIAPI
GicCv2IntcNodeParser (
IN CONST VOID *Fdt,
IN INT32 Gicv2IntcNode,
IN OUT CM_OBJ_DESCRIPTOR *GicCCmObjDesc
)
{
EFI_STATUS Status;
UINT32 Index;
CM_ARM_GICC_INFO *GicCInfo;
INT32 AddressCells;
INT32 SizeCells;
CONST UINT8 *GicCValue;
CONST UINT8 *GicVValue;
CONST UINT8 *GicHValue;
CONST UINT8 *Data;
INT32 DataSize;
UINT32 RegSize;
UINT32 RegCount;
if (GicCCmObjDesc == NULL) {
ASSERT (0);
return EFI_INVALID_PARAMETER;
}
GicCInfo = (CM_ARM_GICC_INFO *)GicCCmObjDesc->Data;
GicVValue = NULL;
GicHValue = NULL;
// Get the #address-cells and #size-cells property values.
Status = FdtGetParentAddressInfo (
Fdt,
Gicv2IntcNode,
&AddressCells,
&SizeCells
);
if (EFI_ERROR (Status)) {
ASSERT (0);
return Status;
}
// Don't support more than 64 bits and less than 32 bits addresses.
if ((AddressCells < 1) ||
(AddressCells > 2) ||
(SizeCells < 1) ||
(SizeCells > 2))
{
ASSERT (0);
return EFI_ABORTED;
}
RegSize = (AddressCells + SizeCells) * sizeof (UINT32);
Data = fdt_getprop (Fdt, Gicv2IntcNode, "reg", &DataSize);
if ((Data == NULL) ||
(DataSize < 0) ||
((DataSize % RegSize) != 0))
{
// If error or wrong size.
ASSERT (0);
return EFI_ABORTED;
}
RegCount = DataSize/RegSize;
switch (RegCount) {
case 4:
{
// GicV is at index 3 in the reg property. GicV is optional.
GicVValue = Data + (sizeof (UINT32) *
GET_DT_REG_ADDRESS_OFFSET (3, AddressCells, SizeCells));
// fall-through.
}
case 3:
{
// GicH is at index 2 in the reg property. GicH is optional.
GicHValue = Data + (sizeof (UINT32) *
GET_DT_REG_ADDRESS_OFFSET (2, AddressCells, SizeCells));
// fall-through.
}
case 2:
{
// GicC is at index 1 in the reg property. GicC is mandatory.
GicCValue = Data + (sizeof (UINT32) *
GET_DT_REG_ADDRESS_OFFSET (1, AddressCells, SizeCells));
break;
}
default:
{
// Not enough or too much information.
ASSERT (0);
return EFI_ABORTED;
}
}
// Patch the relevant fields of the CM_ARM_GICC_INFO objects.
for (Index = 0; Index < GicCCmObjDesc->Count; Index++) {
if (AddressCells == 2) {
GicCInfo[Index].PhysicalBaseAddress = fdt64_to_cpu (*(UINT64 *)GicCValue);
GicCInfo[Index].GICH = (GicHValue == NULL) ? 0 :
fdt64_to_cpu (*(UINT64 *)GicHValue);
GicCInfo[Index].GICV = (GicVValue == NULL) ? 0 :
fdt64_to_cpu (*(UINT64 *)GicVValue);
} else {
GicCInfo[Index].PhysicalBaseAddress = fdt32_to_cpu (*(UINT32 *)GicCValue);
GicCInfo[Index].GICH = (GicHValue == NULL) ? 0 :
fdt32_to_cpu (*(UINT32 *)GicHValue);
GicCInfo[Index].GICV = (GicVValue == NULL) ? 0 :
fdt32_to_cpu (*(UINT32 *)GicVValue);
}
} // for
return EFI_SUCCESS;
}
/** Parse a Gic compatible interrupt-controller node,
extracting GicCv3 information.
This function modifies a CM_OBJ_DESCRIPTOR object.
The following CM_ARM_GICC_INFO fields are patched:
- PhysicalAddress;
- GICH;
- GICV;
@param [in] Fdt Pointer to a Flattened Device Tree (Fdt).
@param [in] Gicv3IntcNode Offset of a Gicv3 compatible
interrupt-controller node.
@param [in, out] GicCCmObjDesc The CM_ARM_GICC_INFO to patch.
@retval EFI_SUCCESS The function completed successfully.
@retval EFI_ABORTED An error occurred.
@retval EFI_INVALID_PARAMETER Invalid parameter.
**/
STATIC
EFI_STATUS
EFIAPI
GicCv3IntcNodeParser (
IN CONST VOID *Fdt,
IN INT32 Gicv3IntcNode,
IN OUT CM_OBJ_DESCRIPTOR *GicCCmObjDesc
)
{
EFI_STATUS Status;
UINT32 Index;
CM_ARM_GICC_INFO *GicCInfo;
INT32 AddressCells;
INT32 SizeCells;
UINT32 AdditionalRedistReg;
CONST UINT8 *GicCValue;
CONST UINT8 *GicVValue;
CONST UINT8 *GicHValue;
CONST UINT8 *Data;
INT32 DataSize;
UINT32 RegSize;
UINT32 RegCount;
if (GicCCmObjDesc == NULL) {
ASSERT (0);
return EFI_INVALID_PARAMETER;
}
GicCInfo = (CM_ARM_GICC_INFO *)GicCCmObjDesc->Data;
GicCValue = NULL;
GicVValue = NULL;
GicHValue = NULL;
// Get the #address-cells and #size-cells property values.
Status = FdtGetParentAddressInfo (
Fdt,
Gicv3IntcNode,
&AddressCells,
&SizeCells
);
if (EFI_ERROR (Status)) {
ASSERT (0);
return Status;
}
// Don't support more than 64 bits and less than 32 bits addresses.
if ((AddressCells < 1) ||
(AddressCells > 2) ||
(SizeCells < 1) ||
(SizeCells > 2))
{
ASSERT (0);
return EFI_ABORTED;
}
// The "#redistributor-regions" property is optional.
Data = fdt_getprop (Fdt, Gicv3IntcNode, "#redistributor-regions", &DataSize);
if ((Data != NULL) && (DataSize == sizeof (UINT32))) {
ASSERT (fdt32_to_cpu (*(UINT32 *)Data) > 1);
AdditionalRedistReg = fdt32_to_cpu (*(UINT32 *)Data) - 1;
} else {
AdditionalRedistReg = 0;
}
RegSize = (AddressCells + SizeCells) * sizeof (UINT32);
/*
Ref: linux/blob/master/Documentation/devicetree/bindings/
interrupt-controller/arm%2Cgic-v3.yaml
reg:
description: |
Specifies base physical address(s) and size of the GIC
registers, in the following order:
- GIC Distributor interface (GICD)
- GIC Redistributors (GICR), one range per redistributor region
- GIC CPU interface (GICC)
- GIC Hypervisor interface (GICH)
- GIC Virtual CPU interface (GICV)
GICC, GICH and GICV are optional.
minItems: 2
maxItems: 4096
*/
Data = fdt_getprop (Fdt, Gicv3IntcNode, "reg", &DataSize);
if ((Data == NULL) ||
(DataSize < 0) ||
((DataSize % RegSize) != 0))
{
// If error or wrong size.
ASSERT (0);
return EFI_ABORTED;
}
RegCount = (DataSize / RegSize) - AdditionalRedistReg;
// The GicD and GicR info is mandatory.
switch (RegCount) {
case 5:
{
// GicV is at index 4 in the reg property. GicV is optional.
GicVValue = Data + (sizeof (UINT32) *
GET_DT_REG_ADDRESS_OFFSET (
4 + AdditionalRedistReg,
AddressCells,
SizeCells
));
// fall-through.
}
case 4:
{
// GicH is at index 3 in the reg property. GicH is optional.
GicHValue = Data + (sizeof (UINT32) *
GET_DT_REG_ADDRESS_OFFSET (
3 + AdditionalRedistReg,
AddressCells,
SizeCells
));
// fall-through.
}
case 3:
{
// GicC is at index 2 in the reg property. GicC is optional.
// Even though GicC is optional, it is made mandatory in this parser.
GicCValue = Data + (sizeof (UINT32) *
GET_DT_REG_ADDRESS_OFFSET (
2 + AdditionalRedistReg,
AddressCells,
SizeCells
));
// fall-through
}
case 2:
{
// GicR is discribed by the CM_ARM_GIC_REDIST_INFO object.
// GicD is described by the CM_ARM_GICD_INFO object.
break;
}
default:
{
// Not enough or too much information.
ASSERT (0);
return EFI_ABORTED;
}
}
// Patch the relevant fields of the CM_ARM_GICC_INFO objects.
if (AddressCells == 2) {
for (Index = 0; Index < GicCCmObjDesc->Count; Index++) {
// GicR is discribed by the CM_ARM_GIC_REDIST_INFO object.
GicCInfo[Index].GICRBaseAddress = 0;
GicCInfo[Index].PhysicalBaseAddress = (GicCValue == NULL) ? 0 :
fdt64_to_cpu (*(UINT64 *)GicCValue);
GicCInfo[Index].GICH = (GicHValue == NULL) ? 0 :
fdt64_to_cpu (*(UINT64 *)GicHValue);
GicCInfo[Index].GICV = (GicVValue == NULL) ? 0 :
fdt64_to_cpu (*(UINT64 *)GicVValue);
}
} else {
for (Index = 0; Index < GicCCmObjDesc->Count; Index++) {
// GicR is discribed by the CM_ARM_GIC_REDIST_INFO object.
GicCInfo[Index].GICRBaseAddress = 0;
GicCInfo[Index].PhysicalBaseAddress = (GicCValue == NULL) ? 0 :
fdt32_to_cpu (*(UINT32 *)GicCValue);
GicCInfo[Index].GICH = (GicHValue == NULL) ? 0 :
fdt32_to_cpu (*(UINT32 *)GicHValue);
GicCInfo[Index].GICV = (GicVValue == NULL) ? 0 :
fdt32_to_cpu (*(UINT32 *)GicVValue);
}
}
return EFI_SUCCESS;
}
/** Parse a Pmu compatible node, extracting Pmu information.
This function modifies a CM_OBJ_DESCRIPTOR object.
The following CM_ARM_GICC_INFO fields are patched:
- PerformanceInterruptGsiv;
@param [in] Fdt Pointer to a Flattened Device Tree (Fdt).
@param [in] GicIntcNode Offset of a Gic compatible
interrupt-controller node.
@param [in, out] GicCCmObjDesc The CM_ARM_GICC_INFO to patch.
@retval EFI_SUCCESS The function completed successfully.
@retval EFI_ABORTED An error occurred.
@retval EFI_INVALID_PARAMETER Invalid parameter.
**/
STATIC
EFI_STATUS
EFIAPI
GicCPmuNodeParser (
IN CONST VOID *Fdt,
IN INT32 GicIntcNode,
IN OUT CM_OBJ_DESCRIPTOR *GicCCmObjDesc
)
{
EFI_STATUS Status;
INT32 IntCells;
INT32 PmuNode;
UINT32 PmuNodeCount;
UINT32 PmuIrq;
UINT32 Index;
CM_ARM_GICC_INFO *GicCInfo;
CONST UINT8 *Data;
INT32 DataSize;
if (GicCCmObjDesc == NULL) {
ASSERT (GicCCmObjDesc != NULL);
return EFI_INVALID_PARAMETER;
}
GicCInfo = (CM_ARM_GICC_INFO *)GicCCmObjDesc->Data;
PmuNode = 0;
// Count the number of pmu nodes.
Status = FdtCountCompatNodeInBranch (
Fdt,
0,
&PmuCompatibleInfo,
&PmuNodeCount
);
if (EFI_ERROR (Status)) {
ASSERT_EFI_ERROR (Status);
return Status;
}
if (PmuNodeCount == 0) {
return EFI_NOT_FOUND;
}
Status = FdtGetNextCompatNodeInBranch (
Fdt,
0,
&PmuCompatibleInfo,
&PmuNode
);
if (EFI_ERROR (Status)) {
ASSERT_EFI_ERROR (Status);
if (Status == EFI_NOT_FOUND) {
// Should have found the node.
Status = EFI_ABORTED;
}
}
// Get the number of cells used to encode an interrupt.
Status = FdtGetInterruptCellsInfo (Fdt, GicIntcNode, &IntCells);
if (EFI_ERROR (Status)) {
ASSERT_EFI_ERROR (Status);
return Status;
}
Data = fdt_getprop (Fdt, PmuNode, "interrupts", &DataSize);
if ((Data == NULL) || (DataSize != (IntCells * sizeof (UINT32)))) {
// If error or not 1 interrupt.
ASSERT (Data != NULL);
ASSERT (DataSize == (IntCells * sizeof (UINT32)));
return EFI_ABORTED;
}
PmuIrq = FdtGetInterruptId ((CONST UINT32 *)Data);
// Only supports PPI 23 for now.
// According to BSA 1.0 s3.6 PPI assignments, PMU IRQ ID is 23. A non BSA
// compliant system may assign a different IRQ for the PMU, however this
// is not implemented for now.
if (PmuIrq != BSA_PMU_IRQ) {
ASSERT (PmuIrq == BSA_PMU_IRQ);
return EFI_ABORTED;
}
for (Index = 0; Index < GicCCmObjDesc->Count; Index++) {
GicCInfo[Index].PerformanceInterruptGsiv = PmuIrq;
}
return EFI_SUCCESS;
}
/** CM_ARM_GICC_INFO parser function.
This parser expects FdtBranch to be the "\cpus" node node.
At most one CmObj is created.
The following structure is populated:
typedef struct CmArmGicCInfo {
UINT32 CPUInterfaceNumber; // {Populated}
UINT32 AcpiProcessorUid; // {Populated}
UINT32 Flags; // {Populated}
UINT32 ParkingProtocolVersion; // {default = 0}
UINT32 PerformanceInterruptGsiv; // {Populated}
UINT64 ParkedAddress; // {default = 0}
UINT64 PhysicalBaseAddress; // {Populated}
UINT64 GICV; // {Populated}
UINT64 GICH; // {Populated}
UINT32 VGICMaintenanceInterrupt; // {Populated}
UINT64 GICRBaseAddress; // {default = 0}
UINT64 MPIDR; // {Populated}
UINT8 ProcessorPowerEfficiencyClass; // {default = 0}
UINT16 SpeOverflowInterrupt; // {default = 0}
UINT32 ProximityDomain; // {default = 0}
UINT32 ClockDomain; // {default = 0}
UINT32 AffinityFlags; // {default = 0}
} CM_ARM_GICC_INFO;
A parser parses a Device Tree to populate a specific CmObj type. None,
one or many CmObj can be created by the parser.
The created CmObj are then handed to the parser's caller through the
HW_INFO_ADD_OBJECT interface.
This can also be a dispatcher. I.e. a function that not parsing a
Device Tree but calling other parsers.
@param [in] FdtParserHandle A handle to the parser instance.
@param [in] FdtBranch When searching for DT node name, restrict
the search to this Device Tree branch.
@retval EFI_SUCCESS The function completed successfully.
@retval EFI_ABORTED An error occurred.
@retval EFI_INVALID_PARAMETER Invalid parameter.
@retval EFI_NOT_FOUND Not found.
@retval EFI_UNSUPPORTED Unsupported.
**/
EFI_STATUS
EFIAPI
ArmGicCInfoParser (
IN CONST FDT_HW_INFO_PARSER_HANDLE FdtParserHandle,
IN INT32 FdtBranch
)
{
EFI_STATUS Status;
INT32 IntcNode;
UINT32 GicVersion;
CM_OBJ_DESCRIPTOR *NewCmObjDesc;
VOID *Fdt;
if (FdtParserHandle == NULL) {
ASSERT (0);
return EFI_INVALID_PARAMETER;
}
Fdt = FdtParserHandle->Fdt;
NewCmObjDesc = NULL;
// The FdtBranch points to the Cpus Node.
// Get the interrupt-controller node associated to the "cpus" node.
Status = FdtGetIntcParentNode (Fdt, FdtBranch, &IntcNode);
if (EFI_ERROR (Status)) {
ASSERT (0);
if (Status == EFI_NOT_FOUND) {
// Should have found the node.
Status = EFI_ABORTED;
}
return Status;
}
Status = GetGicVersion (Fdt, IntcNode, &GicVersion);
if (EFI_ERROR (Status)) {
ASSERT (0);
return Status;
}
// Parse the "cpus" nodes and its children "cpu" nodes,
// and create a CM_OBJ_DESCRIPTOR.
Status = CpusNodeParser (Fdt, FdtBranch, GicVersion, &NewCmObjDesc);
if (EFI_ERROR (Status)) {
ASSERT (0);
return Status;
}
// Parse the interrupt-controller node according to the Gic version.
switch (GicVersion) {
case 2:
{
Status = GicCv2IntcNodeParser (Fdt, IntcNode, NewCmObjDesc);
break;
}
case 3:
{
Status = GicCv3IntcNodeParser (Fdt, IntcNode, NewCmObjDesc);
break;
}
default:
{
// Unsupported Gic version.
ASSERT (0);
Status = EFI_UNSUPPORTED;
}
}
if (EFI_ERROR (Status)) {
ASSERT (0);
goto exit_handler;
}
// Parse the Gic information common to Gic v2 and v3.
Status = GicCIntcNodeParser (Fdt, IntcNode, NewCmObjDesc);
if (EFI_ERROR (Status)) {
ASSERT (0);
goto exit_handler;
}
// Parse the Pmu Interrupt.
Status = GicCPmuNodeParser (Fdt, IntcNode, NewCmObjDesc);
if (EFI_ERROR (Status) && (Status != EFI_NOT_FOUND)) {
ASSERT_EFI_ERROR (Status);
goto exit_handler;
}
// Add all the CmObjs to the Configuration Manager.
Status = AddMultipleCmObj (FdtParserHandle, NewCmObjDesc, 0, NULL);
if (EFI_ERROR (Status)) {
ASSERT (0);
goto exit_handler;
}
exit_handler:
FreeCmObjDesc (NewCmObjDesc);
return Status;
}