Files
system76-edk2/DynamicTablesPkg/Library/FdtHwInfoParserLib/Pci/ArmPciConfigSpaceParser.c
Pierre Gondois c67bf628c8 DynamicTablesPkg: FdtHwInfoParser: Add PCI config parser
On platforms that implement PCIe, the PCIe configuration space
information must be described to a standards-based operating
system in the Memory mapped configuration space base address
Description (MCFG) table.

The PCIe information is described in the platform Device Tree,
the bindings for which can be found at:
- linux/Documentation/devicetree/bindings/pci/
  host-generic-pci.yaml

The FdtHwInfoParser implements a PCI configuration space Parser
that parses the platform Device Tree to create
CM_ARM_PCI_CONFIG_SPACE_INFO objects which are encapsulated in a
Configuration Manager descriptor object and added to the platform
information repository.

The platform Configuration Manager can then utilise this
information when generating the MCFG table.

Signed-off-by: Pierre Gondois <Pierre.Gondois@arm.com>
Reviewed-by: Sami Mujawar <sami.mujawar@arm.com>
2021-12-14 16:07:00 +00:00

814 lines
24 KiB
C

/** @file
Arm PCI Configuration Space Parser.
Copyright (c) 2021, ARM Limited. All rights reserved.<BR>
SPDX-License-Identifier: BSD-2-Clause-Patent
@par Reference(s):
- linux/Documentation/devicetree/bindings/pci/host-generic-pci.yaml
- PCI Firmware Specification - Revision 3.0
- Open Firmware Recommended Practice: Interrupt Mapping, Version 0.9
- Devicetree Specification Release v0.3
- linux kernel code
**/
#include "CmObjectDescUtility.h"
#include <Library/DebugLib.h>
#include "FdtHwInfoParser.h"
#include "Pci/ArmPciConfigSpaceParser.h"
#include "Gic/ArmGicDispatcher.h"
/** List of "compatible" property values for host PCIe bridges nodes.
Any other "compatible" value is not supported by this module.
*/
STATIC CONST COMPATIBILITY_STR PciCompatibleStr[] = {
{ "pci-host-ecam-generic" }
};
/** COMPATIBILITY_INFO structure for the PCIe.
*/
STATIC CONST COMPATIBILITY_INFO PciCompatibleInfo = {
ARRAY_SIZE (PciCompatibleStr),
PciCompatibleStr
};
/** Get the Segment group (also called: Domain Id) of a host-pci node.
kernel/Documentation/devicetree/bindings/pci/pci.txt:
"It is required to either not set this property at all or set it for all
host bridges in the system"
The function checks the "linux,pci-domain" property of the host-pci node.
Either all host-pci nodes must have this property, or none of them. If the
property is available, read it. Otherwise dynamically assign the Ids.
@param [in] Fdt Pointer to a Flattened Device Tree (Fdt).
@param [in] HostPciNode Offset of a host-pci node.
@param [out] SegGroup Segment group assigned to the host-pci controller.
@retval EFI_SUCCESS The function completed successfully.
@retval EFI_ABORTED An error occurred.
@retval EFI_INVALID_PARAMETER Invalid parameter.
**/
STATIC
EFI_STATUS
EFIAPI
GetPciSegGroup (
IN CONST VOID *Fdt,
IN INT32 HostPciNode,
OUT INT32 *SegGroup
)
{
CONST UINT8 *Data;
INT32 DataSize;
STATIC INT32 LocalSegGroup = 0;
if ((Fdt == NULL) ||
(SegGroup == NULL))
{
ASSERT (0);
return EFI_INVALID_PARAMETER;
}
Data = fdt_getprop (Fdt, HostPciNode, "linux,pci-domain", &DataSize);
if ((Data == NULL) || (DataSize < 0)) {
// Did not find property, assign the DomainIds ourselves.
if (LocalSegGroup < 0) {
// "linux,pci-domain" property was defined for another node.
ASSERT (0);
return EFI_ABORTED;
}
*SegGroup = LocalSegGroup++;
return EFI_SUCCESS;
}
if ((DataSize > sizeof (UINT32)) ||
(LocalSegGroup > 0))
{
// Property on more than 1 cell or
// "linux,pci-domain" property was not defined for a node.
ASSERT (0);
return EFI_ABORTED;
}
// If one node has the "linux,pci-domain" property, then all the host-pci
// nodes must have it.
LocalSegGroup = -1;
*SegGroup = fdt32_to_cpu (*(UINT32 *)Data);
return EFI_SUCCESS;
}
/** Parse the bus-range controlled by this host-pci node.
@param [in] Fdt Pointer to a Flattened Device Tree (Fdt).
@param [in] HostPciNode Offset of a host-pci node.
@param [in, out] PciInfo PCI_PARSER_TABLE structure storing
information about the current host-pci.
@retval EFI_SUCCESS The function completed successfully.
@retval EFI_ABORTED An error occurred.
@retval EFI_INVALID_PARAMETER Invalid parameter.
**/
STATIC
EFI_STATUS
EFIAPI
PopulateBusRange (
IN CONST VOID *Fdt,
IN INT32 HostPciNode,
IN OUT PCI_PARSER_TABLE *PciInfo
)
{
CONST UINT8 *Data;
INT32 DataSize;
UINT32 StartBus;
UINT32 EndBus;
if ((Fdt == NULL) ||
(PciInfo == NULL))
{
ASSERT (0);
return EFI_INVALID_PARAMETER;
}
Data = fdt_getprop (Fdt, HostPciNode, "bus-range", &DataSize);
if ((Data == NULL) || (DataSize < 0)) {
// No evidence this property is mandatory. Use default values.
StartBus = 0;
EndBus = 255;
} else if (DataSize == (2 * sizeof (UINT32))) {
// If available, the property is on two integers.
StartBus = fdt32_to_cpu (((UINT32 *)Data)[0]);
EndBus = fdt32_to_cpu (((UINT32 *)Data)[1]);
} else {
ASSERT (0);
return EFI_ABORTED;
}
PciInfo->PciConfigSpaceInfo.StartBusNumber = StartBus;
PciInfo->PciConfigSpaceInfo.EndBusNumber = EndBus;
return EFI_SUCCESS;
}
/** Parse the PCI address map.
The PCI address map is available in the "ranges" device-tree property.
@param [in] Fdt Pointer to a Flattened Device Tree (Fdt).
@param [in] HostPciNode Offset of a host-pci node.
@param [in] AddressCells # of cells used to encode an address on
the parent bus.
@param [in, out] PciInfo PCI_PARSER_TABLE structure storing
information about the current host-pci.
@retval EFI_SUCCESS The function completed successfully.
@retval EFI_ABORTED An error occurred.
@retval EFI_INVALID_PARAMETER Invalid parameter.
@retval EFI_OUT_OF_RESOURCES An allocation has failed.
**/
STATIC
EFI_STATUS
EFIAPI
ParseAddressMap (
IN CONST VOID *Fdt,
IN INT32 HostPciNode,
IN INT32 AddressCells,
IN OUT PCI_PARSER_TABLE *PciInfo
)
{
CONST UINT8 *Data;
INT32 DataSize;
UINT32 Index;
UINT32 Offset;
UINT32 AddressMapSize;
UINT32 Count;
UINT32 PciAddressAttr;
CM_ARM_PCI_ADDRESS_MAP_INFO *PciAddressMapInfo;
UINT32 BufferSize;
// The mapping is done on AddressMapSize bytes.
AddressMapSize = (PCI_ADDRESS_CELLS + AddressCells + PCI_SIZE_CELLS) *
sizeof (UINT32);
Data = fdt_getprop (Fdt, HostPciNode, "ranges", &DataSize);
if ((Data == NULL) ||
(DataSize < 0) ||
((DataSize % AddressMapSize) != 0))
{
// If error or not on AddressMapSize bytes.
ASSERT (0);
return EFI_ABORTED;
}
Count = DataSize / AddressMapSize;
// Allocate a buffer to store each address mapping.
BufferSize = Count * sizeof (CM_ARM_PCI_ADDRESS_MAP_INFO);
PciAddressMapInfo = AllocateZeroPool (BufferSize);
if (PciAddressMapInfo == NULL) {
ASSERT (0);
return EFI_OUT_OF_RESOURCES;
}
for (Index = 0; Index < Count; Index++) {
Offset = Index * AddressMapSize;
// Pci address attributes
PciAddressAttr = fdt32_to_cpu (*(UINT32 *)&Data[Offset]);
PciAddressMapInfo[Index].SpaceCode = READ_PCI_SS (PciAddressAttr);
Offset += sizeof (UINT32);
// Pci address
PciAddressMapInfo[Index].PciAddress =
fdt64_to_cpu (*(UINT64 *)&Data[Offset]);
Offset += (PCI_ADDRESS_CELLS - 1) * sizeof (UINT32);
// Cpu address
if (AddressCells == 2) {
PciAddressMapInfo[Index].CpuAddress =
fdt64_to_cpu (*(UINT64 *)&Data[Offset]);
} else {
PciAddressMapInfo[Index].CpuAddress =
fdt32_to_cpu (*(UINT32 *)&Data[Offset]);
}
Offset += AddressCells * sizeof (UINT32);
// Address size
PciAddressMapInfo[Index].AddressSize =
fdt64_to_cpu (*(UINT64 *)&Data[Offset]);
Offset += PCI_SIZE_CELLS * sizeof (UINT32);
} // for
PciInfo->Mapping[PciMappingTableAddress].ObjectId =
CREATE_CM_ARM_OBJECT_ID (EArmObjPciAddressMapInfo);
PciInfo->Mapping[PciMappingTableAddress].Size =
sizeof (CM_ARM_PCI_ADDRESS_MAP_INFO) * Count;
PciInfo->Mapping[PciMappingTableAddress].Data = PciAddressMapInfo;
PciInfo->Mapping[PciMappingTableAddress].Count = Count;
return EFI_SUCCESS;
}
/** Parse the PCI interrupt map.
The PCI interrupt map is available in the "interrupt-map"
and "interrupt-map-mask" device-tree properties.
Cf Devicetree Specification Release v0.3,
s2.4.3 Interrupt Nexus Properties
An interrupt-map must be as:
interrupt-map = < [child unit address] [child interrupt specifier]
[interrupt-parent]
[parent unit address] [parent interrupt specifier] >
@param [in] Fdt Pointer to a Flattened Device Tree (Fdt).
@param [in] HostPciNode Offset of a host-pci node.
@param [in, out] PciInfo PCI_PARSER_TABLE structure storing
information about the current host-pci.
@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_OUT_OF_RESOURCES An allocation has failed.
**/
STATIC
EFI_STATUS
EFIAPI
ParseIrqMap (
IN CONST VOID *Fdt,
IN INT32 HostPciNode,
IN OUT PCI_PARSER_TABLE *PciInfo
)
{
EFI_STATUS Status;
CONST UINT8 *Data;
INT32 DataSize;
UINT32 Index;
UINT32 Offset;
INT32 IntcNode;
INT32 IntcAddressCells;
INT32 IntcCells;
INT32 PciIntCells;
INT32 IntcPhandle;
INT32 IrqMapSize;
UINT32 IrqMapCount;
CONST UINT8 *IrqMapMask;
INT32 IrqMapMaskSize;
INT32 PHandleOffset;
UINT32 GicVersion;
UINT32 PciAddressAttr;
CM_ARM_PCI_INTERRUPT_MAP_INFO *PciInterruptMapInfo;
UINT32 BufferSize;
Data = fdt_getprop (Fdt, HostPciNode, "interrupt-map", &DataSize);
if ((Data == NULL) || (DataSize <= 0)) {
DEBUG ((
DEBUG_WARN,
"Fdt parser: No Legacy interrupts found for PCI configuration space at "
"address: 0x%lx, group segment: %d\n",
PciInfo->PciConfigSpaceInfo.BaseAddress,
PciInfo->PciConfigSpaceInfo.PciSegmentGroupNumber
));
return EFI_NOT_FOUND;
}
// PCI interrupts are expected to be on 1 cell. Check it.
Status = FdtGetInterruptCellsInfo (Fdt, HostPciNode, &PciIntCells);
if (EFI_ERROR (Status)) {
ASSERT (0);
return Status;
}
if (PciIntCells != PCI_INTERRUPTS_CELLS) {
ASSERT (0);
return EFI_ABORTED;
}
IrqMapMask = fdt_getprop (
Fdt,
HostPciNode,
"interrupt-map-mask",
&IrqMapMaskSize
);
if ((IrqMapMask == NULL) ||
(IrqMapMaskSize !=
(PCI_ADDRESS_CELLS + PCI_INTERRUPTS_CELLS) * sizeof (UINT32)))
{
ASSERT (0);
return EFI_ABORTED;
}
// Get the interrupt-controller of the first irq mapping.
PHandleOffset = (PCI_ADDRESS_CELLS + PciIntCells) * sizeof (UINT32);
if (PHandleOffset > DataSize) {
ASSERT (0);
return EFI_ABORTED;
}
IntcPhandle = fdt32_to_cpu (*(UINT32 *)&Data[PHandleOffset]);
IntcNode = fdt_node_offset_by_phandle (Fdt, IntcPhandle);
if (IntcNode < 0) {
ASSERT (0);
return EFI_ABORTED;
}
// Only support Gic(s) for now.
Status = GetGicVersion (Fdt, IntcNode, &GicVersion);
if (EFI_ERROR (Status)) {
ASSERT (0);
return Status;
}
// Get the "address-cells" property of the IntcNode.
Status = FdtGetAddressInfo (Fdt, IntcNode, &IntcAddressCells, NULL);
if (EFI_ERROR (Status)) {
ASSERT (0);
return Status;
}
// Get the "interrupt-cells" property of the IntcNode.
Status = FdtGetInterruptCellsInfo (Fdt, IntcNode, &IntcCells);
if (EFI_ERROR (Status)) {
ASSERT (0);
return Status;
}
// An irq mapping is done on IrqMapSize bytes
// (which includes 1 cell for the PHandle).
IrqMapSize = (PCI_ADDRESS_CELLS + PciIntCells + 1
+ IntcAddressCells + IntcCells) * sizeof (UINT32);
if ((DataSize % IrqMapSize) != 0) {
// The mapping is not done on IrqMapSize bytes.
ASSERT (0);
return EFI_ABORTED;
}
IrqMapCount = DataSize / IrqMapSize;
// We assume the same interrupt-controller is used for all the mappings.
// Check this is correct.
for (Index = 0; Index < IrqMapCount; Index++) {
if (IntcPhandle != fdt32_to_cpu (
*(UINT32 *)&Data[(Index * IrqMapSize) + PHandleOffset]
))
{
ASSERT (0);
return EFI_ABORTED;
}
}
// Allocate a buffer to store each interrupt mapping.
IrqMapCount = DataSize / IrqMapSize;
BufferSize = IrqMapCount * sizeof (CM_ARM_PCI_ADDRESS_MAP_INFO);
PciInterruptMapInfo = AllocateZeroPool (BufferSize);
if (PciInterruptMapInfo == NULL) {
ASSERT (0);
return EFI_OUT_OF_RESOURCES;
}
for (Index = 0; Index < IrqMapCount; Index++) {
Offset = Index * IrqMapSize;
// Pci address attributes
PciAddressAttr = fdt32_to_cpu (
(*(UINT32 *)&Data[Offset]) &
(*(UINT32 *)&IrqMapMask[0])
);
PciInterruptMapInfo[Index].PciBus = READ_PCI_BBBBBBBB (PciAddressAttr);
PciInterruptMapInfo[Index].PciDevice = READ_PCI_DDDDD (PciAddressAttr);
Offset += PCI_ADDRESS_CELLS * sizeof (UINT32);
// Pci irq
PciInterruptMapInfo[Index].PciInterrupt = fdt32_to_cpu (
(*(UINT32 *)&Data[Offset]) &
(*(UINT32 *)&IrqMapMask[3 * sizeof (UINT32)])
);
// -1 to translate from device-tree (INTA=1) to ACPI (INTA=0) irq IDs.
PciInterruptMapInfo[Index].PciInterrupt -= 1;
Offset += PCI_INTERRUPTS_CELLS * sizeof (UINT32);
// PHandle (skip it)
Offset += sizeof (UINT32);
// "Parent unit address" (skip it)
Offset += IntcAddressCells * sizeof (UINT32);
// Interrupt controller interrupt and flags
PciInterruptMapInfo[Index].IntcInterrupt.Interrupt =
FdtGetInterruptId ((UINT32 *)&Data[Offset]);
PciInterruptMapInfo[Index].IntcInterrupt.Flags =
FdtGetInterruptFlags ((UINT32 *)&Data[Offset]);
} // for
PciInfo->Mapping[PciMappingTableInterrupt].ObjectId =
CREATE_CM_ARM_OBJECT_ID (EArmObjPciInterruptMapInfo);
PciInfo->Mapping[PciMappingTableInterrupt].Size =
sizeof (CM_ARM_PCI_INTERRUPT_MAP_INFO) * IrqMapCount;
PciInfo->Mapping[PciMappingTableInterrupt].Data = PciInterruptMapInfo;
PciInfo->Mapping[PciMappingTableInterrupt].Count = IrqMapCount;
return Status;
}
/** Parse a Host-pci node.
@param [in] Fdt Pointer to a Flattened Device Tree (Fdt).
@param [in] HostPciNode Offset of a host-pci node.
@param [in, out] PciInfo The CM_ARM_PCI_CONFIG_SPACE_INFO to populate.
@retval EFI_SUCCESS The function completed successfully.
@retval EFI_ABORTED An error occurred.
@retval EFI_INVALID_PARAMETER Invalid parameter.
@retval EFI_OUT_OF_RESOURCES An allocation has failed.
**/
STATIC
EFI_STATUS
EFIAPI
PciNodeParser (
IN CONST VOID *Fdt,
IN INT32 HostPciNode,
IN OUT PCI_PARSER_TABLE *PciInfo
)
{
EFI_STATUS Status;
INT32 AddressCells;
INT32 SizeCells;
CONST UINT8 *Data;
INT32 DataSize;
INT32 SegGroup;
if ((Fdt == NULL) ||
(PciInfo == NULL))
{
ASSERT (0);
return EFI_INVALID_PARAMETER;
}
// Segment Group / DomainId
Status = GetPciSegGroup (Fdt, HostPciNode, &SegGroup);
if (EFI_ERROR (Status)) {
ASSERT (0);
return Status;
}
PciInfo->PciConfigSpaceInfo.PciSegmentGroupNumber = SegGroup;
// Bus range
Status = PopulateBusRange (Fdt, HostPciNode, PciInfo);
if (EFI_ERROR (Status)) {
ASSERT (0);
return Status;
}
Status = FdtGetParentAddressInfo (
Fdt,
HostPciNode,
&AddressCells,
&SizeCells
);
if (EFI_ERROR (Status)) {
ASSERT (0);
return Status;
}
// Only support 32/64 bits addresses.
if ((AddressCells < 1) ||
(AddressCells > 2) ||
(SizeCells < 1) ||
(SizeCells > 2))
{
ASSERT (0);
return EFI_ABORTED;
}
Data = fdt_getprop (Fdt, HostPciNode, "reg", &DataSize);
if ((Data == NULL) ||
(DataSize != ((AddressCells + SizeCells) * sizeof (UINT32))))
{
// If error or wrong size.
ASSERT (0);
return EFI_ABORTED;
}
// Base address
if (AddressCells == 2) {
PciInfo->PciConfigSpaceInfo.BaseAddress = fdt64_to_cpu (*(UINT64 *)Data);
} else {
PciInfo->PciConfigSpaceInfo.BaseAddress = fdt32_to_cpu (*(UINT32 *)Data);
}
// Address map
Status = ParseAddressMap (
Fdt,
HostPciNode,
AddressCells,
PciInfo
);
if (EFI_ERROR (Status)) {
ASSERT (0);
return Status;
}
// Irq map
Status = ParseIrqMap (
Fdt,
HostPciNode,
PciInfo
);
if (EFI_ERROR (Status) && (Status != EFI_NOT_FOUND)) {
ASSERT (0);
}
return EFI_SUCCESS;
}
/** Add the parsed Pci information to the Configuration Manager.
CmObj of the following types are concerned:
- EArmObjPciConfigSpaceInfo
- EArmObjPciAddressMapInfo
- EArmObjPciInterruptMapInfo
@param [in] FdtParserHandle A handle to the parser instance.
@param [in] PciTableInfo PCI_PARSER_TABLE structure containing the
CmObjs to add.
@retval EFI_SUCCESS The function completed successfully.
@retval EFI_INVALID_PARAMETER Invalid parameter.
@retval EFI_OUT_OF_RESOURCES An allocation has failed.
**/
STATIC
EFI_STATUS
EFIAPI
PciInfoAdd (
IN CONST FDT_HW_INFO_PARSER_HANDLE FdtParserHandle,
IN PCI_PARSER_TABLE *PciTableInfo
)
{
EFI_STATUS Status;
CM_ARM_PCI_CONFIG_SPACE_INFO *PciConfigSpaceInfo;
if ((FdtParserHandle == NULL) ||
(PciTableInfo == NULL))
{
ASSERT (0);
return EFI_INVALID_PARAMETER;
}
PciConfigSpaceInfo = &PciTableInfo->PciConfigSpaceInfo;
// Add the address map space CmObj to the Configuration Manager.
Status = AddMultipleCmObjWithCmObjRef (
FdtParserHandle,
&PciTableInfo->Mapping[PciMappingTableAddress],
&PciConfigSpaceInfo->AddressMapToken
);
if (EFI_ERROR (Status)) {
ASSERT (0);
return Status;
}
// Add the interrupt map space CmObj to the Configuration Manager.
// Possible to have no legacy interrupts, or no device described and
// thus no interrupt-mapping.
if (PciTableInfo->Mapping[PciMappingTableInterrupt].Count != 0) {
Status = AddMultipleCmObjWithCmObjRef (
FdtParserHandle,
&PciTableInfo->Mapping[PciMappingTableInterrupt],
&PciConfigSpaceInfo->InterruptMapToken
);
if (EFI_ERROR (Status)) {
ASSERT (0);
return Status;
}
}
// Add the configuration space CmObj to the Configuration Manager.
Status = AddSingleCmObj (
FdtParserHandle,
CREATE_CM_ARM_OBJECT_ID (EArmObjPciConfigSpaceInfo),
&PciTableInfo->PciConfigSpaceInfo,
sizeof (CM_ARM_PCI_CONFIG_SPACE_INFO),
NULL
);
ASSERT_EFI_ERROR (Status);
return Status;
}
/** Free the CmObjDesc of the ParserTable.
@param [in] PciTableInfo PCI_PARSER_TABLE structure containing the
CmObjs to free.
@retval EFI_SUCCESS The function completed successfully.
@retval EFI_INVALID_PARAMETER Invalid parameter.
**/
STATIC
EFI_STATUS
EFIAPI
FreeParserTable (
IN PCI_PARSER_TABLE *PciTableInfo
)
{
UINT32 Index;
VOID *Data;
if (PciTableInfo == NULL) {
ASSERT (0);
return EFI_INVALID_PARAMETER;
}
for (Index = 0; Index < PciMappingTableMax; Index++) {
Data = PciTableInfo->Mapping[Index].Data;
if (Data != NULL) {
FreePool (Data);
}
}
return EFI_SUCCESS;
}
/** CM_ARM_PCI_CONFIG_SPACE_INFO parser function.
The following structure is populated:
typedef struct CmArmPciConfigSpaceInfo {
UINT64 BaseAddress; // {Populated}
UINT16 PciSegmentGroupNumber; // {Populated}
UINT8 StartBusNumber; // {Populated}
UINT8 EndBusNumber; // {Populated}
} CM_ARM_PCI_CONFIG_SPACE_INFO;
typedef struct CmArmPciAddressMapInfo {
UINT8 SpaceCode; // {Populated}
UINT64 PciAddress; // {Populated}
UINT64 CpuAddress; // {Populated}
UINT64 AddressSize; // {Populated}
} CM_ARM_PCI_ADDRESS_MAP_INFO;
typedef struct CmArmPciInterruptMapInfo {
UINT8 PciBus; // {Populated}
UINT8 PciDevice; // {Populated}
UINT8 PciInterrupt; // {Populated}
CM_ARM_GENERIC_INTERRUPT IntcInterrupt; // {Populated}
} CM_ARM_PCI_INTERRUPT_MAP_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
ArmPciConfigInfoParser (
IN CONST FDT_HW_INFO_PARSER_HANDLE FdtParserHandle,
IN INT32 FdtBranch
)
{
EFI_STATUS Status;
UINT32 Index;
INT32 PciNode;
UINT32 PciNodeCount;
PCI_PARSER_TABLE PciTableInfo;
VOID *Fdt;
if (FdtParserHandle == NULL) {
ASSERT (0);
return EFI_INVALID_PARAMETER;
}
Fdt = FdtParserHandle->Fdt;
// Only search host-pci devices.
// PCI Firmware Specification Revision 3.0, s4.1.2. "MCFG Table Description":
// "This table directly refers to PCI Segment Groups defined in the system
// via the _SEG object in the ACPI name space for the applicable host bridge
// device."
Status = FdtCountCompatNodeInBranch (
Fdt,
FdtBranch,
&PciCompatibleInfo,
&PciNodeCount
);
if (EFI_ERROR (Status)) {
ASSERT (0);
return Status;
}
if (PciNodeCount == 0) {
return EFI_NOT_FOUND;
}
// Parse each host-pci node in the branch.
PciNode = FdtBranch;
for (Index = 0; Index < PciNodeCount; Index++) {
ZeroMem (&PciTableInfo, sizeof (PCI_PARSER_TABLE));
Status = FdtGetNextCompatNodeInBranch (
Fdt,
FdtBranch,
&PciCompatibleInfo,
&PciNode
);
if (EFI_ERROR (Status)) {
ASSERT (0);
if (Status == EFI_NOT_FOUND) {
// Should have found the node.
Status = EFI_ABORTED;
}
return Status;
}
Status = PciNodeParser (Fdt, PciNode, &PciTableInfo);
if (EFI_ERROR (Status)) {
ASSERT (0);
goto error_handler;
}
// Add Pci information to the Configuration Manager.
Status = PciInfoAdd (FdtParserHandle, &PciTableInfo);
if (EFI_ERROR (Status)) {
ASSERT (0);
goto error_handler;
}
Status = FreeParserTable (&PciTableInfo);
if (EFI_ERROR (Status)) {
ASSERT (0);
return Status;
}
} // for
return Status;
error_handler:
FreeParserTable (&PciTableInfo);
return Status;
}