mirror of https://github.com/acidanthera/audk.git
778 lines
24 KiB
C
778 lines
24 KiB
C
/** @file
|
|
Arm Gic cpu parser.
|
|
|
|
Copyright (c) 2021, 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
|
|
**/
|
|
|
|
#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,cortex-a15" },
|
|
{ "arm,cortex-a7" },
|
|
{ "arm,cortex-a57" }
|
|
};
|
|
|
|
/** COMPATIBILITY_INFO structure for CPU nodes.
|
|
*/
|
|
STATIC CONST COMPATIBILITY_INFO CpuCompatibleInfo = {
|
|
ARRAY_SIZE (CpuCompatibleStr),
|
|
CpuCompatibleStr
|
|
};
|
|
|
|
/** 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;
|
|
}
|
|
|
|
/** 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; // {default = 0}
|
|
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;
|
|
|
|
The pmu information can be found in the pmu node. There is no support
|
|
for now.
|
|
|
|
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;
|
|
}
|
|
|
|
// 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;
|
|
}
|