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